diff --git a/.gitignore b/.gitignore index 883d63fd..d3ee04ac 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,6 @@ libexample.json test *.log -*.xml \ No newline at end of file +*.xml + +.xmake \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..0e379f7c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,7 @@ +fail_fast: false +repos: + - repo: https://github.com/pocc/pre-commit-hooks + rev: master + hooks: + - id: clang-format + - id: cppcheck \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a57e6dd..f1305852 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ # Author: Max Qian # License: GPL3 -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.20) project(Lithium) enable_language(C CXX) @@ -87,6 +87,7 @@ endif(APPLE) # Lithium includes include_directories(${CMAKE_SOURCE_DIR}/libs/) +include_directories(${CMAKE_SOURCE_DIR}/driverlibs/) include_directories(${lithium_src_dir}) include_directories(${lithium_module_dir}) include_directories(${lithium_src_dir}) @@ -94,6 +95,18 @@ include_directories(${CMAKE_SOURCE_DIR}/libs/oatpp) include_directories(${CMAKE_SOURCE_DIR}/libs/oatpp-swagger) include_directories(${CMAKE_SOURCE_DIR}/libs/oatpp-websocket) +find_package(OpenSSL REQUIRED) +if(OpenSSL_FOUND) + message("-- Found OpenSSL ${OPENSSL_VERSION}: ${OPENSSL_LIBRARIES}") +else() + message("-- OpenSSL Not Found") +endif() + +find_package(CFITSIO REQUIRED) +find_package(ZLIB REQUIRED) +find_package(SQLite3 REQUIRED) +find_package(fmt REQUIRED) + ################################################################################# # # Subdirectories of Lithium @@ -116,6 +129,10 @@ add_subdirectory(modules) add_subdirectory(driver) +add_subdirectory(${lithium_src_dir}/carbon) +add_subdirectory(${lithium_src_dir}/config) +add_subdirectory(${lithium_src_dir}/server) + ################################################################################# # # General defines for compatibility across different platforms @@ -179,41 +196,19 @@ set(component_module ${lithium_component_dir}/addons.cpp ${lithium_component_dir}/compiler.cpp - ${lithium_component_dir}/finder.cpp ${lithium_component_dir}/loader.cpp ${lithium_component_dir}/manager.cpp ${lithium_component_dir}/sandbox.cpp + ${lithium_component_dir}/sort.cpp ) set(config_module ${lithium_src_dir}/config/configor.cpp - ${lithium_src_dir}/config/HubsConfig.cpp ) -if (ENABLE_DEBUG_FLAG) -set(debug_module - ${lithium_src_dir}/debug/terminal.cpp -) -else() set(debug_module ${lithium_src_dir}/debug/terminal.cpp ) -endif() - -if (ENABLE_ASYNC_FLAG) -set(server_module - ${lithium_src_dir}/websocket/Hub.cpp - ${lithium_src_dir}/websocket/Connection.cpp - ${lithium_src_dir}/websocket/Registry.cpp - ${lithium_src_dir}/websocket/Session.cpp -) -else() -set(server_module - ${lithium_src_dir}/websocket/WsServer.cpp - ${lithium_src_dir}/websocket/WsHub.cpp - ${lithium_src_dir}/websocket/WsInstance.cpp -) -endif() set(script_module ${lithium_src_dir}/script/manager.cpp @@ -221,6 +216,8 @@ set(script_module ${lithium_src_dir}/script/custom/config.cpp ${lithium_src_dir}/script/sheller.cpp + + ${lithium_src_dir}/script/carbon.cpp ) set(task_module @@ -235,36 +232,25 @@ set(task_module ) set(Lithium_module - ${lithium_src_dir}/AppComponent.hpp - ${lithium_src_dir}/ErrorHandler.cpp ${lithium_src_dir}/LithiumApp.cpp ) -find_package(OpenSSL REQUIRED) -if(OpenSSL_FOUND) - message("-- Found OpenSSL ${OPENSSL_VERSION}: ${OPENSSL_LIBRARIES}") -else() - message("-- OpenSSL Not Found") -endif() - -find_package(CFITSIO REQUIRED) -find_package(ZLIB REQUIRED) -find_package(SQLite3 REQUIRED) -find_package(fmt REQUIRED) - ################################################################################# # Main -add_library(lithium_server-library STATIC ${component_module} ${config_module} ${debug_module} ${module_module} ${device_module} ${task_module} ${server_module} ${script_module} ${Lithium_module}) -add_executable(lithium_server ${lithium_src_dir}/App.cpp) +add_library(lithium_server-library STATIC ${component_module} ${config_module} ${debug_module} ${module_module} ${device_module} ${task_module} ${script_module} ${Lithium_module}) +target_link_libraries(lithium_server-library loguru) +add_executable(lithium_server ${lithium_src_dir}/app.cpp) target_link_directories(lithium_server PUBLIC ${CMAKE_BINARY_DIR}/libs) target_link_libraries(lithium_server lithium_server-library) +target_link_libraries(lithium_server lithium_webserver) target_link_libraries(lithium_server oatpp-websocket oatpp-swagger oatpp-openssl oatpp-zlib oatpp) target_link_libraries(lithium_server loguru) target_link_libraries(lithium_server libzippp) target_link_libraries(lithium_server atomstatic) -target_link_libraries(lithium_server-library fmt::fmt) +target_link_libraries(lithium_server carbon) +target_link_libraries(lithium_server fmt::fmt) target_compile_definitions(lithium_server PRIVATE LOGURU_DEBUG_LOGGING) @@ -293,43 +279,3 @@ set_target_properties( PROPERTIES OUTPUT_NAME lithium_server ) - -################################################################################# -# Hello Element Astro Launcher - -add_library(heal-library STATIC - ${lithium_src_dir}/auth/AuthHandler.cpp - ${lithium_src_dir}/auth/AuthHandler.hpp - ${lithium_src_dir}/auth/JWT.cpp - ${lithium_src_dir}/auth/JWT.hpp - ${lithium_src_dir}/controller/StaticController.hpp - ${lithium_src_dir}/controller/StoryController.hpp - ${lithium_src_dir}/controller/AuthController.hpp - ${lithium_src_dir}/database/model/StoryModel.hpp - ${lithium_src_dir}/database/model/UserModel.hpp - ${lithium_src_dir}/database/StoryDb.hpp - ${lithium_src_dir}/database/UserDb.hpp - ${lithium_src_dir}/data/AuthDto.hpp - ${lithium_src_dir}/data/PageDto.hpp - ${lithium_src_dir}/data/SignInDto.hpp - ${lithium_src_dir}/data/SignUpDto.hpp - ${lithium_src_dir}/data/StatusDto.hpp - ${lithium_src_dir}/data/StoryDto.hpp - ${lithium_src_dir}/interceptor/AuthInterceptor.cpp - ${lithium_src_dir}/interceptor/AuthInterceptor.hpp - ${lithium_src_dir}/service/AuthService.cpp - ${lithium_src_dir}/service/AuthService.hpp - ${lithium_src_dir}/service/StoryService.cpp - ${lithium_src_dir}/service/StoryService.hpp - ${lithium_src_dir}/LauncherComponent.hpp - ${lithium_src_dir}/components/DatabaseComponent.hpp - ${lithium_src_dir}/components/SwaggerComponent.hpp - ${lithium_src_dir}/ErrorHandler.cpp - ${lithium_src_dir}/ErrorHandler.hpp -) - -target_include_directories(heal-library PUBLIC ${lithium_src_dir}) - -add_executable(heal ${lithium_src_dir}/Launcher.cpp) -target_link_libraries(heal heal-library) -target_link_libraries(heal oatpp-websocket oatpp-swagger oatpp-openssl oatpp-zlib oatpp-sqlite oatpp) diff --git a/README.md b/README.md index fdd391fb..90e4a31b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Lithium +# Lithium

@@ -72,6 +72,10 @@ pacman -S mingw-w64-x86_64-libzip pacman -S mingw-w64-x86_64-zlib pacman -S mingw-w64-x86_64-fmt pacman -S mingw-w64-x86_64-libnova +pacman -S mingw-w64-x86_64-gsl + +# for test +pacman -S mingw-w64-x86_64-gtest ``` #### On Ubuntu or other similar Linux platforms (No INDI needed) @@ -79,7 +83,7 @@ pacman -S mingw-w64-x86_64-libnova ```shell sudo apt-get update && sudo apt-get upgrade -y sudo apt install gcc g++ cmake -sudo apt install libcfitsio-dev zlib1g-dev libssl-dev libzip-dev libnova-dev libfmt-dev libudev-dev libuv1-dev +sudo apt install libcfitsio-dev zlib1g-dev libssl-dev libzip-dev libnova-dev libfmt-dev libudev-dev ``` Alternatively, you can directly run the provided script according to your platform: @@ -96,10 +100,18 @@ Unfortunately, the newest GCC and CMake are not available on Github Codespace, s ```shell sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y sudo apt-get update -sudo apt-get install gcc-13 # GCC13 is the best choice +sudo apt-get install gcc-13 g++-13 # GCC13 is the best choice, clang is alse OK wget https://cmake.org/files/v3.28/cmake-3.28.0-rc5.tar.gz tar -zxvf cmake-3.28.0-rc5.tar.gz +./bootstrap && make && sudo make install + +#install newest clang-format +wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - +sudo nano /etc/apt/sources.list +#deb http://apt.llvm.org/focal/ llvm-toolchain-focal-17 main +#deb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-17 main +sudo apt install -y clang-format-17 ``` Build the code: @@ -116,7 +128,7 @@ Everything is very simple. The entire process is straightforward. Here is a poem adapted from a quote : -``` +```txt Learning requires not mere imagination, Nor can it be attained through mediocre dedication. It is through diligent accumulation, @@ -196,6 +208,11 @@ pacman -S mingw-w64-x86_64-fmt pacman -S mingw-w64-x86_64-libnova # 如果想用make构建 pacman -S make # 注意添加对应的目录,否则会当场爆炸 + +pacman -S mingw-w64-x86_64-gsl + +# 测试用 +pacman -S mingw-w64-x86_64-gtest ``` #### Ubuntu/Debian/Other Linux diff --git a/STYLE_OF_CODE.md b/STYLE_OF_CODE.md index 538210ad..cd527f25 100644 --- a/STYLE_OF_CODE.md +++ b/STYLE_OF_CODE.md @@ -1,4 +1,6 @@ -# C++ Naming Conventions +# Style of Code + +## C++ Naming Conventions ## Variable Naming @@ -109,7 +111,7 @@ Good naming conventions improve code readability and maintainability, making it easier for others to understand and use your code. -# C++命名规则 +## C++命名规则 ## 变量命名 diff --git a/launcher/CMakeLists.txt b/driver/camera/atom-asi/_component.cpp similarity index 100% rename from launcher/CMakeLists.txt rename to driver/camera/atom-asi/_component.cpp diff --git a/driver/camera/atom-asi/_component.hpp b/driver/camera/atom-asi/_component.hpp new file mode 100644 index 00000000..e69de29b diff --git a/driver/camera/atom-asi/camera.cpp b/driver/camera/atom-asi/camera.cpp new file mode 100644 index 00000000..3903f999 --- /dev/null +++ b/driver/camera/atom-asi/camera.cpp @@ -0,0 +1,474 @@ +/* + * camera.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-3-29 + +Description: ASICamera Simulator and Basic Definition + +**************************************************/ + +#include "camera.hpp" + +#include "atom/driver/macro.hpp" +#include "atom/log/loguru.hpp" + +#include +#include + +#define ASI_CAMERA_CONNECTION_CHECK \ + if (is_connected.load()) { \ + LOG_F(WARNING, "Camera already connected"); \ + return true; \ + } + +#define ASI_CAMERA_CONNECT_CHECK \ + if (!is_connected.load()) { \ + LOG_F(ERROR, "Camera not connected"); \ + return false; \ + } + +#define ASI_CAMERA_EXPOSURE_CHECK \ + if (is_exposing.load()) { \ + LOG_F(ERROR, "Camera is exposing"); \ + return false; \ + } + +#define ASI_CAMERA_VIDEO_CHECK \ + if (is_videoing.load()) { \ + LOG_F(ERROR, "Camera is videoing"); \ + return false; \ + } + +using ImgBufferPtr = std::unique_ptr; + +ASICamera::ASICamera(const std::string &name) : AtomCamera(name) {} + +ASICamera::~ASICamera() {} + +bool ASICamera::initialize() { + AtomCamera::initialize(); + + registerVariable("CAMERA_COUNT", 0, "the number of connected cameras"); + return true; +} + +bool ASICamera::destroy() { + AtomCamera::destroy(); + return true; +} + +bool ASICamera::connect(const json ¶ms) { + ASI_CAMERA_CONNECTION_CHECK; + if (!params.contains("name")) { + LOG_F(ERROR, "No camera name provided"); + return false; + } + auto camera_name = params["name"].get(); + + auto camera_count = ASIGetNumOfConnectedCameras(); + if (camera_count <= 0) { + LOG_F(ERROR, + "ASI camera not found, please check the power supply or make " + "sure the camera is connected."); + return false; + } + for (int i = 0; i < camera_count; i++) { + /*获取相机信息*/ + if ((errCode = ASIGetCameraProperty(&ASICameraInfo, i)) != + ASI_SUCCESS) { + LOG_F(ERROR, + "Unable to get {} configuration information,the error " + "code is {},please check program permissions.\n", + ASICameraInfo.Name, errCode); + return false; + } + if (ASICameraInfo.Name == camera_name) { + LOG_F(INFO, "Find camera {}", ASICameraInfo.Name); + // Max: The member variable is faster than component variable + setVariable("DEVICE_ID", ASICameraInfo.CameraID); + setVariable("DEVICE_NAME", ASICameraInfo.Name); + m_camera_id = ASICameraInfo.CameraID; + m_camera_name = ASICameraInfo.Name; + /*打开相机*/ + if ((errCode = ASIOpenCamera(ASICameraInfo.CameraID)) != + ASI_SUCCESS) { + LOG_F(ERROR, "Unable to turn on the {}, error code: {}.", + ASICameraInfo.Name, errCode); + return false; + } + /*初始化相机*/ + if ((errCode = ASIInitCamera(ASICameraInfo.CameraID)) != + ASI_SUCCESS) { + LOG_F(ERROR, + "Unable to initialize connection to " + "camera,the error code is {}.", + errCode); + return false; + } + setVariable("DEVICE_CONNECTED", true); + is_connected.store(true); + LOG_F(INFO, "Camera connected successfully\n"); + return true; + } else { + LOG_F(ERROR, "This is not a designated camera"); + } + } + LOG_F(ERROR, "No camera found"); + return false; +} + +bool ASICamera::disconnect(const json ¶ms) { /*在关闭相机之前停止所有任务*/ + ASI_CAMERA_CONNECT_CHECK; + if (!params.empty()) { + LOG_F(ERROR, "No parameters are allowed"); + return false; + } + + if (is_videoing.load()) { + if ((errCode = ASIStopVideoCapture(m_camera_id)) != + ASI_SUCCESS) // 停止视频拍摄 + { + LOG_F(ERROR, + "Unable to stop video capture,error code is {},please try " + "again.", + errCode); + return false; + } + is_videoing.store(false); + setVariable("CCD_VIDEO_STATUS", false); + LOG_F(INFO, "Stop video capture"); + } + if (is_exposing.load()) { + if ((errCode = ASIStopExposure(m_camera_id)) != + ASI_SUCCESS) // 停止曝光 + { + LOG_F(ERROR, + "Unable to stop exposure,error code is {}, please try again.", + errCode); + return false; + } + is_exposing.store(false); + setVariable("CCD_EXPOSURE_STATUS", false); + LOG_F(INFO, "Stop exposure"); + } + /*关闭相机*/ + if ((errCode = ASICloseCamera(m_camera_id)) != ASI_SUCCESS) // 关闭相机 + { + LOG_F(ERROR, "Unable to turn off the camera,error code: {}", errCode); + return false; + } + setVariable("DEVICE_CONNECTED", false); + is_connected.store(false); + LOG_F(INFO, "Disconnect from camera"); + return true; +} + +bool ASICamera::reconnect(const json ¶ms) { + ASI_CAMERA_CONNECT_CHECK; + int timeout = 0; + if (params.contains("timeout")) { + timeout = params["timeout"].get(); + } + + if (!disconnect({})) { + LOG_F(ERROR, "Unable to disconnect from camera"); + return false; + } + if (!connect({})) { + LOG_F(ERROR, "Unable to connect to camera"); + return false; + } + LOG_F(INFO, "Reconnect to camera: {}", + getVariable("DEVICE_NAME")); + return true; +} + +bool ASICamera::isConnected() { return is_connected.load(); } + +bool ASICamera::startExposure(const double &duration) { + ASI_CAMERA_CONNECT_CHECK; + if (is_exposing.load()) { + LOG_F(ERROR, "Exposure is already in progress"); + return false; + } + + const long blink_duration = duration * 1000000; + LOG_F(INFO, "Blinking {} time(us) before exposure", blink_duration); + if ((errCode = ASISetControlValue(m_camera_id, ASI_EXPOSURE, blink_duration, + ASI_FALSE)) != ASI_SUCCESS) { + LOG_F(ERROR, "Failed to set blink exposure to {}us, error {}", + blink_duration, errCode); + return false; + } + if ((errCode = ASIStartExposure(m_camera_id, ASI_FALSE)) != ASI_SUCCESS) { + LOG_F(ERROR, "Failed to start blink exposure, error code: {}", errCode); + return false; + } + is_exposing.store(true); + setVariable("CCD_EXPOSURE_STATUS", true); + // Max: A timer is needed here + do { + usleep(10000); + errCode = ASIGetExpStatus(m_camera_id, &expStatus); + } while (errCode == ASI_SUCCESS && expStatus == ASI_EXP_WORKING); + if (errCode != ASI_SUCCESS) { + LOG_F(ERROR, "Blink exposure failed, error {}, error code: {}", errCode, + expStatus); + return false; + } + is_exposing.store(false); + setVariable("CCD_EXPOSURE_STATUS", false); + LOG_F(INFO, "Blink exposure completed"); + return true; +} + +bool ASICamera::abortExposure() { + ASI_CAMERA_CONNECT_CHECK; + if (!is_exposing.load()) { + LOG_F(ERROR, "No exposure is in progress"); + return false; + } + if ((errCode = ASIStopExposure(m_camera_id)) != ASI_SUCCESS) { + LOG_F(ERROR, "Unable to stop camera exposure, error code: {}", errCode); + return false; + } + setVariable("CCD_EXPOSURE_STATUS", false); + is_exposing.store(false); + LOG_F(INFO, "Abort exposure"); + return true; +} + +bool ASICamera::getExposureStatus() { + ASI_CAMERA_CONNECT_CHECK; + if ((errCode = ASIGetExpStatus(m_camera_id, &expStatus)) != ASI_SUCCESS) { + LOG_F(INFO, "Camera is busy, status code: {}", errCode); + setVariable("CCD_EXPOSURE_STATUS", true); + is_exposing.store(true); + return true; + } + LOG_F(INFO, "Camera is idle"); + return false; +} + +bool ASICamera::getExposureResult() { + ASI_CAMERA_CONNECT_CHECK; + ASI_CAMERA_EXPOSURE_CHECK; + + GET_INT_VARIABLE(width); + GET_INT_VARIABLE(height); + + long imgSize = width * height; + //* (1 + (ASICAMERA->ImageType == ASI_IMG_RAW16)); + + // 使用智能指针管理图像缓冲区内存 + ImgBufferPtr imgBuf(new unsigned char[imgSize]); + + /*曝光后获取图像信息*/ + int errCode = ASIGetDataAfterExp(m_camera_id, imgBuf.get(), imgSize); + if (errCode != ASI_SUCCESS) { + // 获取图像失败 + LOG_F(ERROR, "Unable to get image from camera, error code: {}", + errCode); + return; + } + + // 图像下载完成 + LOG_F(INFO, "Download from camera completely."); + + GET_STR_VARIABLE(upload_mode); + if (upload_mode == "LOCAL") [[likely]] { + // Max: image filename generation logic is needed + std::string FitsName = "test.fits"; + LOG_F(INFO, "Upload mode is LOCAL, save image to {}", FitsName); + /*将图像写入本地文件*/ + // auto res = getComponent("LITHIUM_IMAGE") + // ->runFunc("SaveImage", {{"filename", FitsName}, + // {"data", imgBuf}, + //{ "size", imgSize } + //}); + // if (res.contains("error")) { + // LOG_F(ERROR, "Unable to save image to {}, error: {}", FitsName, + // res["error"].get()); + // return false; + //} + } else if (upload_mode == "CLIENT") [[unlikely]] { + } else if (upload_mode == "None") [[unlikely]] { + LOG_F(INFO, "Upload mode is NONE, skip upload"); + } else { + LOG_F(ERROR, "Invalid upload mode: {}", upload_mode); + return false; + } + return true; +} + +bool ASICamera::saveExposureResult() { return true; } + +bool ASICamera::startVideo() { return true; } + +bool ASICamera::stopVideo() { return true; } + +bool ASICamera::getVideoStatus() { return true; } + +bool ASICamera::getVideoResult() { return true; } + +bool ASICamera::saveVideoResult() { return true; } + +bool ASICamera::startCooling() { return true; } + +bool ASICamera::stopCooling() { return true; } + +bool ASICamera::getCoolingStatus() { return true; } + +bool ASICamera::isCoolingAvailable() { return true; } + +bool ASICamera::getTemperature() { return true; } + +bool ASICamera::getCoolingPower() { return true; } + +bool ASICamera::setTemperature(const double &temperature) { + ASI_CAMERA_CONNECT_CHECK; + ASI_CAMERA_EXPOSURE_CHECK; + ASI_CAMERA_VIDEO_CHECK; + if (!is_cooling_available) { + LOG_F(ERROR, "Cooling is not available"); + return false; + } + /*转化温度参数*/ + long TargetTemp; + if (temperature > 0.5) + TargetTemp = static_cast(temperature + 0.49); + else if (temperature < 0.5) + TargetTemp = static_cast(temperature - 0.49); + else + TargetTemp = 0; + /*设置相机温度*/ + if ((errCode = ASISetControlValue(m_camera_id, ASI_TEMPERATURE, TargetTemp, + ASI_FALSE)) != ASI_SUCCESS) { + LOG_F(ERROR, "Unable to set camera temperature, error code: {}", + errCode); + return false; + } + setVariable("CCD_TEMPERATURE_VALUE", TargetTemp); + LOG_F(INFO, "Set camera cooling temperature to {}", TargetTemp); + return true; +} + +bool ASICamera::setCoolingPower(const double &power) { return true; } + +bool ASICamera::getGain() { + ASI_CAMERA_CONNECT_CHECK; + long gain; + if ((errCode = ASIGetControlValue(m_camera_id, ASI_GAIN, &gain, NULL)) != + ASI_SUCCESS) { + LOG_F(ERROR, "Unable to get camera gain, error code: {}", errCode); + return false; + } + setVariable("CCD_GAIN", static_cast(gain)); + m_gain.store(static_cast(gain)); + LOG_F(INFO, "Get camera gain: {}", gain); + return true; +} + +bool ASICamera::setGain(const int &gain) { + ASI_CAMERA_CONNECT_CHECK; + ASI_CAMERA_EXPOSURE_CHECK; + ASI_CAMERA_VIDEO_CHECK; + if ((errCode = ASISetControlValue(m_camera_id, ASI_GAIN, gain, + ASI_FALSE)) != ASI_SUCCESS) { + LOG_F(ERROR, "Unable to set camera gain,error code: {}", errCode); + return false; + } + setVariable("CCD_GAIN", gain); + m_gain.store(gain); + LOG_F(INFO, "Set camera gain to {}", gain); + return true; +} + +bool ASICamera::isGainAvailable() { + LOG_F(INFO, "Gain is available for {}", m_camera_name); + return true; +} + +bool ASICamera::getOffset() { + ASI_CAMERA_CONNECT_CHECK; + long offset; + if ((errCode = ASIGetControlValue(m_camera_id, ASI_BRIGHTNESS, &offset, + NULL)) != ASI_SUCCESS) { + LOG_F(ERROR, "Unable to get camera offset, error code: {}", errCode); + return false; + } + setVariable("CCD_OFFSET", static_cast(offset)); + m_offset.store(static_cast(offset)); + LOG_F(INFO, "Get camera offset: {}", offset); + return true; +} + +bool ASICamera::setOffset(const int &offset) { + ASI_CAMERA_CONNECT_CHECK; + ASI_CAMERA_EXPOSURE_CHECK; + ASI_CAMERA_VIDEO_CHECK; + if ((errCode = ASISetControlValue(m_camera_id, ASI_BRIGHTNESS, offset, + ASI_FALSE)) != ASI_SUCCESS) { + LOG_F(ERROR, "Unable to set camera offset, error code: {}", errCode); + return false; + } + setVariable("CCD_OFFSET", offset); + m_offset.store(offset); + LOG_F(INFO, "Set camera offset to {}", offset); + return true; +} + +bool ASICamera::isOffsetAvailable() { + LOG_F(INFO, "Offset is available for {}", m_camera_name); + return true; +} + +bool ASICamera::getISO() { + LOG_F(ERROR, "ISO is not available for {}", m_camera_name); + return false; +} + +bool ASICamera::setISO(const int &iso) { + LOG_F(ERROR, "ISO is not available for {}", m_camera_name); + return false; +} + +bool ASICamera::isISOAvailable() { + LOG_F(INFO, "ISO is not available for {}", m_camera_name); + return false; +} + +bool ASICamera::getFrame() { return true; } + +bool ASICamera::setFrame(const int &x, const int &y, const int &w, + const int &h) { + return true; +} + +bool ASICamera::isFrameSettingAvailable() { return true; } + +bool ASICamera::getBinning() { return true; } + +bool ASICamera::setBinning(const int &hor, const int &ver) { return true; } + +bool ASICamera::getFrameType() { return true; } + +bool ASICamera::setFrameType(FrameType type) { return true; } + +bool ASICamera::getUploadMode() { return true; } + +bool ASICamera::setUploadMode(UploadMode mode) { return true; } + +bool ASICamera::refreshCameraInfo() { + if ((errCode = ASIGetCameraProperty(&ASICameraInfo, i)) != ASI_SUCCESS) { + LOG_F(ERROR, "Unable to get camera information, error code: {}", + errCode); + return false; + } + return true; +} \ No newline at end of file diff --git a/driver/camera/atom-asi/camera.hpp b/driver/camera/atom-asi/camera.hpp new file mode 100644 index 00000000..8c7fe6f2 --- /dev/null +++ b/driver/camera/atom-asi/camera.hpp @@ -0,0 +1,120 @@ +#ifndef ATOM_ASI_COMPONENT_HPP +#define ATOM_ASI_COMPONENT_HPP + +#include "atom/driver/camera.hpp" + +#include "driverlibs/libasi/ASICamera2.h" + +#include + +class ASICamera : public AtomCamera { +public: + explicit ASICamera(const std::string &name); + ~ASICamera(); + + bool initialize() final; + bool destroy() final; + + bool connect(const json ¶ms) final; + + bool disconnect(const json ¶ms) final; + + bool reconnect(const json ¶ms) final; + + bool isConnected() final; + + bool startExposure(const double &duration) final; + + bool abortExposure() final; + + bool getExposureStatus() final; + + bool getExposureResult() final; + + bool saveExposureResult() final; + + bool startVideo() final; + + bool stopVideo() final; + + bool getVideoStatus() final; + + bool getVideoResult() final; + + bool saveVideoResult() final; + + bool startCooling() final; + + bool stopCooling() final; + + bool getCoolingStatus() final; + + bool isCoolingAvailable() final; + + bool getTemperature() final; + + bool getCoolingPower() final; + + bool setTemperature(const double &temperature) final; + + bool setCoolingPower(const double &power) final; + + bool getGain() final; + + bool setGain(const int &gain) final; + + bool isGainAvailable() final; + + bool getOffset() final; + + bool setOffset(const int &offset) final; + + bool isOffsetAvailable() final; + + bool getISO() final; + + bool setISO(const int &iso) final; + + bool isISOAvailable() final; + + bool getFrame() final; + + bool setFrame(const int &x, const int &y, const int &w, const int &h) final; + + bool isFrameSettingAvailable() final; + + bool getBinning() final; + + bool setBinning(const int &hor, const int &ver) final; + + bool getFrameType() final; + + bool setFrameType(FrameType type) final; + + bool getUploadMode() final; + + bool setUploadMode(UploadMode mode) final; + +private: + bool refreshCameraInfo(); + + /*ASI相机参数*/ + ASI_CAMERA_INFO ASICameraInfo; + ASI_ERROR_CODE errCode; + ASI_EXPOSURE_STATUS expStatus; + + int m_camera_id; + std::string m_camera_name; + + std::atomic_bool is_connected; + std::atomic_bool is_exposing; + std::atomic_bool is_videoing; + std::atomic_bool is_cooling; + + bool is_cooling_available; + + std::atomic m_gain; + std::atomic m_offset; +}; + +#endif \ No newline at end of file diff --git a/driver/camera/atom-asi/package.json b/driver/camera/atom-asi/package.json new file mode 100644 index 00000000..cbd72db2 --- /dev/null +++ b/driver/camera/atom-asi/package.json @@ -0,0 +1,35 @@ +{ + "name": "Atom-Camera-ASI", + "version": "1.0.0", + "type": "shared", + "description": "Atom driver for ASI Camera", + "license": "LGPL-3.0-or-later", + "author": "Max Qian", + "repository": { + "type": "git", + "url": "https://github.com/ElementAstro/Atom-ASI" + }, + "bugs": { + "url": "https://github.com/ElementAstro/Atom-ASI/issues" + }, + "homepage": "https://github.com/ElementAstro/Atom-ASI", + "keywords": [ + "asi", + "camera", + "filter wheel" + ], + "scripts": { + "dev": "./atom-asi --standalone", + "build": "cmake --build-type=Release -- -j 4", + "lint": "clang-format -i src/*.cpp src/*.h" + }, + "dependencies": { + "asi-sdk" : "^1.34" + }, + "main": { + "ASICamera": { + "func": "getInstance", + "type" : "shared" + } + } +} \ No newline at end of file diff --git a/driver/camera/atom-touptek/_component.cpp b/driver/camera/atom-touptek/_component.cpp new file mode 100644 index 00000000..e69de29b diff --git a/driver/camera/atom-touptek/_component.hpp b/driver/camera/atom-touptek/_component.hpp new file mode 100644 index 00000000..e69de29b diff --git a/driver/camera/atom-touptek/camera.cpp b/driver/camera/atom-touptek/camera.cpp new file mode 100644 index 00000000..79814e10 --- /dev/null +++ b/driver/camera/atom-touptek/camera.cpp @@ -0,0 +1,111 @@ +/* + * camera.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-3-29 + +Description: Touptek Camera + +**************************************************/ + +#include "camera.hpp" + +#include "atom/log/loguru.hpp" + +#include + +ToupCamera::ToupCamera(const std::string &name) + : ToupCamera(name) {} + +ToupCamera::~ToupCamera() {} + +bool ToupCamera::initialize() { return true; } + +bool ToupCamera::destroy() { return true; } + +bool ToupCamera::connect(const json ¶ms) { return true; } + +bool ToupCamera::disconnect(const json ¶ms) { return true; } + +bool ToupCamera::reconnect(const json ¶ms) { return true; } + +bool ToupCamera::isConnected() { return true; } + +bool ToupCamera::startExposure(const double &duration) { return true; } + +bool ToupCamera::abortExposure() { return true; } + +bool ToupCamera::getExposureStatus() { return true; } + +bool ToupCamera::getExposureResult() { return true; } + +bool ToupCamera::saveExposureResult() { return true; } + +bool ToupCamera::startVideo() { return true; } + +bool ToupCamera::stopVideo() { return true; } + +bool ToupCamera::getVideoStatus() { return true; } + +bool ToupCamera::getVideoResult() { return true; } + +bool ToupCamera::saveVideoResult() { return true; } + +bool ToupCamera::startCooling() { return true; } + +bool ToupCamera::stopCooling() { return true; } + +bool ToupCamera::getCoolingStatus() { return true; } + +bool ToupCamera::isCoolingAvailable() { return true; } + +bool ToupCamera::getTemperature() { return true; } + +bool ToupCamera::getCoolingPower() { return true; } + +bool ToupCamera::setTemperature(const double &temperature) { return true; } + +bool ToupCamera::setCoolingPower(const double &power) { return true; } + +bool ToupCamera::getGain() { return true; } + +bool ToupCamera::setGain(const int &gain) { return true; } + +bool ToupCamera::isGainAvailable() { return true; } + +bool ToupCamera::getOffset() { return true; } + +bool ToupCamera::setOffset(const int &offset) { return true; } + +bool ToupCamera::isOffsetAvailable() { return true; } + +bool ToupCamera::getISO() { return true; } + +bool ToupCamera::setISO(const int &iso) { return true; } + +bool ToupCamera::isISOAvailable() { return true; } + +bool ToupCamera::getFrame() { return true; } + +bool ToupCamera::setFrame(const int &x, const int &y, const int &w, + const int &h) { + return true; +} + +bool ToupCamera::isFrameSettingAvailable() { return true; } + +bool ToupCamera::getBinning() { return true; } + +bool ToupCamera::setBinning(const int &hor, const int &ver) { return true; } + +bool ToupCamera::getFrameType() { return true; } + +bool ToupCamera::setFrameType(FrameType type) { return true; } + +bool ToupCamera::getUploadMode() { return true; } + +bool ToupCamera::setUploadMode(UploadMode mode) { return true; } \ No newline at end of file diff --git a/driver/camera/atom-touptek/camera.hpp b/driver/camera/atom-touptek/camera.hpp new file mode 100644 index 00000000..ee8a7aa5 --- /dev/null +++ b/driver/camera/atom-touptek/camera.hpp @@ -0,0 +1,101 @@ +#ifndef ATOM_TOUPTEK_COMPONENT_HPP +#define ATOM_TOUPTEK_COMPONENT_HPP + +#include "atom/driver/camera.hpp" + +#include "libtouptek/toupcam.h" + +#include + +class ToupCamera : public AtomCamera { +public: + explicit ToupCamera(const std::string &name); + ~ToupCamera(); + + bool initialize() final; + bool destroy() final; + + bool connect(const json ¶ms) final; + + bool disconnect(const json ¶ms) final; + + bool reconnect(const json ¶ms) final; + + bool isConnected() final; + + bool startExposure(const double &duration) final; + + bool abortExposure() final; + + bool getExposureStatus() final; + + bool getExposureResult() final; + + bool saveExposureResult() final; + + bool startVideo() final; + + bool stopVideo() final; + + bool getVideoStatus() final; + + bool getVideoResult() final; + + bool saveVideoResult() final; + + bool startCooling() final; + + bool stopCooling() final; + + bool getCoolingStatus() final; + + bool isCoolingAvailable() final; + + bool getTemperature() final; + + bool getCoolingPower() final; + + bool setTemperature(const double &temperature) final; + + bool setCoolingPower(const double &power) final; + + bool getGain() final; + + bool setGain(const int &gain) final; + + bool isGainAvailable() final; + + bool getOffset() final; + + bool setOffset(const int &offset) final; + + bool isOffsetAvailable() final; + + bool getISO() final; + + bool setISO(const int &iso) final; + + bool isISOAvailable() final; + + bool getFrame() final; + + bool setFrame(const int &x, const int &y, const int &w, const int &h) final; + + bool isFrameSettingAvailable() final; + + bool getBinning() final; + + bool setBinning(const int &hor, const int &ver) final; + + bool getFrameType() final; + + bool setFrameType(FrameType type) final; + + bool getUploadMode() final; + + bool setUploadMode(UploadMode mode) final; + +private: +}; + +#endif \ No newline at end of file diff --git a/driver/camera/atom-touptek/detail/libtoupbase.cpp b/driver/camera/atom-touptek/detail/libtoupbase.cpp new file mode 100644 index 00000000..b4928da2 --- /dev/null +++ b/driver/camera/atom-touptek/detail/libtoupbase.cpp @@ -0,0 +1,46 @@ +/* + * libtoupbase.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 20234-3-1 + +Description: ToupBase Library + +**************************************************/ + +#include "libtoupbase.hpp" +#include + +std::string errorCodes(HRESULT rc) { + static std::unordered_map errCodes = { + {0x00000000, "Success"}, + {0x00000001, "Yet another success"}, + {0x8000ffff, "Catastrophic failure"}, + {0x80004001, "Not supported or not implemented"}, + {0x80070005, "Permission denied"}, + {0x8007000e, "Out of memory"}, + {0x80070057, "One or more arguments are not valid"}, + {0x80004003, "Pointer that is not valid"}, + {0x80004005, "Generic failure"}, + {0x8001010e, "Call function in the wrong thread"}, + {0x8007001f, "Device not functioning"}, + {0x800700aa, "The requested resource is in use"}, + {0x8000000a, + "The data necessary to complete this operation is not yet available"}, + {0x8001011f, + "This operation returned because the timeout period expired"}}; + + const std::unordered_map::iterator it = + errCodes.find(rc); + if (it != errCodes.end()) + return it->second; + else { + char str[256]; + snprintf(str, sizeof(str), "Unknown error: 0x%08x", rc); + return std::string(str); + } +} \ No newline at end of file diff --git a/driver/camera/atom-touptek/detail/libtoupbase.hpp b/driver/camera/atom-touptek/detail/libtoupbase.hpp new file mode 100644 index 00000000..ad4e4923 --- /dev/null +++ b/driver/camera/atom-touptek/detail/libtoupbase.hpp @@ -0,0 +1,91 @@ +/* + * libtoupbase.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 20234-3-1 + +Description: ToupBase Library + +**************************************************/ + +#pragma once + +#include + +#ifdef BUILD_TOUPCAM +#include +#define FP(x) Toupcam_##x +#define CP(x) TOUPCAM_##x +#define XP(x) Toupcam##x +#define THAND HToupcam +#define DNAME "ToupTek" +#elif BUILD_ALTAIRCAM +#include +#define FP(x) Altaircam_##x +#define CP(x) ALTAIRCAM_##x +#define XP(x) Altaircam##x +#define THAND HAltaircam +#define DNAME "Altair" +#elif BUILD_BRESSERCAM +#include +#define FP(x) Bressercam_##x +#define CP(x) BRESSERCAM_##x +#define XP(x) Bressercam##x +#define THAND HBressercam +#define DNAME "Bresser" +#elif BUILD_MALLINCAM +#include +#define FP(x) Mallincam_##x +#define CP(x) MALLINCAM_##x +#define XP(x) Mallincam##x +#define THAND HMallincam +#define DNAME "MALLINCAM" +#elif BUILD_NNCAM +#include +#define FP(x) Nncam_##x +#define CP(x) NNCAM_##x +#define XP(x) Nncam##x +#define THAND HNncam +#define DNAME "Nn" +#elif BUILD_OGMACAM +#include +#define FP(x) Ogmacam_##x +#define CP(x) OGMACAM_##x +#define XP(x) Ogmacam##x +#define THAND HOgmacam +#define DNAME "OGMAVision" +#elif BUILD_OMEGONPROCAM +#include +#define FP(x) Omegonprocam_##x +#define CP(x) OMEGONPROCAM_##x +#define XP(x) Omegonprocam##x +#define THAND HOmegonprocam +#define DNAME "Astroshop" +#elif BUILD_STARSHOOTG +#include +#define FP(x) Starshootg_##x +#define CP(x) STARSHOOTG_##x +#define XP(x) Starshootg##x +#define THAND HStarshootg +#define DNAME "Orion" +#elif BUILD_TSCAM +#include +#define FP(x) Tscam_##x +#define CP(x) TSCAM_##x +#define XP(x) Tscam##x +#define THAND HTscam +#define DNAME "Teleskop" +#elif BUILD_MEADECAM +#include +#define FP(x) Toupcam_##x +#define CP(x) TOUPCAM_##x +#define XP(x) Toupcam##x +#define THAND HToupcam +#define DNAME "Meade" +#endif + +extern std::string errorCodes(HRESULT rc); diff --git a/driver/camera/atom-touptek/package.json b/driver/camera/atom-touptek/package.json new file mode 100644 index 00000000..b54365c1 --- /dev/null +++ b/driver/camera/atom-touptek/package.json @@ -0,0 +1,35 @@ +{ + "name": "Atom-Camera-Touptek", + "version": "1.0.0", + "type": "shared", + "description": "Atom driver for Touptek Camera", + "license": "LGPL-3.0-or-later", + "author": "Max Qian", + "repository": { + "type": "git", + "url": "https://github.com/ElementAstro/Atom-Touptek" + }, + "bugs": { + "url": "https://github.com/ElementAstro/Atom-Touptek/issues" + }, + "homepage": "https://github.com/ElementAstro/Atom-Touptek", + "keywords": [ + "asi", + "camera", + "filter wheel" + ], + "scripts": { + "dev": "./atom-touptek --standalone", + "build": "cmake --build-type=Release -- -j 4", + "lint": "clang-format -i src/*.cpp src/*.h" + }, + "dependencies": { + "asi-sdk" : "^1.34" + }, + "main": { + "TouptekCamera": { + "func": "getInstance", + "type" : "shared" + } + } +} \ No newline at end of file diff --git a/driver/client/atom-hydrogen/hydrogencamera.cpp b/driver/client/atom-hydrogen/hydrogencamera.cpp index 897c2426..00f5be27 100644 --- a/driver/client/atom-hydrogen/hydrogencamera.cpp +++ b/driver/client/atom-hydrogen/hydrogencamera.cpp @@ -26,6 +26,16 @@ HydrogenCamera::HydrogenCamera(const std::string &name) : Camera(name) { Atom::Utils::StringSwitch>(); m_text_switch = std::make_unique< Atom::Utils::StringSwitch>(); + + registerFunc("connect", &HydrogenCamera::connect, this); + registerFunc("disconnect", &HydrogenCamera::disconnect, this); + registerFunc("reconnect", &HydrogenCamera::reconnect, this); + registerFunc("isConnected", &HydrogenCamera::isConnected, this); + registerFunc("startExposure", &HydrogenCamera::startExposure, this); + registerFunc("abortExposure", &HydrogenCamera::abortExposure, this); + registerFunc("getExposureStatus", &HydrogenCamera::getExposureStatus, this); + registerFunc("getExposureResult", &HydrogenCamera::getExposureResult, this); + } HydrogenCamera::~HydrogenCamera() {} diff --git a/driver/filterwheel/atom-touptek/_component.cpp b/driver/filterwheel/atom-touptek/_component.cpp new file mode 100644 index 00000000..e69de29b diff --git a/driver/filterwheel/atom-touptek/_component.hpp b/driver/filterwheel/atom-touptek/_component.hpp new file mode 100644 index 00000000..e69de29b diff --git a/example/atom/algorithm/base.cpp b/example/atom/algorithm/base.cpp new file mode 100644 index 00000000..e19e659d --- /dev/null +++ b/example/atom/algorithm/base.cpp @@ -0,0 +1,44 @@ +#include "atom/algorithm/base.hpp" + +#include + +using namespace Atom::Algorithm; + +int main() { + std::string input = "Hello, world!"; + auto encoded = base32Encode(reinterpret_cast(input.data()), + input.size()); + std::cout << "Encoded: " << encoded << std::endl; + + try { + auto decoded = base32Decode(encoded); + std::cout << "Decoded: " << decoded << std::endl; + } catch (const std::invalid_argument& e) { + std::cout << "Error: " << e.what() << std::endl; + } + + std::string input = "Hello, world!"; + auto encoded = base128Encode( + reinterpret_cast(input.data()), input.size()); + std::cout << "Encoded: " << encoded << std::endl; + + try { + auto decoded = base128Decode(encoded); + std::cout << "Decoded: " << decoded << std::endl; + } catch (const std::invalid_argument& e) { + std::cout << "Error: " << e.what() << std::endl; + } + + uint8_t key = 0x42; + auto encrypted = xorEncrypt(input, key); + std::cout << "Encrypted: " + << base128Encode( + reinterpret_cast(encrypted.data()), + encrypted.size()) + << std::endl; + + auto decrypted = xorDecrypt(encrypted, key); + std::cout << "Decrypted: " << decrypted << std::endl; + + return 0; +} \ No newline at end of file diff --git a/example/atom/async/queue.cpp b/example/atom/async/queue.cpp new file mode 100644 index 00000000..7750871b --- /dev/null +++ b/example/atom/async/queue.cpp @@ -0,0 +1,118 @@ +#include +#include +#include + +using namespace Atom::Async; + +void producer(ThreadSafeQueue& queue) { + for (int i = 0; i < 10; ++i) { + queue.put(i); + std::cout << "Produced: " << i << std::endl; + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } +} + +void consumer(ThreadSafeQueue& queue) { + while (true) { + auto item = queue.take(); + if (!item) { + break; + } + std::cout << "Consumed: " << *item << std::endl; + } +} + +int main() { + ThreadSafeQueue queue; + + std::thread t1(producer, std::ref(queue)); + std::thread t2(consumer, std::ref(queue)); + + t1.join(); + queue.destroy(); + t2.join(); + + // 使用 emplace + ThreadSafeQueue strQueue; + strQueue.emplace("Hello"); + strQueue.emplace("World"); + std::cout << "Front: " << *strQueue.front() << std::endl; + std::cout << "Back: " << *strQueue.back() << std::endl; + + // 使用 waitFor + ThreadSafeQueue intQueue; + std::thread t3([&intQueue]() { + std::this_thread::sleep_for(std::chrono::seconds(1)); + intQueue.put(42); + }); + auto item = + intQueue.waitFor([](const std::queue& q) { return !q.empty(); }); + std::cout << "Waited for: " << *item << std::endl; + t3.join(); + + // 使用 extractIf + /* + ThreadSafeQueue extractQueue; + for (int i = 0; i < 10; ++i) { + extractQueue.put(i); + } + auto evenNumbers = extractQueue.extractIf([](int i) { return i % 2 == 0; }); + std::cout << "Extracted even numbers: "; + for (auto num : evenNumbers) { + std::cout << num << " "; + } + std::cout << std::endl; + */ + + + // 使用 sort + ThreadSafeQueue sortQueue; + sortQueue.put(3); + sortQueue.put(1); + sortQueue.put(4); + sortQueue.put(1); + sortQueue.put(5); + sortQueue.sort(std::greater()); + std::cout << "Sorted queue: "; + while (!sortQueue.empty()) { + std::cout << *sortQueue.take() << " "; + } + std::cout << std::endl; + + // 使用 transform + ThreadSafeQueue transformQueue; + for (int i = 1; i <= 5; ++i) { + transformQueue.put(i); + } + /*auto squaredQueue = + transformQueue.transform([](int i) { return i * i; }); + std::cout << "Squared queue: "; + while (!squaredQueue.empty()) { + std::cout << *squaredQueue.take() << " "; + } + std::cout << std::endl;*/ + + // 使用 groupBy + /* + ThreadSafeQueue groupByQueue; + groupByQueue.put("apple"); + groupByQueue.put("banana"); + groupByQueue.put("cherry"); + groupByQueue.put("date"); + groupByQueue.put("elderberry"); + auto groupedQueues = groupByQueue.groupBy( + [](const std::string& s) -> std::string { + return std::to_string(s[0]); + }); + std::cout << "Grouped queues: " << std::endl; + for (auto& queue : groupedQueues) { + std::cout << "Group: "; + while (!queue.empty()) { + std::cout << *queue.take() << " "; + } + std::cout << std::endl; + } + */ + + return 0; +} \ No newline at end of file diff --git a/example/atom/experiment/bind_first.cpp b/example/atom/experiment/bind_first.cpp new file mode 100644 index 00000000..135d2cd4 --- /dev/null +++ b/example/atom/experiment/bind_first.cpp @@ -0,0 +1,52 @@ +#include "atom/experiment/bind_first.hpp" + +#include +#include + +int add(int a, int b) { return a + b; } + +struct Adder { + int operator()(int a, int b) const { return a + b; } +}; + +struct Person { + std::string name; + + void greet(const std::string &message) const { + std::cout << "Hello, " << name << "! " << message << std::endl; + } +}; + +int main() { + // 绑定普通函数 + auto add5 = bind_first(add, 5); + std::cout << add5(3) << std::endl; // 输出: 8 + + // 绑定函数对象 + // Adder adder; + // auto adder10 = bind_first(adder, 10); + // std::cout << adder10(7) << std::endl; // 输出: 17 + + // 绑定成员函数 + Person person{"Alice"}; + auto greet_alice = bind_first(&Person::greet, person); + greet_alice("How are you?"); // 输出: Hello, Alice! How are you? + + // 绑定 std::function + std::function fn = [](int a, int b) { return a * b; }; + auto multiply3 = bind_first(fn, 3); + std::cout << multiply3(5) << std::endl; // 输出: 15 + + // 绑定成员函数对象 + // auto greet_bob = bind_first(std::cref(person), "Bob", &Person::greet); + // greet_bob("Nice to meet you!"); // 输出: Hello, Bob! Nice to meet you! + + // 使用 is_invocable_v 和 is_nothrow_invocable_v + static_assert(is_invocable_v); + // static_assert(is_nothrow_invocable_v); + static_assert(is_invocable_v); + static_assert(is_invocable_v); + // static_assert(is_invocable_v); + + return 0; +} \ No newline at end of file diff --git a/example/atom/experiment/callable.cpp b/example/atom/experiment/callable.cpp new file mode 100644 index 00000000..aadf1d8b --- /dev/null +++ b/example/atom/experiment/callable.cpp @@ -0,0 +1,64 @@ +#include "atom/experiment/callable.hpp" + +#include +#include + +struct Person { + Person(std::string name, int age) : m_name(std::move(name)), m_age(age) {} + + void greet(const std::string &message) const { + std::cout << "Hello, " << m_name << "! " << message << std::endl; + } + + std::string m_name; + int m_age; +}; + +int add(int a, int b) { return a + b; } + +int main() { + // 使用 Constructor 构造对象 + Constructor person_constructor; + auto person = person_constructor("Alice", 30); + std::cout << "Name: " << person->m_name << ", Age: " << person->m_age + << std::endl; + + // 使用 Const_Caller 调用常成员函数 + Const_Caller greet_caller( + &Person::greet); + greet_caller(*person, "How are you?"); + + // 使用 Fun_Caller 调用普通函数 + Fun_Caller add_caller(add); + int sum = add_caller(3, 5); + std::cout << "Sum: " << sum << std::endl; + + // 使用 Caller 调用非常成员函数 + struct Square { + int operator()(int x) { return x * x; } + }; + Square square; + Caller square_caller(&Square::operator()); + int result = square_caller(square, 4); + std::cout << "Square: " << result << std::endl; + + // 使用 Arity 获取函数参数个数 + // static_assert(Arity::value == 3); + + // 使用 Function_Signature 获取函数签名 + static_assert( + std::is_same_v::Return_Type, + int>); + static_assert( + std::is_same_v::Signature, + int (*)(double, char)>); + + // 使用 Callable_Traits 获取可调用对象的特征 + auto lambda = [](int x, int y) { return x + y; }; + static_assert( + std::is_same_v::Return_Type, int>); + static_assert(std::is_same_v::Signature, + int (*)(int, int)>); + + return 0; +} \ No newline at end of file diff --git a/example/atom/experiment/static_vector.cpp b/example/atom/experiment/static_vector.cpp new file mode 100644 index 00000000..88fa77df --- /dev/null +++ b/example/atom/experiment/static_vector.cpp @@ -0,0 +1,87 @@ +#include "atom/experiment/static_vector.hpp" + +#include +#include + +int main() { + // 创建一个容量为5的StaticVector,存储int类型 + StaticVector vec; + + // 使用push_back添加元素 + vec.push_back(1); + vec.push_back(2); + vec.push_back(3); + + // 使用emplace_back添加元素 + vec.emplace_back(4); + vec.emplace_back(5); + + // 使用下标操作符访问元素 + std::cout << "vec[0]: " << vec[0] << std::endl; + std::cout << "vec[1]: " << vec[1] << std::endl; + + // 使用at函数访问元素 + std::cout << "vec.at(2): " << vec.at(2) << std::endl; + try { + std::cout << "vec.at(5): " << vec.at(5) << std::endl; + } catch (const std::out_of_range& e) { + std::cout << "Out of range: " << e.what() << std::endl; + } + + // 使用front和back函数 + std::cout << "vec.front(): " << vec.front() << std::endl; + std::cout << "vec.back(): " << vec.back() << std::endl; + + // 使用size和capacity函数 + std::cout << "vec.size(): " << vec.size() << std::endl; + std::cout << "vec.capacity(): " << vec.capacity() << std::endl; + + // 使用范围for循环遍历元素 + std::cout << "Elements in vec: "; + for (int x : vec) { + std::cout << x << " "; + } + std::cout << std::endl; + + // 使用初始化列表构造函数 + StaticVector str_vec{"Hello", "World", "!"}; + + // 使用比较操作符 + StaticVector vec1{1, 2, 3}; + StaticVector vec2{1, 2, 3}; + StaticVector vec3{1, 2, 4}; + + std::cout << "vec1 == vec2: " << (vec1 == vec2) << std::endl; + std::cout << "vec1 == vec3: " << (vec1 == vec3) << std::endl; + // std::cout << "vec1 <=> vec2: " << (vec1 <=> vec2) << std::endl; + // std::cout << "vec1 <=> vec3: " << (vec1 <=> vec3) << std::endl; + + // 使用swap函数 + StaticVector vec4{1, 2, 3}; + StaticVector vec5{4, 5, 6}; + std::cout << "Before swap: "; + std::cout << "vec4: "; + for (int x : vec4) { + std::cout << x << " "; + } + std::cout << "vec5: "; + for (int x : vec5) { + std::cout << x << " "; + } + std::cout << std::endl; + + swap(vec4, vec5); + + std::cout << "After swap: "; + std::cout << "vec4: "; + for (int x : vec4) { + std::cout << x << " "; + } + std::cout << "vec5: "; + for (int x : vec5) { + std::cout << x << " "; + } + std::cout << std::endl; + + return 0; +} \ No newline at end of file diff --git a/example/benchmark.hpp b/example/benchmark.hpp new file mode 100644 index 00000000..3e8dd87d --- /dev/null +++ b/example/benchmark.hpp @@ -0,0 +1,88 @@ +#ifndef LITHIUM_BENCHMARK_HPP +#define LITHIUM_BENCHMARK_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Benchmark { +public: + using Clock = std::chrono::high_resolution_clock; + using TimePoint = Clock::time_point; + using Duration = Clock::duration; + + Benchmark(const std::string& name) : name_(name) {} + + template + void Run(Func&& func, int iterations) { + std::vector durations(iterations); + + std::transform(std::execution::par, durations.begin(), durations.end(), + durations.begin(), [&func](const Duration&) { + TimePoint start = Clock::now(); + func(); + return Clock::now() - start; + }); + + Duration totalDuration = std::accumulate( + durations.begin(), durations.end(), Duration::zero()); + double averageDuration = static_cast(totalDuration.count()) / + iterations / 1000.0; // in microseconds + + double variance = + std::transform_reduce( + std::execution::par, durations.begin(), durations.end(), 0.0, + std::plus<>(), + [&averageDuration](const Duration& d) { + double durationInMicroseconds = + static_cast(d.count()) / 1000.0; + return std::pow(durationInMicroseconds - averageDuration, + 2); + }) / + iterations; + + double standardDeviation = std::sqrt(variance); + + results_.push_back({name_, totalDuration, averageDuration, + standardDeviation, iterations}); + } + + static void PrintResults() { + std::cout << "Benchmark Results:\n"; + for (const auto& result : results_) { + std::cout << std::setw(20) << std::left << result.name << ": " + << std::chrono::duration_cast( + result.totalDuration) + .count() + << " us (avg: " << std::setprecision(4) + << result.averageDuration + << " us, std dev: " << result.standardDeviation << " us, " + << result.iterations << " iterations)\n"; + } + } + +private: + struct Result { + std::string name; + Duration totalDuration; + double averageDuration; + double standardDeviation; + int iterations; + }; + + static inline std::vector results_; + std::string name_; +}; + +#define BENCHMARK(name, func, iterations) Benchmark(name).Run(func, iterations) + +std::vector Benchmark::results_; + +#endif diff --git a/example/component_test/package.json b/example/component_test/package.json index 86abadb4..840e9317 100644 --- a/example/component_test/package.json +++ b/example/component_test/package.json @@ -1,55 +1,36 @@ { - "name": "example", - "version": "0.0.0", + "name": "atom-config", + "version": "1.0.0", "type": "shared", - "description": "An example project", + "description": "Atom driver for Touptek Camera", "license": "LGPL-3.0-or-later", "author": "Max Qian", "repository": { "type": "git", - "url": "https://github.com/maxqian/cobalt-example.git" + "url": "https://github.com/ElementAstro/Atom-Touptek" }, "bugs": { - "url": "https://github.com/maxqian/cobalt-example/issues" + "url": "https://github.com/ElementAstro/Atom-Touptek/issues" }, - "homepage": "https://github.com/maxqian/cobalt-example", + "homepage": "https://github.com/ElementAstro/Atom-Touptek", "keywords": [ - "cobalt", - "example" + "asi", + "camera", + "filter wheel" ], "scripts": { - "dev": "vite", - "build": "vite build", - "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" + "build": "cmake --build-type=Release -- -j 4", + "foramt": "clang-format -i src/*.cpp src/*.h", + "lint": "clang-tidy src/*.cpp src/*.h", + "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { - "axios": "^1.6.2", - "bootstrap": "^5.3.2", - "chart.js": "^4.4.1", - "easy-peasy": "^6.0.4", - "formik": "^2.4.5", - "isomorphic-ws": "^5.0.0", - "less": "^4.2.0", - "localforage": "^1.10.0", - "lodash": "^4.17.21", - "lodash-es": "^4.17.21", - "match-sorter": "^6.3.1", - "react": "^18.2.0", - "react-bootstrap": "^2.9.1", - "react-bootstrap-icons": "^1.10.3", - "react-dom": "^18.2.0", - "react-router-bootstrap": "^0.26.2", - "react-router-dom": "^6.21.1", - "redux": "^4.2.1", - "sort-by": "^0.0.2", - "vite-plugin-svgr": "^4.2.0", - "yup": "^1.3.3" + "asi-sdk": "^1.34" }, - "main": { - "example1": { - "func": "getExample1", - "type" : "shared" + "modules": { + "main": { + "func": "getInstance", + "check": true } } } \ No newline at end of file diff --git a/launcher/#launcher.glade# b/launcher/#launcher.glade# deleted file mode 100644 index ef2bca54..00000000 --- a/launcher/#launcher.glade# +++ /dev/null @@ -1,437 +0,0 @@ - - - - - - - - - - - False - True - - - True - False - 介绍页 - - - progress - False - - - - - True - False - 内容页 - - - False - - - - - True - False - 确认页 - - - confirm - False - - - - - False - end - 6 - 6 - 6 - 6 - 6 - 6 - 6 - - - intro - False - - - - - False - 440 - 250 - assets\atom.png - - - True - False - vertical - top - - - True - False - - - True - False - 文件(_F) - True - - - True - False - - - gtk-new - True - False - True - True - - - - - gtk-open - True - False - True - True - - - - - gtk-save - True - False - True - True - - - - - gtk-save-as - True - False - True - True - - - - - True - False - - - - - gtk-quit - True - False - True - True - - - - - - - - - True - False - 编辑(_E) - True - - - True - False - - - gtk-cut - True - False - True - True - - - - - gtk-copy - True - False - True - True - - - - - gtk-paste - True - False - True - True - - - - - gtk-delete - True - False - True - True - - - - - - - - - True - False - 视图(_V) - True - - - - - True - False - 帮助(_H) - True - - - True - False - - - gtk-about - True - False - True - True - - - - - - - - - False - True - 0 - - - - - True - True - left - True - True - - - True - False - vertical - - - - - - - - - - - - - - True - False - Home - center - True - - - Home - True - True - True - - - - - True - False - vertical - - - - - - - - - - - - 1 - - - - - True - False - Installation - - - 1 - False - - - - - True - False - vertical - - - - - - - - - - - - 2 - - - - - True - False - Settings - - - 2 - False - - - - - True - True - 1 - - - - - True - True - - - False - True - 2 - - - - - - - False - About - True - help-about - dialog - Hello ElementAstro Launcher - 1.0.0 - https://github.com/ElementAstro - Official Github Repository - Max Qian - Max Qian - Max Qian - Max Qian - assets\atom.png - gpl-3-0 - - - False - vertical - 2 - - - False - end - - - False - False - 0 - - - - - - - - - - False - Lithium Script Editor - True - True - edit-find-replace - dialog - lithium - Lithium Script Editor - - - False - vertical - 2 - - - False - end - - - False - False - 0 - - - - - - diff --git a/launcher/assets/atom.png b/launcher/assets/atom.png deleted file mode 100644 index 6d32705b..00000000 Binary files a/launcher/assets/atom.png and /dev/null differ diff --git a/launcher/launcher.cpp b/launcher/launcher.cpp deleted file mode 100644 index a8ea8e84..00000000 --- a/launcher/launcher.cpp +++ /dev/null @@ -1,552 +0,0 @@ -/* - * launcher.cpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-3-29 - -Description: Lithium Server Launcher - -**************************************************/ - -#include "launcher.hpp" - -#include -#include -#include -#include - -#include "atom/system/crash.hpp" -#include "atom/log/loguru.hpp" - -ServerLauncher::ServerLauncher(const std::string &config_file_path, const std::string &DLOG_File_path) - : _config_file_path(config_file_path), _DLOG_File_path(DLOG_File_path) -{ - try - { - // 加载配置文件 - load_config(); - } - catch (const std::exception &e) - { - DLOG_F(ERROR, "Failed to initialize ServerLauncher: {}", e.what()); - throw; - } -} - -void ServerLauncher::run() -{ - try - { - // 检查服务器所需的资源文件是否存在 - if (!check_resources()) - { - DLOG_F(INFO, "Some resource files are missing, downloading now..."); - download_resources(); - } - - check_dependencies(); - - check_config_file(_config_file_path); - - // 启动服务器 - start_server(); - - // 读取服务器输出 - read_server_output(); - - // 发送停止命令给服务器,并等待服务器退出 - stop_server(); - - // 等待服务器退出 - wait_for_server_to_exit(); - - DLOG_F(INFO, "Server stopped."); - } - catch (const std::exception &e) - { - LOG_F(ERROR, "Error occurred in ServerLauncher::run(): {}", e.what()); - throw; - } -} - -void ServerLauncher::stop() -{ - // 设置请求停止服务器的标志 - _stop_requested = true; - - // 唤醒服务器条件变量,以便服务器检测到停止请求 - _server_cv.notify_all(); - - DLOG_F(INFO, "Stop command sent to server."); -} - -bool ServerLauncher::is_running() const -{ - return _server_running; -} - -void ServerLauncher::load_config() -{ - std::ifstream config_file(_config_file_path); - if (!config_file) - { - LOG_F(ERROR, "Failed to open config file: {}", _config_file_path); - throw std::runtime_error("Failed to open config file: " + _config_file_path); - } - try - { - config_file >> _config; - DLOG_F(INFO, "Config file loaded successfully."); - } - catch (const std::exception &e) - { - LOG_F(ERROR, "Error occurred when reading config file: {}", e.what()); - throw; - } -} - -bool ServerLauncher::check_resources() -{ - const auto &resources = _config["resources"]; - - for (const auto &res_file : resources) - { - const std::string filename = res_file; - - if (!fs::exists(filename)) - { - LOG_F(ERROR, "Resource file '{}' is missing.", filename); - return false; - } - - // 计算 SHA256 值 - std::string sha256_val; - if (!calculate_sha256(filename, sha256_val)) - { - LOG_F(ERROR, "Failed to calculate SHA256 value of '" << filename << "'." << std::endl; - return false; - } - - const std::string expected_sha256 = res_file["sha256"]; - if (sha256_val != expected_sha256) - { - LOG_F(ERROR, "SHA256 check failed for '" << filename << "'." << std::endl); - return false; - } - } - - DLOG_F(INFO, "All resource files are found."); - return true; -} - -void ServerLauncher::download_resources() -{ - DLOG_F(INFO, "Downloading missing resources..."); - - // 创建线程池 - ThreadPool pool(std::thread::hardware_concurrency()); - - // 创建任务列表 - std::vector> tasks; - - for (const auto &res_file : _config["resources"]) - { - // 发送 HTTP GET 请求下载文件 - const std::string url = _config["resource_server"].get() + "/" + res_file.get(); - - // 添加下载任务到线程池 - tasks.emplace_back(pool.enqueue([url, res_file, this] - { - try { - httplib::Client client(_config["resource_server"]); - auto res = client.Get(url.c_str()); - - if (!res) { - LOG_F(ERROR, "Failed to download resource: {}", res_file.get()); - return false; - } - - // 将下载的数据写入文件 - std::ofstream outfile(res_file); - outfile.write(res->body.c_str(), res->body.size()); - - DLOG_F(INFO,"Resource file '{}' downloaded.", res_file.get()); - return true; - } - catch (const std::exception &e) { - LOG_F(ERROR, "Error occurred when downloading resource '{}: {}" ,res_file.get(), << e.what()); - return false; - } })); - } - - // 等待所有任务完成 - for (auto &&task : tasks) - { - task.wait(); - } - - // 检查是否有任务失败 - for (auto &&task : tasks) - { - if (!task.get()) - { - LOG_F(ERROR, "Failed to download some resources."); - } - } - - DLOG_F(INFO, "Downloading finished."); -} - -bool check_process(const std::string &name) -{ -#ifdef _WIN32 - std::string command = "tasklist /FI \"IMAGENAME eq " + name + "\""; - - // 创建匿名管道 - SECURITY_ATTRIBUTES saAttr; - HANDLE hReadPipe, hWritePipe; - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - saAttr.lpSecurityDescriptor = NULL; - - if (!CreatePipe(&hReadPipe, &hWritePipe, &saAttr, 0)) - { - return false; - } - - // 设置命令行输出重定向到管道 - STARTUPINFOA si; - PROCESS_INFORMATION pi; - ZeroMemory(&si, sizeof(STARTUPINFOA)); - ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); - si.cb = sizeof(STARTUPINFOA); - si.hStdError = hWritePipe; - si.hStdOutput = hWritePipe; - si.dwFlags |= STARTF_USESTDHANDLES; - - // 启动命令行进程 - if (!CreateProcessA(NULL, const_cast(command.c_str()), NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) - { - CloseHandle(hReadPipe); - CloseHandle(hWritePipe); - return false; - } - - // 关闭写入端,避免读取阻塞 - CloseHandle(hWritePipe); - - // 读取命令行的输出结果 - const int BUFFER_SIZE = 4096; - char buffer[BUFFER_SIZE]; - DWORD bytesRead = 0; - std::string output; - - while (ReadFile(hReadPipe, buffer, BUFFER_SIZE - 1, &bytesRead, NULL)) - { - if (bytesRead == 0) - { - break; - } - - buffer[bytesRead] = '\0'; - output += buffer; - } - - // 关闭管道和进程句柄 - CloseHandle(hReadPipe); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - - // 检查输出结果中是否包含进程名 - return (output.find(name) != std::string::npos); -#else - std::string command = "ps aux | grep -v grep | grep -q '" + name + "'"; - return (system(command.c_str()) == 0); -#endif -} - -bool ServerLauncher::check_dependencies() -{ - const std::vector dependencies = {"redis-server", "mysqld"}; - - for (const auto &dependency : dependencies) - { - if (!check_process(dependency)) - { - DLOG_F(INFO, "Dependency process '{}' is not running.", dependency); - return false; - } - } - DLOG_F(INFO, "All dependencies are ready."); - return true; -} - -bool ServerLauncher::check_config_file(const std::string &config_file) -{ - if (!std::filesystem::exists(config_file)) - { - LOG_F(ERROR, "Config file not found: {}", config_file); - return false; - } - - try - { - std::ifstream ifs(config_file); - json config = json::parse(ifs); - - // 检查 "port" 配置项是否存在 - if (config.find("port") == config.end()) - { - LOG_F(ERROR, "Config item 'port' not found in config file."); - return false; - } - - // 检查 "port" 配置项是否合法 - int port = config["port"].get(); - if (port < 0 || port > 65535) - { - LOG_F(ERROR, "Invalid 'port' configuration value: {}", port); - return false; - } - } - catch (const std::exception &e) - { - LOG_F(ERROR, "Failed to parse config file: {}", e.what()); - return false; - } - - return true; -} - -bool ServerLauncher::check_modules(const std::string &modules_dir, const json &module_list) -{ - if (!std::filesystem::exists(modules_dir)) - { - DLOG_F(INFO, "Modules directory not found: {}", modules_dir); - if (!std::filesystem::create_directory(modules_dir)) - { - LOG_F(ERROR, "Failed to create modules directory: {}", modules_dir); - return false; - } - } - - bool all_found = true; - for (const auto &module : module_list) - { - std::string module_path = modules_dir + "/" + module.get(); - if (!std::filesystem::exists(module_path)) - { - DLOG_F(ERROR, "Required module not found: {}", module_path); - all_found = false; - } - } - - return all_found; -} - -void ServerLauncher::start_server() -{ - DLOG_F(INFO, "Starting server..."); - - // 执行启动服务器的命令 - const std::string cmd = _config["server_command"]; - - _server_process = std::shared_ptr(_popen(cmd.c_str(), "r"), [](FILE *f) - { if (f) { _pclose(f); } }); - - if (!_server_process) - { - DLOG_F(ERROR, "Failed to execute server command: {}", cmd); - throw std::runtime_error("Failed to execute server command: " + cmd); - } - else - { - DLOG_F(INFO, "Server process started with command: {}", cmd); - std::cout << "Server process started with command: " << cmd << std::endl; - } - - // 创建一个线程来等待服务器启动 - _server_thread = std::jthread([&] - { - std::unique_lock lock(_server_mutex); - - while (!_stop_requested) { - // 在条件变量上等待,直到服务器启动 - _server_cv.wait(lock, [&] { return _server_running || _stop_requested; }); - } - - // 如果请求停止服务器,则发送停止命令给服务器 - if (_stop_requested) { - fprintf(_server_process.get(), "%c", _config["stop_command"]); - fflush(_server_process.get()); - DLOG_F(INFO,"Stop command sent to server process."); - } }); - - DLOG_F(INFO, "Server started."); -} - -void ServerLauncher::stop_server() -{ - DLOG_F(INFO, "Stopping server..."); - - // 发送停止命令给服务器 - fprintf(_server_process.get(), "%c", _config["stop_command"]); - fflush(_server_process.get()); - - DLOG_F(INFO, "Stop command sent to server process."); -} - -void ServerLauncher::wait_for_server_to_exit() -{ - // 等待服务器退出并获取返回值 - int status = -1; - _server_thread.join(); - _server_process.reset(); - _server_running = false; -} - -void ServerLauncher::read_server_output() -{ - // 定义正则表达式模板,匹配错误信息 - std::regex error_regex("ERROR: \\[(\\S+)\\] (.*)"); - - // 创建一个线程来读取服务器输出 - std::thread read_thread([&] - { - char buffer[1024]; - while (fgets(buffer, sizeof(buffer), _server_process.get())) { - std::cout << buffer; - - // 判断输出中是否包含错误信息 - std::string line(buffer); - std::smatch match; - - if (std::regex_search(line, match, error_regex)) { - // 匹配成功,提取错误类型和错误消息 - std::string error_type = match[1].str(); - std::string error_message = match[2].str(); - - // 根据错误类型处理错误 - if (error_type == "CRITICAL") { - // 生成冲突日志 - //Lithium::CrashReport::saveCrashLog(error_message); - } - else if (error_type == "WARNING") { - } - - // 读取结束,设置服务器运行标志为 false - _server_running = false; - - // 唤醒等待服务器退出的条件变量 - _server_cv.notify_all(); - return; - } - } - - // 读取结束,设置服务器运行标志为 false - _server_running = false; - - // 唤醒等待服务器退出的条件变量 - _server_cv.notify_all(); }); - - // 启动成功,设置服务器运行标志为 true - _server_running = true; - - // 让分离线程自行运行,不阻塞 run() 函数 - read_thread.detach(); -} - -bool ServerLauncher::calculate_sha256(const std::string &filename, std::string &sha256_val) -{ - // 打开文件 - std::ifstream file(filename, std::ios::binary); - if (!file) - { - return false; - } - - // 计算 SHA256 值 - EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); - EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL); - - char buffer[1024]; - while (file.read(buffer, sizeof(buffer))) - { - EVP_DigestUpdate(mdctx, buffer, sizeof(buffer)); - } - - if (file.gcount() > 0) - { - EVP_DigestUpdate(mdctx, buffer, file.gcount()); - } - - unsigned char hash[EVP_MAX_MD_SIZE]; - unsigned int hash_len = 0; - EVP_DigestFinal_ex(mdctx, hash, &hash_len); - EVP_MD_CTX_free(mdctx); - - // 转换为十六进制字符串 - sha256_val.clear(); - for (unsigned int i = 0; i < hash_len; ++i) - { - char hex_str[3]; - sprintf(hex_str, "%02x", hash[i]); - sha256_val += hex_str; - } - - return true; -} - -void setupLogFile() -{ - std::filesystem::path logsFolder = std::filesystem::current_path() / "logs"; - if (!std::filesystem::exists(logsFolder)) - { - std::filesystem::create_directory(logsFolder); - } - auto now = std::chrono::system_clock::now(); - auto now_time_t = std::chrono::system_clock::to_time_t(now); - std::tm *local_time = std::localtime(&now_time_t); - char filename[100]; - std::strftime(filename, sizeof(filename), "%Y%m%d_%H%M%S.log", local_time); - std::filesystem::path logFilePath = logsFolder / filename; - loguru::add_file(logFilePath.string().c_str(), loguru::Append, loguru::Verbosity_MAX); - - loguru::set_fatal_handler([](const loguru::Message &message) - { Lithium::CrashReport::saveCrashLog(std::string(message.prefix) + message.message); }); -} - -int main(int argc, char *argv[]) -{ - std::vector args(argv + 1, argv + argc); - - if (args.size() < 2) - { - LOG_F(INFO, "Error: Missing arguments."); - LOG_F(INFO, "Usage: launcher "); - return 1; - } - - try - { - ServerLauncher launcher(args[0], args[1]); - launcher.run(); - - if (launcher.is_running()) - { - launcher.stop(); - } - } - catch (const std::exception &e) - { - // 输出错误信息并返回 - LOG_F(ERROR, "Error: {}", e.what()); - return 1; - } - - return 0; -} \ No newline at end of file diff --git a/launcher/launcher.glade b/launcher/launcher.glade deleted file mode 100644 index ffd0f7f7..00000000 --- a/launcher/launcher.glade +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - - - - - - False - True - - - True - False - 介绍页 - - - progress - False - - - - - True - False - 内容页 - - - False - - - - - True - False - 确认页 - - - confirm - False - - - - - False - end - 6 - 6 - 6 - 6 - 6 - 6 - 6 - - - intro - False - - - - - False - Hello ElementAstro Launcher - True - assets\atom.png - - - True - False - 0 - none - - - True - False - 12 - - - - - - - - True - False - __glade_unnamed_14 - - - - - - - False - About - True - help-about - dialog - Hello ElementAstro Launcher - 1.0.0 - https://github.com/ElementAstro - Official Github Repository - Max Qian - Max Qian - Max Qian - Max Qian - assets\atom.png - gpl-3-0 - - - False - vertical - 2 - - - False - end - - - False - False - 0 - - - - - - - - - - False - Lithium Script Editor - True - True - edit-find-replace - dialog - lithium - Lithium Script Editor - - - False - vertical - 2 - - - False - end - - - False - False - 0 - - - - - - diff --git a/launcher/launcher.glade~ b/launcher/launcher.glade~ deleted file mode 100644 index 9fbbf465..00000000 --- a/launcher/launcher.glade~ +++ /dev/null @@ -1,167 +0,0 @@ - - - - - - - - - - - False - True - - - True - False - 介绍页 - - - progress - False - - - - - True - False - 内容页 - - - False - - - - - True - False - 确认页 - - - confirm - False - - - - - False - end - 6 - 6 - 6 - 6 - 6 - 6 - 6 - - - intro - False - - - - - False - Hello ElementAstro Launcher - True - assets\atom.png - - - True - True - False - True - - - - - - - - False - About - True - help-about - dialog - Hello ElementAstro Launcher - 1.0.0 - https://github.com/ElementAstro - Official Github Repository - Max Qian - Max Qian - Max Qian - Max Qian - assets\atom.png - gpl-3-0 - - - False - vertical - 2 - - - False - end - - - False - False - 0 - - - - - - - - - - False - Lithium Script Editor - True - True - edit-find-replace - dialog - lithium - Lithium Script Editor - - - False - vertical - 2 - - - False - end - - - False - False - 0 - - - - - - diff --git a/launcher/launcher.hpp b/launcher/launcher.hpp deleted file mode 100644 index 18a1989d..00000000 --- a/launcher/launcher.hpp +++ /dev/null @@ -1,292 +0,0 @@ -/* - * launcher.hpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-3-29 - -Description: Lithium Server Launcher - -**************************************************/ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "atom/web/httplib.h" -#include "atom/type/json.hpp" - -using json = nlohmann::json; -namespace fs = std::filesystem; - -/** - * @brief 一个服务器启动器类,用于管理和启动服务器,包括资源下载、依赖检查、配置文件解析等操作。 - */ -class ServerLauncher -{ -public: - /** - * @brief 构造函数,初始化配置文件路径和日志文件路径,但不进行任何初始化操作。 - * @param config_file_path 服务器配置文件路径 - * @param DLOG_File_path 服务器日志文件路径 - */ - ServerLauncher(const std::string &config_file_path, const std::string &DLOG_File_path); - - /** - * @brief 启动服务器并开始监听连接请求。 - * - * 该函数首先检查运行所需的资源是否已下载和依赖项是否已安装,然后加载配置文件, - * 启动服务器进程并开始监听连接请求。启动过程中,服务器输出将定向至日志文件中。 - * - * 如果启动失败,则会发送警告邮件,并打印相关错误并退出程序。 - * - * 在 start_server() 函数内部,会创建一个新的线程来启动服务器进程,并等待子进程结束。 - */ - void run(); - - /** - * @brief 停止服务器进程和启动器。 - * - * 停止服务器进程前会发送停止信号以确保服务器进程优雅地退出。 - */ - void stop(); - - /** - * @brief 检查服务器是否正在运行。 - * @return 如果服务器正在运行,则返回 true,否则返回 false。 - */ - bool is_running() const; - - /** - * @brief 重定向服务器的标准输出和标准错误输出到日志文件中。 - * - * 该函数在服务器启动后应当被调用,以便将服务器输出记录到日志文件中。 - * - * @param DLOG_File_path 日志文件路径。 - */ - void redirect_stdout_stderr(const std::string &DLOG_File_path); - - /** - * @brief 计算指定文件的 SHA-256 哈希值。 - * @param filename 要计算哈希值的文件名。 - * @param sha256_val 输出参数,保存计算出的哈希值。 - * @return 如果文件存在并成功计算出哈希值,则返回 true,否则返回 false。 - */ - bool calculate_sha256(const std::string &filename, std::string &sha256_val); - -private: - /** - * @brief 加载配置文件,并检查其中的参数是否合法。 - * - * 该函数在 ServerLauncher::run() 函数中被调用,用于加载服务器的配置文件, - * 并检查其中的参数是否设置正确。如果参数检查失败,则会打印相关错误信息并退出程序。 - */ - void load_config(); - - /** - * @brief 检查启动器所需的资源是否已下载。 - * - * 该函数在 ServerLauncher::run() 函数中被调用,用于检查启动器所需的资源是否已下载。 - * 如果没有下载,则会提示用户开始下载,或自动下载并安装缺失的资源。 - * - * @return 如果所有必要的资源均已下载,则返回 true,否则返回 false。 - */ - bool check_resources(); - - /** - * @brief 下载启动器所需的资源。 - * - * 该函数在 ServerLauncher::check_resources() 函数中被调用,用于下载启动器所需的资源, - * 包括服务器程序、配置文件、数据文件等。下载完成后,将自动解压和安装这些资源。 - */ - void download_resources(); - - /** - * @brief 检查启动器所需的依赖项是否已安装。 - * - * 该函数在 ServerLauncher::run() 函数中被调用,用于检查启动器所需的依赖项是否已安装。 - * 如果某些依赖项未安装,则会提示用户开始安装,或自动下载并安装这些依赖项。 - * - * @return 如果所有必要的依赖项均已安装,则返回 true,否则返回 false。 - */ - bool check_dependencies(); - - /** - * @brief 检查服务器配置文件是否合法。 - * @param config_file 支持 JSON 或 YAML 格式的配置文件路径。 - * @return 如果配置文件有效,则返回 true,否则返回 false。 - */ - bool check_config_file(const std::string &config_file); - - /** - * @brief 检查已安装的模组是否符合要求。 - * - * 该函数在 ServerLauncher::run() 函数中被调用,用于检查已安装的模组是否符合要求, - * 如果存在不支持或过时的模组,则会提示用户更新或删除这些模组。 - * - * @param modules_dir 模组所在的目录路径。 - * @param module_list 从配置文件中读取的模组列表。 - * @return 如果所有必要的模组均已安装并且都是最新版,则返回 true,否则返回 false。 - */ - bool check_modules(const std::string &modules_dir, const json &module_list); - - /** - * @brief 启动服务器进程并等待其结束。 - * - * 该函数在 ServerLauncher::run() 函数中被调用,用于启动服务器进程并等待其结束。 - * 在启动服务器进程前,将检查服务器程序是否存在以及配置文件是否设置正确。 - * 若服务器程序不存在或配置文件设置错误,则会输出错误信息并退出程序。 - * - * 在函数内部,会创建一个新的线程来启动服务器进程,并等待子进程结束。如果服务器进程 - * 提前结束,则会发送警告邮件给管理员,并打印相关错误信息并退出程序。 - */ - void start_server(); - - /** - * @brief 发送停止信号给服务器进程,并等待其优雅地退出。 - * - * 该函数在 ServerLauncher::stop() 函数中被调用,用于发送停止信号给服务器进程, - * 并等待其在一段时间内优雅地退出。如果服务器未能在规定时间内退出,则会发送 - * kill 信号以强制结束服务器进程。 - */ - void stop_server(); - - /** - * @brief 等待服务器进程结束。 - * - * 该函数在 ServerLauncher::stop_server() 和 ServerLauncher::start_server() - * 函数中都可能被调用,用于等待服务器进程在子线程中结束。 - * - * 通过使用条件变量和互斥锁来控制等待流程,避免了死锁和忙等情况的出现。 - */ - void wait_for_server_to_exit(); - - /** - * @brief 读取服务器输出并将其记录到日志文件中。 - * - * 该函数在 ServerLauncher::start_server() 中被调用,用于读取服务器进程的标准输出 - * 并将其记录到日志文件中。该函数在一个独立的线程中运行,以避免阻塞其他操作。 - */ - void read_server_output(); - - std::string _config_file_path; ///< 配置文件路径 - std::string _DLOG_File_path; ///< 日志文件路径 - json _config; ///< 服务器配置文件 - std::atomic_bool _stop_requested = false; ///< 是否请求停止服务器的标志 - std::atomic_bool _server_running = false; ///< 是否正在运行服务器的标志 - std::jthread _server_thread; ///< 执行服务器进程的线程对象 - std::mutex _server_mutex; ///< 控制等待服务器进程结束的互斥锁 - std::condition_variable _server_cv; ///< 控制等待服务器进程结束的条件变量 - std::shared_ptr _server_process; ///< 指向服务器进程标准输出流的指针 -}; - -/** - * @brief 线程池 - */ -class ThreadPool { -public: - /** - * @brief 构造函数,初始化线程池大小和停止标志位。 - * - * 构造函数会创建 n_threads 个线程,并等待来自任务队列的任务分配。 - * - * @param n_threads 线程池大小 - */ - ThreadPool(std::size_t n_threads) - : stop(false) - { - for (std::size_t i = 0; i < n_threads; ++i) - { - threads.emplace_back([this] - { - for (;;) { - std::function task; - { - std::unique_lock lock(queue_mutex); - condition.wait(lock, [this] { return stop || !tasks.empty(); }); - if (stop && tasks.empty()) { - return; - } - task = std::move(tasks.front()); - tasks.pop(); - } - task(); - } }); - } - } - - /** - * @brief 析构函数,销毁所有线程并退出。 - * - * 析构函数会向任务队列中插入空任务,并等待所有线程完成该任务并退出。 - */ - ~ThreadPool() - { - { - std::unique_lock lock(queue_mutex); - stop = true; - } - condition.notify_all(); - for (std::thread &thread : threads) - { - thread.join(); - } - } - - /** - * @brief 将指定任务添加到任务队列中,并返回该任务的 future 对象。 - * - * 该函数用于将函数 f 和其参数 args 添加到任务队列中等待执行,并返回一 - * 个 std::future 对象,以便查询任务完成情况。当任务队列已满或线程池被 - * 停止时,将会抛出 std::runtime_error 异常。 - * - * @tparam F 函数类型 - * @tparam Args 参数类型 - * @param f 要执行的函数对象 - * @param args 函数参数 - * @return 返回一个 std::future 对象,用于查询任务完成情况 - */ - template - auto enqueue(F &&f, Args &&...args) -> std::future::type> - { - using return_type = typename std::result_of::type; - - auto task = std::make_shared>( - std::bind(std::forward(f), std::forward(args)...)); - - std::future res = task->get_future(); - { - std::unique_lock lock(queue_mutex); - - if (stop) - { - throw std::runtime_error("enqueue on stopped ThreadPool"); - } - - tasks.emplace([task] - { (*task)(); }); - } - condition.notify_one(); - return res; - } - -private: - std::vector threads; ///< 线程池中的线程列表 - std::queue> tasks; ///< 任务队列 - - std::mutex queue_mutex; ///< 任务队列的互斥锁 - std::condition_variable condition; ///< 任务队列的条件变量 - bool stop; ///< 停止标志位 -}; diff --git a/launcher/launcher.json b/launcher/launcher.json deleted file mode 100644 index de0b14a6..00000000 --- a/launcher/launcher.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "server_command": "./server", - "stop_command": "q\n", - "resources": [ - "data.json", - "config.ini" - ], - "resource_server": "http://example.com" - } - \ No newline at end of file diff --git a/locale/lithium.pot b/locale/lithium.pot deleted file mode 100644 index 6876ffa8..00000000 --- a/locale/lithium.pot +++ /dev/null @@ -1,23 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR Max Qian -# This file is distributed under the same license as the package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: astro_air@126.com\n" -"POT-Creation-Date: 2024-02-06 17:50+0800\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#: E:/msys64/home/Lithium/src/LithiumApp.cpp:551 -#, c++-format -msgid "Dispatched command {} with result: {}" -msgstr "" diff --git a/locale/po/en_US.UTF-8/lithium.po b/locale/po/en_US.UTF-8/lithium.po deleted file mode 100644 index 629b9775..00000000 --- a/locale/po/en_US.UTF-8/lithium.po +++ /dev/null @@ -1,4 +0,0 @@ -#: E:/msys64/home/Lithium/src/LithiumApp.cpp:551 -#, c++-format -msgid "Dispatched command {} with result: {}" -msgstr "" diff --git a/locale/po/es/lithium.po b/locale/po/es/lithium.po deleted file mode 100644 index 94c1bb2c..00000000 --- a/locale/po/es/lithium.po +++ /dev/null @@ -1,46 +0,0 @@ -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:232 -msgid "port the server running on" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:233 -msgid "host the server running on" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:234 -msgid "path to the config file" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:235 -msgid "path to the modules directory" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:236 -msgid "web panel" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:237 -msgid "path to log file" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:239 -msgid "Lithium Command Line Interface:" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:240 -msgid "End." -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:248 -#, c-format -msgid "Failed to parser command line : %s" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:269 -#, c-format -msgid "Command line server port : %d" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:275 -#, c-format -msgid "Set server port to %d" -msgstr "" diff --git a/locale/po/fr_FR/lithium.po b/locale/po/fr_FR/lithium.po deleted file mode 100644 index 94c1bb2c..00000000 --- a/locale/po/fr_FR/lithium.po +++ /dev/null @@ -1,46 +0,0 @@ -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:232 -msgid "port the server running on" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:233 -msgid "host the server running on" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:234 -msgid "path to the config file" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:235 -msgid "path to the modules directory" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:236 -msgid "web panel" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:237 -msgid "path to log file" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:239 -msgid "Lithium Command Line Interface:" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:240 -msgid "End." -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:248 -#, c-format -msgid "Failed to parser command line : %s" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:269 -#, c-format -msgid "Command line server port : %d" -msgstr "" - -#: E:/msys64/home/Qrm/OpenAPT/src/App.cpp:275 -#, c-format -msgid "Set server port to %d" -msgstr "" diff --git a/locale/po/zh-CN.UTF-8/lithium.po b/locale/po/zh-CN.UTF-8/lithium.po deleted file mode 100644 index bdf1643a..00000000 --- a/locale/po/zh-CN.UTF-8/lithium.po +++ /dev/null @@ -1,258 +0,0 @@ -# Language zh-CN translations for Lithium package. -# Copyright (C) 2023 Max Qian -# This file is distributed under the same license as the Lithium package. -# , 2023. -# -msgid "" -msgstr "" -"Project-Id-Version: Lithium \n" -"Report-Msgid-Bugs-To: astro_air@126.com\n" -"POT-Creation-Date: 2023-10-07 12:34+0000\n" -"PO-Revision-Date: 2023-10-07 12:36+0000\n" -"Last-Translator: \n" -"Language-Team: Language zh-CN\n" -"Language: zh-CN\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ASCII\n" -"Content-Transfer-Encoding: 8bit\n" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:63 -msgid "Failed to destroy ThreadManager: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:82 -msgid "Thread manager has stopped, cannot add new thread" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:100 -#: /workspaces/Lithium/src/modules/thread/thread.cpp:122 -msgid "Unhandled exception in thread: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:128 -msgid "Added thread: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:133 -msgid "Failed to add thread {}: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:149 -msgid "All threads joined" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:153 -msgid "Failed to join all threads: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:163 -#: /workspaces/Lithium/src/modules/thread/thread.cpp:180 -#: /workspaces/Lithium/src/modules/thread/thread.cpp:194 -#: /workspaces/Lithium/src/modules/thread/thread.cpp:205 -msgid "Thread {} not found" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:172 -msgid "Thread {} joined" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:184 -msgid "Failed to join thread {}: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:210 -msgid "Failed to check if thread {} is running: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:75 -msgid "Failed to create PowerShell process" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:85 -msgid "Running command: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:98 -#: /workspaces/Lithium/src/modules/system/process.cpp:122 -#: /workspaces/Lithium/src/modules/system/process.cpp:143 -msgid "Failed to create process" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:108 -#: /workspaces/Lithium/src/modules/system/process.cpp:153 -msgid "Process created: {} (PID: {})" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:132 -msgid "Running script: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:170 -#: /workspaces/Lithium/src/modules/system/process.cpp:182 -msgid "Process terminated: {} (PID: {})" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:174 -msgid "Failed to terminate process" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:190 -#: /workspaces/Lithium/src/modules/system/process.cpp:246 -msgid "Process not found" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:205 -msgid "Process not found by name: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:212 -msgid "Currently running processes:" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:216 -msgid "{} (PID: {})" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:261 -msgid "Process completed: {} (PID: {})" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:265 -msgid "Failed to wait for process completion" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:271 -#, c-format -msgid "Process completed: %s (PID: %d)" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:276 -msgid "All processes completed." -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:287 -msgid "Failed to create process snapshot" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:328 -msgid "Failed to open /proc directory" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:357 -msgid "Failed to get process path" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:378 -msgid "Failed to get process info length" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:385 -msgid "Failed to allocate memory" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:391 -msgid "Failed to get process info" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:94 -msgid "CreateToolhelp32Snapshot failed." -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:113 -msgid "Watching process with PID: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:124 -msgid "Process exited with code: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:133 -msgid "GetExitCodeProcess failed." -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:143 -msgid "OpenProcess failed." -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:170 -msgid "Process exited with status: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:179 -msgid "Process terminated by signal: {}" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:223 -msgid "port the server running on" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:224 -msgid "host the server running on" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:225 -msgid "path to the config file" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:226 -msgid "path to the modules directory" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:227 -msgid "web panel" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:228 -msgid "path to log file" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:230 -msgid "Lithium Command Line Interface:" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:231 -msgid "End." -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:239 -#, c-format -msgid "Failed to parser command line : %s" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:260 -#, c-format -msgid "Command line server port : %d" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:266 -#, c-format -msgid "Set server port to %d" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:67 -msgid "Failed to load Lithium App , error : {}" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:79 -msgid "Get config value: {}" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:85 -msgid "Set {} to {}" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:272 -msgid "Failed to run chai command : {}" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:290 -msgid "Failed to run chai multi command {}" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:303 -msgid "Failed to load chaiscript file {}" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:316 -msgid "Failed to run chai script {}" -msgstr "" diff --git a/locale/po/zh-CN/lithium.po b/locale/po/zh-CN/lithium.po deleted file mode 100644 index c863404b..00000000 --- a/locale/po/zh-CN/lithium.po +++ /dev/null @@ -1,258 +0,0 @@ -# Language zh-CN translations for Lithium package. -# Copyright (C) 2023 Max Qian -# This file is distributed under the same license as the Lithium package. -# , 2023. -# -msgid "" -msgstr "" -"Project-Id-Version: Lithium \n" -"Report-Msgid-Bugs-To: astro_air@126.com\n" -"POT-Creation-Date: 2023-10-07 12:34+0000\n" -"PO-Revision-Date: 2023-10-07 12:37+0000\n" -"Last-Translator: \n" -"Language-Team: Language zh-CN\n" -"Language: zh-CN\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ASCII\n" -"Content-Transfer-Encoding: 8bit\n" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:63 -msgid "Failed to destroy ThreadManager: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:82 -msgid "Thread manager has stopped, cannot add new thread" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:100 -#: /workspaces/Lithium/src/modules/thread/thread.cpp:122 -msgid "Unhandled exception in thread: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:128 -msgid "Added thread: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:133 -msgid "Failed to add thread {}: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:149 -msgid "All threads joined" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:153 -msgid "Failed to join all threads: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:163 -#: /workspaces/Lithium/src/modules/thread/thread.cpp:180 -#: /workspaces/Lithium/src/modules/thread/thread.cpp:194 -#: /workspaces/Lithium/src/modules/thread/thread.cpp:205 -msgid "Thread {} not found" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:172 -msgid "Thread {} joined" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:184 -msgid "Failed to join thread {}: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/thread/thread.cpp:210 -msgid "Failed to check if thread {} is running: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:75 -msgid "Failed to create PowerShell process" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:85 -msgid "Running command: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:98 -#: /workspaces/Lithium/src/modules/system/process.cpp:122 -#: /workspaces/Lithium/src/modules/system/process.cpp:143 -msgid "Failed to create process" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:108 -#: /workspaces/Lithium/src/modules/system/process.cpp:153 -msgid "Process created: {} (PID: {})" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:132 -msgid "Running script: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:170 -#: /workspaces/Lithium/src/modules/system/process.cpp:182 -msgid "Process terminated: {} (PID: {})" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:174 -msgid "Failed to terminate process" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:190 -#: /workspaces/Lithium/src/modules/system/process.cpp:246 -msgid "Process not found" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:205 -msgid "Process not found by name: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:212 -msgid "Currently running processes:" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:216 -msgid "{} (PID: {})" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:261 -msgid "Process completed: {} (PID: {})" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:265 -msgid "Failed to wait for process completion" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:271 -#, c-format -msgid "Process completed: %s (PID: %d)" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:276 -msgid "All processes completed." -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:287 -msgid "Failed to create process snapshot" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:328 -msgid "Failed to open /proc directory" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:357 -msgid "Failed to get process path" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:378 -msgid "Failed to get process info length" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:385 -msgid "Failed to allocate memory" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/process.cpp:391 -msgid "Failed to get process info" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:94 -msgid "CreateToolhelp32Snapshot failed." -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:113 -msgid "Watching process with PID: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:124 -msgid "Process exited with code: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:133 -msgid "GetExitCodeProcess failed." -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:143 -msgid "OpenProcess failed." -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:170 -msgid "Process exited with status: {}" -msgstr "" - -#: /workspaces/Lithium/src/modules/system/pid.cpp:179 -msgid "Process terminated by signal: {}" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:223 -msgid "port the server running on" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:224 -msgid "host the server running on" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:225 -msgid "path to the config file" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:226 -msgid "path to the modules directory" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:227 -msgid "web panel" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:228 -msgid "path to log file" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:230 -msgid "Lithium Command Line Interface:" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:231 -msgid "End." -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:239 -#, c-format -msgid "Failed to parser command line : %s" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:260 -#, c-format -msgid "Command line server port : %d" -msgstr "" - -#: /workspaces/Lithium/src/App.cpp:266 -#, c-format -msgid "Set server port to %d" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:67 -msgid "Failed to load Lithium App , error : {}" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:79 -msgid "Get config value: {}" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:85 -msgid "Set {} to {}" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:272 -msgid "Failed to run chai command : {}" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:290 -msgid "Failed to run chai multi command {}" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:303 -msgid "Failed to load chaiscript file {}" -msgstr "" - -#: /workspaces/Lithium/src/LithiumApp.cpp:316 -msgid "Failed to run chai script {}" -msgstr "" diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..d7e5043a --- /dev/null +++ b/meson.build @@ -0,0 +1,80 @@ +project('Lithium', 'cpp') + +lithium_src_dir = 'src' +lithium_module_dir = join_paths(lithium_src_dir, 'atom') +lithium_client_dir = join_paths(lithium_src_dir, 'client') +lithium_component_dir = join_paths(lithium_src_dir, 'addon') +lithium_task_dir = join_paths(lithium_src_dir, 'task') + +libs_dir = 'libs' + +lithium_deps = [ + dependency('oatpp-websocket'), + dependency('oatpp-swagger'), + dependency('oatpp-openssl'), + dependency('oatpp-zlib'), + dependency('loguru'), + dependency('libzippp'), + dependency('fmt'), + dependency('cfitsio'), + dependency('openssl'), + dependency('sqlite3'), + dependency('cpp_httplib'), + dependency('backward'), + dependency('tinyxml2'), + dependency('pocketpy'), +] + +cpp = meson.get_compiler('cpp') + +lithium_server_src = files([ + join_paths(lithium_src_dir, 'app.cpp'), +]) + +lithium_server_library_src = files([ + join_paths(lithium_src_dir, 'device/server/ascom.cpp'), + join_paths(lithium_src_dir, 'device/server/hydrogen.cpp'), + join_paths(lithium_src_dir, 'device/server/hydrogen_driver.cpp'), + join_paths(lithium_src_dir, 'device/server/connector.cpp'), + join_paths(lithium_src_dir, 'device/manager.cpp'), + join_paths(lithium_src_dir, 'device/utils/utils.cpp'), + join_paths(lithium_component_dir, 'addons.cpp'), + join_paths(lithium_component_dir, 'compiler.cpp'), + join_paths(lithium_component_dir, 'finder.cpp'), + join_paths(lithium_component_dir, 'loader.cpp'), + join_paths(lithium_component_dir, 'manager.cpp'), + join_paths(lithium_component_dir, 'sandbox.cpp'), + join_paths(lithium_component_dir, 'sort.cpp'), + join_paths(lithium_src_dir, 'config/configor.cpp'), + join_paths(lithium_src_dir, 'debug/terminal.cpp'), + join_paths(lithium_task_dir, 'manager.cpp'), + join_paths(lithium_task_dir, 'generator.cpp'), + join_paths(lithium_task_dir, 'container.cpp'), + join_paths(lithium_task_dir, 'tick.cpp'), + join_paths(lithium_task_dir, 'loader.cpp'), + join_paths(lithium_task_dir, 'list.cpp'), + join_paths(lithium_task_dir, 'pool.cpp'), + join_paths(lithium_src_dir, 'LithiumApp.cpp'), +]) + +lithium_server_library = static_library( + 'lithium_server-library', + lithium_server_library_src, + dependencies: lithium_deps, + include_directories: include_directories('.', 'libs', 'libs/oatpp', 'libs/oatpp-swagger', 'libs/oatpp-websocket'), +) + +lithium_server = executable( + 'lithium_server', + lithium_server_src, + dependencies: lithium_deps, + include_directories: include_directories('.', 'libs', 'libs/oatpp', 'libs/oatpp-swagger', 'libs/oatpp-websocket'), + link_with: lithium_server_library, + link_args: cpp.get_supported_link_arguments(), +) + +configure_file( + input: 'config.h.in', + output: 'config.h', +) + diff --git a/scripts/build_ci.sh b/scripts/build_ci.sh index ab1314e6..d092c611 100644 --- a/scripts/build_ci.sh +++ b/scripts/build_ci.sh @@ -1,3 +1,68 @@ -sudo apt-get update && sudo apt-get upgrade -y -sudo apt install gcc g++ cmake -sudo apt install libcfitsio-dev zlib1g-dev libssl-dev libzip-dev libnova-dev libfmt-dev \ No newline at end of file +#!/bin/bash + +set -e + +echo "Checking system environment..." + +# Check if the script is run with sudo +if [[ $EUID -ne 0 ]]; then + echo "This script must be run with sudo." + exit 1 +fi + +# Check if the system is Debian-based +if ! command -v apt-get &> /dev/null; then + echo "This script only supports Debian-based systems." + exit 1 +fi + +# Check if gcc and g++ are installed +if ! command -v gcc &> /dev/null || ! command -v g++ &> /dev/null; then + echo "gcc and g++ are not installed. Installing..." + apt-get install -y gcc g++ +else + echo "gcc and g++ are already installed." +fi + +# Check if g++ version is at least 10 +gpp_version=$(g++ --version | grep -oP '(?<=g\+\+ )[0-9]+') +if [ "$gpp_version" -lt "10" ]; then + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt-get install gcc-13 g++-13 -y +fi + +# Check if CMake is installed +if ! command -v cmake &> /dev/null; then + echo "CMake is not installed. Installing..." + apt-get install -y cmake +else + echo "CMake is already installed." +fi + +# Check if CMake version is at least 3.20 +cmake_version=$(cmake --version | grep -oP '(?<=version )([0-9]+\.[0-9]+)') +if [ "$(printf "%s\n" "3.20" "$cmake_version" | sort -V | head -n1)" != "3.20" ]; then + wget https://cmake.org/files/v3.28/cmake-3.28.0-rc5.tar.gz + tar -zxvf cmake-3.28.0-rc5.tar.gz + cd cmake-3.28.0-rc5 + ./bootstrap && make && sudo make install + cd .. +fi + +echo "Updating system packages..." +apt-get update +apt-get upgrade -y + +echo "Installing dependencies..." +apt-get install -y libcfitsio-dev zlib1g-dev libssl-dev libzip-dev libnova-dev libfmt-dev + +echo "Checking installed dependencies..." +for lib in cfitsio zlib ssl zip nova fmt; do + if ! ldconfig -p | grep -q "lib$lib"; then + echo "lib$lib is not properly installed. Please check the installation manually." + else + echo "lib$lib is properly installed." + fi +done + +echo "Build environment setup completed." diff --git a/scripts/build_win.sh b/scripts/build_win.sh index 13bd7725..d910a358 100644 --- a/scripts/build_win.sh +++ b/scripts/build_win.sh @@ -1,10 +1,75 @@ +#!/bin/bash + +set -e + +echo "Checking system environment..." + +# Check if pacman is available +if ! command -v pacman &> /dev/null; then + echo "This script requires an MSYS2 environment with pacman." + exit 1 +fi + +echo "Updating MSYS2 mirror list..." sed -i "s#https\?://mirror.msys2.org/#https://mirrors.tuna.tsinghua.edu.cn/msys2/#g" /etc/pacman.d/mirrorlist* -pacman -Syu -pacman -S mingw-w64-x86_64-toolchain -pacman -S mingw-w64-x86_64-dlfcn -pacman -S mingw-w64-x86_64-cfitsio -pacman -S mingw-w64-x86_64-cmake -pacman -S mingw-w64-x86_64-libzip -pacman -S mingw-w64-x86_64-zlib -pacman -S mingw-w64-x86_64-fmt -pacman -S mingw-w64-x86_64-libnova \ No newline at end of file + +echo "Updating system packages..." +pacman -Syu --noconfirm + +echo "Installing mingw-w64-x86_64-toolchain..." +if ! pacman -Q mingw-w64-x86_64-toolchain &> /dev/null; then + pacman -S --noconfirm mingw-w64-x86_64-toolchain +else + echo "mingw-w64-x86_64-toolchain is already installed." +fi + +echo "Installing mingw-w64-x86_64-dlfcn..." +if ! pacman -Q mingw-w64-x86_64-dlfcn &> /dev/null; then + pacman -S --noconfirm mingw-w64-x86_64-dlfcn +else + echo "mingw-w64-x86_64-dlfcn is already installed." +fi + +echo "Installing mingw-w64-x86_64-cfitsio..." +if ! pacman -Q mingw-w64-x86_64-cfitsio &> /dev/null; then + pacman -S --noconfirm mingw-w64-x86_64-cfitsio +else + echo "mingw-w64-x86_64-cfitsio is already installed." +fi + +echo "Installing mingw-w64-x86_64-cmake..." +if ! pacman -Q mingw-w64-x86_64-cmake &> /dev/null; then + pacman -S --noconfirm mingw-w64-x86_64-cmake +else + echo "mingw-w64-x86_64-cmake is already installed." +fi + +echo "Installing mingw-w64-x86_64-libzip..." +if ! pacman -Q mingw-w64-x86_64-libzip &> /dev/null; then + pacman -S --noconfirm mingw-w64-x86_64-libzip +else + echo "mingw-w64-x86_64-libzip is already installed." +fi + +echo "Installing mingw-w64-x86_64-zlib..." +if ! pacman -Q mingw-w64-x86_64-zlib &> /dev/null; then + pacman -S --noconfirm mingw-w64-x86_64-zlib +else + echo "mingw-w64-x86_64-zlib is already installed." +fi + +echo "Installing mingw-w64-x86_64-fmt..." +if ! pacman -Q mingw-w64-x86_64-fmt &> /dev/null; then + pacman -S --noconfirm mingw-w64-x86_64-fmt +else + echo "mingw-w64-x86_64-fmt is already installed." +fi + +echo "Installing mingw-w64-x86_64-libnova..." +if ! pacman -Q mingw-w64-x86_64-libnova &> /dev/null; then + pacman -S --noconfirm mingw-w64-x86_64-libnova +else + echo "mingw-w64-x86_64-libnova is already installed." +fi + +echo "Environment setup completed." \ No newline at end of file diff --git a/scripts/install_msys2.ps1 b/scripts/install_msys2.ps1 new file mode 100644 index 00000000..34577ad4 --- /dev/null +++ b/scripts/install_msys2.ps1 @@ -0,0 +1,29 @@ +# 设置MSYS2的下载URL和安装路径 +$msys2_url = "https://github.com/msys2/msys2-installer/releases/download/2022-06-03/msys2-x86_64-20220603.exe" +$msys2_installer = "msys2-x86_64-20220603.exe" +$install_dir = "C:\msys64" + +# 下载MSYS2安装程序 +Write-Output "Downloading MSYS2 installer..." +Invoke-WebRequest -Uri $msys2_url -OutFile $msys2_installer + +# 安装MSYS2 +Write-Output "Installing MSYS2..." +Start-Process -FilePath .\$msys2_installer -ArgumentList "/S /D=$install_dir" -Wait + +# 配置pacman +Write-Output "Configuring pacman..." +$pacman_conf = "C:\msys64\etc\pacman.conf" +Add-Content -Path $pacman_conf -Value 'Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/x86_64/' +Add-Content -Path $pacman_conf -Value 'Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/i686/' +Add-Content -Path $pacman_conf -Value 'Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/msys/$arch/' + +# 更新系统包 +Write-Output "Updating system packages..." +C:\msys64\usr\bin\bash.exe -lc "pacman -Syu --noconfirm" + +# 安装必要的开发工具和库 +Write-Output "Installing development tools and libraries..." +C:\msys64\usr\bin\bash.exe -lc "pacman -S --noconfirm mingw-w64-x86_64-toolchain mingw-w64-x86_64-dlfcn mingw-w64-x86_64-cfitsio mingw-w64-x86_64-cmake mingw-w64-x86_64-libzip mingw-w64-x86_64-zlib mingw-w64-x86_64-fmt mingw-w64-x86_64-libnova" + +Write-Output "MSYS2 installation and setup completed." \ No newline at end of file diff --git a/scripts/nginx.sh b/scripts/nginx.sh index 20a7b377..83781c1d 100644 --- a/scripts/nginx.sh +++ b/scripts/nginx.sh @@ -1,66 +1,86 @@ #!/bin/bash -# 定义颜色 +# Define colors GREEN='\033[0;32m' RED='\033[0;31m' NC='\033[0m' -# 定义 Nginx 路径 +# Define Nginx paths NGINX_PATH="/etc/nginx" NGINX_CONF="$NGINX_PATH/nginx.conf" NGINX_BINARY="/usr/sbin/nginx" -# 函数: 启动 Nginx +# Function: Install Nginx if not installed +install_nginx() { + if ! command -v nginx &>/dev/null; then + echo "Installing Nginx..." + # Add installation commands according to the package manager used (apt, yum, etc.) + # Example for apt: + sudo apt-get update + sudo apt-get install nginx -y + fi +} + +# Function: Start Nginx start_nginx() { if [ -f "$NGINX_BINARY" ]; then $NGINX_BINARY - echo -e "${GREEN}Nginx 已启动${NC}" + echo -e "${GREEN}Nginx has been started${NC}" else - echo -e "${RED}无法找到 Nginx 二进制文件${NC}" + echo -e "${RED}Nginx binary not found${NC}" fi } -# 函数: 停止 Nginx +# Function: Stop Nginx stop_nginx() { if [ -f "$NGINX_BINARY" ]; then $NGINX_BINARY -s stop - echo -e "${GREEN}Nginx 已停止${NC}" + echo -e "${GREEN}Nginx has been stopped${NC}" else - echo -e "${RED}无法找到 Nginx 二进制文件${NC}" + echo -e "${RED}Nginx binary not found${NC}" fi } -# 函数: 重新加载 Nginx 配置 +# Function: Reload Nginx configuration reload_nginx() { if [ -f "$NGINX_BINARY" ]; then $NGINX_BINARY -s reload - echo -e "${GREEN}Nginx 配置已重新加载${NC}" + echo -e "${GREEN}Nginx configuration has been reloaded${NC}" else - echo -e "${RED}无法找到 Nginx 二进制文件${NC}" + echo -e "${RED}Nginx binary not found${NC}" fi } -# 函数: 检查 Nginx 配置文件语法 +# Function: Check Nginx configuration syntax check_config() { if [ -f "$NGINX_CONF" ]; then $NGINX_BINARY -t -c "$NGINX_CONF" else - echo -e "${RED}无法找到 Nginx 配置文件${NC}" + echo -e "${RED}Nginx configuration file not found${NC}" fi } -# 函数: 显示帮助信息 +# Function: Show help message show_help() { echo "Usage: $0 [start|stop|reload|check|help]" - echo " start 启动 Nginx" - echo " stop 停止 Nginx" - echo " reload 重新加载 Nginx 配置" - echo " check 检查 Nginx 配置文件语法" - echo " help 显示帮助信息" + echo " start Start Nginx" + echo " stop Stop Nginx" + echo " reload Reload Nginx configuration" + echo " check Check Nginx configuration syntax" + echo " help Show help message" } -# 主函数 +# Main function main() { + if [ "$1" == "help" ]; then + show_help + exit 0 + fi + + # Check if Nginx is installed + install_nginx + + # Execute the command case "$1" in start) start_nginx @@ -74,15 +94,13 @@ main() { check) check_config ;; - help) - show_help - ;; *) - echo -e "${RED}无效的命令${NC}" + echo -e "${RED}Invalid command${NC}" show_help + exit 1 ;; esac } -# 执行主函数 -main "$1" \ No newline at end of file +# Execute main function with the provided argument +main "$1" diff --git a/scripts/start-hotspot.sh b/scripts/start-hotspot.sh index 0995a745..bbb50ddb 100644 --- a/scripts/start-hotspot.sh +++ b/scripts/start-hotspot.sh @@ -3,35 +3,82 @@ # Set default values SSID="LithiumServer" PASSWORD="lithiumserver" +PACKAGE_MANAGER="" + +# Function to detect package manager +detect_package_manager() { + if command -v apt-get &>/dev/null; then + PACKAGE_MANAGER="apt" + elif command -v yum &>/dev/null; then + PACKAGE_MANAGER="yum" + elif command -v dnf &>/dev/null; then + PACKAGE_MANAGER="dnf" + else + echo "Unsupported package manager. Please install 'hostapd' and 'dnsmasq' manually." + exit 1 + fi +} + +# Function to install packages +install_packages() { + case $PACKAGE_MANAGER in + apt) + sudo apt-get update + sudo apt-get install hostapd dnsmasq -y + ;; + yum|dnf) + sudo $PACKAGE_MANAGER install hostapd dnsmasq -y + ;; + *) + echo "Unsupported package manager." + exit 1 + ;; + esac +} + +# Function to start services +start_services() { + sudo systemctl start dnsmasq + sudo systemctl start hostapd +} + +# Function to check if services are running +check_services() { + if ! systemctl is-active --quiet dnsmasq || ! systemctl is-active --quiet hostapd; then + echo "Failed to start the hotspot." + exit 1 + fi +} + +# Main script # Process arguments -while [[ $# -gt 0 ]] -do -key="$1" +while [[ $# -gt 0 ]]; do + key="$1" -case $key in - -s|--ssid) - SSID="$2" - shift # past argument - shift # past value - ;; - -p|--password) - PASSWORD="$2" - shift # past argument - shift # past value - ;; - *) # unknown option - echo "Unknown option: $1" - exit 1 - ;; -esac + case $key in + -s|--ssid) + SSID="$2" + shift 2 + ;; + -p|--password) + PASSWORD="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac done +# Detect package manager +detect_package_manager + # Check if hostapd and dnsmasq are installed if ! dpkg -s hostapd dnsmasq > /dev/null 2>&1; then echo "Installing hostapd and dnsmasq..." - sudo apt-get update - sudo apt-get install hostapd dnsmasq -y + install_packages fi # Configure dnsmasq @@ -54,12 +101,9 @@ wpa_pairwise=TKIP rsn_pairwise=CCMP" | sudo tee /etc/hostapd/hostapd.conf > /dev/null # Start dnsmasq and hostapd -sudo systemctl start dnsmasq -sudo systemctl start hostapd +start_services # Check if the hotspot is started -ip addr show wlan0 | grep inet | awk '{print $2}' | cut -d/ -f1 | if grep -qE '^(192\.168\.4\.[0-9]{1,3})$'; then - echo "Hotspot $SSID has been started with password $PASSWORD" -else - echo "Failed to start the hotspot" -fi +check_services + +echo "Hotspot $SSID has been started with password $PASSWORD" diff --git a/src/App.cpp b/src/App.cpp index 2bb8c4ed..3125a486 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -1,49 +1,20 @@ /* - * App.cpp + * app.cpp * * Copyright (C) 2023-2024 Max Qian */ /************************************************* -Date: 2023-7-13 +Date: 2024-14 -Description: Main +Description: Main Entry **************************************************/ -#ifdef ENABLE_WEB_SERVER -// This is for debug only, please remove it in production -// Oatpp server is still experimental, it may be improved in the future -#include "AppComponent.hpp" +#include "lithiumapp.hpp" -#ifdef ENABLE_ASYNC -#include "controller/AsyncConfigController.hpp" -#include "controller/AsyncDeviceController.hpp" -#include "controller/AsyncIOController.hpp" -#include "controller/AsyncProcessController.hpp" -#include "controller/AsyncStaticController.hpp" -#include "controller/AsyncSystemController.hpp" -#include "controller/AsyncUploadController.hpp" -// #include "controller/AsyncWebSocketController.hpp" -#include "controller/AsyncClientController.hpp" -#include "oatpp-swagger/AsyncController.hpp" -#else -#include "oatpp-swagger/Controller.hpp" -#endif - -#include "oatpp/network/Server.hpp" - -#define ADD_CONTROLLER(controller, docEndpoints, router, logMessage) \ - auto controller##_ptr = controller::createShared(); \ - docEndpoints.append(controller##_ptr->getEndpoints()); \ - router->addController(controller##_ptr); \ - DLOG_F(INFO, logMessage " loaded"); - -#endif -#include - -#include "LithiumApp.hpp" +#include "preload.hpp" #include "atom/log/loguru.hpp" #include "atom/server/global_ptr.hpp" @@ -57,6 +28,8 @@ Description: Main using namespace Lithium::Terminal; #endif +#include "server/App.hpp" + #include #include #include @@ -66,97 +39,7 @@ using namespace Lithium::Terminal; #include #endif -void BusLoggerFunction(void *user_data, const loguru::Message &message) { - Lithium::MyApp->sendJsonMessage("log", {{"message", message.message}, - {"level", message.verbosity}, - {"file", message.filename}, - {"line", message.line}, - {"timestamp", message.preamble}}); -} - -#ifdef ENABLE_WEB_SERVER -void runServer() { - DLOG_F(INFO, "Loading App component ..."); -#if ENABLE_IPV6 - AppComponent components( - Lithium::MyApp->GetConfig("config/server").value("host", "::"), - Lithium::MyApp->GetConfig("config/server") - .value("port", 8000)); // Create scope Environment components -#else - DLOG_F(INFO, "Server host:", Lithium::MyApp->GetConfig({"key", "config/server"}) - .value("host", "0.0.0.0")); - DLOG_F(INFO, "Server port:", Lithium::MyApp->GetConfig({"key", "config/server"}) - .value("port", 8000)); - AppComponent components( - Lithium::MyApp->GetConfig({"key", "config/server"}) - .value("host", "0.0.0.0"), - Lithium::MyApp->GetConfig({"key", "config/server"}) - .value("port", 8000)); // Create scope Environment components -#endif - DLOG_F(INFO, "App component loaded"); - - /* Get router component */ - OATPP_COMPONENT(std::shared_ptr, router); - oatpp::web::server::api::Endpoints docEndpoints; - /* Add routes & documents */ - - ADD_CONTROLLER(ConfigController, docEndpoints, router, - "AsyncConfigController"); - - ADD_CONTROLLER(StaticController, docEndpoints, router, - "AsyncStaticController"); - - ADD_CONTROLLER(SystemController, docEndpoints, router, - "AsyncSystemController"); - - // ADD_CONTROLLER(WebSocketController, docEndpoints, router, - // "AsyncWebSocketController"); - - ADD_CONTROLLER(IOController, docEndpoints, router, "AsyncIOController"); - - ADD_CONTROLLER(ProcessController, docEndpoints, router, - "AsyncProcessController"); - - ADD_CONTROLLER(ClientController, docEndpoints, router, - "AsyncClientController"); - - DLOG_F(INFO, "Starting to load API doc controller"); -#if ENABLE_ASYNC - router->addController( - oatpp::swagger::AsyncController::createShared(docEndpoints)); -#else - router->addController( - oatpp::swagger::Controller::createShared(docEndpoints)); -#endif - DLOG_F(INFO, "API doc controller loaded"); - - /* Load websocket route */ - // router->addController(WebSocketController::createShared()); - - /* Get connection handler component */ - OATPP_COMPONENT(std::shared_ptr, - connectionHandler, "http"); - - /* Get connection provider component */ - OATPP_COMPONENT(std::shared_ptr, - connectionProvider); - - DLOG_F(INFO, "Loaded server components ... Prepare for starting ..."); - /* create server */ - oatpp::network::Server server(connectionProvider, connectionHandler); - - DLOG_F(INFO, "Server running on port {}...", - connectionProvider->getProperty("port").toString()->c_str()); - - /* This is a block function that will be called when the server is started - */ - server.run(); -} -#endif - - -// TODO: add network logger, not implemented yet -// struct MyNetworkLogger {}; +#include "argparse/argparse.hpp" /** * @brief setup log file @@ -176,16 +59,9 @@ void setupLogFile() { loguru::add_file(logFilePath.string().c_str(), loguru::Append, loguru::Verbosity_MAX); - // MyNetworkLogger network_logger; - // TODO loguru::add_callback("network_logger", BusLoggerFunction, - // &network_logger, loguru::Verbosity_INFO); - loguru::set_fatal_handler([](const loguru::Message &message) { Atom::System::saveCrashLog(std::string(message.prefix) + message.message); -#if ENABLE_WEB_SERVER - oatpp::base::Environment::destroy(); -#endif }); } @@ -196,6 +72,8 @@ void setupLogFile() { * @return 0 on success */ int main(int argc, char *argv[]) { + // NOTE: gettext is not supported yet, it will cause compilation error on + // Mingw64 /* Add gettext */ #if ENABLE_GETTEXT bindtextdomain("lithium", "locale"); @@ -203,15 +81,16 @@ int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); textdomain("lithium"); #endif + // Set log file + setupLogFile(); // Init loguru log system loguru::init(argc, argv); - // Set log file - setupLogFile(); /* Parse arguments */ argparse::ArgumentParser program("Lithium Server"); + // NOTE: The command arguments' priority is higher than the config file program.add_argument("-P", "--port") .help("port the server running on") .default_value(8000); @@ -223,10 +102,13 @@ int main(int argc, char *argv[]) { .default_value("config.json"); program.add_argument("-M", "--module-path") .help("path to the modules directory") - .default_value("modules"); + .default_value("./modules"); program.add_argument("-W", "--web-panel") .help("web panel") .default_value(true); + program.add_argument("-D", "--debug") + .help("debug mode") + .default_value(false); program.add_argument("-L", "--log-file").help("path to log file"); program.add_description("Lithium Command Line Interface:"); @@ -235,32 +117,33 @@ int main(int argc, char *argv[]) { program.parse_args(argc, argv); Lithium::InitLithiumApp(argc, argv); - // Run oatpp server + // Create shared instance Lithium::MyApp = Lithium::LithiumApp::createShared(); - - auto cmd_port = program.get("--port"); - if (cmd_port != 8000) { - DLOG_F(INFO, "Command line server port : {}", cmd_port); - - auto port = - Lithium::MyApp->GetConfig("config/server").value("port", 8000); - if (port != cmd_port) { - Lithium::MyApp->SetConfig( - {{"key", "config/server/port"}, {"value", cmd_port}}); - DLOG_F(INFO, "Set server port to {}", cmd_port); - } - } + // Parse arguments try { auto cmd_host = program.get("--host"); + auto cmd_port = program.get("--port"); auto cmd_config_path = program.get("--config"); auto cmd_module_path = program.get("--module-path"); auto cmd_web_panel = program.get("--web-panel"); + auto cmd_debug = program.get("--debug"); if (!cmd_host.empty()) { Lithium::MyApp->SetConfig( {{"key", "config/server/host"}, {"value", cmd_host}}); DLOG_F(INFO, "Set server host to {}", cmd_host); } + if (cmd_port != 8000) { + DLOG_F(INFO, "Command line server port : {}", cmd_port); + + auto port = Lithium::MyApp->GetConfig("config/server") + .value("port", 8000); + if (port != cmd_port) { + Lithium::MyApp->SetConfig( + {{"key", "config/server/port"}, {"value", cmd_port}}); + DLOG_F(INFO, "Set server port to {}", cmd_port); + } + } if (!cmd_config_path.empty()) { Lithium::MyApp->SetConfig({{"key", "config/server/configpath"}, {"value", cmd_config_path}}); @@ -279,49 +162,30 @@ int main(int argc, char *argv[]) { DLOG_F(INFO, "Disable web panel"); } } + + if (cmd_debug) { + if (!Lithium::MyApp->GetConfig("config/server/debug").get()) { + Lithium::MyApp->SetConfig( + {{"key", "config/server/debug"}, {"value", true}}); + } + } else { + Lithium::MyApp->SetConfig( + {{"key", "config/server/debug"}, {"value", false}}); + DLOG_F(INFO, "Disable debug mode"); + } } catch (const std::bad_any_cast &e) { LOG_F(ERROR, "Invalid args format! Error: {}", e.what()); + Atom::System::saveCrashLog(e.what()); + return 1; } -#if ENABLE_TERMINAL - Lithium::MyApp->SetConfig( - {{"key", "config/terminal/enabled"}, {"value", true}}); - - CommandManager manager; - - // 注册指令函数 - manager.registerCommand("ls", lsCommand); - manager.registerCommand("pwd", pwdCommand); - manager.registerCommand("mkdir", mkdirCommand); - manager.registerCommand("cp", cpCommand); - manager.registerCommand("system", systemCommand); - - clearTerminal(); - - // 打印终端头部信息 - printHeader(); - - while (true) - { - // 获取终端输入 - std::string input = getTerminalInput(manager); - - // 运行指令函数 - std::string result = manager.runCommand(input, ""); - - // 在终端上显示执行结果 - std::cout << result << std::endl; + // In debug mode run the terminal first and will not run the server + if (Lithium::MyApp->GetConfig("config/server/debug").get()) { + ConsoleTerminal terminal; + terminal.run(); + } else { + runServer(); } -#endif - -#if ENABLE_WEB_SERVER - oatpp::base::Environment::init(); - // Run the main server - runServer(); - // Clean up all - oatpp::base::Environment::destroy(); -#endif - return 0; } \ No newline at end of file diff --git a/src/Launcher.cpp b/src/Launcher.cpp deleted file mode 100644 index d2a75bb0..00000000 --- a/src/Launcher.cpp +++ /dev/null @@ -1,69 +0,0 @@ - -#include "LauncherComponent.hpp" - -#include "controller/AuthController.hpp" -#include "controller/StaticController.hpp" -#include "controller/StoryController.hpp" - - -#include "oatpp-swagger/Controller.hpp" - -#include "oatpp/network/Server.hpp" - -#include - -void run() { - AppComponent components; // Create scope Environment components - - /* create ApiControllers and add endpoints to router */ - - auto router = components.httpRouter.getObject(); - - oatpp::web::server::api::Endpoints docEndpoints; - - docEndpoints.append( - router->addController(AuthController::createShared())->getEndpoints()); - docEndpoints.append( - router->addController(StoryController::createShared())->getEndpoints()); - - router->addController( - oatpp::swagger::Controller::createShared(docEndpoints)); - router->addController(StaticController::createShared()); - - /* create server */ - - oatpp::network::Server server( - components.serverConnectionProvider.getObject(), - components.serverConnectionHandler.getObject()); - - OATPP_LOGD("Server", "Running on port %s...", - components.serverConnectionProvider.getObject() - ->getProperty("port") - .toString() - ->c_str()); - - server.run(); -} - -/** - * main - */ -int main(int argc, const char *argv[]) { - oatpp::base::Environment::init(); - - run(); - - /* Print how much objects were created during app running, and what have - * left-probably leaked */ - /* Disable object counting for release builds using '-D - * OATPP_DISABLE_ENV_OBJECT_COUNTERS' flag for better performance */ - std::cout << "\nEnvironment:\n"; - std::cout << "objectsCount = " - << oatpp::base::Environment::getObjectsCount() << "\n"; - std::cout << "objectsCreated = " - << oatpp::base::Environment::getObjectsCreated() << "\n\n"; - - oatpp::base::Environment::destroy(); - - return 0; -} diff --git a/src/LauncherComponent.hpp b/src/LauncherComponent.hpp deleted file mode 100644 index 21572946..00000000 --- a/src/LauncherComponent.hpp +++ /dev/null @@ -1,109 +0,0 @@ - -#ifndef AppComponent_hpp -#define AppComponent_hpp - -#include "components/DatabaseComponent.hpp" -#include "components/SwaggerComponent.hpp" - - -#include "ErrorHandler.hpp" - -#include "interceptor/AuthInterceptor.hpp" -#include "oatpp/web/server/interceptor/AllowCorsGlobal.hpp" - - -#include "oatpp/network/tcp/server/ConnectionProvider.hpp" -#include "oatpp/web/server/HttpConnectionHandler.hpp" -#include "oatpp/web/server/HttpRouter.hpp" - - -#include "oatpp/parser/json/mapping/ObjectMapper.hpp" - -#include "oatpp/core/macro/component.hpp" - -/** - * Class which creates and holds Application components and registers - * components in oatpp::base::Environment Order of components initialization is - * from top to bottom - */ -class AppComponent { -public: - /** - * Swagger component - */ - SwaggerComponent swaggerComponent; - - /** - * Database component - */ - DatabaseComponent databaseComponent; - - OATPP_CREATE_COMPONENT(std::shared_ptr, jwt) - ([] { return std::make_shared("", ""); }()); - - /** - * Create ObjectMapper component to serialize/deserialize DTOs in - * Contoller's API - */ - OATPP_CREATE_COMPONENT(std::shared_ptr, - apiObjectMapper) - ([] { - auto objectMapper = - oatpp::parser::json::mapping::ObjectMapper::createShared(); - objectMapper->getDeserializer()->getConfig()->allowUnknownFields = - false; - return objectMapper; - }()); - - /** - * Create ConnectionProvider component which listens on the port - */ - OATPP_CREATE_COMPONENT( - std::shared_ptr, - serverConnectionProvider) - ([] { - return oatpp::network::tcp::server::ConnectionProvider::createShared( - {"0.0.0.0", 8000, oatpp::network::Address::IP_4}); - }()); - - /** - * Create Router component - */ - OATPP_CREATE_COMPONENT(std::shared_ptr, - httpRouter) - ([] { return oatpp::web::server::HttpRouter::createShared(); }()); - - /** - * Create ConnectionHandler component which uses Router component to route - * requests - */ - OATPP_CREATE_COMPONENT(std::shared_ptr, - serverConnectionHandler) - ([] { - OATPP_COMPONENT(std::shared_ptr, jwt); // get JWT component - OATPP_COMPONENT(std::shared_ptr, - router); // get Router component - OATPP_COMPONENT(std::shared_ptr, - objectMapper); // get ObjectMapper component - - auto connectionHandler = - oatpp::web::server::HttpConnectionHandler::createShared(router); - - connectionHandler->setErrorHandler( - std::make_shared(objectMapper)); - - connectionHandler->addRequestInterceptor( - std::make_shared< - oatpp::web::server::interceptor::AllowOptionsGlobal>()); - connectionHandler->addRequestInterceptor( - std::make_shared(jwt)); - - connectionHandler->addResponseInterceptor( - std::make_shared< - oatpp::web::server::interceptor::AllowCorsGlobal>()); - - return connectionHandler; - }()); -}; - -#endif /* AppComponent_hpp */ diff --git a/src/LithiumApp.cpp b/src/LithiumApp.cpp index 42bcdc02..4206c490 100644 --- a/src/LithiumApp.cpp +++ b/src/LithiumApp.cpp @@ -12,7 +12,7 @@ Description: Lithium App Enter **************************************************/ -#include "LithiumApp.hpp" +#include "lithiumapp.hpp" #include "config.h" @@ -31,9 +31,9 @@ Description: Lithium App Enter #include "atom/error/error_stack.hpp" #include "atom/log/loguru.hpp" +#include "atom/server/global_ptr.hpp" #include "atom/system/process.hpp" #include "atom/utils/time.hpp" -#include "atom/server/global_ptr.hpp" #include "utils/marco.hpp" #include "magic_enum/magic_enum.hpp" @@ -84,7 +84,6 @@ namespace Lithium { std::shared_ptr MyApp = nullptr; LithiumApp::LithiumApp() { - DLOG_SCOPE_FUNCTION(INFO); DLOG_F(INFO, "LithiumApp Constructor"); try { // Specialized Managers and Threads @@ -112,10 +111,10 @@ LithiumApp::LithiumApp() { // message // to the right type to process. // All of the messages are based on the Message class. - DLOG_SCOPE_F(INFO, "Start Message Processing Thread"); + DLOG_F(INFO, "Start Message Processing Thread"); m_MessageBus.lock()->StartProcessingThread(); - DLOG_SCOPE_F(INFO, "Register LithiumApp Member Functions"); + DLOG_F(INFO, "Register LithiumApp Member Functions"); LiRegisterMemberFunc("GetConfig", &LithiumApp::GetConfig); LiRegisterMemberFunc("SetConfig", &LithiumApp::SetConfig); @@ -125,7 +124,7 @@ LithiumApp::LithiumApp() { LOG_F(ERROR, "Failed to load Lithium App , error : {}", e.what()); throw std::runtime_error("Failed to load Lithium App"); } - DLOG_SCOPE_F(INFO, "Lithium App Initialized"); + DLOG_F(INFO, "Lithium App Initialized"); } LithiumApp::~LithiumApp() { @@ -159,8 +158,8 @@ void InitLithiumApp(int argc, char **argv) { // ScriptManager::createShared(GetPtr("MessageBus"))); AddPtr("lithium.device", DeviceManager::createShared( - GetPtr("lithium.bus"), - GetPtr("lithium.config"))); + GetPtr("lithium.bus").value(), + GetPtr("lithium.config").value())); AddPtr("lithium.device.hydrogen", HydrogenManager::createShared()); AddPtr("lithium.error.stack", std::make_shared()); @@ -178,7 +177,7 @@ void InitLithiumApp(int argc, char **argv) { AddPtr("lithium.utils.env", Atom::Utils::Env::createShared(argc, argv)); - // TODO: Addons path need to be configurable + // TODO: Addons path need to be configurable AddPtr("lithium.addon.loader", ModuleLoader::createShared("./modules")); AddPtr("lithium.addon.addon", AddonManager::createShared()); AddPtr("lithium.addon.manager", ComponentManager::createShared()); @@ -249,9 +248,9 @@ json LithiumApp::GetConfig(const json ¶ms) { CHECK_PARAM("key"); std::string key_path = params["key"].get(); json res; - if (json value = m_ConfigManager.lock()->getValue(key_path); - !value.is_null()) { - return createSuccessResponse(__func__, value); + if (auto value = m_ConfigManager.lock()->getValue(key_path); + value.has_value()) { + return createSuccessResponse(__func__, value.value()); } return createErrorResponse(__func__, json(), std::format("Key {} not found", key_path)); diff --git a/src/LithiumApp.hpp b/src/LithiumApp.hpp index c4801614..bd97ea3e 100644 --- a/src/LithiumApp.hpp +++ b/src/LithiumApp.hpp @@ -1,5 +1,5 @@ /* - * LithiumApp.hpp + * lithiumapp.hpp * * Copyright (C) 2023-2024 Max Qian */ diff --git a/src/addon/compiler.cpp b/src/addon/compiler.cpp index 336ee903..6840addc 100644 --- a/src/addon/compiler.cpp +++ b/src/addon/compiler.cpp @@ -14,403 +14,160 @@ Description: Compiler #include "compiler.hpp" -#include -#include +#include "utils/constant.hpp" + #include #include -#include +#include #include "atom/log/loguru.hpp" #include "atom/type/json.hpp" using json = nlohmann::json; namespace fs = std::filesystem; -#ifdef _WIN32 -#include -#define COMPILER "cl.exe" -#define CMD_PREFIX "" -#define CMD_SUFFIX ".dll" -#elif __APPLE__ -#include -#define COMPILER "clang++" -#define CMD_PREFIX "lib" -#define CMD_SUFFIX ".dylib" -#else -#include -#define COMPILER "g++" -#define CMD_PREFIX "lib" -#define CMD_SUFFIX ".so" -#endif - namespace Lithium { -bool Compiler::CompileToSharedLibraryAllinOne(const std::string &code, - const std::string &moduleName, - const std::string &functionName) { - DLOG_F(INFO, "Compiling module {}::{}...", moduleName, functionName); +bool Compiler::compileToSharedLibrary(std::string_view code, + std::string_view moduleName, + std::string_view functionName, + std::string_view optionsFile) { + LOG_F(INFO, "Compiling module {}::{}...", moduleName, functionName); - // 参数校验 if (code.empty() || moduleName.empty() || functionName.empty()) { LOG_F(ERROR, "Invalid parameters."); return false; } - // Check if the module is already compiled and cached - auto cachedResult = cache_.find(moduleName + "::" + functionName); - if (cachedResult != cache_.end()) { - DLOG_F(WARNING, - "Module {}::{} is already compiled, returning cached result.", - moduleName, functionName); - return true; - } - - // Create output directory if it does not exist - const std::string outputDir = "atom/global/"; - if (!fs::exists(outputDir)) { - DLOG_F(WARNING, "Output directory does not exist, creating it: {}", - outputDir); - try { - fs::create_directories(outputDir); - } catch (const std::exception &e) { - LOG_F(ERROR, "Failed to create output directory: {}", e.what()); - return false; - } - } - - // Read compile options from JSON file - std::string compileOptions = "-shared -fPIC -x c++ "; - std::ifstream compileOptionFile("compile_options.json"); - if (compileOptionFile.is_open()) { - json compileOptionsJson; - try { - compileOptionFile >> compileOptionsJson; - if (compileOptionsJson.contains("optimization_level") && - compileOptionsJson.contains("cplus_version") && - compileOptionsJson.contains("warnings")) { - compileOptions = - compileOptionsJson["optimization_level"] - .get() + - " " + - compileOptionsJson["cplus_version"].get() + - " " + compileOptionsJson["warnings"].get() + - " "; - } else { - LOG_F(ERROR, "Invalid format in compile_options.json."); - return false; - } - } catch (const std::exception &e) { - LOG_F(ERROR, "Error reading compile_options.json: {}", e.what()); - return false; - } - } - - // Specify output file path - std::string output = outputDir + moduleName + CMD_SUFFIX; - - // Syntax and semantic checking - std::stringstream syntaxCheckCmd; - syntaxCheckCmd << COMPILER << " -fsyntax-only -x c++ -"; - std::string syntaxCheckOutput; - if (RunShellCommand(syntaxCheckCmd.str(), code, syntaxCheckOutput) != 0) { - LOG_F(ERROR, "Syntax error in C++ code: {}", syntaxCheckOutput); - return false; - } - - // Compile code - std::string compilationOutput; - std::string cmd = - std::string(COMPILER) + " " + compileOptions + " - " + " -o " + output; - DLOG_F(INFO, "{}", cmd); - - int exitCode = RunShellCommand(cmd, code, compilationOutput); - if (exitCode != 0) { - LOG_F(ERROR, "Failed to compile C++ code: {}", compilationOutput); - return false; - } - - // Cache compiled module - cache_[moduleName + "::" + functionName] = output; - - /* - // Load the compiled module - if(m_App.GetModuleLoader()->LoadModule(output, moduleName)) { - LOG_S(INFO) << "Module " << moduleName << "::" << functionName << " - compiled successfully."; return true; } else { LOG_F(ERROR, "Failed to load - the compiled module: {}", output); return false; - } - */ - return false; -} - -// 编译为共享库 -bool Compiler::CompileToSharedLibrary(const std::string &code, - const std::string &moduleName, - const std::string &functionName, - const std::string &optionsFile) { - LOG_F(INFO, "Compiling module {}::{}...", moduleName, functionName); - - // 参数校验 - if (!CheckParameters(code, moduleName, functionName)) { - return false; - } - - // 检查是否已经编译并缓存 - if (IsModuleCached(moduleName, functionName, cache_)) { + // 检查模块是否已编译并缓存 + auto cachedModule = + cache_.find(fmt::format("{}::{}", moduleName, functionName)); + if (cachedModule != cache_.end()) { + LOG_F(WARNING, + "Module {}::{} is already compiled, using cached result.", + moduleName, functionName); return true; } // 创建输出目录 - const std::string outputDir = "atom/global/"; - if (!CreateOutputDirectory(outputDir)) { + const fs::path outputDir = "atom/global"; + createOutputDirectory(outputDir); + + const auto availableCompilers = findAvailableCompilers(); + if (availableCompilers.empty()) { + LOG_F(ERROR, "No available compilers found."); return false; } + LOG_F(INFO, "Available compilers: {}", fmt::join(availableCompilers, ", ")); // 读取编译选项 - std::string compileOptions = ReadCompileOptions(optionsFile); - if (compileOptions.empty()) { - return false; - } + std::ifstream optionsStream(optionsFile.data()); + const auto compileOptions = [&optionsStream] { + if (!optionsStream) { + LOG_F( + WARNING, + "Failed to open compile options file, using default options."); + return std::string{"-O2 -std=c++20 -Wall -shared -fPIC"}; + } + + try { + json optionsJson; + optionsStream >> optionsJson; + return fmt::format( + "{} {} {}", + optionsJson["optimization_level"].get(), + optionsJson["cplus_version"].get(), + optionsJson["warnings"].get()); + } catch (const std::exception& e) { + LOG_F(ERROR, "Failed to parse compile options file: {}", e.what()); + return std::string{"-O2 -std=c++20 -Wall -shared -fPIC"}; + } + }(); - // 检查语法 - if (!SyntaxCheck(code, COMPILER)) { + // 语法检查 + if (!syntaxCheck(code, constants::COMPILER)) { return false; } - // 指定输出文件路径 - std::string output = outputDir + moduleName + CMD_SUFFIX; - // 编译代码 - if (!CompileCode(code, COMPILER, compileOptions, output)) { + const auto outputPath = + outputDir / fmt::format("{}{}{}", constants::LIB_EXTENSION, moduleName, + constants::LIB_EXTENSION); + if (!compileCode(code, constants::COMPILER, compileOptions, outputPath)) { return false; } - // 缓存已编译的模块 - CacheCompiledModule(moduleName, functionName, output, cache_); - - return true; -} - -// 检查参数是否有效 -bool Compiler::CheckParameters(const std::string &code, - const std::string &moduleName, - const std::string &functionName) { - if (code.empty() || moduleName.empty() || functionName.empty()) { - LOG_F(ERROR, "Invalid parameters."); - return false; - } + // 缓存编译结果 + cache_[fmt::format("{}::{}", moduleName, functionName)] = outputPath; return true; } -// 检查模块是否已经编译并缓存 -bool Compiler::IsModuleCached( - const std::string &moduleName, const std::string &functionName, - std::unordered_map &cache_) { - std::string key = moduleName + "::" + functionName; - auto cachedResult = cache_.find(key); - if (cachedResult != cache_.end()) { - LOG_F(WARNING, "Module {}::{} is already compiled.", moduleName, - functionName); - return true; - } - return false; -} - -// 创建输出目录 -bool Compiler::CreateOutputDirectory(const std::string &outputDir) { +void Compiler::createOutputDirectory(const fs::path& outputDir) { if (!fs::exists(outputDir)) { - LOG_F(WARNING, "Output directory does not exist."); - try { - fs::create_directories(outputDir); - } catch (const std::exception &e) { - LOG_F(ERROR, "Failed to create output directory. {}", e.what()); - return false; - } - } - return true; -} - -// 从 JSON 文件中读取编译选项 -std::string Compiler::ReadCompileOptions(const std::string &optionsFile) { - std::ifstream compileOptionFile(optionsFile); - if (compileOptionFile.is_open()) { - json compileOptionsJson; - try { - compileOptionFile >> compileOptionsJson; - if (compileOptionsJson.contains("optimization_level") && - compileOptionsJson.contains("cplus_version") && - compileOptionsJson.contains("warnings")) { - std::string compileOptions = - compileOptionsJson["optimization_level"] - .get() + - " " + - compileOptionsJson["cplus_version"].get() + - " " + compileOptionsJson["warnings"].get(); - return compileOptions; - } else { - LOG_F(ERROR, "Invalid format in compile_options.json."); - return ""; - } - } catch (const std::exception &e) { - LOG_F(ERROR, "Error reading compile_options.json: {}", e.what()); - return ""; - } + LOG_F(WARNING, "Output directory {} does not exist, creating it.", + outputDir.string()); + fs::create_directories(outputDir); } - return ""; } -// 语法检查 -bool Compiler::SyntaxCheck(const std::string &code, - const std::string &compiler) { - std::stringstream syntaxCheckCmd; - syntaxCheckCmd << compiler << " -fsyntax-only -x c++ -"; - std::string syntaxCheckOutput; - if (RunShellCommand(syntaxCheckCmd.str(), code, syntaxCheckOutput) != 0) { - LOG_F(ERROR, "Syntax error in C++ code: {}", syntaxCheckOutput); +bool Compiler::syntaxCheck(std::string_view code, std::string_view compiler) { + const auto command = fmt::format("{} -fsyntax-only -xc++ -", compiler); + std::string output; + const auto exitCode = runCommand(command, code, output); + if (exitCode != 0) { + LOG_F(ERROR, "Syntax check failed:\n{}", output); return false; } return true; } -// 编译代码 -bool Compiler::CompileCode(const std::string &code, const std::string &compiler, - const std::string &compileOptions, - const std::string &output) { - std::string cmd = compiler + " " + compileOptions + " - " + " -o " + output; - DLOG_F(INFO, "{}", cmd); - +bool Compiler::compileCode(std::string_view code, std::string_view compiler, + std::string_view compileOptions, + const fs::path& output) { + const auto command = fmt::format("{} {} -xc++ - -o {}", compiler, + compileOptions, output.string()); std::string compilationOutput; - int exitCode = RunShellCommand(cmd, code, compilationOutput); + const auto exitCode = runCommand(command, code, compilationOutput); if (exitCode != 0) { - LOG_F(ERROR, "Failed to compile C++ code: {}", compilationOutput); + LOG_F(ERROR, "Compilation failed:\n{}", compilationOutput); return false; } return true; } -// 缓存已编译的模块 -void Compiler::CacheCompiledModule( - const std::string &moduleName, const std::string &functionName, - const std::string &output, - std::unordered_map &cache_) { - std::string key = moduleName + "::" + functionName; - cache_[key] = output; -} - -bool Compiler::CopyFile_(const std::string &source, - const std::string &destination) { - try { - std::ifstream src(source, std::ios::binary); - if (!src) { - LOG_F(ERROR, "Failed to open file for copy: {}", source); - return false; - } - - std::ofstream dst(destination, std::ios::binary); - if (!dst) { - LOG_F(ERROR, "Failed to create file for copy: {}", destination); - return false; - } +int Compiler::runCommand(std::string_view command, std::string_view input, + std::string& output) { + std::array buffer; + output.clear(); - dst << src.rdbuf(); - - if (!dst.good()) { - LOG_F(ERROR, "Error occurred while writing to destination file: {}", - destination); - return false; - } - - return true; - } catch (const std::exception &e) { - LOG_F(ERROR, "Exception occurred during file copy: {}", e.what()); - return false; + auto pipe = popen(command.data(), "r+"); + if (!pipe) { + LOG_F(ERROR, "Failed to run command: {}", command); + return -1; } -} -int Compiler::RunShellCommand(const std::string &command, - const std::string &input, std::string &output) { - int exitCode = -1; -#ifdef _WIN32 - HANDLE hStdoutRead = NULL; + fwrite(input.data(), sizeof(char), input.size(), pipe); + fclose(pipe); - STARTUPINFO si = {sizeof(si)}; - PROCESS_INFORMATION pi; - HANDLE hStdinRead = NULL, hStdoutWrite = NULL; - SECURITY_ATTRIBUTES sa; - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = NULL; - sa.bInheritHandle = TRUE; - if (!CreatePipe(&hStdinRead, &hStdoutWrite, &sa, 0)) { - LOG_F(ERROR, "Failed to create input pipe for shell command: {}", - command); - return exitCode; - } - if (!SetHandleInformation(hStdoutWrite, HANDLE_FLAG_INHERIT, 0)) { - LOG_F(ERROR, - "Failed to set input handle information for shell command: {}", - command); - CloseHandle(hStdinRead); - return exitCode; + pipe = popen(command.data(), "r"); + while (fgets(buffer.data(), buffer.size(), pipe) != nullptr) { + output += buffer.data(); } - if (!CreatePipe(&hStdoutRead, &hStdoutWrite, &sa, 0)) { - LOG_F(ERROR, "Failed to create output pipe for shell command: {}", - command); - CloseHandle(hStdinRead); - CloseHandle(hStdoutWrite); - return exitCode; - } - - si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - si.hStdInput = hStdinRead; - si.hStdOutput = hStdoutWrite; - si.hStdError = GetStdHandle(STD_ERROR_HANDLE); - - std::vector commandBuffer(command.begin(), command.end()); - commandBuffer.push_back('\0'); + return pclose(pipe); +} - if (!CreateProcess(NULL, &commandBuffer[0], NULL, NULL, TRUE, 0, NULL, NULL, - &si, &pi)) { - LOG_F(ERROR, "Failed to launch shell command: {}", command); - CloseHandle(hStdinRead); - CloseHandle(hStdoutWrite); - CloseHandle(hStdoutRead); - return exitCode; - } - CloseHandle(hStdinRead); - CloseHandle(hStdoutWrite); +std::vector Compiler::findAvailableCompilers() { + std::vector availableCompilers; - // Read the command output - char buffer[4096]; - DWORD bytesRead; - while (ReadFile(hStdoutRead, buffer, sizeof(buffer), &bytesRead, NULL)) { - if (bytesRead > 0) { - output.append(buffer, bytesRead); - } else { - break; + for (const auto& path : constants::COMPILER_PATHS) { + for (const auto& compiler : constants::COMMON_COMPILERS) { + std::filesystem::path compilerPath = + std::filesystem::path(path) / compiler; + if (std::filesystem::exists(compilerPath)) { + availableCompilers.push_back(compilerPath.string()); + } } } - // Wait for the command to finish - WaitForSingleObject(pi.hProcess, INFINITE); - GetExitCodeProcess(pi.hProcess, (LPDWORD)&exitCode); - - // Clean up - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - CloseHandle(hStdoutRead); -#else - FILE *pipe = popen(command.c_str(), "w"); - if (!pipe) { - LOG_F(ERROR, "Failed to popen shell command: {}", command); - return exitCode; - } - - fwrite(input.c_str(), 1, input.size(), pipe); - fclose(pipe); - - exitCode = WEXITSTATUS(pclose(pipe)); -#endif - - return exitCode; + return availableCompilers; } - } // namespace Lithium diff --git a/src/addon/compiler.hpp b/src/addon/compiler.hpp index 5829c4b4..4a9b63b8 100644 --- a/src/addon/compiler.hpp +++ b/src/addon/compiler.hpp @@ -12,140 +12,77 @@ Description: Compiler **************************************************/ -#pragma once +#ifndef LITHIUM_ADDON_COMPILER_HPP +#define LITHIUM_ADDON_COMPILER_HPP +#include #include - -#if ENABLE_FASTHASH -#include "emhash/hash_table8.hpp" -#else +#include #include -#endif +#include namespace Lithium { class Compiler { public: /** - * \brief 编译 C++ 代码为共享库,并加载到内存中 - * - * \param code 要编译的代码 - * \param moduleName 模块名 - * \param functionName 入口函数名 - * \return 编译是否成功 - */ - bool CompileToSharedLibrary( - const std::string &code, const std::string &moduleName, - const std::string &functionName, - const std::string &optionsFile = "compile_options.json"); - - /** - * \brief 编译 C++ 代码为共享库,并加载到内存中 - * - * \param code 要编译的代码 - * \param moduleName 模块名 - * \param functionName 入口函数名 - * \return 编译是否成功 + * 编译 C++ 代码为共享库,并加载到内存中 + * @param code 要编译的代码 + * @param moduleName 模块名 + * @param functionName 入口函数名 + * @param optionsFile 编译选项文件路径,默认为 "compile_options.json" + * @return 编译是否成功 */ - bool CompileToSharedLibraryAllinOne(const std::string &code, - const std::string &moduleName, - const std::string &functionName); + [[nodiscard]] bool compileToSharedLibrary( + std::string_view code, std::string_view moduleName, + std::string_view functionName, + std::string_view optionsFile = "compile_options.json"); private: /** - * \brief 检查参数 - * \param code 要编译的代码 - * \param moduleName 模块名 - * \param functionName 入口函数名 - * \return 检查是否成功 - */ - bool CheckParameters(const std::string &code, const std::string &moduleName, - const std::string &functionName); - - /** - * \brief 判断模块是否已缓存 - * \param moduleName 模块名 - * \param functionName 入口函数名 - * \return 模块是否已缓存 - */ - bool IsModuleCached(const std::string &moduleName, - const std::string &functionName, - std::unordered_map &cache_); - - /** - * \brief 创建输出目录 - * \param outputDir 输出目录路径 - * \return 创建是否成功 - */ - bool CreateOutputDirectory(const std::string &outputDir); - - /** - * \brief 读取编译选项 - * \param optionsFile 编译选项文件路径 - * \return 编译选项 - */ - std::string ReadCompileOptions(const std::string &optionsFile); - - /** - * \brief 语法检查 - * \param code 要编译的代码 - * \param compiler 编译器路径 - * \return 语法检查是否成功 + * 创建输出目录 + * @param outputDir 输出目录路径 */ - bool SyntaxCheck(const std::string &code, const std::string &compiler); + void createOutputDirectory(const std::filesystem::path& outputDir); /** - * \brief 编译代码 - * \param code 要编译的代码 - * \param compiler 编译器路径 - * \param compileOptions 编译选项 - * \param output 编译输出路径 - * \return 编译是否成功 + * 语法检查 + * @param code 要编译的代码 + * @param compiler 编译器路径 + * @return 语法检查是否成功 */ - bool CompileCode(const std::string &code, const std::string &compiler, - const std::string &compileOptions, - const std::string &output); + [[nodiscard]] bool syntaxCheck(std::string_view code, + std::string_view compiler); /** - * \brief 缓存编译好的模块 - * \param moduleName 模块名 - * \param functionName 入口函数名 - * \param output 编译输出路径 - * \return 缓存是否成功 + * 编译代码 + * @param code 要编译的代码 + * @param compiler 编译器路径 + * @param compileOptions 编译选项 + * @param output 编译输出路径 + * @return 编译是否成功 */ - void CacheCompiledModule( - const std::string &moduleName, const std::string &functionName, - const std::string &output, - std::unordered_map &cache_); + [[nodiscard]] bool compileCode(std::string_view code, + std::string_view compiler, + std::string_view compileOptions, + const std::filesystem::path& output); - // ---------------------------------------------------- - // File and Directory - // ---------------------------------------------------- /** - * \brief 复制文件 - * - * \param source 源文件路径 - * \param destination 目标文件路径 - * \return 是否复制成功 + * 查找可用的编译器 + * @return 可用的编译器列表 */ - bool CopyFile_(const std::string &source, const std::string &destination); + [[nodiscard]] std::vector findAvailableCompilers(); /** - * \brief 运行外部 shell - * 命令,并将标准输入输出流转发到命令的标准输入输出流中 - * - * \param command 要运行的命令 - * \param inputStream 标准输入流 - * \param outputStream 标准输出流 - * \return 命令运行的返回值 + * 运行外部 shell 命令,并将标准输入输出流转发到命令的标准输入输出流中 + * @param command 要运行的命令 + * @param input 标准输入 + * @param output 标准输出 + * @return 命令运行的返回值 */ - int RunShellCommand(const std::string &command, const std::string &input, - std::string &output); + int runCommand(std::string_view command, std::string_view input, + std::string& output); -#if ENABLE_FASTHASH - emhash8::HashMap cache_; -#else - std::unordered_map cache_; -#endif + std::unordered_map cache_; }; - } // namespace Lithium +#endif diff --git a/src/addon/finder.cpp b/src/addon/finder.cpp deleted file mode 100644 index e60a1a6e..00000000 --- a/src/addon/finder.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* - * finder.cpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2024-1-4 - -Description: Addons finder - -**************************************************/ - -#include "finder.hpp" - -#include "atom/log/loguru.hpp" - -namespace Lithium { -DirContainer::DirContainer(const std::filesystem::path &path) : m_path(path) {} - -const std::filesystem::path &DirContainer::getPath() const { return m_path; } - -const std::vector &DirContainer::getSubdirs() const { - return m_subdirs; -} - -const std::vector &DirContainer::getFiles() const { - return m_files; -} - -void DirContainer::addSubdir(const DirContainer &subdir) { - m_subdirs.push_back(subdir); -} - -void DirContainer::addFile(const std::filesystem::path &file) { - m_files.push_back(file); -} - -AddonFinder::FilterFunction AddonFinder::m_filterFunc = nullptr; - -AddonFinder::AddonFinder(const std::filesystem::path &path, - const FilterFunction &filterFunc) - : m_path(path), m_dirContainer(m_path) {} - -bool AddonFinder::traverseDir(const std::filesystem::path &path) { - if (std::filesystem::exists(m_path) && - std::filesystem::is_directory(m_path)) { - traverseDir(m_path, m_dirContainer); - } else { - LOG_F(ERROR, "Invalid path: {}", m_path.string()); - return false; - } - return true; -} - -std::vector AddonFinder::getAvailableDirs() const { - std::vector matchingSubdirs; - - // Recursive function to find matching subdirectories - std::function findMatchingSubdirs = - [&](const DirContainer &dir) { - for (const auto &subdir : dir.getSubdirs()) { - if (!m_filterFunc && m_filterFunc(subdir.getPath())) { - matchingSubdirs.push_back( - subdir.getPath().filename().string()); - } - findMatchingSubdirs(subdir); - } - }; - - findMatchingSubdirs(m_dirContainer); - - return matchingSubdirs; -} - -bool AddonFinder::hasFile(const std::filesystem::path &path, - const std::string &filename) { - for (const auto &entry : std::filesystem::directory_iterator(path)) { - if (entry.is_directory()) { - if (hasFile(entry.path(), filename)) { - return true; - } - } else { - if (entry.path().filename() == filename) { - return true; - } - } - } - return false; -} - -void AddonFinder::traverseDir(const std::filesystem::path &path, - DirContainer &container) { - for (const auto &entry : std::filesystem::directory_iterator(path)) { - if (entry.is_directory()) { - DirContainer subdir(entry.path()); - traverseDir(entry.path(), subdir); - if (!subdir.getFiles().empty()) { - container.addSubdir(subdir); - } - } else { - if (!m_filterFunc || m_filterFunc(entry.path())) { - container.addFile(entry.path()); - } - } - } -} -} // namespace Lithium diff --git a/src/addon/finder.hpp b/src/addon/finder.hpp deleted file mode 100644 index fb7c5507..00000000 --- a/src/addon/finder.hpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * finder.hpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2024-1-4 - -Description: Component finder (the core of the plugin system) - -**************************************************/ - -#pragma once - -#include -#include -#include - -namespace Lithium { -/** - * @brief The DirContainer class represents a container for directory contents. - */ -class DirContainer { -public: - /** - * @brief Constructs a DirContainer object with the specified path. - * @param path The path of the directory represented by this container. - */ - explicit DirContainer(const std::filesystem::path &path); - - /** - * @brief Gets the path of the directory represented by this container. - * @return The path of the directory represented by this container. - */ - const std::filesystem::path &getPath() const; - - /** - * @brief Gets the subdirectories of the directory represented by this - * container. - * @return The subdirectories of the directory represented by this - * container. - */ - const std::vector &getSubdirs() const; - - /** - * @brief Gets the files of the directory represented by this container. - * @return The files of the directory represented by this container. - */ - const std::vector &getFiles() const; - - /** - * @brief Adds a subdirectory to the directory represented by this - * container. - * @param subdir The subdirectory to add. - */ - void addSubdir(const DirContainer &subdir); - - /** - * @brief Adds a file to the directory represented by this container. - * @param file The file to add. - */ - void addFile(const std::filesystem::path &file); - -private: - std::filesystem::path m_path; /**< The path of the directory. */ - std::vector - m_subdirs; /**< Subdirectories within the directory. */ - std::vector - m_files; /**< Files within the directory. */ -}; - -/** - * @brief The AddonFinder class is responsible for finding components within a - * given directory. - */ -class AddonFinder { -public: - /** - * @brief FilterFunction represents a function type used for filtering - * paths. - */ - using FilterFunction = std::function; - - /** - * @brief Constructs a AddonFinder object with the specified path and filter - * function. - * @param path The path to the directory to search for components. - * @param filterFunc The function used to filter paths within the directory - * (optional). - */ - explicit AddonFinder(const std::filesystem::path &path, - const FilterFunction &filterFunc = {}); - - /** - * @brief Traverses the directory structure and populates the DirContainer - * object. - * @param path The path of the directory to traverse. - * @return True if the traversal was successful, false otherwise. - */ - bool traverseDir(const std::filesystem::path &path); - - /** - * @brief Gets the names of the subdirectories that match the filter - * function. - * @return The names of the subdirectories that match the filter function. - */ - std::vector getAvailableDirs() const; - - /** - * @brief Checks if a file with the specified name exists within the given - * path. - * @param path The path to the directory to search for the file. - * @param filename The name of the file to search for. - * @return True if the file exists, false otherwise. - */ - static bool hasFile(const std::filesystem::path &path, - const std::string &filename); - -private: - /** - * @brief Recursively traverses the directory structure and populates the - * DirContainer object. - * @param path The path of the directory to traverse. - * @param container The DirContainer object to populate with directory - * contents. - */ - static void traverseDir(const std::filesystem::path &path, - DirContainer &container); - -private: - std::filesystem::path m_path; /**< The path of the directory. */ - DirContainer m_dirContainer; /**< The DirContainer object representing the - directory. */ - static FilterFunction - m_filterFunc; /**< The filter function for path filtering. */ -}; -} // namespace Lithium - -/* -// Example filter function: exclude files with ".txt" extension -bool filterFunc(const std::filesystem::path &path) -{ - return path.extension() != ".txt"; -} - -// Example check function: check if "example.txt" file exists in subdir -bool checkFunc(const std::filesystem::path &path) -{ - return AddonFinder::hasFile(path, "example.txt"); -} - -int main(int argc, char *argv[]) -{ - if (argc != 2) - { - std::cerr << "Usage: " << argv[0] << " " << std::endl; - return -1; - } - - std::filesystem::path path(argv[1]); - if (!std::filesystem::exists(path) || !std::filesystem::is_directory(path)) - { - std::cerr << "Invalid path: " << argv[1] << std::endl; - return -1; - } - - AddonFinder finder(path, filterFunc); - finder.print(); - - // Check if "example.txt" file exists in subdirs - AddonFinder exampleFinder(path, checkFunc); - if (exampleFinder.hasFile(path, "package.json")) - { - std::cout << "Found 'example.txt' in subdirs." << std::endl; - } - else - { - std::cout << "Did not find 'example.txt' in subdirs." << std::endl; - } - - return 0; -} - -*/ diff --git a/src/addon/loader.cpp b/src/addon/loader.cpp index e3ecf8c8..a58bdfde 100644 --- a/src/addon/loader.cpp +++ b/src/addon/loader.cpp @@ -69,7 +69,8 @@ std::shared_ptr ModuleLoader::createShared( bool ModuleLoader::LoadModule(const std::string &path, const std::string &name) { - std::unique_lock lock(m_SharedMutex); + // dead lock if use std::lock_guard + // std::unique_lock lock(m_SharedMutex); try { // Max : The mod's name should be unique, so we check if it already // exists @@ -90,6 +91,7 @@ bool ModuleLoader::LoadModule(const std::string &path, return false; } mod_info->handle = handle; + mod_info->functions = loadModuleFunctions(name); // Load infomation from the library // Store the library handle in handles_ map with the module name as key @@ -183,110 +185,69 @@ std::vector> ModuleLoader::loadModuleFunctions( dl_iterate_phdr( [](struct dl_phdr_info *info, size_t size, void *data) { - std::vector *funcs = - reinterpret_cast *>(data); - + auto funcs = + static_cast> *>(data); if (info->dlpi_name && strcmp(info->dlpi_name, "") != 0) { - DLOG_F(INFO, "Module: {}", info->dlpi_name); - - Dl_info dl_info; - memset(&dl_info, 0, sizeof(Dl_info)); - dladdr(reinterpret_cast(info->dlpi_addr), - &dl_info); - - ElfW(Ehdr) *elfHeader = - reinterpret_cast(info->dlpi_addr); - int numOfSymbols = elfHeader->e_shnum; - ElfW(Shdr) *symbolSection = nullptr; - ElfW(Sym) *symbolTable = nullptr; - char *strTable = nullptr; - int strTableSize = 0; - - /* - TODO: fix this when we have a way to get the correct module - base address from lldb for (int i = 0; i < numOfSymbols; ++i) { - ElfW(Shdr)* sectionHeader = - reinterpret_cast(info->dlpi_addr + - elfHeader->e_shoff + i * elfHeader->e_shentsize); if - (sectionHeader->sh_type == SHT_DYNSYM) { symbolSection = - sectionHeader; symbolTable = - reinterpret_cast(info->dlpi_addr + - symbolSection->sh_offset); strTable = - reinterpret_cast(info->dlpi_addr + - reinterpret_cast(info->dlpi_phdr + - info->dlpi_phnum)->sh_offset); strTableSize = - reinterpret_cast(info->dlpi_phdr + - info->dlpi_phnum)->sh_size; break; - } - - // Get function parameters using libdw - Dwfl* dwfl = dwfl_begin(nullptr); - Dwfl_Module* module = dwfl_module_info(dwfl, - info->dlpi_addr); GElf_Sym sym; GElf_Addr sym_address; - Dwarf_Die* die = nullptr; - - if (gelf_getsym(symtab, i, &sym)) { - sym_address = sym.st_value; - die = dwarf_offdie(elf, sym.st_value); - } - - if (die != nullptr) { - Dwarf_Die params; - Dwarf_Die param; - if (dwarf_child(die, ¶ms) == DW_DLV_OK) - { while (dwarf_siblingof(die, ¶m) == DW_DLV_OK) { char* - paramName = nullptr; dwarf_diename(param, ¶mName); - - if (paramName != nullptr) { - funcs[i].parameters.push_back(paramName); - } + DLOG_F(INFO, "Module: %s", info->dlpi_name); + + const ElfW(Addr) base_address = info->dlpi_addr; + const ElfW(Phdr) *phdr = info->dlpi_phdr; + const auto phnum = info->dlpi_phnum; + + for (int j = 0; j < phnum; ++j) { + if (phdr[j].p_type == PT_DYNAMIC) { + auto *dyn = reinterpret_cast( + base_address + phdr[j].p_vaddr); + ElfW(Sym) *symtab = nullptr; + char *strtab = nullptr; + + for (; dyn->d_tag != DT_NULL; ++dyn) { + if (dyn->d_tag == DT_SYMTAB) { + symtab = reinterpret_cast( + base_address + dyn->d_un.d_ptr); + } else if (dyn->d_tag == DT_STRTAB) { + strtab = reinterpret_cast( + base_address + dyn->d_un.d_ptr); + } + } - dwarf_nextcu(die, &die, nullptr); - } - } + if (symtab && strtab) { + for (ElfW(Sym) *sym = symtab; sym->st_name != 0; + ++sym) { + if (ELF32_ST_TYPE(sym->st_info) == STT_FUNC) { + const char *name = &strtab[sym->st_name]; + std::unique_ptr func = + std::make_unique(); + func->name = name; + func->address = reinterpret_cast( + base_address + sym->st_value); + funcs->push_back(std::move(func)); + DLOG_F(INFO, "Function: %s", name); } } - */ - - if (symbolTable && symbolSection) { - int numOfSymbols = - symbolSection->sh_size / sizeof(ElfW(Sym)); - for (int i = 0; i < numOfSymbols; ++i) { - ElfW(Sym) *symbol = &symbolTable[i]; - char *symName = &strTable[symbol->st_name]; - - if (ELF64_ST_TYPE(symbol->st_info) == STT_FUNC && - symName[0] != '\0') { - std::unique_ptr func = - std::make_unique(); - func->name = symName; - func->address = reinterpret_cast( - info->dlpi_addr + symbol->st_value); - // funcs->push_back(std::move(func)); - LOG_F(INFO, "Function: {}", symName); } } } } - return 0; }, - &funcs); + funcs); - dlclose(handle); #endif return funcs; } bool ModuleLoader::UnloadModule(const std::string &name) { - std::unique_lock lock(m_SharedMutex); + // Check if the module is loaded and has a valid handle + // Max: Check before loading to avaid dead lock + if (!HasModule(name)) { + LOG_F(ERROR, "Module {} is not loaded", name); + return false; + }; + // TODO: Fix this, maybe use a lock + // std::unique_lock lock(m_SharedMutex); try { - // Check if the module is loaded and has a valid handle - if (!HasModule(name)) { - LOG_F(ERROR, "Module {} is not loaded", name); - return false; - }; // Unload the library and remove its handle from handles_ map int result = UNLOAD_LIBRARY(GetModule(name)->handle); if (result != 0) { @@ -302,7 +263,7 @@ bool ModuleLoader::UnloadModule(const std::string &name) { } bool ModuleLoader::UnloadAllModules() { - std::unique_lock lock(m_SharedMutex); + std::unique_lock lock(m_SharedMutex); for (auto entry : modules_) { int result = UNLOAD_LIBRARY(entry.second->handle); if (result != 0) { @@ -315,7 +276,7 @@ bool ModuleLoader::UnloadAllModules() { } bool ModuleLoader::CheckModuleExists(const std::string &name) const { - std::shared_lock lock(m_SharedMutex); + std::shared_lock lock(m_SharedMutex); // Max : Directly check if the library exists seems to be a litle bit slow. // May we use filesystem instead? void *handle = LOAD_LIBRARY(name.c_str()); @@ -330,7 +291,7 @@ bool ModuleLoader::CheckModuleExists(const std::string &name) const { std::shared_ptr ModuleLoader::GetModule( const std::string &name) const { - std::shared_lock lock(m_SharedMutex); + std::shared_lock lock(m_SharedMutex); auto it = modules_.find(name); if (it == modules_.end()) { return nullptr; @@ -339,7 +300,7 @@ std::shared_ptr ModuleLoader::GetModule( } void *ModuleLoader::GetHandle(const std::string &name) const { - std::shared_lock lock(m_SharedMutex); + std::shared_lock lock(m_SharedMutex); auto it = modules_.find(name); if (it == modules_.end()) { return nullptr; @@ -348,12 +309,12 @@ void *ModuleLoader::GetHandle(const std::string &name) const { } bool ModuleLoader::HasModule(const std::string &name) const { - std::shared_lock lock(m_SharedMutex); + std::shared_lock lock(m_SharedMutex); return modules_.count(name) > 0; } bool ModuleLoader::EnableModule(const std::string &name) { - std::unique_lock lock(m_SharedMutex); + std::unique_lock lock(m_SharedMutex); // Check if the module is loaded if (!HasModule(name)) { LOG_F(ERROR, "Module {} is not loaded", name); @@ -381,7 +342,7 @@ bool ModuleLoader::EnableModule(const std::string &name) { } bool ModuleLoader::DisableModule(const std::string &name) { - std::unique_lock lock(m_SharedMutex); + std::unique_lock lock(m_SharedMutex); // Check if the module is loaded if (!HasModule(name)) { LOG_F(ERROR, "Module {} is not loaded", name); @@ -408,7 +369,7 @@ bool ModuleLoader::DisableModule(const std::string &name) { } bool ModuleLoader::IsModuleEnabled(const std::string &name) const { - std::shared_lock lock(m_SharedMutex); + std::shared_lock lock(m_SharedMutex); if (!HasModule(name)) { LOG_F(ERROR, "Module {} is not loaded", name); return false; @@ -420,7 +381,7 @@ bool ModuleLoader::IsModuleEnabled(const std::string &name) const { } std::string ModuleLoader::GetModuleVersion(const std::string &name) { - std::shared_lock lock(m_SharedMutex); + std::shared_lock lock(m_SharedMutex); if (HasModule(name)) { return GetFunction(name, "GetVersion")(); } @@ -428,7 +389,7 @@ std::string ModuleLoader::GetModuleVersion(const std::string &name) { } std::string ModuleLoader::GetModuleDescription(const std::string &name) { - std::shared_lock lock(m_SharedMutex); + std::shared_lock lock(m_SharedMutex); if (HasModule(name)) { return GetFunction(name, "GetDescription")(); } @@ -436,7 +397,7 @@ std::string ModuleLoader::GetModuleDescription(const std::string &name) { } std::string ModuleLoader::GetModuleAuthor(const std::string &name) { - std::shared_lock lock(m_SharedMutex); + std::shared_lock lock(m_SharedMutex); if (HasModule(name)) { return GetFunction(name, "GetAuthor")(); } @@ -444,7 +405,7 @@ std::string ModuleLoader::GetModuleAuthor(const std::string &name) { } std::string ModuleLoader::GetModuleLicense(const std::string &name) { - std::shared_lock lock(m_SharedMutex); + std::shared_lock lock(m_SharedMutex); if (HasModule(name)) { return GetFunction(name, "GetLicense")(); } @@ -452,7 +413,7 @@ std::string ModuleLoader::GetModuleLicense(const std::string &name) { } std::string ModuleLoader::GetModulePath(const std::string &name) { - std::shared_lock lock(m_SharedMutex); + std::shared_lock lock(m_SharedMutex); auto it = modules_.find(name); if (it != modules_.end()) { Dl_info dl_info; @@ -471,7 +432,7 @@ json ModuleLoader::GetModuleConfig(const std::string &name) { } const std::vector ModuleLoader::GetAllExistedModules() const { - std::shared_lock lock(m_SharedMutex); + std::shared_lock lock(m_SharedMutex); std::vector modules_name; if (modules_.empty()) { return modules_name; @@ -484,7 +445,7 @@ const std::vector ModuleLoader::GetAllExistedModules() const { bool ModuleLoader::HasFunction(const std::string &name, const std::string &function_name) { - std::shared_lock lock(m_SharedMutex); + std::shared_lock lock(m_SharedMutex); auto handle_it = modules_.find(name); if (handle_it == modules_.end()) { LOG_F(ERROR, "Failed to find module {}", name); diff --git a/src/addon/loader.hpp b/src/addon/loader.hpp index 14b23e41..c2327758 100644 --- a/src/addon/loader.hpp +++ b/src/addon/loader.hpp @@ -45,8 +45,8 @@ Description: C++ and Modules Loader #include #include #define MODULE_HANDLE void * -#define LOAD_LIBRARY(p) dlopen(p, RTLD_NOW | RTLD_GLOBAL) -#define LOAD_SHARED_LIBRARY(file, size) dlopen(nullptr, RTLD_NOW | RTLD_GLOBAL) +#define LOAD_LIBRARY(p) dlopen(p, RTLD_LAZY | RTLD_GLOBAL) +#define LOAD_SHARED_LIBRARY(file, size) dlopen(nullptr, RTLD_LAZY | RTLD_GLOBAL) #define UNLOAD_LIBRARY(p) dlclose(p) #define LOAD_ERROR() dlerror() #define LOAD_FUNCTION(handle, name) dlsym(handle, name) diff --git a/src/addon/manager.cpp b/src/addon/manager.cpp index 73a6796a..d9af5ec9 100644 --- a/src/addon/manager.cpp +++ b/src/addon/manager.cpp @@ -19,21 +19,16 @@ Description: Component Manager (the core of the plugin system) // #include "finder.hpp" #include "loader.hpp" #include "sandbox.hpp" +#include "sort.hpp" #include "atom/io/io.hpp" #include "atom/log/loguru.hpp" #include "atom/server/global_ptr.hpp" +#include "atom/utils/string.hpp" +#include "utils/constant.hpp" #include "utils/marco.hpp" -#ifdef _WIN32 -constexpr std::string PATH_SEPARATOR = "\\"; -constexpr std::string DYNAMIC_LIBRARY_EXTENSION = ".dll"; -#else -constexpr std::string PATH_SEPARATOR = "/"; -constexpr std::string DYNAMIC_LIBRARY_EXTENSION = ".so"; -#endif - #define IS_ARGUMENT_EMPTY() \ if (params.is_null()) { \ return false; \ @@ -47,19 +42,23 @@ constexpr std::string DYNAMIC_LIBRARY_EXTENSION = ".so"; type name = params[#name].get(); namespace Lithium { -ComponentManager::ComponentManager() - : m_Sandbox(nullptr), m_Compiler(nullptr) { - m_ModuleLoader = GetWeakPtr("lithium.addon.loader"); +ComponentManager::ComponentManager() : m_Sandbox(nullptr), m_Compiler(nullptr) { + m_ModuleLoader = + GetWeakPtr(constants::LITHIUM_MODULE_LOADER); CHECK_WEAK_PTR_EXPIRED(m_ModuleLoader, "load module loader from gpm: lithium.addon.loader"); - m_Env = GetWeakPtr("lithium.utils.env"); + m_Env = GetWeakPtr(constants::LITHIUM_UTILS_ENV); CHECK_WEAK_PTR_EXPIRED(m_Env, "load env from gpm: lithium.utils.env"); - m_AddonManager = GetWeakPtr("lithium.addon.addon"); + m_AddonManager = + GetWeakPtr(constants::LITHIUM_ADDON_MANAGER); CHECK_WEAK_PTR_EXPIRED(m_AddonManager, "load addon manager from gpm: lithium.addon.addon"); // m_ComponentFinder = std::make_unique( // m_Env.lock()->getEnv("LITHIUM_ADDON_PATH", "./modules"), checkFunc); + // NOTE: AddonFinder is not supported yet + + // Initialize sandbox and compiler, these are not shared objects m_Sandbox = std::make_unique(); m_Compiler = std::make_unique(); @@ -83,8 +82,8 @@ bool ComponentManager::Initialize() { // Check if the module path is valid or reset by the user // Default path is ./modules // TODO: Windows support - const std::string &module_path = - m_Env.lock()->getEnv("LITHIUM_ADDON_PATH", "./modules"); + const std::string &module_path = m_Env.lock()->getEnv( + constants::ENV_VAR_MODULE_PATH, constants::MODULE_FOLDER); // Get all of the available addon path /* /if (!m_ComponentFinder->traverseDir(std::filesystem::path(module_path))) { @@ -93,7 +92,15 @@ bool ComponentManager::Initialize() { } */ - for (const auto &dir : getQualifiedSubDirs(module_path)) { + // make a loading list of modules + std::vector qualified_subdirs = + resolveDependencies(getQualifiedSubDirs(module_path)); + if (qualified_subdirs.empty()) { + LOG_F(WARNING, "No modules found"); + return true; + } + + for (const auto &dir : qualified_subdirs) { std::filesystem::path path = std::filesystem::path(module_path) / dir; if (!m_AddonManager.lock()->addModule(path, dir)) { @@ -101,21 +108,86 @@ bool ComponentManager::Initialize() { continue; } const json &addon_info = m_AddonManager.lock()->getModule(dir); + if (!addon_info.is_object() || !addon_info.contains("name") || + !addon_info["name"].is_string()) { + LOG_F(ERROR, "Invalid module name: {}", path.string()); + continue; + } + auto addon_name = addon_info["name"].get(); // Check if the addon info is valid if (!addon_info.contains("modules") || addon_info.is_null()) { LOG_F(ERROR, "Failed to load module: {}", path.string()); + LOG_F(ERROR, "Missing modules field in addon info"); + m_AddonManager.lock()->removeModule(dir); continue; } - for (const auto &module_name : - addon_info["modules"].get>()) { - std::filesystem::path module_path = path / module_name; - if (!m_ModuleLoader.lock()->LoadModule(module_path.string(), + // loading + for (const auto &module_info : + addon_info["modules"].get()) { + if (module_info.is_null() || !module_info.contains("name") || + !module_info.contains("entry")) { + LOG_F(ERROR, "Failed to load module: {}/{}", path.string(), + module_info.dump()); + continue; + } + auto module_name = + addon_name + "." + module_info["name"].get(); + std::filesystem::path module_path = + path / (module_info["name"].get() + + std::string(constants::LIB_EXTENSION)); + + DLOG_F(INFO, "Loading module: {}", module_path.string()); +#ifdef _WIN32 + // This is to pass file name check + auto module_path_str = + Atom::Utils::replaceString(module_path.string(), "/", "\\"); +#else + auto module_path_str = + Atom::Utils::replaceString(module_path.string(), "\\", "/"); +#endif + + // This step is to load the dynamic library + if (!m_ModuleLoader.lock()->LoadModule(module_path_str, module_name)) { + LOG_F(ERROR, "Failed to load module: {}", module_path_str); + continue; + } + DLOG_F(INFO, "Loaded module: {}/{}", path.string(), + module_info.dump()); + auto component_entry = module_info["entry"].get(); + if (component_entry.empty()) { LOG_F(ERROR, "Failed to load module: {}/{}", path.string(), module_name); continue; } - DLOG_F(INFO, "Loaded module: {}/{}", path.string(), module_name); + auto component_identifier = + addon_name + module_name + component_entry; + if (auto component = + m_ModuleLoader.lock()->GetInstance( + module_name, {}, component_entry); + component) { + LOG_F(INFO, "Loaded shared component: {}", + component_identifier); + try { + if (component->initialize()) { + m_SharedComponents[addon_name + module_name] = + component; + LOG_F(INFO, "Loaded shared component: {}", + component_identifier); + } else { + LOG_F(ERROR, + "Failed to initialize shared component: {}", + component_identifier); + } + } catch (const std::exception &e) { + LOG_F(ERROR, "Failed to initialize shared component: {}", + component_identifier); + } + + } else { + LOG_F(ERROR, "Failed to load shared component: {}", + component_identifier); + } } } return true; @@ -127,7 +199,8 @@ std::shared_ptr ComponentManager::createShared() { return std::make_shared(); } -std::vector ComponentManager::getFilesInDir(const std::string &path) { +std::vector ComponentManager::getFilesInDir( + const std::string &path) { std::vector files; for (const auto &entry : std::filesystem::directory_iterator(path)) { if (!entry.is_directory()) { @@ -137,7 +210,8 @@ std::vector ComponentManager::getFilesInDir(const std::string &path return files; } -std::vector ComponentManager::getQualifiedSubDirs(const std::string &path) { +std::vector ComponentManager::getQualifiedSubDirs( + const std::string &path) { std::vector qualifiedSubDirs; for (const auto &entry : std::filesystem::directory_iterator(path)) { if (entry.is_directory()) { @@ -145,11 +219,11 @@ std::vector ComponentManager::getQualifiedSubDirs(const std::string std::vector files = getFilesInDir(entry.path().string()); for (const auto &fileName : files) { - if (fileName == "package.json") { + if (fileName == constants::PACKAGE_NAME) { hasJson = true; } else if (fileName.size() > 4 && fileName.substr(fileName.size() - 4) == - DYNAMIC_LIBRARY_EXTENSION) { + constants::LIB_EXTENSION) { hasLib = true; } if (hasJson && hasLib) { @@ -221,15 +295,15 @@ bool ComponentManager::checkComponent(const std::string &module_name, } // Check component package.json file, this is for the first time loading // And we need to know how to load component's ptr from this file - if (!Atom::IO::isFileExists(module_path + PATH_SEPARATOR + - "package.json")) { + if (!Atom::IO::isFileExists(module_path + constants::PATH_SEPARATOR + + constants::PACKAGE_NAME)) { LOG_F(ERROR, "Component path {} does not contain package.json", module_path); return false; } // Check component library files std::vector files = Atom::IO::checkFileTypeInFolder( - module_path, DYNAMIC_LIBRARY_EXTENSION, Atom::IO::FileOption::Name); + module_path, constants::LIB_EXTENSION, Atom::IO::FileOption::Name); if (files.empty()) { LOG_F(ERROR, "Component path {} does not contain dll or so file", @@ -237,12 +311,12 @@ bool ComponentManager::checkComponent(const std::string &module_name, return false; } auto it = std::find(files.begin(), files.end(), - module_name + DYNAMIC_LIBRARY_EXTENSION); + module_name + constants::LIB_EXTENSION); if (it != files.end()) { - if (!m_ModuleLoader.lock()->LoadModule(module_path + PATH_SEPARATOR + - module_name + - DYNAMIC_LIBRARY_EXTENSION, - module_name)) { + if (!m_ModuleLoader.lock()->LoadModule( + module_path + constants::PATH_SEPARATOR + module_name + + constants::LIB_EXTENSION, + module_name)) { LOG_F(ERROR, "Failed to load module: {}'s library {}", module_name, module_path); return false; @@ -259,14 +333,15 @@ bool ComponentManager::checkComponent(const std::string &module_name, bool ComponentManager::loadComponentInfo(const std::string &module_path) { // Load the Package.json // Max: We will only load the root package.json - std::string file_path = module_path + PATH_SEPARATOR + "package.json"; + std::string file_path = + module_path + constants::PATH_SEPARATOR + constants::PACKAGE_NAME; if (!Atom::IO::isFileExists(file_path)) { LOG_F(ERROR, "Component path {} does not contain package.json", module_path); return false; } - std::string module_name = - module_path.substr(module_path.find_last_of(PATH_SEPARATOR) + 1); + std::string module_name = module_path.substr( + module_path.find_last_of(constants::PATH_SEPARATOR) + 1); try { m_ComponentInfos[module_name] = json::parse(std::ifstream(file_path)); } catch (const json::parse_error &e) { diff --git a/src/addon/sandbox.cpp b/src/addon/sandbox.cpp index ed715ff1..63bb308e 100644 --- a/src/addon/sandbox.cpp +++ b/src/addon/sandbox.cpp @@ -14,6 +14,8 @@ Description: A sandbox for alone componnents, such as executables. #include "sandbox.hpp" +#include "atom/utils/convert.hpp" + #ifdef _WIN32 #include #include @@ -27,119 +29,89 @@ Description: A sandbox for alone componnents, such as executables. namespace Lithium { bool Sandbox::setTimeLimit(int timeLimitMs) { -#ifdef _WIN32 - m_timeLimit = timeLimitMs; -#else - struct rlimit limit; - limit.rlim_cur = timeLimitMs / 1000; - limit.rlim_max = timeLimitMs / 1000; - if (setrlimit(RLIMIT_CPU, &limit) != 0) { - return false; - } m_timeLimit = timeLimitMs; +#ifndef _WIN32 + rlimit limit{.rlim_cur = static_cast(timeLimitMs) / 1000, + .rlim_max = static_cast(timeLimitMs) / 1000}; + return setrlimit(RLIMIT_CPU, &limit) == 0; #endif return true; } bool Sandbox::setMemoryLimit(long memoryLimitKb) { -#ifdef _WIN32 - m_memoryLimit = memoryLimitKb; -#else - struct rlimit limit; - limit.rlim_cur = memoryLimitKb * 1024; - limit.rlim_max = memoryLimitKb * 1024; - if (setrlimit(RLIMIT_AS, &limit) != 0) { - return false; - } m_memoryLimit = memoryLimitKb; +#ifndef _WIN32 + rlimit limit{.rlim_cur = static_cast(memoryLimitKb) * 1024, + .rlim_max = static_cast(memoryLimitKb) * 1024}; + return setrlimit(RLIMIT_AS, &limit) == 0; #endif return true; } -bool Sandbox::setRootDirectory(const std::string &rootDirectory) { -#ifdef _WIN32 - return false; // Not supported on Windows -#else - if (chdir(rootDirectory.c_str()) != 0 || - chroot(rootDirectory.c_str()) != 0) { - return false; - } +bool Sandbox::setRootDirectory(const std::string& rootDirectory) { m_rootDirectory = rootDirectory; - return true; +#ifndef _WIN32 + return (chdir(rootDirectory.c_str()) == 0) && + (chroot(rootDirectory.c_str()) == 0); #endif + return false; } bool Sandbox::setUserId(int userId) { -#ifdef _WIN32 - return false; // Not supported on Windows -#else - if (setuid(userId) != 0 || setgid(userId) != 0) { - return false; - } m_userId = userId; - return true; +#ifndef _WIN32 + return (setuid(userId) == 0) && (setgid(userId) == 0); #endif + return false; } -bool Sandbox::setProgramPath(const std::string &programPath) { +bool Sandbox::setProgramPath(const std::string& programPath) { m_programPath = programPath; return true; } -bool Sandbox::setProgramArgs(const std::vector &programArgs) { +bool Sandbox::setProgramArgs(const std::vector& programArgs) { m_programArgs = programArgs; return true; } bool Sandbox::run() { #ifdef _WIN32 - STARTUPINFO startupInfo; - PROCESS_INFORMATION processInfo; - ZeroMemory(&startupInfo, sizeof(startupInfo)); + STARTUPINFO startupInfo{}; startupInfo.cb = sizeof(startupInfo); - ZeroMemory(&processInfo, sizeof(processInfo)); + PROCESS_INFORMATION processInfo{}; - std::string commandLine = m_programPath + " "; - for (auto &arg : m_programArgs) { - commandLine += arg + " "; + std::string commandLine = m_programPath; + for (const auto& arg : m_programArgs) { + commandLine += ' ' + arg; } - BOOL success = CreateProcess(NULL, const_cast(commandLine.c_str()), - NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, - NULL, &startupInfo, &processInfo); - if (!success) { + if (!CreateProcess(nullptr, commandLine.data(), + nullptr, nullptr, FALSE, CREATE_SUSPENDED, nullptr, + nullptr, &startupInfo, &processInfo)) { return false; } - HANDLE processHandle = processInfo.hProcess; + const HANDLE processHandle = processInfo.hProcess; - // 设置时间和内存限制 if (m_timeLimit > 0) { - DWORD_PTR timeLimitMs = static_cast(m_timeLimit); - success = SetProcessAffinityMask(processHandle, timeLimitMs); - if (!success) { - return false; - } + SetProcessAffinityMask(processHandle, + static_cast(m_timeLimit)); } if (m_memoryLimit > 0) { - SIZE_T memoryLimitKb = static_cast(m_memoryLimit); - success = SetProcessWorkingSetSize(processHandle, memoryLimitKb, - memoryLimitKb); - if (!success) { - return false; - } + SetProcessWorkingSetSize(processHandle, + static_cast(m_memoryLimit), + static_cast(m_memoryLimit)); } - // 恢复子进程并等待它完成 ResumeThread(processInfo.hThread); WaitForSingleObject(processHandle, INFINITE); - // 获取子进程的退出状态和资源使用情况 DWORD exitCode = 0; GetExitCodeProcess(processHandle, &exitCode); - PROCESS_MEMORY_COUNTERS memoryCounters; + PROCESS_MEMORY_COUNTERS memoryCounters{}; GetProcessMemoryInfo(processHandle, &memoryCounters, sizeof(memoryCounters)); @@ -151,76 +123,32 @@ bool Sandbox::run() { return exitCode == 0; #else - pid_t pid = fork(); + const pid_t pid = fork(); if (pid < 0) { return false; - } else if (pid == 0) { // child process - ptrace(PTRACE_TRACEME, 0, NULL, NULL); - // 将 std::string 类型的数组转换为 char* 类型的指针数组 - std::vector args; - for (const auto &arg : m_programArgs) { - args.push_back(const_cast(arg.c_str())); + } else if (pid == 0) { + ptrace(PTRACE_TRACEME, 0, nullptr, nullptr); + + std::vector args; + args.reserve(m_programArgs.size() + 2); + args.emplace_back(m_programPath.data()); + for (const auto& arg : m_programArgs) { + args.emplace_back(const_cast(arg.c_str())); } - args.push_back(nullptr); // execvp 需要以 nullptr 结尾 + args.emplace_back(nullptr); execvp(m_programPath.c_str(), args.data()); exit(0); - } else { // parent process + } else { int status = 0; - struct rusage usage; - while (wait4(pid, &status, 0, &usage) != pid) { - } - m_timeUsed = - usage.ru_utime.tv_sec * 1000 + usage.ru_utime.tv_usec / 1000; - m_memoryUsed = usage.ru_maxrss; + rusage usage{}; + while (wait4(pid, &status, 0, &usage) != pid) + ; + m_timeUsed = static_cast(usage.ru_utime.tv_sec * 1000 + + usage.ru_utime.tv_usec / 1000); + m_memoryUsed = static_cast(usage.ru_maxrss); return WIFEXITED(status) && WEXITSTATUS(status) == 0; } #endif } - -int Sandbox::getTimeUsed() const { return m_timeUsed; } - -long Sandbox::getMemoryUsed() const { return m_memoryUsed; } -} // namespace Lithium - -/* -int main() -{ - std::vector threads; - std::vector timeUsedList; - std::vector memoryUsedList; - - int numThreads = 5; // 设置线程数量 - - for (int i = 0; i < numThreads; ++i) - { - threads.emplace_back([i, &timeUsedList, &memoryUsedList]() - { - Sandbox sandbox; - sandbox.setTimeLimit(10000); // 设置时间限制为1秒 - sandbox.setMemoryLimit(1024102); // 设置内存限制为1MB - sandbox.setProgramPath("E:\\python3.10\\python.exe"); // 设置程序路径 - sandbox.setProgramArgs({}); // 设置程序参数 - if (sandbox.run()) { - timeUsedList[i] = sandbox.getTimeUsed(); - memoryUsedList[i] = sandbox.getMemoryUsed(); - } }); - } - - for (auto &thread : threads) - { - thread.join(); - } - - // 打印每个线程的时间和内存使用情况 - for (int i = 0; i < numThreads; ++i) - { - std::cout << "Thread " << i << ": " << std::endl; - std::cout << "Time used: " << timeUsedList[i] << "ms" << std::endl; - std::cout << "Memory used: " << memoryUsedList[i] << "KB" << std::endl; - } - - return 0; -} - -*/ +} // namespace Lithium \ No newline at end of file diff --git a/src/addon/sandbox.hpp b/src/addon/sandbox.hpp index f5043498..929ed44e 100644 --- a/src/addon/sandbox.hpp +++ b/src/addon/sandbox.hpp @@ -12,32 +12,28 @@ Description: A sandbox for alone componnents, such as executables. **************************************************/ -#pragma once +#ifndef LITHIUM_ADDON_SANDBOX_HPP +#define LITHIUM_ADDON_SANDBOX_HPP #include #include namespace Lithium { /** - * @class Sandbox - * @brief Represents a sandbox for running programs with specified time and - * memory limits. - * @details This class is used to run programs with specified time and memory - * limits. - * @note The implementation of this class on windows is not complete. - * @note If we run this on windows, we can't create a sandbox properly. + * @brief Sandbox class for running programs with time and memory limits in a + * restricted environment. */ class Sandbox { public: /** - * @brief Constructor for Sandbox class. + * @brief Default constructor for Sandbox class. */ - Sandbox() {} + Sandbox() = default; /** - * @brief Destructor for Sandbox class. + * @brief Default destructor for Sandbox class. */ - ~Sandbox() {} + ~Sandbox() = default; /** * @brief Sets the time limit for program execution. @@ -54,61 +50,68 @@ class Sandbox { bool setMemoryLimit(long memoryLimitKb); /** - * @brief Sets the root directory for program execution. + * @brief Sets the root directory for the sandbox environment. * @param rootDirectory The root directory path. * @return True if the root directory is set successfully, false otherwise. */ - bool setRootDirectory(const std::string &rootDirectory); + bool setRootDirectory(const std::string& rootDirectory); /** - * @brief Sets the user ID for program execution. + * @brief Sets the user ID for running the program in the sandbox. * @param userId The user ID. * @return True if the user ID is set successfully, false otherwise. */ bool setUserId(int userId); /** - * @brief Sets the program path for execution. - * @param programPath The program path. + * @brief Sets the path to the program to be executed in the sandbox. + * @param programPath The path to the program. * @return True if the program path is set successfully, false otherwise. */ - bool setProgramPath(const std::string &programPath); + bool setProgramPath(const std::string& programPath); /** - * @brief Sets the program arguments for execution. - * @param programArgs The program arguments. + * @brief Sets the arguments for the program to be executed in the sandbox. + * @param programArgs The vector of program arguments. * @return True if the program arguments are set successfully, false * otherwise. */ - bool setProgramArgs(const std::vector &programArgs); + bool setProgramArgs(const std::vector& programArgs); /** - * @brief Runs the program in the sandbox. - * @return True if the program runs successfully, false otherwise. + * @brief Runs the program in the sandbox environment. + * @return True if the program runs successfully within the time and memory + * limits, false otherwise. */ bool run(); /** - * @brief Gets the time used by the program during execution. + * @brief Retrieves the actual time used by the program during execution. * @return The time used in milliseconds. */ - int getTimeUsed() const; + [[nodiscard]] int getTimeUsed() const { return m_timeUsed; } /** - * @brief Gets the memory used by the program during execution. + * @brief Retrieves the actual memory used by the program during execution. * @return The memory used in kilobytes. */ - long getMemoryUsed() const; + [[nodiscard]] long getMemoryUsed() const { return m_memoryUsed; } private: - int m_timeLimit = 0; - long m_memoryLimit = 0; - std::string m_rootDirectory; - int m_userId = 0; - std::string m_programPath; - std::vector m_programArgs; - int m_timeUsed = 0; - long m_memoryUsed = 0; + int m_timeLimit = + 0; /**< Time limit for program execution in milliseconds. */ + long m_memoryLimit = + 0; /**< Memory limit for program execution in kilobytes. */ + std::string + m_rootDirectory; /**< Root directory for the sandbox environment. */ + int m_userId = 0; /**< User ID for running the program in the sandbox. */ + std::string m_programPath; /**< Path to the program to be executed. */ + std::vector m_programArgs; /**< Program arguments. */ + int m_timeUsed = + 0; /**< Actual time used by the program during execution. */ + long m_memoryUsed = + 0; /**< Actual memory used by the program during execution. */ }; -} // namespace Lithium +#endif +} \ No newline at end of file diff --git a/src/addon/sort.cpp b/src/addon/sort.cpp new file mode 100644 index 00000000..ef274be7 --- /dev/null +++ b/src/addon/sort.cpp @@ -0,0 +1,127 @@ +#include "sort.hpp" + +#include +#include +#include +#include + +#include "atom/log/loguru.hpp" +#include "atom/type/json.hpp" + +using json = nlohmann::json; + +namespace Lithium { +std::vector removeDuplicates( + const std::vector& input) { + std::unordered_set seen; + std::vector result; + + for (const auto& element : input) { + if (seen.insert(element).second) { + result.push_back(element); + } + } + + return result; +} + +std::pair> parsePackageJson( + const std::string& path) { + std::ifstream file(path); + if (!file.is_open()) { + throw std::runtime_error("Failed to open " + path); + } + + json package_json; + try { + file >> package_json; + } catch (const json::exception& e) { + throw std::runtime_error("Error parsing JSON in " + path + ": " + + e.what()); + } + + if (!package_json.contains("name")) { + throw std::runtime_error("Missing package name in " + path); + } + + std::string package_name = package_json["name"]; + std::vector deps; + + if (package_json.contains("dependencies")) { + for (auto& dep : package_json["dependencies"].items()) { + deps.push_back(dep.key()); + } + } + + file.close(); + return {package_name, deps}; +} + +std::vector resolveDependencies( + const std::vector& directories) { + std::unordered_map> dependency_graph; + std::unordered_map indegree; + std::vector sorted_packages; + std::unordered_set visited; + + for (const auto& dir : directories) { + std::string package_path = dir + "/package.json"; + auto [package_name, deps] = parsePackageJson(package_path); + + // 构建依赖图和入度表 + if (!dependency_graph.count(package_name)) { + dependency_graph[package_name] = {}; + indegree[package_name] = 0; + } + + for (const auto& dep : deps) { + dependency_graph[dep].push_back(package_name); + indegree[package_name]++; + } + } + + if (dependency_graph.empty()) { + LOG_F(ERROR, "No packages found."); + return {}; + } + + std::queue q; + for (const auto& [node, _] : dependency_graph) { + q.push(node); + } + + while (!q.empty()) { + std::string current = q.front(); + q.pop(); + sorted_packages.push_back(current); + visited.insert(current); + + for (const auto& neighbor : dependency_graph[current]) { + if (visited.count(neighbor)) { + LOG_F(WARNING, + "Circular dependency detected. Ignoring dependency from " + "{} to {}", + current, neighbor); + continue; + } + indegree[neighbor]--; + if (indegree[neighbor] == 0) { + q.push(neighbor); + } + } + } + + for (const auto& node : indegree) { + if (node.second > 0) { + LOG_F(WARNING, "Unresolved dependency for {}", node.first); + } + } + + if (sorted_packages.size() != dependency_graph.size()) { + LOG_F(WARNING, "Some packages were not included in the load order."); + } + + return removeDuplicates(sorted_packages); +} + +} // namespace Lithium diff --git a/src/addon/sort.hpp b/src/addon/sort.hpp new file mode 100644 index 00000000..2bce1738 --- /dev/null +++ b/src/addon/sort.hpp @@ -0,0 +1,12 @@ +#ifndef LITHIUM_COMPONENTS_SORT_HPP +#define LITHIUM_COMPONENTS_SORT_HPP + +#include +#include + +namespace Lithium { +[[nodiscard("result is discarded")]] std::vector +resolveDependencies(const std::vector& directories); +} + +#endif \ No newline at end of file diff --git a/src/atom/CMakeLists.txt b/src/atom/CMakeLists.txt index 58c330fb..d2a23517 100644 --- a/src/atom/CMakeLists.txt +++ b/src/atom/CMakeLists.txt @@ -19,6 +19,14 @@ set(ATOM_SOVERSION ${CMAKE_ATOM_VERSION_MAJOR}) set(CMAKE_ATOM_VERSION_STRING "${CMAKE_ATOM_VERSION_MAJOR}.${CMAKE_ATOM_VERSION_MINOR}.${CMAKE_ATOM_VERSION_RELEASE}") set(ATOM_VERSION ${CMAKE_ATOM_VERSION_MAJOR}.${CMAKE_ATOM_VERSION_MINOR}.${CMAKE_ATOM_VERSION_RELEASE}) +option(ATOM_BUILD_PYTHON " Build Atom with Python support" OFF) +find_package(Python COMPONENTS Interpreter Development REQUIRED) +if(${PYTHON_FOUND}) + message("-- Found Python ${PYTHON_VERSION_STRING}: ${PYTHON_EXECUTABLE}") + find_package(pybind11 REQUIRED) + set(ATOM_BUILD_PYTHON ON) +endif() +add_subdirectory(algorithm) add_subdirectory(async) add_subdirectory(components) add_subdirectory(connection) @@ -41,6 +49,7 @@ endif() # Sources list(APPEND ${PROJECT_NAME}_SOURCES error/error_stack.cpp + error/exception.cpp log/logger.cpp log/global_logger.cpp @@ -66,6 +75,8 @@ list(APPEND ${PROJECT_NAME}_LIBS loguru cpp_httplib libzippp + atom-algorithm + #atom-connection atom-async atom-task atom-io diff --git a/src/atom/algorithm/CMakeLists.txt b/src/atom/algorithm/CMakeLists.txt index fe3d04a0..ee460e74 100644 --- a/src/atom/algorithm/CMakeLists.txt +++ b/src/atom/algorithm/CMakeLists.txt @@ -11,25 +11,27 @@ project(atom-algorithm C CXX) # Sources set(${PROJECT_NAME}_SOURCES + algorithm.cpp base.cpp convolve.cpp fraction.cpp huffman.cpp math.cpp - mathutils.cpp md5.cpp + mhash.cpp ) # Headers set(${PROJECT_NAME}_HEADERS + algorithm.hpp base.hpp convolve.hpp fraction.hpp hash.hpp huffman.hpp math.hpp - mathutils.hpp md5.hpp + mhash.hpp ) # Build Object Library @@ -59,4 +61,9 @@ set_target_properties(${PROJECT_NAME} PROPERTIES install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) \ No newline at end of file +) + +if (ATOM_BUILD_PYTHON) +pybind11_add_module(${PROJECT_NAME}-py _pybind.cpp) +target_link_libraries(${PROJECT_NAME}-py PRIVATE ${PROJECT_NAME}) +endif() \ No newline at end of file diff --git a/src/atom/algorithm/_component.cpp b/src/atom/algorithm/_component.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/atom/algorithm/_pybind.cpp b/src/atom/algorithm/_pybind.cpp new file mode 100644 index 00000000..8e6ef5f7 --- /dev/null +++ b/src/atom/algorithm/_pybind.cpp @@ -0,0 +1,117 @@ +/* + * _pybind.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-13 + +Description: Python Binding of Atom-Algorithm + +**************************************************/ + +#include +#include +#include + +#include "algorithm.hpp" +#include "base.hpp" +#include "convolve.hpp" +#include "fraction.hpp" +#include "hash.hpp" +#include "huffman.hpp" +#include "math.hpp" +#include "md5.hpp" +#include "mhash.hpp" + +namespace py = pybind11; + +using namespace Atom::Algorithm; +using namespace Atom::Utils; + +PYBIND11_MODULE(atom_algorithm, m) { + m.doc() = "Atom Algorithm Python Binding"; + + m.def("base16encode", &base16Encode, "Base16 Encode"); + m.def("base16decode", &base16Decode, "Base16 Decode"); + m.def("base32encode", &base32Encode, "Base32 Encode"); + m.def("base32decode", &base32Decode, "Base32 Decode"); + m.def("base64encode", &base64Encode, "Base64 Encode"); + m.def("base64decode", &base64Decode, "Base64 Decode"); + m.def("base85encode", &base85Encode, "Base85 Encode"); + m.def("base85decode", &base85Decode, "Base85 Decode"); + m.def("base91encode", &base91Encode, "Base91 Encode"); + m.def("base91decode", &base91Decode, "Base91 Decode"); + m.def("base128encode", &base128Encode, "Base128 Encode"); + m.def("base128decode", &base128Decode, "Base128 Decode"); + + py::class_(m, "KMP") + .def(py::init()) + .def("search", &KMP::Search) + .def("set_pattern", &KMP::SetPattern); + + py::class_(m, "MinHash") + .def(py::init()) + .def("compute_signature", &MinHash::compute_signature) + .def("estimate_similarity", &MinHash::estimate_similarity); + + m.def("convolve", &convolve, "Perform one-dimensional convolution"); + m.def("deconvolve", &deconvolve, "Perform one-dimensional deconvolution"); + m.def("convolve2D", &convolve2D, "Perform two-dimensional convolution", + py::arg("input"), py::arg("kernel"), py::arg("numThreads") = 1); + m.def("deconvolve2D", &deconvolve2D, + "Perform two-dimensional deconvolution"); + + py::class_(m, "Fraction") + .def(py::init()) + .def_readwrite("numerator", &Fraction::numerator) + .def_readwrite("denominator", &Fraction::denominator) + .def("__add__", &Fraction::operator+, py::is_operator()) + .def("__sub__", &Fraction::operator-, py::is_operator()) + .def("__mul__", &Fraction::operator*, py::is_operator()) + .def("__truediv__", &Fraction::operator/, py::is_operator()) + .def("__eq__", &Fraction::operator==, py::is_operator()) + .def("__neg__", &Fraction::operator-) + .def("__str__", &Fraction::to_string) + .def("__float__", &Fraction::to_double) + .def("__int__", &Fraction::operator int) + .def("__repr__", [](const Fraction &f) { return f.to_string(); }); + + // Bindings for createHuffmanTree function + m.def("create_huffman_tree", &createHuffmanTree, + "Create a Huffman tree from character frequencies"); + + // Bindings for generateHuffmanCodes function + m.def("generate_huffman_codes", &generateHuffmanCodes, + "Generate Huffman codes for characters in the Huffman tree"); + + // Bindings for compressText function + m.def("compress_text", &compressText, + "Compress text using Huffman encoding"); + + // Bindings for decompressText function + m.def("decompress_text", &decompressText, + "Decompress text using Huffman decoding"); + + m.def("mul_div_64", &mulDiv64, + "Perform 64-bit multiplication and division"); + m.def("safe_add", &safeAdd, "Safe addition"); + m.def("safe_sub", &safeSub, "Safe subtraction"); + m.def("safe_mul", &safeMul, "Safe multiplication"); + m.def("safe_div", &safeDiv, "Safe division"); + m.def("normalize", &normalize, "Normalize a 64-bit number"); + m.def("rotl64", &rotl64, "Rotate left"); + m.def("rotr64", &rotr64, "Rotate right"); + m.def("clz64", &clz64, "Count leading zeros"); + + py::class_(m, "MD5") + .def(py::init<>()) + .def_static("encrypt", &MD5::encrypt); + + m.def("murmur3_hash", &murmur3Hash, "Murmur3 Hash"); + m.def("murmur3_hash64", &murmur3Hash64, "Murmur3 Hash64"); + m.def("hexstring_from_data", py::overload_cast(&hexstringFromData), "Hexstring from Data"); + m.def("data_from_hexstring", &dataFromHexstring, "Data from Hexstring"); +} diff --git a/src/atom/algorithm/_script.hpp b/src/atom/algorithm/_script.hpp new file mode 100644 index 00000000..d41011fa --- /dev/null +++ b/src/atom/algorithm/_script.hpp @@ -0,0 +1,117 @@ +#include "carbon/carbon.hpp" + +#include "algorithm.hpp" +#include "base.hpp" +#include "convolve.hpp" +#include "fraction.hpp" +#include "hash.hpp" +#include "huffman.hpp" +#include "math.hpp" +#include "md5.hpp" +#include "mhash.hpp" + +using namespace Atom::Algorithm; +using namespace Atom::Utils; + +namespace Atom::_Script::Algorithm { +/** + * Adds the String Methods to the given Carbon m. + */ +Carbon::ModulePtr bootstrap( + Carbon::ModulePtr m = std::make_shared()) { + m->add(user_type(), "KMP"); + m->add(Carbon::fun(&KMP::Search), "search"); + m->add(Carbon::fun(&KMP::SetPattern), "set_pattern"); + + m->add(user_type(), "MinHash"); + m->add(Carbon::fun(&MinHash::compute_signature), "compute_signature"); + m->add(Carbon::fun(&MinHash::estimate_similarity), "estimate_similarity"); + + m->add(user_type(), "BoyerMoore"); + m->add(Carbon::fun(&BoyerMoore::Search), "search"); + m->add(Carbon::fun(&BoyerMoore::SetPattern), "set_pattern"); + + m->add(user_type>(), "BloomFilter"); + m->add(Carbon::fun(&BloomFilter<16>::insert), "insert"); + m->add(Carbon::fun(&BloomFilter<16>::contains), "contains"); + + m->add(Carbon::fun(&base16Encode), "base16encode"); + m->add(Carbon::fun(&base16Decode), "base16decode"); + m->add(Carbon::fun(&base32Encode), "base32encode"); + m->add(Carbon::fun(&base32Decode), "base32decode"); + m->add(Carbon::fun(&base64Encode), "base64encode"); + m->add(Carbon::fun(&base64Decode), "base64decode"); + m->add(Carbon::fun(&base85Encode), "base85encode"); + m->add(Carbon::fun(&base85Decode), "base85decode"); + m->add(Carbon::fun(&base91Encode), "base91encode"); + m->add(Carbon::fun(&base91Decode), "base91decode"); + m->add(Carbon::fun(&base128Encode), "base128encode"); + m->add(Carbon::fun(&base128Decode), "base128decode"); + + m->add(Carbon::fun(&xorEncrypt), "xor_encrypt"); + m->add(Carbon::fun(&xorDecrypt), "xor_decrypt"); + + m->add(Carbon::fun(&convolve), "convolve"); + m->add(Carbon::fun(&deconvolve), "deconvolve"); + m->add(Carbon::fun(&convolve2D), "convolve2d"); + m->add(Carbon::fun(&deconvolve2D), "deconvolve2d"); + + m->add(user_type(), "Fraction"); + m->add(Carbon::fun(&Fraction::operator+=), "+="); + m->add(Carbon::fun(&Fraction::operator-=), "-="); + m->add(Carbon::fun(&Fraction::operator*=), "*="); + m->add(Carbon::fun(&Fraction::operator/=), "/="); + m->add(Carbon::fun(&Fraction::operator+), "+"); + m->add(Carbon::fun(&Fraction::operator-), "-"); + m->add(Carbon::fun(&Fraction::operator*), "*"); + m->add(Carbon::fun(&Fraction::operator/), "/"); + m->add(Carbon::fun(&Fraction::operator==), "=="); + m->add( + Carbon::fun(&Fraction::operator double), + "to_double"); + m->add(Carbon::fun(&Fraction::operator float), + "to_float"); + m->add(Carbon::fun(&Fraction::operator int), + "to_int"); + m->add(Carbon::fun(&Fraction::to_string), "to_string"); + m->add(Carbon::fun(&Fraction::to_double), "to_double"); + + m->add(Carbon::fun(static_cast( + &quickHash)), + "hash"); + m->add(Carbon::fun( + static_cast(&quickHash)), + "hash"); + + m->add(user_type(), "HuffmanNode"); + m->add(Carbon::fun(&HuffmanNode::data), "data"); + m->add(Carbon::fun(&HuffmanNode::frequency), "frequency"); + m->add(Carbon::fun(&HuffmanNode::left), "left"); + m->add(Carbon::fun(&HuffmanNode::right), "right"); + + m->add(Carbon::fun(&createHuffmanTree), "create_huffman_tree"); + m->add(Carbon::fun(&generateHuffmanCodes), "generate_huffman_codes"); + m->add(Carbon::fun(&compressText), "compress_text"); + m->add(Carbon::fun(&decompressText), "decompress_text"); + + m->add(Carbon::fun(&mulDiv64), "mul_div_64"); + m->add(Carbon::fun(&safeAdd), "safe_add"); + m->add(Carbon::fun(&safeSub), "safe_sub"); + m->add(Carbon::fun(&safeMul), "safe_mul"); + m->add(Carbon::fun(&safeDiv), "safe_div"); + m->add(Carbon::fun(&normalize), "normalize"); + m->add(Carbon::fun(&rotl64), "rotl_64"); + m->add(Carbon::fun(&rotr64), "rotr_64"); + m->add(Carbon::fun(&clz64), "clz_64"); + + m->add(Carbon::fun(&MD5::encrypt), "md5_encrypt"); + + m->add(Carbon::fun(&murmur3Hash), "murmur3_hash"); + m->add(Carbon::fun(&murmur3Hash64), "murmur3_hash_64"); + m->add(Carbon::fun(&dataFromHexstring), "data_from_hexstring"); + m->add( + Carbon::fun(&hexstringFromData), + "hexstring_from_data"); + return m; +} +} // namespace Atom::_Script::Algorithm diff --git a/src/atom/algorithm/algorithm.cpp b/src/atom/algorithm/algorithm.cpp new file mode 100644 index 00000000..ddbb8576 --- /dev/null +++ b/src/atom/algorithm/algorithm.cpp @@ -0,0 +1,164 @@ +#include "algorithm.hpp" + +#include + +namespace Atom::Algorithm { + +KMP::KMP(std::string_view pattern) : pattern_(pattern) { + failure_ = ComputeFailureFunction(pattern_); +} + +std::vector KMP::Search(std::string_view text) { + std::vector occurrences; + int n = static_cast(text.length()); + int m = static_cast(pattern_.length()); + int i = 0, j = 0; + while (i < n) { + if (text[i] == pattern_[j]) { + ++i; + ++j; + if (j == m) { + occurrences.push_back(i - m); + j = failure_[j - 1]; + } + } else if (j > 0) { + j = failure_[j - 1]; + } else { + ++i; + } + } + return occurrences; +} + +void KMP::SetPattern(std::string_view pattern) { + pattern_ = pattern; + failure_ = ComputeFailureFunction(pattern_); +} + +std::vector KMP::ComputeFailureFunction(std::string_view pattern) { + int m = static_cast(pattern.length()); + std::vector failure(m, 0); + int i = 1, j = 0; + while (i < m) { + if (pattern[i] == pattern[j]) { + failure[i] = j + 1; + ++i; + ++j; + } else if (j > 0) { + j = failure[j - 1]; + } else { + failure[i] = 0; + ++i; + } + } + return failure; +} + +MinHash::MinHash(int num_hash_functions) + : m_num_hash_functions(num_hash_functions) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dis; + + for (int i = 0; i < num_hash_functions; ++i) { + m_coefficients_a.push_back(dis(gen)); + m_coefficients_b.push_back(dis(gen)); + } +} + +std::vector MinHash::compute_signature( + const std::unordered_set& set) { + std::vector signature( + m_num_hash_functions, std::numeric_limits::max()); + + for (const auto& element : set) { + for (int i = 0; i < m_num_hash_functions; ++i) { + unsigned long long hash_value = hash(element, i); + signature[i] = std::min(signature[i], hash_value); + } + } + + return signature; +} + +double MinHash::estimate_similarity( + const std::vector& signature1, + const std::vector& signature2) { + int num_matches = 0; + for (int i = 0; i < m_num_hash_functions; ++i) { + if (signature1[i] == signature2[i]) { + ++num_matches; + } + } + return static_cast(num_matches) / m_num_hash_functions; +} + +unsigned long long MinHash::hash(const std::string& element, int index) { + unsigned long long hash_value = 0; + for (char c : element) { + hash_value += + (m_coefficients_a[index] * static_cast(c) + + m_coefficients_b[index]); + } + return hash_value; +} + +BoyerMoore::BoyerMoore(std::string_view pattern) : pattern_(pattern) { + ComputeBadCharacterShift(); + ComputeGoodSuffixShift(); +} + +std::vector BoyerMoore::Search(std::string_view text) { + std::vector occurrences; + int n = static_cast(text.length()); + int m = static_cast(pattern_.length()); + int i = 0; + while (i <= n - m) { + int j = m - 1; + while (j >= 0 && pattern_[j] == text[i + j]) { + --j; + } + if (j < 0) { + occurrences.push_back(i); + i += good_suffix_shift_[0]; + } else { + i += std::max(good_suffix_shift_[j + 1], + bad_char_shift_[text[i + j]] - m + 1 + j); + } + } + return occurrences; +} + +void BoyerMoore::SetPattern(std::string_view pattern) { + pattern_ = pattern; + ComputeBadCharacterShift(); + ComputeGoodSuffixShift(); +} + +void BoyerMoore::ComputeBadCharacterShift() { + bad_char_shift_.clear(); + for (int i = 0; i < static_cast(pattern_.length()) - 1; ++i) { + bad_char_shift_[pattern_[i]] = + static_cast(pattern_.length()) - 1 - i; + } +} + +void BoyerMoore::ComputeGoodSuffixShift() { + int m = static_cast(pattern_.length()); + good_suffix_shift_.resize(m, m); + std::vector suffix(m, 0); + int j = 0; + for (int i = m - 1; i >= 0; --i) { + if (pattern_.substr(i) == pattern_.substr(m - j - 1, j + 1)) { + suffix[i] = j + 1; + } + if (i > 0) { + good_suffix_shift_[m - suffix[i]] = m - i; + } + } + for (int i = 0; i < m - 1; ++i) { + good_suffix_shift_[m - suffix[i]] = m - i; + } +} + +} // namespace Atom::Algorithm \ No newline at end of file diff --git a/src/atom/algorithm/algorithm.hpp b/src/atom/algorithm/algorithm.hpp new file mode 100644 index 00000000..94095af3 --- /dev/null +++ b/src/atom/algorithm/algorithm.hpp @@ -0,0 +1,221 @@ +/* + * base.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-4-5 + +Description: A collection of algorithms for C++ + +**************************************************/ + +#ifndef ATOM_ALGORITHM_ALGORITHM_HPP +#define ATOM_ALGORITHM_ALGORITHM_HPP + +#include +#include +#include +#include +#include +#include + +namespace Atom::Algorithm { +/** + * @brief The KMP class implements the Knuth-Morris-Pratt string searching + * algorithm. + */ +class KMP { +public: + /** + * @brief Constructs a new KMP object with the specified pattern. + * @param pattern The pattern to search for. + */ + explicit KMP(std::string_view pattern); + + /** + * @brief Searches for occurrences of the pattern in the given text. + * @param text The text to search in. + * @return A vector containing the starting positions of all occurrences of + * the pattern in the text. + */ + std::vector Search(std::string_view text); + + /** + * @brief Sets a new pattern to search for. + * @param pattern The new pattern to set. + */ + void SetPattern(std::string_view pattern); + +private: + /** + * @brief Computes the failure function for the specified pattern. + * @param pattern The pattern for which to compute the failure function. + * @return A vector containing the failure function values. + */ + std::vector ComputeFailureFunction(std::string_view pattern); + + std::string pattern_; /**< The pattern to search for. */ + std::vector + failure_; /**< The failure function for the current pattern. */ +}; + +/** + * @brief The MinHash class implements the MinHash algorithm for estimating the + * Jaccard similarity between sets. + */ +class MinHash { +public: + /** + * @brief Constructs a new MinHash object with the specified number of hash + * functions. + * @param num_hash_functions The number of hash functions to use for + * computing MinHash signatures. + */ + explicit MinHash(int num_hash_functions); + + /** + * @brief Computes the MinHash signature for the given set. + * @param set The set for which to compute the MinHash signature. + * @return A vector containing the MinHash signature of the set. + */ + std::vector compute_signature( + const std::unordered_set& set); + + /** + * @brief Estimates the Jaccard similarity between two sets using their + * MinHash signatures. + * @param signature1 The MinHash signature of the first set. + * @param signature2 The MinHash signature of the second set. + * @return The estimated Jaccard similarity between the two sets. + */ + double estimate_similarity( + const std::vector& signature1, + const std::vector& signature2); + +private: + /** + * @brief Computes the hash value of an element using a specific hash + * function. + * @param element The element to hash. + * @param index The index of the hash function to use. + * @return The hash value of the element. + */ + unsigned long long hash(const std::string& element, int index); + + int m_num_hash_functions; /**< The number of hash functions used for + MinHash. */ + std::vector + m_coefficients_a; /**< Coefficients 'a' for hash functions. */ + std::vector + m_coefficients_b; /**< Coefficients 'b' for hash functions. */ +}; + +/** + * @brief The BloomFilter class implements a Bloom filter data structure. + * @tparam N The size of the Bloom filter (number of bits). + */ +template +class BloomFilter { +public: + /** + * @brief Constructs a new BloomFilter object with the specified number of + * hash functions. + * @param num_hash_functions The number of hash functions to use for the + * Bloom filter. + */ + explicit BloomFilter(std::size_t num_hash_functions) + : m_num_hash_functions(num_hash_functions) {} + + /** + * @brief Inserts an element into the Bloom filter. + * @param element The element to insert. + */ + void insert(std::string_view element) { + for (std::size_t i = 0; i < m_num_hash_functions; ++i) { + std::size_t hash_value = hash(element, i); + m_bits.set(hash_value % N); + } + } + + /** + * @brief Checks if an element might be present in the Bloom filter. + * @param element The element to check. + * @return True if the element might be present, false otherwise. + */ + bool contains(std::string_view element) const { + for (std::size_t i = 0; i < m_num_hash_functions; ++i) { + std::size_t hash_value = hash(element, i); + if (!m_bits.test(hash_value % N)) { + return false; + } + } + return true; + } + +private: + std::bitset m_bits; /**< The bitset representing the Bloom filter. */ + std::size_t m_num_hash_functions; /**< The number of hash functions used. */ + + /** + * @brief Computes the hash value of an element using a specific seed. + * @param element The element to hash. + * @param seed The seed value for the hash function. + * @return The hash value of the element. + */ + std::size_t hash(std::string_view element, std::size_t seed) const { + std::size_t hash_value = seed; + for (char c : element) { + hash_value = hash_value * 31 + static_cast(c); + } + return hash_value; + } +}; + +/** + * @brief The BoyerMoore class implements the Boyer-Moore algorithm for string + * searching. + */ +class BoyerMoore { +public: + /** + * @brief Constructs a new BoyerMoore object with the specified pattern. + * @param pattern The pattern to search for. + */ + explicit BoyerMoore(std::string_view pattern); + + /** + * @brief Searches for occurrences of the pattern in the given text. + * @param text The text in which to search for the pattern. + * @return A vector containing the starting positions of all occurrences of + * the pattern in the text. + */ + std::vector Search(std::string_view text); + + /** + * @brief Sets a new pattern for the BoyerMoore object. + * @param pattern The new pattern to set. + */ + void SetPattern(std::string_view pattern); + +private: + /** + * @brief Computes the bad character shift table for the pattern. + */ + void ComputeBadCharacterShift(); + + /** + * @brief Computes the good suffix shift table for the pattern. + */ + void ComputeGoodSuffixShift(); + + std::string pattern_; /**< The pattern to search for. */ + std::unordered_map + bad_char_shift_; /**< The bad character shift table. */ + std::vector good_suffix_shift_; /**< The good suffix shift table. */ +}; +} // namespace Atom::Algorithm + +#endif diff --git a/src/atom/algorithm/base.cpp b/src/atom/algorithm/base.cpp index 7a379509..0f8d1f21 100644 --- a/src/atom/algorithm/base.cpp +++ b/src/atom/algorithm/base.cpp @@ -12,16 +12,15 @@ Description: A collection of algorithms for C++ **************************************************/ -#include "base16.hpp" +#include "base.hpp" #include +#include #include #include #include -#include #include - #ifdef _WIN32 #include #else @@ -29,7 +28,7 @@ Description: A collection of algorithms for C++ #endif namespace Atom::Algorithm { -std::string encodeBase16(const std::vector &data) { +std::string base16Encode(const std::vector &data) { std::stringstream ss; ss << std::hex << std::uppercase << std::setfill('0'); @@ -40,7 +39,7 @@ std::string encodeBase16(const std::vector &data) { return ss.str(); } -std::vector decodeBase16(const std::string &data) { +std::vector base16Decode(const std::string &data) { std::vector result; for (size_t i = 0; i < data.length(); i += 2) { @@ -53,254 +52,144 @@ std::vector decodeBase16(const std::string &data) { return result; } -std::string base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; - -std::string encodeBase32(const std::string &data) { - std::string output; - - auto getChunk = [&](size_t index) { - uint64_t chunk = 0; - for (size_t i = 0; i < 5; ++i) { - if (index * 5 + i < data.size()) { - chunk |= static_cast(data[index * 5 + i]) - << (32 - i * 8); - } - } - return chunk; - }; - - for (size_t i = 0; i < (data.size() + 4) / 5; ++i) { - uint64_t chunk = getChunk(i); +constexpr std::string_view base32_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; - for (size_t j = 0; j < 8; ++j) { - uint8_t index = (chunk >> (35 - j * 5)) & 0x1f; - output += base32Chars[index]; - } +std::string base32Encode(const uint8_t *data, size_t length) { + std::string result; + result.reserve((length + 4) / 5 * 8); - if (data.size() - i * 5 < 5) { - output.replace(output.size() - (5 - data.size() % 5), - 5 - data.size() % 5, 5 - data.size() % 5, '='); + size_t bits = 0; + int num_bits = 0; + for (size_t i = 0; i < length; ++i) { + bits = (bits << 8) | data[i]; + num_bits += 8; + while (num_bits >= 5) { + result.push_back(base32_chars[(bits >> (num_bits - 5)) & 0x1F]); + num_bits -= 5; } } - return output; -} + if (num_bits > 0) { + bits <<= (5 - num_bits); + result.push_back(base32_chars[bits & 0x1F]); + } -std::string decodeBase32(const std::string &data) { - std::string output; - std::vector bytes; + int padding_chars = (8 - result.size() % 8) % 8; + result.append(padding_chars, '='); - auto getChunk = [&](size_t index) { - uint64_t chunk = 0; - for (size_t i = 0; i < 8; ++i) { - char c = data[index * 8 + i]; - if (c == '=') { - break; - } + return result; +} - uint8_t base32Index = base32Chars.find(c); - if (base32Index == std::string::npos) { - throw std::invalid_argument("Invalid Base32 character: " + c); - } +std::string base32Decode(std::string_view encoded) { + std::string result; + result.reserve(encoded.size() * 5 / 8); - chunk |= static_cast(base32Index) << (35 - i * 5); + size_t bits = 0; + int num_bits = 0; + for (char c : encoded) { + if (c == '=') { + break; } - return chunk; - }; - - for (size_t i = 0; i < data.size() / 8; ++i) { - uint64_t chunk = getChunk(i); - - for (size_t j = 0; j < 5; ++j) { - uint8_t byte = (chunk >> (32 - j * 8)) & 0xff; - if (byte != 0) { - bytes.push_back(byte); - } + auto pos = base32_chars.find(c); + if (pos == std::string_view::npos) { + throw std::invalid_argument( + "Invalid character in Base32 encoded string"); + } + bits = (bits << 5) | pos; + num_bits += 5; + if (num_bits >= 8) { + result.push_back(static_cast(bits >> (num_bits - 8))); + num_bits -= 8; } } - output.assign(bytes.begin(), bytes.end()); - - return output; + return result; } -std::string base64Encode(const std::vector &bytes_to_encode) { - static const std::string kBase64Chars = +std::string base64Encode(std::string_view bytes_to_encode) { + static constexpr std::string_view kBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; + std::string ret; - int i = 0; - int j = 0; - unsigned char char_array_3[3]; - unsigned char char_array_4[4]; - for (const auto &byte : bytes_to_encode) { - char_array_3[i++] = byte; - if (i == 3) { - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + - ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + - ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; - for (i = 0; i < 4; i++) { - ret += kBase64Chars[char_array_4[i]]; - } - i = 0; - } - } - if (i) { - for (j = i; j < 3; j++) { - char_array_3[j] = '\0'; + ret.reserve((bytes_to_encode.size() + 2) / 3 * 4); + + std::array char_array_3{}; + std::array char_array_4{}; + + auto it = bytes_to_encode.begin(); + auto end = bytes_to_encode.end(); + + while (it != end) { + int i = 0; + for (; i < 3 && it != end; ++i, ++it) { + char_array_3[i] = static_cast(*it); } + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; - for (j = 0; j < i + 1; j++) { - ret += kBase64Chars[char_array_4[j]]; - } - while ((i++ < 3)) { - ret += '='; - } - } - return ret; -} -std::vector base64Decode(const std::string &encoded_string) { - static const std::string kBase64Chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; + for (int j = 0; j < i + 1; ++j) { + ret.push_back(kBase64Chars[char_array_4[j]]); + } - int in_len = encoded_string.size(); - int i = 0; - int j = 0; - int in_ = 0; - unsigned char char_array_4[4], char_array_3[3]; - std::vector ret; - - while (in_len-- && (encoded_string[in_] != '=') && - (kBase64Chars.find(encoded_string[in_]) != std::string::npos)) { - char_array_4[i++] = encoded_string[in_]; - in_++; - if (i == 4) { - for (i = 0; i < 4; i++) { - char_array_4[i] = static_cast( - kBase64Chars.find(char_array_4[i])); + if (i < 3) { + for (int j = i; j < 3; ++j) { + ret.push_back('='); } - char_array_3[0] = - (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + - ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - for (i = 0; i < 3; i++) { - ret.push_back(char_array_3[i]); - } - i = 0; - } - } - if (i) { - for (j = i; j < 4; j++) { - char_array_4[j] = 0; - } - for (j = 0; j < 4; j++) { - char_array_4[j] = - static_cast(kBase64Chars.find(char_array_4[j])); - } - char_array_3[0] = - (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = - ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - for (j = 0; j < i - 1; j++) { - ret.push_back(char_array_3[j]); + break; } } + return ret; } -std::string base64EncodeEnhance(const std::vector &bytes_to_encode) { - const std::string base64_chars = +std::string base64Decode(std::string_view encoded_string) { + static constexpr std::string_view kBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; - std::string encoded_data; - size_t input_length = bytes_to_encode.size(); + std::string ret; + ret.reserve(encoded_string.size() / 4 * 3); - for (size_t i = 0; i < input_length; i += 3) { - uint32_t padded_value = 0; - int padding_count = 0; + std::array char_array_4{}; + std::array char_array_3{}; - for (size_t j = 0; j < 3; ++j) { - if (i + j < input_length) { - padded_value <<= 8; - padded_value |= bytes_to_encode[i + j]; - } else { - padded_value <<= 8; - ++padding_count; - } - } + auto it = encoded_string.begin(); + auto end = encoded_string.end(); - for (int k = 0; k < 4 - padding_count; ++k) { - uint8_t index = (padded_value >> (6 * (3 - k))) & 0x3F; - encoded_data += base64_chars[index]; + while (it != end) { + int i = 0; + for (; i < 4 && it != end && *it != '='; ++i, ++it) { + char_array_4[i] = + static_cast(kBase64Chars.find(*it)); } - for (int k = 0; k < padding_count; ++k) { - encoded_data += '='; - } - } - - return encoded_data; -} - -std::vector base64DecodeEnhance(const std::string &encoded_string) { - const std::string base64_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - std::vector decoded_data; - size_t input_length = encoded_string.length(); - size_t padding_count = - std::count(encoded_string.begin(), encoded_string.end(), '='); - size_t output_length = (3 * input_length) / 4 - padding_count; - - uint32_t padded_value = 0; - int padding_index = 0; - - for (size_t i = 0; i < input_length; ++i) { - if (encoded_string[i] == '=') { - padded_value <<= 6; - ++padding_index; - } else { - uint8_t value = base64_chars.find(encoded_string[i]); - padded_value <<= 6; - padded_value |= value; - } + char_array_3[0] = + (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = + ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - if ((i + 1) % 4 == 0 && padding_index < padding_count) { - for (int j = 0; j < 3 - padding_index; ++j) { - uint8_t byte = (padded_value >> (16 - (j + 1) * 8)) & 0xFF; - decoded_data.push_back(byte); - } - padding_index = 0; - padded_value = 0; + for (int j = 0; j < i - 1; ++j) { + ret.push_back(static_cast(char_array_3[j])); } } - return decoded_data; + return ret; } const std::string base85_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<" "=>?@^_`{|}~"; -std::string encodeBase85(const std::vector &data) { +std::string base85Encode(const std::vector &data) { std::string result; unsigned int value = 0; @@ -326,7 +215,7 @@ std::string encodeBase85(const std::vector &data) { return result; } -std::vector decodeBase85(const std::string &data) { +std::vector base85Decode(const std::string &data) { std::vector result; unsigned int value = 0; @@ -347,52 +236,148 @@ std::vector decodeBase85(const std::string &data) { return result; } -std::vector encodeBase128(const std::span &input) { - std::vector output; - output.reserve(input.size() * 8 / 7); // 预留足够的空间 +constexpr std::array kEncodeTable = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$', + '%', '&', '(', ')', '*', '+', ',', '.', '/', ':', ';', '<', '=', + '>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"'}; + +constexpr std::array kDecodeTable = []() { + std::array table{}; + table.fill(-1); + for (int i = 0; i < kEncodeTable.size(); ++i) { + table[kEncodeTable[i]] = i; + } + return table; +}(); - size_t bits = 0; - uint32_t value = 0; +std::string base91Encode(std::string_view data) { + std::string result; + result.reserve(data.size() * 2); + + int ebq = 0; + int en = 0; + for (char c : data) { + ebq |= static_cast(c) << en; + en += 8; + if (en > 13) { + int ev = ebq & 8191; + if (ev > 88) { + ebq >>= 13; + en -= 13; + } else { + ev = ebq & 16383; + ebq >>= 14; + en -= 14; + } + result += kEncodeTable[ev % 91]; + result += kEncodeTable[ev / 91]; + } + } + + if (en > 0) { + result += kEncodeTable[ebq % 91]; + if (en > 7 || ebq > 90) { + result += kEncodeTable[ebq / 91]; + } + } + + return result; +} + +std::string base91Decode(std::string_view data) { + std::string result; + result.reserve(data.size()); - for (auto byte : input) { - value = (value << 8) | byte; // 将新字节加入到value中 - bits += 8; + int dbq = 0; + int dn = 0; + int dv = -1; - while (bits >= 7) { - bits -= 7; - output.push_back((value >> bits) & 0x7F); // 提取最高的7位 + for (char c : data) { + if (c == '"') { + continue; + } + if (dv == -1) { + dv = kDecodeTable[c]; + } else { + dv += kDecodeTable[c] * 91; + dbq |= dv << dn; + dn += (dv & 8191) > 88 ? 13 : 14; + do { + result += static_cast(dbq & 0xFF); + dbq >>= 8; + dn -= 8; + } while (dn > 7); + dv = -1; } } - if (bits > 0) { // 处理剩余的bits - output.push_back((value << (7 - bits)) & 0x7F); + if (dv != -1) { + result += static_cast((dbq | dv << dn) & 0xFF); } - return output; + return result; } -// 解码 -std::vector decodeBase128(const std::span &input) { - std::vector output; - output.reserve(input.size() * 7 / 8); // 预留足够的空间 +std::string base128Encode(const uint8_t *data, size_t length) { + std::string result; + result.reserve((length * 8 + 6) / 7); size_t bits = 0; - uint32_t value = 0; - - for (auto byte : input) { - if (byte & 0x80) { - throw std::invalid_argument("Input is not valid Base128 encoded."); + int num_bits = 0; + for (size_t i = 0; i < length; ++i) { + bits = (bits << 8) | data[i]; + num_bits += 8; + while (num_bits >= 7) { + result.push_back( + static_cast((bits >> (num_bits - 7)) & 0x7F)); + num_bits -= 7; } + } - value = (value << 7) | byte; - bits += 7; + if (num_bits > 0) { + bits <<= (7 - num_bits); + result.push_back(static_cast(bits & 0x7F)); + } + + return result; +} - if (bits >= 8) { - bits -= 8; - output.push_back((value >> bits) & 0xFF); // 提取最高的8位 +std::string base128Decode(std::string_view encoded) { + std::string result; + result.reserve(encoded.size() * 7 / 8); + + size_t bits = 0; + int num_bits = 0; + for (char c : encoded) { + if (static_cast(c) > 127) { + throw std::invalid_argument( + "Invalid character in Base128 encoded string"); + } + bits = (bits << 7) | static_cast(c); + num_bits += 7; + if (num_bits >= 8) { + result.push_back(static_cast(bits >> (num_bits - 8))); + num_bits -= 8; } } - return output; + return result; +} + +std::string xorEncrypt(std::string_view plaintext, uint8_t key) { + std::string ciphertext; + ciphertext.reserve(plaintext.size()); + for (char c : plaintext) { + ciphertext.push_back(static_cast(static_cast(c) ^ key)); + } + return ciphertext; +} + +std::string xorDecrypt(std::string_view ciphertext, uint8_t key) { + return xorEncrypt(ciphertext, key); } } // namespace Atom::Algorithm \ No newline at end of file diff --git a/src/atom/algorithm/base.hpp b/src/atom/algorithm/base.hpp index aa0b15d8..c9bd3240 100644 --- a/src/atom/algorithm/base.hpp +++ b/src/atom/algorithm/base.hpp @@ -19,7 +19,6 @@ Description: A collection of algorithms for C++ #include #include - namespace Atom::Algorithm { /** * @brief Encodes a vector of unsigned characters into a Base16 string. @@ -33,7 +32,7 @@ namespace Atom::Algorithm { * @return The Base16 encoded string. */ [[nodiscard("The result of base16Encode is not used.")]] std::string -encodeBase16(const std::vector &data); +base16Encode(const std::vector &data); /** * @brief Decodes a Base16 string into a vector of unsigned characters. @@ -48,7 +47,7 @@ encodeBase16(const std::vector &data); */ [[nodiscard( "The result of base16Decode is not used.")]] std::vector -decodeBase16(const std::string &data); +base16Decode(const std::string &data); /** * @brief Encodes a string to Base32 @@ -56,7 +55,7 @@ decodeBase16(const std::string &data); * @return The encoded string */ [[nodiscard("The result of base32Encode is not used.")]] std::string -encodeBase32(const std::string &data); +base32Encode(const uint8_t *data, size_t length); /** * @brief Decodes a Base32 string @@ -64,7 +63,7 @@ encodeBase32(const std::string &data); * @return The decoded string */ [[nodiscard("The result of base32Decode is not used.")]] std::string -decodeBase32(const std::string &data); +base32Decode(std::string_view encoded); /** * @brief Base64编码函数 @@ -73,7 +72,7 @@ decodeBase32(const std::string &data); * @return std::string 编码后的字符串 */ [[nodiscard("The result of base64Encode is not used.")]] std::string -base64Encode(const std::vector &bytes_to_encode); +base64Encode(std::string_view bytes_to_encode); /** * @brief Base64解码函数 @@ -81,28 +80,8 @@ base64Encode(const std::vector &bytes_to_encode); * @param encoded_string 待解码字符串 * @return std::vector 解码后的数据 */ -[[nodiscard( - "The result of base64Decode is not used.")]] std::vector -base64Decode(const std::string &encoded_string); - -/** - * @brief Base64编码函数 - * - * @param bytes_to_encode 待编码数据 - * @return std::string 编码后的字符串 - */ -[[nodiscard("The result of base64EncodeEnhance is not used.")]] std::string -base64EncodeEnhance(const std::vector &bytes_to_encode); - -/** - * @brief Base64解码函数 - * - * @param encoded_string 待解码字符串 - * @return std::vector 解码后的数据 - */ -[[nodiscard( - "The result of base64DecodeEnhance is not used.")]] std::vector -base64DecodeEnhance(const std::string &encoded_string); +[[nodiscard("The result of base64Decode is not used.")]] std::string +base64Decode(std::string_view encoded_string); /** * @brief Encodes a vector of unsigned characters into a Base85 string. @@ -115,7 +94,7 @@ base64DecodeEnhance(const std::string &encoded_string); * @return The Base85 encoded string. */ [[nodiscard("The result of base85Encode is not used.")]] std::string -encodeBase85(const std::vector &data); +base85Encode(const std::vector &data); /** * @brief Decodes a Base85 string into a vector of unsigned characters. @@ -129,7 +108,23 @@ encodeBase85(const std::vector &data); */ [[nodiscard( "The result of base85Decode is not used.")]] std::vector -decodeBase85(const std::string &data); +base85Decode(const std::string &data); + +/** + * @brief Encodes a string to Base91 + * @param data The string to encode + * @return The encoded string + */ +[[nodiscard("The result of base91Encode is not used.")]] +std::string base91Encode(std::string_view data); + +/** + * @brief Decodes a Base91 string + * @param data The string to decode + * @return The decoded string + */ +[[nodiscard("The result of base91Decode is not used.")]] std::string +base91Decode(std::string_view data); /** * @brief Encodes a vector of unsigned characters into a Base128 string. @@ -141,8 +136,8 @@ decodeBase85(const std::string &data); * @param data The vector of unsigned characters to be encoded. * @return The Base128 encoded string. */ -[[nodiscard("The result of encodeBase128 is not used.")]] std::vector -encodeBase128(const std::span &data); +[[nodiscard("The result of encodeBase128 is not used.")]] std::string +base128Encode(const uint8_t *data, size_t length); /** * @brief Decodes a Base128 string into a vector of unsigned characters. @@ -154,8 +149,14 @@ encodeBase128(const std::span &data); * @param data The Base128 encoded string to be decoded. * @return The decoded vector of unsigned characters. */ -[[nodiscard("The result of decodeBase128 is not used.")]] std::vector -decodeBase128(const std::span &data); +[[nodiscard("The result of decodeBase128 is not used.")]] std::string +base128Decode(std::string_view encoded); + +[[nodiscard("The result of xorEncrypt is not used.")]] std::string xorEncrypt( + std::string_view plaintext, uint8_t key); + +[[nodiscard("The result of xorDecrypt is not used.")]] std::string xorDecrypt( + std::string_view ciphertext, uint8_t key); } // namespace Atom::Algorithm #endif diff --git a/src/atom/algorithm/fraction.cpp b/src/atom/algorithm/fraction.cpp index 2bd25c9b..85814d3f 100644 --- a/src/atom/algorithm/fraction.cpp +++ b/src/atom/algorithm/fraction.cpp @@ -2,6 +2,7 @@ #include #include +#include namespace Atom::Algorithm { @@ -75,53 +76,15 @@ Fraction Fraction::operator/(const Fraction &other) const { return result; } -auto Fraction::operator<=>(const Fraction &other) const { - double diff = this->to_double() - other.to_double(); - if (diff > 0) - return std::strong_ordering::greater; - else if (diff < 0) - return std::strong_ordering::less; - else - return std::strong_ordering::equal; -} - bool Fraction::operator==(const Fraction &other) const { return (numerator == other.numerator) && (denominator == other.denominator); } -Fraction &Fraction::operator++() { - *this += 1; - return *this; -} - -Fraction Fraction::operator++(int) { - Fraction temp = *this; - ++(*this); - return temp; -} +Fraction::operator double() const { return to_double(); } -Fraction &Fraction::operator--() { - *this -= 1; - return *this; -} - -Fraction Fraction::operator--(int) { - Fraction temp = *this; - --(*this); - return temp; -} - -Fraction Fraction::operator-() const { - return Fraction(-numerator, denominator); -} - -explicit Fraction::operator double() const { return to_double(); } - -explicit Fraction::operator float() const { - return static_cast(to_double()); -} +Fraction::operator float() const { return static_cast(to_double()); } -explicit Fraction::operator int() const { return numerator / denominator; } +Fraction::operator int() const { return numerator / denominator; } std::string Fraction::to_string() const { if (denominator == 1) { @@ -142,8 +105,10 @@ std::ostream &operator<<(std::ostream &os, const Fraction &f) { std::istream &operator>>(std::istream &is, Fraction &f) { int n, d; char slash; - is >> n >> slash >> d; - f = Fraction(n, d); + // TODO: Fix this + // is >> n >> slash >> d; + f = Fraction(n, d); // Assuming Fraction has a constructor that takes + // numerator and denominator return is; } diff --git a/src/atom/algorithm/fraction.hpp b/src/atom/algorithm/fraction.hpp index 38742982..9530852b 100644 --- a/src/atom/algorithm/fraction.hpp +++ b/src/atom/algorithm/fraction.hpp @@ -19,40 +19,162 @@ Description: Implementation of fraction class #include #include - namespace Atom::Algorithm { - +/** + * @brief Represents a fraction with numerator and denominator. + */ class Fraction { -private: - int numerator; - int denominator; - + /** + * @brief Computes the greatest common divisor (GCD) of two numbers. + * @param a The first number. + * @param b The second number. + * @return The GCD of the two numbers. + */ static int gcd(int a, int b); + + /** + * @brief Reduces the fraction to its simplest form. + */ void reduce(); + // For pybind11 compatibility +public: + int numerator; /**< The numerator of the fraction. */ + int denominator; /**< The denominator of the fraction. */ + public: - Fraction(int n = 0, int d = 1); + /** + * @brief Constructs a new Fraction object with the given numerator and + * denominator. + * @param n The numerator (default is 0). + * @param d The denominator (default is 1). + */ + explicit Fraction(int n = 0, int d = 1); + + /** + * @brief Adds another fraction to this fraction. + * @param other The fraction to add. + * @return Reference to the modified fraction. + */ Fraction& operator+=(const Fraction& other); + + /** + * @brief Subtracts another fraction from this fraction. + * @param other The fraction to subtract. + * @return Reference to the modified fraction. + */ Fraction& operator-=(const Fraction& other); + + /** + * @brief Multiplies this fraction by another fraction. + * @param other The fraction to multiply by. + * @return Reference to the modified fraction. + */ Fraction& operator*=(const Fraction& other); + + /** + * @brief Divides this fraction by another fraction. + * @param other The fraction to divide by. + * @return Reference to the modified fraction. + */ Fraction& operator/=(const Fraction& other); + + /** + * @brief Adds another fraction to this fraction. + * @param other The fraction to add. + * @return The result of addition. + */ Fraction operator+(const Fraction& other) const; + + /** + * @brief Subtracts another fraction from this fraction. + * @param other The fraction to subtract. + * @return The result of subtraction. + */ Fraction operator-(const Fraction& other) const; + + /** + * @brief Multiplies this fraction by another fraction. + * @param other The fraction to multiply by. + * @return The result of multiplication. + */ Fraction operator*(const Fraction& other) const; + + /** + * @brief Divides this fraction by another fraction. + * @param other The fraction to divide by. + * @return The result of division. + */ Fraction operator/(const Fraction& other) const; - auto operator<=>(const Fraction& other) const; + +#if __cplusplus >= 202002L + /** + * @brief Compares this fraction with another fraction. + * @param other The fraction to compare with. + * @return An integer indicating the comparison result. + */ + auto operator<=>(const Fraction& other) const { + double diff = this->to_double() - other.to_double(); + if (diff > 0) + return std::strong_ordering::greater; + else if (diff < 0) + return std::strong_ordering::less; + else + return std::strong_ordering::equal; + } +#endif + + /** + * @brief Checks if this fraction is equal to another fraction. + * @param other The fraction to compare with. + * @return True if fractions are equal, false otherwise. + */ bool operator==(const Fraction& other) const; - Fraction& operator++(); - Fraction operator++(int); - Fraction& operator--(); - Fraction operator--(int); - Fraction operator-() const; + + /** + * @brief Converts the fraction to a double value. + * @return The fraction as a double. + */ explicit operator double() const; + + /** + * @brief Converts the fraction to a float value. + * @return The fraction as a float. + */ explicit operator float() const; + + /** + * @brief Converts the fraction to an integer value. + * @return The fraction as an integer. + */ explicit operator int() const; + + /** + * @brief Converts the fraction to a string representation. + * @return The string representation of the fraction. + */ std::string to_string() const; + + /** + * @brief Converts the fraction to a double value. + * @return The fraction as a double. + */ double to_double() const; + + /** + * @brief Outputs the fraction to the output stream. + * @param os The output stream. + * @param f The fraction to output. + * @return Reference to the output stream. + */ friend std::ostream& operator<<(std::ostream& os, const Fraction& f); + + /** + * @brief Inputs the fraction from the input stream. + * @param is The input stream. + * @param f The fraction to input. + * @return Reference to the input stream. + */ friend std::istream& operator>>(std::istream& is, Fraction& f); }; diff --git a/src/atom/algorithm/hash.hpp b/src/atom/algorithm/hash.hpp index d33e2683..58394575 100644 --- a/src/atom/algorithm/hash.hpp +++ b/src/atom/algorithm/hash.hpp @@ -16,22 +16,49 @@ Description: A collection of hash algorithms #define ATOM_ALGORITHM_HASH_HPP #include +#include #include +#include +#include #include #include #include - +namespace Atom::Algorithm { +/** + * @brief Concept to check if a type is hashable. + * + * A type is considered hashable if it can be used as a key in hash-based + * containers. + */ template concept Hashable = requires(T a) { { std::hash{}(a) } -> std::convertible_to; }; +/** + * @brief Computes the hash value of a single value. + * + * This function computes the hash value of a single value using std::hash. + * + * @param value The value for which to compute the hash. + * @return The hash value of the input value. + */ template std::size_t computeHash(const T& value) { return std::hash{}(value); } +/** + * @brief Computes the hash value of a vector of hashable values. + * + * This function computes the hash value of a vector of hashable values by + * combining the hash values of individual elements using a bitwise XOR + * operation. + * + * @param values The vector of hashable values. + * @return The hash value of the vector. + */ template std::size_t computeHash(const std::vector& values) { std::size_t result = 0; @@ -42,10 +69,20 @@ std::size_t computeHash(const std::vector& values) { return result; } +/** + * @brief Computes the hash value of a tuple of hashable values. + * + * This function computes the hash value of a tuple of hashable values by + * applying the computeHash function to each element of the tuple and combining + * the hash values using a bitwise XOR operation. + * + * @param tuple The tuple of hashable values. + * @return The hash value of the tuple. + */ template std::size_t computeHash(const std::tuple& tuple) { std::size_t result = 0; - apply( + std::apply( [&result](const Ts&... values) { ((result ^= computeHash(values) + 0x9e3779b9 + (result << 6) + (result >> 2)), @@ -55,6 +92,16 @@ std::size_t computeHash(const std::tuple& tuple) { return result; } +/** + * @brief Computes the hash value of an array of hashable values. + * + * This function computes the hash value of an array of hashable values by + * combining the hash values of individual elements using a bitwise XOR + * operation. + * + * @param array The array of hashable values. + * @return The hash value of the array. + */ template std::size_t computeHash(const std::array& array) { std::size_t result = 0; @@ -65,4 +112,153 @@ std::size_t computeHash(const std::array& array) { return result; } +/** + * @brief Computes the FNV-1a hash value of a range. + * + * This function computes the FNV-1a hash value of a range defined by iterators. + * + * @param begin Iterator to the beginning of the range. + * @param end Iterator to the end of the range. + * @return The FNV-1a hash value of the range. + */ +template +constexpr std::uint32_t fnv1a_hash(Itr begin, Itr end) noexcept { + std::uint32_t h = 0x811c9dc5; + + while (begin != end) { + h = (h ^ static_cast(*begin)) * 0x01000193; + ++begin; + } + return h; +} + +/** + * @brief Computes the FNV-1a hash value of a null-terminated string literal. + * + * This function computes the FNV-1a hash value of a null-terminated string + * literal. + * + * @param str The null-terminated string literal. + * @return The FNV-1a hash value of the string. + */ +template +constexpr std::uint32_t fnv1a_hash(const char (&str)[N]) noexcept { + return fnv1a_hash(std::begin(str), std::end(str) - 1); +} + +/** + * @brief Computes the FNV-1a hash value of a string view. + * + * This function computes the FNV-1a hash value of a string view. + * + * @param sv The string view. + * @return The FNV-1a hash value of the string view. + */ +constexpr std::uint32_t fnv1a_hash(std::string_view sv) noexcept { + return fnv1a_hash(sv.begin(), sv.end()); +} + +/** + * @brief Computes the FNV-1a hash value of a string. + * + * This function computes the FNV-1a hash value of a string. + * + * @param s The string. + * @return The FNV-1a hash value of the string. + */ +inline std::uint32_t fnv1a_hash(const std::string& s) noexcept { + return fnv1a_hash(std::string_view{s}); +} + +/** + * @brief Computes the Jenkins One-at-a-Time hash value of a range. + * + * This function computes the Jenkins One-at-a-Time hash value of a range + * defined by iterators. + * + * @param begin Iterator to the beginning of the range. + * @param end Iterator to the end of the range. + * @return The Jenkins One-at-a-Time hash value of the range. + */ +template +constexpr std::uint32_t jenkins_one_at_a_time_hash(Itr begin, + Itr end) noexcept { + std::uint32_t hash = 0; + + while (begin != end) { + hash += static_cast(*begin); + hash += hash << 10; + hash ^= hash >> 6; + ++begin; + } + + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + return hash; +} + +/** + * @brief Computes the Jenkins One-at-a-Time hash value of a null-terminated + * string literal. + * + * This function computes the Jenkins One-at-a-Time hash value of a + * null-terminated string literal. + * + * @param str The null-terminated string literal. + * @return The Jenkins One-at-a-Time hash value of the string. + */ +template +constexpr std::uint32_t jenkins_one_at_a_time_hash( + const char (&str)[N]) noexcept { + return jenkins_one_at_a_time_hash(std::begin(str), std::end(str) - 1); +} + +/** + * @brief Computes the Jenkins One-at-a-Time hash value of a string view. + * + * This function computes the Jenkins One-at-a-Time hash value of a string view. + * + * @param sv The string view. + * @return The Jenkins One-at-a-Time hash value of the string view. + */ +constexpr std::uint32_t jenkins_one_at_a_time_hash( + std::string_view sv) noexcept { + return jenkins_one_at_a_time_hash(sv.begin(), sv.end()); +} + +/** + * @brief Computes the Jenkins One-at-a-Time hash value of a string. + * + * This function computes the Jenkins One-at-a-Time hash value of a string. + * + * @param s The string. + * @return The Jenkins One-at-a-Time hash value of the string. + */ +inline std::uint32_t jenkins_one_at_a_time_hash(const std::string& s) noexcept { + return jenkins_one_at_a_time_hash(std::string_view{s}); +} + +inline uint32_t quickHash(std::string_view str) { + uint32_t h = 0; + for (char c : str) { + h = 31 * h + static_cast(c); + } + return h; +} + +inline uint32_t quickHash(const void* data, size_t size) { + if (data == nullptr || size == 0) + return 0; + + const auto* str = static_cast(data); + uint32_t h = 0; + for (size_t i = 0; i < size; ++i) { + h = 31 * h + str[i]; + } + return h; +} + +} // namespace Atom::Algorithm + #endif diff --git a/src/atom/algorithm/math.cpp b/src/atom/algorithm/math.cpp index 0e106bd3..f4253829 100644 --- a/src/atom/algorithm/math.cpp +++ b/src/atom/algorithm/math.cpp @@ -1,5 +1,5 @@ /* - * math.cpp + * mathutils.cpp * * Copyright (C) 2023-2024 Max Qian */ @@ -14,246 +14,107 @@ Description: Extra Math Library #include "math.hpp" -#include -#include +#include +#include -namespace Atom::Utils { -/* "+",加法重载部分 */ -// 友元函数是可以通过成员访问运算符访问私有成员的 -Fraction operator+(const Fraction &f1, const Fraction &f2) { - // 如果声明中加了const,定义中没有加const,可能会出现无权访问私有成员的报错 - // 加法友元重载函数定义 - int retnum = f1.getNumerator() * f2.getDenominator() + - f2.getNumerator() * f1.getDenominator(); - int retden = f1.getDenominator() * f2.getDenominator(); - int ratio = Fraction::Euclid(retnum, retden); - if (retden < 0 && retnum >= 0) { - ratio *= -1; - } - return Fraction( - retnum / ratio, - retden / ratio); // 在使用的时候编译器会自动调用构造函数Fraction(const - // Fraction&),用来处理作用域问题(深复制了) -} +namespace Atom::Algorithm { -/* "-",减法重载部分 */ -Fraction operator-(const Fraction &f1, const Fraction &f2) { - // 减法友元重载函数定义 - int retnum = f1.getNumerator() * f2.getDenominator() - - f2.getNumerator() * f1.getDenominator(); - int retden = f1.getDenominator() * f2.getDenominator(); - int ratio = Fraction::Euclid(retnum, retden); - if (retden < 0 && retnum >= 0) { - ratio *= -1; - } - return Fraction( - retnum / ratio, - retden / - ratio); // 在使用的时候编译器会自动调用Fraction默认构造函数,用来处理作用域问题(深复制了) -} +#if defined(__GNUC__) && defined(__SIZEOF_INT128__) +uint64_t mulDiv64(uint64_t operant, uint64_t multiplier, + uint64_t divider) noexcept { + __uint128_t a = operant; + __uint128_t b = multiplier; + __uint128_t c = divider; -/* "*",乘法重载部分 */ -Fraction operator*(const Fraction &f1, const Fraction &f2) { - // 加法友元重载函数定义 - int retnum = f1.getNumerator() * f2.getNumerator(); - int retden = f1.getDenominator() * f2.getDenominator(); - int ratio = Fraction::Euclid(retnum, retden); - if (retden < 0 && retnum >= 0) { - ratio *= -1; - } - return Fraction(retnum / ratio, retden / ratio); + return static_cast((a * b) / c); } +#elif defined(_MSC_VER) +#include // For _umul128 and _BitScanReverse64 +#include -/* '/'乘法重载 */ -Fraction operator/(const Fraction &f1, const Fraction &f2) { - // 减法友元重载函数定义 - int retnum = f1.getNumerator() * f2.getDenominator(); - int retden = f1.getDenominator() * f2.getNumerator(); - int ratio = Fraction::Euclid(retnum, retden); - if (retden < 0 && retnum >= 0) { - ratio *= -1; - } - return Fraction(retnum / ratio, retden / ratio); -} +uint64_t mulDiv64(uint64_t operant, uint64_t multiplier, + uint64_t divider) noexcept { + uint64_t highProd; + uint64_t lowProd = _umul128( + operant, multiplier, + &highProd); // Directly get the low and high parts of the product -// "+="运算符作为成员函数进行重载 -Fraction &Fraction::operator+=(const Fraction &f) { - int retnum = this->numerator * f.getDenominator() + - this->denominator * f.getNumerator(); - int retden = this->denominator * f.getDenominator(); - int ratio = Fraction::Euclid(retnum, retden); - // 保证分母永远大于0 - if (this->denominator < 0 && this->numerator >= 0) { - ratio *= -1; + if (divider == 0) { + throw std::runtime_error("Division by zero"); } - this->numerator = retnum / ratio; - this->denominator = retden / ratio; - return (*this); -} - -// "-="运算符作为成员函数进行重载 -Fraction &Fraction::operator-=(const Fraction &f) { - int retnum = this->numerator * f.getDenominator() - - this->denominator * f.getNumerator(); - int retden = this->denominator * f.getDenominator(); - int ratio = Fraction::Euclid(retnum, retden); - if (this->denominator < 0 && this->numerator >= 0) { - ratio *= -1; - } - this->numerator = retnum / ratio; - this->denominator = retden / ratio; - return (*this); -} + // Normalize divisor + unsigned long shift = 63 - std::bit_width(divider - 1); + uint64_t normDiv = divider << shift; -// "*="运算符作为成员函数进行重载 -Fraction &Fraction::operator*=(const Fraction &f) { - int retnum = this->numerator * f.getNumerator(); - int retden = this->denominator * f.getDenominator(); - int ratio = Fraction::Euclid(retnum, retden); - if (this->denominator < 0 && this->numerator >= 0) { - ratio *= -1; - } - this->numerator = retnum / ratio; - this->denominator = retden / ratio; - return *this; -} -Fraction &Fraction::operator/=(const Fraction &f) { - int retnum = this->numerator * f.getDenominator(); - int retden = this->denominator * f.getNumerator(); - int ratio = Fraction::Euclid(retnum, retden); - if (this->denominator < 0 && this->numerator >= 0) { - ratio *= -1; - } - this->numerator = retnum / ratio; - this->denominator = retden / ratio; + // Normalize high part + highProd = (highProd << shift) | (lowProd >> (64 - shift)); + lowProd <<= shift; - return *this; -} -Fraction &Fraction::operator=(const Fraction &f) { - this->numerator = f.getNumerator(); - this->denominator = f.getDenominator(); - return *this; -} + // Division using high and low parts + uint64_t quotient, remainder; + _udiv128(highProd, lowProd, normDiv, &remainder); -Fraction &Fraction::operator=(const Fraction &&f) { - this->numerator = f.getNumerator(); - this->denominator = f.getDenominator(); - return *this; + return quotient; } +#else +#error "Platform not supported for mulDiv64 function!" +#endif -bool operator==(const Fraction &f1, const Fraction &f2) { - int f1_num = f1.getNumerator(); - int f1_den = f1.getDenominator(); - int f2_num = f2.getNumerator(); - int f2_den = f2.getDenominator(); - int res_num = f1_num * f2_den; - int res_den = f1_den * f2_num; - bool result = false; - if (res_num == res_den) { - result = true; +uint64_t safeAdd(uint64_t a, uint64_t b) { + uint64_t result; + if (__builtin_add_overflow(a, b, &result)) { + throw std::overflow_error("Overflow in addition"); } return result; } -bool operator!=(const Fraction &f1, const Fraction &f2) { - int f1_num = f1.getNumerator(); - int f1_den = f1.getDenominator(); - int f2_num = f2.getNumerator(); - int f2_den = f2.getDenominator(); - int res_num = f1_num * f2_den; - int res_den = f1_den * f2_num; - bool result = false; - if (res_num != res_den) { - result = true; +uint64_t safeMul(uint64_t a, uint64_t b) { + uint64_t result; + if (__builtin_mul_overflow(a, b, &result)) { + throw std::overflow_error("Overflow in multiplication"); } return result; } -bool operator>(const Fraction &f1, const Fraction &f2) { - int f1_num = f1.getNumerator(); - int f1_den = f1.getDenominator(); - int f2_num = f2.getNumerator(); - int f2_den = f2.getDenominator(); - int res_num = f1_num * f2_den; - int res_den = f1_den * f2_num; - bool result = false; - if (res_num > res_den) { - result = true; - } - return result; +uint64_t rotl64(uint64_t n, unsigned int c) { + const unsigned int mask = 63; + c &= mask; + return (n << c) | (n >> (-c & mask)); } -bool operator>=(const Fraction &f1, const Fraction &f2) { - int f1_num = f1.getNumerator(); - int f1_den = f1.getDenominator(); - int f2_num = f2.getNumerator(); - int f2_den = f2.getDenominator(); - int res_num = f1_num * f2_den; - int res_den = f1_den * f2_num; - bool result = false; - if (res_num >= res_den) { - result = true; - } - return result; +uint64_t rotr64(uint64_t n, unsigned int c) { + const unsigned int mask = 63; + c &= mask; + return (n >> c) | (n << (-c & mask)); } -bool operator<(const Fraction &f1, const Fraction &f2) { - int f1_num = f1.getNumerator(); - int f1_den = f1.getDenominator(); - int f2_num = f2.getNumerator(); - int f2_den = f2.getDenominator(); - int res_num = f1_num * f2_den; - int res_den = f1_den * f2_num; - bool result = false; - if (res_num < res_den) { - result = true; - } - return result; +int clz64(uint64_t x) { + if (x == 0) + return 64; + return __builtin_clzll(x); // GCC built-in +} + +uint64_t normalize(uint64_t x) { + if (x == 0) + return 0; + int n = clz64(x); + return x << n; } -bool operator<=(const Fraction &f1, const Fraction &f2) { - int f1_num = f1.getNumerator(); - int f1_den = f1.getDenominator(); - int f2_num = f2.getNumerator(); - int f2_den = f2.getDenominator(); - int res_num = f1_num * f2_den; - int res_den = f1_den * f2_num; - bool result = false; - if (res_num <= res_den) { - result = true; +uint64_t safeSub(uint64_t a, uint64_t b) { + uint64_t result; + if (__builtin_sub_overflow(a, b, &result)) { + throw std::underflow_error("Underflow in subtraction"); } return result; } -std::istream &operator>>(std::istream &input, Fraction &f) { - input >> f.numerator; - char split_c = input.peek(); - - if (split_c > '9' || split_c < '0') { - if (split_c == '/') { - input >> split_c; - input >> f.denominator; - if (f.denominator == 0) { - throw Exception::WrongArgument( - "Got 0 in the denominator of Math::Fraction object!"); - } - if (f.denominator < 0) { - f.denominator *= -1; - f.numerator *= -1; - } - return input; - } +uint64_t safeDiv(uint64_t a, uint64_t b) { + if (b == 0) { + throw std::runtime_error("Division by zero"); } - f.denominator = 1; - return input; + return a / b; } -std::ostream &operator<<(std::ostream &output, const Fraction &f) { - output << f.getNumerator() << "/" << f.getDenominator(); - return output; -} -std::ostream &operator<<(std::ostream &output, const Fraction &&f) { - output << f.getNumerator() << "/" << f.getDenominator(); - return output; -} -} // namespace Atom::Utils + +} // namespace Atom::Algorithm diff --git a/src/atom/algorithm/math.hpp b/src/atom/algorithm/math.hpp index 6e4b153f..1c392a6b 100644 --- a/src/atom/algorithm/math.hpp +++ b/src/atom/algorithm/math.hpp @@ -15,125 +15,107 @@ Description: Extra Math Library #ifndef ATOM_ALGORITHM_MATH_HPP #define ATOM_ALGORITHM_MATH_HPP -#include -#include -#include "exception.hpp" +#include namespace Atom::Algorithm { -class Fraction; -// 运算符,友元函数声明 -Fraction operator+(const Fraction &f1, const Fraction &f2); - -Fraction operator-(const Fraction &f1, const Fraction &f2); - -Fraction operator*(const Fraction &f1, const Fraction &f2); - -Fraction operator/(const Fraction &f1, const Fraction &f2); -// 接收两个运算算子的引用,返回一个Fraction对象 - -bool operator==(const Fraction &f1, const Fraction &f2); - -bool operator!=(const Fraction &f1, const Fraction &f2); - -bool operator>(const Fraction &f1, const Fraction &f2); -bool operator>=(const Fraction &f1, const Fraction &f2); -bool operator<(const Fraction &f1, const Fraction &f2); -bool operator<=(const Fraction &f1, const Fraction &f2); - -std::istream &operator>>(std::istream &input, Fraction &f); -std::ostream &operator<<(std::ostream &output, const Fraction &f); -std::ostream &operator<<(std::ostream &output, const Fraction &&f); - -class Fraction { -private: - int numerator; // 分子 - int denominator; // 分母 -public: - static int Euclid(int a, int b) { - // 计算两个数的最大公因数(欧几里得算法) - // 声明为静态成员函数 - int c; - while (a % b != 0) { - c = b; - b = a % b; - a = c; - } - return b; - }; - // 保留隐式转换,减少编程工作 - Fraction(int num_value, int den_value) - : numerator(num_value), denominator(den_value) { - if (this->denominator == 0) { - throw Exception::WrongArgument( - "Got 0 in the denominator of Math::Fraction object!"); - } - }; - Fraction(int num_value) : numerator(num_value), denominator(1){}; - Fraction(const char *str) { - this->denominator = 1; - std::stringstream inistream; - inistream << str; - inistream >> (*this); - } - Fraction() : numerator(0), denominator(1){}; - Fraction(const Fraction &f) - : numerator(f.getNumerator()), denominator(f.getDenominator()){}; - Fraction(Fraction &&f) - : numerator(f.numerator), denominator(f.denominator){}; - - inline int getNumerator() const { return numerator; } - // 申明为不可修改数据的函数,const常函数 - inline int getDenominator() const { return denominator; } - - inline void alterValue(int num_value, int den_value) { - this->numerator = num_value; - this->denominator = den_value; - } - - inline void alterValue(const Fraction &f) { - this->numerator = f.getNumerator(); - this->denominator = f.getDenominator(); - } - - inline Fraction inverse() { - // 返回这个分数的倒数 - return Fraction(this->getDenominator(), this->getNumerator()); - } - - friend Fraction operator+(const Fraction &f1, const Fraction &f2); - - friend Fraction operator-(const Fraction &f1, const Fraction &f2); - - friend Fraction operator*(const Fraction &f1, const Fraction &f2); - - friend Fraction operator/(const Fraction &f1, const Fraction &f2); - - // 负号运算符,将一个分数变成其相反数 - inline Fraction operator-() { - return Fraction(-this->numerator, this->denominator); - } - Fraction &operator+=(const Fraction &f); - - Fraction &operator-=(const Fraction &f); - - Fraction &operator*=(const Fraction &f); - - Fraction &operator/=(const Fraction &f); - - Fraction &operator=(const Fraction &f); - - Fraction &operator=(const Fraction &&f); - - friend bool operator==(const Fraction &f1, const Fraction &f2); - friend bool operator!=(const Fraction &f1, const Fraction &f2); - friend bool operator>(const Fraction &f1, const Fraction &f2); - friend bool operator>=(const Fraction &f1, const Fraction &f2); - friend bool operator<(const Fraction &f1, const Fraction &f2); - friend bool operator<=(const Fraction &f1, const Fraction &f2); - friend std::istream &operator>>(std::istream &input, Fraction &f); - friend std::ostream &operator<<(std::ostream &output, const Fraction &f); - friend std::ostream &operator<<(std::ostream &output, const Fraction &&f); -}; +/** + * @brief Performs a 64-bit multiplication followed by division. + * + * This function calculates the result of (operant * multiplier) / divider. + * + * @param operant The first operand for multiplication. + * @param multiplier The second operand for multiplication. + * @param divider The divisor for the division operation. + * @return The result of (operant * multiplier) / divider. + */ +uint64_t mulDiv64(uint64_t operant, uint64_t multiplier, + uint64_t divider) noexcept; + +/** + * @brief Performs a safe addition operation. + * + * This function adds two unsigned 64-bit integers, handling potential overflow. + * + * @param a The first operand for addition. + * @param b The second operand for addition. + * @return The result of a + b, or 0 if there is an overflow. + */ +uint64_t safeAdd(uint64_t a, uint64_t b); + +/** + * @brief Performs a safe multiplication operation. + * + * This function multiplies two unsigned 64-bit integers, handling potential overflow. + * + * @param a The first operand for multiplication. + * @param b The second operand for multiplication. + * @return The result of a * b, or 0 if there is an overflow. + */ +uint64_t safeMul(uint64_t a, uint64_t b); + +/** + * @brief Rotates a 64-bit integer to the left. + * + * This function rotates a 64-bit integer to the left by a specified number of bits. + * + * @param n The 64-bit integer to rotate. + * @param c The number of bits to rotate. + * @return The rotated 64-bit integer. + */ +uint64_t rotl64(uint64_t n, unsigned int c); + +/** + * @brief Rotates a 64-bit integer to the right. + * + * This function rotates a 64-bit integer to the right by a specified number of bits. + * + * @param n The 64-bit integer to rotate. + * @param c The number of bits to rotate. + * @return The rotated 64-bit integer. + */ +uint64_t rotr64(uint64_t n, unsigned int c); + +/** + * @brief Counts the leading zeros in a 64-bit integer. + * + * This function counts the number of leading zeros in a 64-bit integer. + * + * @param x The 64-bit integer to count leading zeros in. + * @return The number of leading zeros in the 64-bit integer. + */ +int clz64(uint64_t x); + +/** + * @brief Normalizes a 64-bit integer. + * + * This function normalizes a 64-bit integer by shifting it to the right until the most significant bit is set. + * + * @param x The 64-bit integer to normalize. + * @return The normalized 64-bit integer. + */ +uint64_t normalize(uint64_t x); + +/** + * @brief Performs a safe subtraction operation. + * + * This function subtracts two unsigned 64-bit integers, handling potential underflow. + * + * @param a The first operand for subtraction. + * @param b The second operand for subtraction. + * @return The result of a - b, or 0 if there is an underflow. + */ +uint64_t safeSub(uint64_t a, uint64_t b); + +/** + * @brief Performs a safe division operation. + * + * This function divides two unsigned 64-bit integers, handling potential division by zero. + * + * @param a The numerator for division. + * @param b The denominator for division. + * @return The result of a / b, or 0 if there is a division by zero. + */ +uint64_t safeDiv(uint64_t a, uint64_t b); } // namespace Atom::Algorithm -#endif // ATOM_ALGORITHM_MATH_HPP +#endif \ No newline at end of file diff --git a/src/atom/algorithm/mathutils.cpp b/src/atom/algorithm/mathutils.cpp deleted file mode 100644 index 5a632eca..00000000 --- a/src/atom/algorithm/mathutils.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - * mathutils.cpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-11-10 - -Description: Extra Math Library - -**************************************************/ - -#include "mathutils.hpp" - -#if defined(_MSC_VER) -#include -#endif - -namespace Atom::Algorithm { - -uint64_t mulDiv64(uint64_t operant, uint64_t multiplier, - uint64_t divider) { -#if defined(__GNUC__) && defined(__SIZEOF_INT128__) - __uint128_t a = operant; - __uint128_t b = multiplier; - __uint128_t c = divider; - - return (uint64_t)(a * b / c); -#elif defined(_MSC_VER) -#if defined(_M_IX86) - // Your x86 specific code here -#elif defined(_M_X64) -#pragma warning(push) -#pragma warning(disable : 4244) // C4244: 'conversion' conversion from 'type1' - // to 'type2', possible loss of data - uint64_t a = operant; - uint64_t b = multiplier; - uint64_t c = divider; - - // Normalize divisor - unsigned long shift; - _BitScanReverse64(&shift, c); - shift = 63 - shift; - - c <<= shift; - - // Multiply - a = _umul128(a, b, &b); - if (((b << shift) >> shift) != b) { - // Overflow - return 0xFFFFFFFFFFFFFFFF; - } - b = __shiftleft128(a, b, shift); - a <<= shift; - - uint32_t div; - uint32_t q0, q1; - uint64_t t0, t1; - - // 1st Reduction - div = (uint32_t)(c >> 32); - t0 = b / div; - if (t0 > 0xFFFFFFFF) - t0 = 0xFFFFFFFF; - q1 = (uint32_t)t0; - while (1) { - t0 = _umul128(c, (uint64_t)q1 << 32, &t1); - if (t1 < b || (t1 == b && t0 <= a)) - break; - q1--; - } - b -= t1; - if (t0 > a) - b--; - a -= t0; - - if (b > 0xFFFFFFFF) { - // Overflow - return 0xFFFFFFFFFFFFFFFF; - } - - // 2nd reduction - t0 = ((b << 32) | (a >> 32)) / div; - if (t0 > 0xFFFFFFFF) - t0 = 0xFFFFFFFF; - q0 = (uint32_t)t0; - - while (1) { - t0 = _umul128(c, q0, &t1); - if (t1 < b || (t1 == b && t0 <= a)) - break; - q0--; - } - - return ((uint64_t)q1 << 32) | q0; -#pragma warning(pop) -#endif -#else -#error MulDiv64 is no supported! -#endif -} - -} // namespace Atom::Algorithm diff --git a/src/atom/algorithm/mathutils.hpp b/src/atom/algorithm/mathutils.hpp deleted file mode 100644 index 5f7762ac..00000000 --- a/src/atom/algorithm/mathutils.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * mathutils.hpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-11-10 - -Description: Extra Math Library - -**************************************************/ - -#ifndef ATOM_ALGORITHM_MATHUTILS_HPP -#define ATOM_ALGORITHM_MATHUTILS_HPP - -#include - -namespace Atom::Algorithm { -/** - * @brief Performs a 64-bit multiplication followed by division. - * - * This function calculates the result of (operant * multiplier) / divider. - * - * @param operant The first operand for multiplication. - * @param multiplier The second operand for multiplication. - * @param divider The divisor for the division operation. - * @return The result of (operant * multiplier) / divider. - */ -uint64_t mulDiv64(uint64_t operant, uint64_t multiplier, uint64_t divider); -} // namespace Atom::Algorithm - -#endif \ No newline at end of file diff --git a/src/atom/algorithm/md5.cpp b/src/atom/algorithm/md5.cpp index e3b771f6..c57efc89 100644 --- a/src/atom/algorithm/md5.cpp +++ b/src/atom/algorithm/md5.cpp @@ -18,8 +18,7 @@ Description: Self implemented MD5 algorithm. #include #include - -namespace Atom::Utils { +namespace Atom::Algorithm { constexpr uint32_t T[64] = { 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, @@ -152,4 +151,4 @@ std::string MD5::encrypt(const std::string &input) { md5.update(input); return md5.finalize(); } -} // namespace Atom::Utils \ No newline at end of file +} // namespace Atom::Algorithm \ No newline at end of file diff --git a/src/atom/algorithm/md5.hpp b/src/atom/algorithm/md5.hpp index 0728612e..6944e71d 100644 --- a/src/atom/algorithm/md5.hpp +++ b/src/atom/algorithm/md5.hpp @@ -20,7 +20,7 @@ Description: Self implemented MD5 algorithm. #include -namespace Atom::Utils { +namespace Atom::Algorithm { /** * @brief The MD5 class for calculating MD5 hash of input data. */ @@ -97,6 +97,6 @@ class MD5 { std::vector _buffer; /**< Buffer for input data. */ }; -} // namespace Atom::Utils +} // namespace Atom::Algorithm #endif // MD5_H diff --git a/src/atom/algorithm/meson.build b/src/atom/algorithm/meson.build new file mode 100644 index 00000000..3118c1be --- /dev/null +++ b/src/atom/algorithm/meson.build @@ -0,0 +1,49 @@ +project('Atom-Algorithm', 'cpp') + +# Define sources and headers +sources = [ + 'algorithm.cpp', + 'base.cpp', + 'convolve.cpp', + 'fraction.cpp', + 'huffman.cpp', + 'math.cpp', + 'md5.cpp' +] +headers = [ + 'algorithm.hpp', + 'base.hpp', + 'convolve.hpp', + 'fraction.hpp', + 'hash.hpp', + 'huffman.hpp', + 'math.hpp', + 'md5.hpp' +] + +# Create object library +atom_algorithm_obj = library('atom_algorithm_obj', + sources + headers, + cpp_std: c_std +) + +# Create static library +atom_algorithm_static = static_library('atom_algorithm_static', atom_algorithm_obj) + +# Set version properties +version = '1.0.0' # You should define your version here +soversion = '1' + +atom_algorithm_static.version = version +atom_algorithm_static.soversion = soversion +atom_algorithm_static.basename = 'Atom-Algorithm' + +# Install target +install_targets(atom_algorithm_static) + +# Check if Python support is enabled +atom_build_python = get_option('atom_build_python') + +if atom_build_python + pybind11_module('atom_algorithm_py' 'pybind.cpp' link_with : [atom_algorithm_static]) +endif diff --git a/src/atom/algorithm/mhash.cpp b/src/atom/algorithm/mhash.cpp new file mode 100644 index 00000000..e0c855ee --- /dev/null +++ b/src/atom/algorithm/mhash.cpp @@ -0,0 +1,155 @@ +/* + * mhash.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-12-16 + +Description: Implementation of murmur3 hash and quick hash + +**************************************************/ + +#include "mhash.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Atom::Utils { +uint32_t fmix32(uint32_t h) noexcept { + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + return h; +} + +uint32_t ROTL(uint32_t x, int8_t r) noexcept { + return (x << r) | (x >> (32 - r)); +} + +uint32_t murmur3Hash(std::string_view data, uint32_t seed) noexcept { + uint32_t hash = seed; + const uint32_t seed1 = 0xcc9e2d51; + const uint32_t seed2 = 0x1b873593; + + // Process 4-byte chunks + const uint32_t *blocks = reinterpret_cast(data.data()); + size_t nblocks = data.size() / 4; + for (size_t i = 0; i < nblocks; i++) { + uint32_t k = blocks[i]; + k *= seed1; + k = ROTL(k, 15); + k *= seed2; + + hash ^= k; + hash = ROTL(hash, 13); + hash = hash * 5 + 0xe6546b64; + } + + // Handle the tail + const uint8_t *tail = + reinterpret_cast(data.data() + nblocks * 4); + uint32_t tail_val = 0; + switch (data.size() & 3) { + case 3: + tail_val |= tail[2] << 16; + [[fallthrough]]; + case 2: + tail_val |= tail[1] << 8; + [[fallthrough]]; + case 1: + tail_val |= tail[0]; + tail_val *= seed1; + tail_val = ROTL(tail_val, 15); + tail_val *= seed2; + hash ^= tail_val; + } + + return fmix32(hash ^ static_cast(data.size())); +} + +uint64_t murmur3Hash64(std::string_view str, uint32_t seed, uint32_t seed2) { + return (static_cast(murmur3Hash(str, seed)) << 32) | + murmur3Hash(str, seed2); +} + +void hexstringFromData(const void *data, size_t len, char *output) { + const unsigned char *buf = static_cast(data); + std::span bytes(buf, len); + std::ostringstream stream; + + // Use iomanip to format output + stream << std::hex << std::setfill('0'); + for (unsigned char byte : bytes) { + stream << std::setw(2) << static_cast(byte); + } + + std::string hexstr = stream.str(); + std::copy(hexstr.begin(), hexstr.end(), output); + output[hexstr.size()] = '\0'; // Null-terminate the output string +} + +std::string hexstringFromData(const std::string &data) { + if (data.empty()) { + return {}; + } + + std::string result; + result.reserve(data.size() * 2); + + for (unsigned char c : data) { + char buf[3]; // buffer for two hex chars and null terminator + std::to_chars_result conv_result = + std::to_chars(buf, buf + sizeof(buf), c, 16); + + if (conv_result.ec == std::errc{}) { + if (buf[1] == '\0') { + result += '0'; // pad single digit hex numbers + } + result.append(buf, conv_result.ptr); + } + } + + return result; +} + +std::string dataFromHexstring(const std::string &hexstring) { + if (hexstring.size() % 2 != 0) { + throw std::invalid_argument("Hex string length must be even"); + } + + std::string result; + result.resize(hexstring.size() / 2); + + size_t output_index = 0; + for (size_t i = 0; i < hexstring.size(); i += 2) { + int byte = 0; + auto [ptr, ec] = std::from_chars(hexstring.data() + i, + hexstring.data() + i + 2, byte, 16); + + if (ec == std::errc::invalid_argument || + ptr != hexstring.data() + i + 2) { + throw std::invalid_argument("Invalid hex character"); + } + + result[output_index++] = static_cast(byte); + } + + return result; +} + +} // namespace Atom::Utils diff --git a/src/atom/algorithm/mhash.hpp b/src/atom/algorithm/mhash.hpp new file mode 100644 index 00000000..cff1097c --- /dev/null +++ b/src/atom/algorithm/mhash.hpp @@ -0,0 +1,76 @@ +/* + * hash_util.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-12-16 + +Description: Implementation of murmur3 hash and quick hash + +**************************************************/ + +#ifndef ATOM_UTILS_HASH_UTIL_HPP +#define ATOM_UTILS_HASH_UTIL_HPP + +#include +#include +#include +#include +#include + +namespace Atom::Utils { +/** + * @brief Calculates the MurmurHash3 hash value for a given string. + * + * @param str The input string. + * @param seed The seed value (optional, default is 1060627423). + * @return uint32_t The calculated hash value. + */ +[[nodiscard]] uint32_t murmur3Hash(std::string_view data, + uint32_t seed = 1060627423) noexcept; + +/** + * @brief Calculates the 64-bit MurmurHash3 hash value for a given string. + * + * @param str The input string. + * @param seed The first seed value (optional, default is 1060627423). + * @param seed2 The second seed value (optional, default is 1050126127). + * @return uint64_t The calculated hash value. + */ +[[nodiscard]] uint64_t murmur3Hash64(std::string_view str, + uint32_t seed = 1060627423, + uint32_t seed2 = 1050126127); + +/** + * @brief Converts binary data to a hexadecimal string representation. + * + * @param data The input data buffer. + * @param len The length of the data buffer. + * @param output The output buffer to store the hexadecimal string (length must + * be len * 2). + */ +void hexstringFromData(const void *data, size_t len, char *output); + +/** + * @brief Converts a string to a hexadecimal string representation. + * + * @param data The input string. + * @return std::string The hexadecimal string representation. + */ +[[nodiscard]] std::string hexstringFromData(const std::string &data); + +/** + * @brief Converts a hexadecimal string representation to binary data. + * + * @param data The input hexadecimal string. + * @return std::string The binary data. + * @throw std::invalid_argument If the input hexstring is not a valid + * hexadecimal string. + */ +[[nodiscard]] std::string dataFromHexstring(const std::string &data); +} // namespace Atom::Utils + +#endif \ No newline at end of file diff --git a/src/atom/algorithm/xmake.lua b/src/atom/algorithm/xmake.lua new file mode 100644 index 00000000..d8a9d533 --- /dev/null +++ b/src/atom/algorithm/xmake.lua @@ -0,0 +1,69 @@ +-- xmake.lua for Atom-Algorithm +-- This project is licensed under the terms of the GPL3 license. +-- +-- Project Name: Atom-Algorithm +-- Description: A collection of algorithms +-- Author: Max Qian +-- License: GPL3 + +add_rules("mode.debug", "mode.release") + +set_project("atom-algorithm") +set_version("1.0.0") +set_license("GPL3") + +-- Sources +local sources = { + "algorithm.cpp", + "base.cpp", + "convolve.cpp", + "fraction.cpp", + "huffman.cpp", + "math.cpp", + "md5.cpp" +} + +-- Headers +local headers = { + "algorithm.hpp", + "base.hpp", + "convolve.hpp", + "fraction.hpp", + "hash.hpp", + "huffman.hpp", + "math.hpp", + "md5.hpp" +} + +-- Build Object Library +target("atom-algorithm-object") + set_kind("object") + add_headerfiles(headers, {public = true}) + add_files(sources, {public = false}) + +-- Build Static Library +target("atom-algorithm") + set_kind("static") + add_deps("atom-algorithm-object") + add_includedirs(".", {public = true}) + + set_targetdir("$(buildir)/lib") + set_objectdir("$(buildir)/obj") + + after_build(function (target) + os.cp("$(buildir)/lib", "$(projectdir)/lib") + os.cp("$(projectdir)/*.hpp", "$(projectdir)/include") + end) + +-- Build Python Module (Optional) +if has_config("atom_build_python") then + target("atom-algorithm-py") + set_kind("shared") + add_deps("atom-algorithm") + add_files("_pybind.cpp") + add_packages("pybind11") + + after_build(function (target) + os.cp("$(buildir)/*.so", "$(projectdir)/python") + end) +end \ No newline at end of file diff --git a/src/atom/async/CMakeLists.txt b/src/atom/async/CMakeLists.txt index b56a9459..ba669cf5 100644 --- a/src/atom/async/CMakeLists.txt +++ b/src/atom/async/CMakeLists.txt @@ -11,12 +11,18 @@ project(atom-async C CXX) # Sources set(${PROJECT_NAME}_SOURCES + lock.cpp timer.cpp ) # Headers set(${PROJECT_NAME}_HEADERS async.hpp + async_impl.hpp + lock.hpp + queue.hpp + queue.inl + thread_wrapper.hpp timer.hpp trigger.hpp trigger_impl.hpp @@ -51,4 +57,9 @@ set_target_properties(${PROJECT_NAME} PROPERTIES install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) \ No newline at end of file +) + +if (ATOM_BUILD_PYTHON) +pybind11_add_module(${PROJECT_NAME}-py _pybind.cpp) +target_link_libraries(${PROJECT_NAME}-py PRIVATE ${PROJECT_NAME}) +endif() \ No newline at end of file diff --git a/src/atom/async/_pybind.cpp b/src/atom/async/_pybind.cpp new file mode 100644 index 00000000..c12fcbdf --- /dev/null +++ b/src/atom/async/_pybind.cpp @@ -0,0 +1,185 @@ +/* + * _pybind.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-13 + +Description: Python Binding of Atom-Async + +**************************************************/ + +#include +#include +#include +#include + +#include "async.hpp" +#include "queue.hpp" +#include "thread_wrapper.hpp" +#include "timer.hpp" +#include "trigger.hpp" + +namespace py = pybind11; + +using namespace Atom::Async; + +template +void start_wrapper(Thread& thread, Callable&& func, Args&&... args) { + thread.start(std::forward(func), std::forward(args)...); +} + +void bind_thread(py::module& m) { + py::class_(m, "Thread") + .def(py::init<>()) + .def("start", &start_wrapper, + "Starts a new thread with the specified callable object and " + "arguments.") + .def("request_stop", &Thread::request_stop, + "Requests the thread to stop execution.") + .def("join", &Thread::join, "Waits for the thread to finish execution.") + .def("running", &Thread::running, + "Checks if the thread is currently running.") + .def("swap", &Thread::swap, + "Swaps the content of this Thread object with another Thread " + "object.") + .def("get_id", &Thread::get_id, "Gets the ID of the thread.") + .def("get_stop_source", &Thread::get_stop_source, + "Gets the underlying std::stop_source object.") + .def("get_stop_token", &Thread::get_stop_token, + "Gets the underlying std::stop_token object.") + //.def_property_readonly("thread", &Thread::get_thread, + // "Gets the underlying std::jthread object.") + .def("__enter__", [](Thread& self) -> Thread& { return self; }) + .def("__exit__", [](Thread& self, py::object, py::object, + py::object) { self.join(); }) + .def("__enter__", [](Thread& self) -> Thread& { return self; }) + .def("__exit__", [](Thread& self, py::object, py::object, py::object) { + self.join(); + }); +} + +// Bind TimerTask class +void bind_timer_task(py::module& m) { + py::class_(m, "TimerTask") + .def(py::init, unsigned int, int, int>()) + .def("__lt__", &TimerTask::operator<) + .def("run", &TimerTask::run) + .def("getNextExecutionTime", &TimerTask::getNextExecutionTime); +} + +// Bind Timer class +void bind_timer(py::module& m) { + py::class_(m, "Timer") + .def(py::init<>()) + .def("setTimeout", &Timer::setTimeout, + "Schedules a task to be executed once after a specified delay.") + .def("setInterval", &Timer::setInterval, + "Schedules a task to be executed repeatedly at a specified " + "interval.") + .def("now", &Timer::now, "Returns the current time.") + .def("cancelAllTasks", &Timer::cancelAllTasks, + "Cancels all scheduled tasks.") + .def("pause", &Timer::pause, "Pauses the execution of scheduled tasks.") + .def("resume", &Timer::resume, + "Resumes the execution of scheduled tasks after pausing.") + .def("stop", &Timer::stop, "Stops the timer and cancels all tasks.") + .def("setCallback", &Timer::setCallback, + "Sets a callback function to be called when a task is executed.") + .def("getTaskCount", &Timer::getTaskCount, + "Gets the number of scheduled tasks."); +} + +/* +// TODO: Fix this +template +void bind_thread_safe_queue(py::module& m, const std::string& name) { + py::class_>(m, name.c_str()) + .def(py::init<>()) + .def("put", &ThreadSafeQueue::put) + .def("take", &ThreadSafeQueue::take) + .def("destroy", &ThreadSafeQueue::destroy) + .def("size", &ThreadSafeQueue::size) + .def("empty", &ThreadSafeQueue::empty) + .def("clear", &ThreadSafeQueue::clear) + .def("front", &ThreadSafeQueue::front) + .def("back", &ThreadSafeQueue::back) + .def("emplace", &ThreadSafeQueue::template emplace) + .def("waitFor", &ThreadSafeQueue::template waitFor) + .def("waitUntilEmpty", &ThreadSafeQueue::waitUntilEmpty) + .def("extractIf", &ThreadSafeQueue::template extractIf) + .def("sort", &ThreadSafeQueue::template sort); +} + +// Bind AsyncWorker class +template +void bind_async_worker(py::module& m, const std::string& name) { + py::class_>(m, name.c_str()) + .def(py::init<>()) + //.def("StartAsync", + // &AsyncWorker::template StartAsync) + .def("GetResult", &AsyncWorker::GetResult) + .def("Cancel", &AsyncWorker::Cancel) + .def("IsDone", &AsyncWorker::IsDone) + .def("IsActive", &AsyncWorker::IsActive) + .def("Validate", &AsyncWorker::Validate) + .def("SetCallback", &AsyncWorker::SetCallback) + .def("SetTimeout", &AsyncWorker::SetTimeout) + .def("WaitForCompletion", &AsyncWorker::WaitForCompletion); +} + +// Bind AsyncWorkerManager class +template +void bind_async_worker_manager(py::module& m, const std::string& name) { + py::class_>(m, name.c_str()) + .def(py::init<>()) + .def( + "CreateWorker", + &AsyncWorkerManager::template CreateWorker) + .def("CancelAll", &AsyncWorkerManager::CancelAll) + .def("AllDone", &AsyncWorkerManager::AllDone) + .def("WaitForAll", &AsyncWorkerManager::WaitForAll) + .def("IsDone", &AsyncWorkerManager::IsDone) + .def("Cancel", &AsyncWorkerManager::Cancel); +} +*/ + +PYBIND11_MODULE(atom_async, m) { + m.doc() = "Atom Async Python Binding"; + + // Define the Trigger class and its methods + py::class_>(m, "Trigger") + .def(py::init<>()) + .def("register_callback", &Trigger::registerCallback) + .def("unregister_callback", &Trigger::unregisterCallback) + .def("trigger", &Trigger::trigger) + .def("schedule_trigger", &Trigger::scheduleTrigger) + .def("schedule_async_trigger", &Trigger::scheduleAsyncTrigger) + .def("cancel_trigger", &Trigger::cancelTrigger) + .def("cancel_all_triggers", &Trigger::cancelAllTriggers); + + + + bind_thread(m); + bind_timer_task(m); + bind_timer(m); + + /* + bind_thread_safe_queue(m, "ThreadSafeQueueInt"); + bind_thread_safe_queue(m, "ThreadSafeQueueDouble"); + bind_thread_safe_queue(m, "ThreadSafeQueueString"); + + bind_async_worker(m, "AsyncWorkerInt"); + bind_async_worker(m, "AsyncWorkerFloat"); + bind_async_worker(m, "AsyncWorkerString"); + + bind_async_worker_manager(m, "AsyncWorkerManagerInt"); + bind_async_worker_manager(m, "AsyncWorkerManagerFloat"); + bind_async_worker_manager(m, "AsyncWorkerManagerString"); + */ +} diff --git a/src/atom/async/async_impl.hpp b/src/atom/async/async_impl.hpp index 7b968b1c..0696698f 100644 --- a/src/atom/async/async_impl.hpp +++ b/src/atom/async/async_impl.hpp @@ -28,7 +28,7 @@ void AsyncWorker::StartAsync(Func &&func, Args &&...args) { template [[nodiscard]] ResultType AsyncWorker::GetResult() { if (!task_.valid()) { - throw Utils::Exception::InvalidArgument("Task is not valid"); + throw std::invalid_argument("Task is not valid"); } return task_.get(); } diff --git a/src/atom/async/meson.build b/src/atom/async/meson.build new file mode 100644 index 00000000..135445ff --- /dev/null +++ b/src/atom/async/meson.build @@ -0,0 +1,49 @@ +project('Atom-Async', 'cpp') + +# Define sources and headers +sources = [ + 'lock.cpp', + 'timer.cpp' +] +headers = [ + 'async.hpp', + 'async_impl.hpp', + 'lock.hpp', + 'queue.hpp', + 'queue.inl', + 'thread_wrapper.hpp', + 'timer.hpp', + 'trigger.hpp', + 'trigger_impl.hpp' +] + +# Create object library +atom_async_obj = library('atom_async_obj', + sources + headers, + cpp_std: c_std +) + +# Link with necessary libraries +atom_async_obj_link_libs = ['loguru'] +atom_async_obj.link_with(atom_async_obj_link_libs) + +# Create static library +atom_async_static = static_library('atom_async_static', atom_async_obj) + +# Set version properties +version = '1.0.0' # You should define your version here +soversion = '1' + +atom_async_static.version = version +atom_async_static.soversion = soversion +atom_async_static.basename = 'Atom-Async' + +# Install target +install_targets(atom_async_static) + +# Check if Python support is enabled +atom_build_python = get_option('atom_build_python') + +if atom_build_python + pybind11_module('atom_async_py' '_pybind.cpp' link_with : [atom_async_static]) +endif diff --git a/src/atom/async/pool.hpp b/src/atom/async/pool.hpp new file mode 100644 index 00000000..9c04dcb0 --- /dev/null +++ b/src/atom/async/pool.hpp @@ -0,0 +1,122 @@ +/* + * pool.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-2-13 + +Description: A very simple thread pool for preload + +**************************************************/ + +#ifndef ATOM_ASYNC_POOL_HPP +#define ATOM_ASYNC_POOL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Atom::Async { +/** + * @brief 线程池 + */ +class ThreadPool { +public: + /** + * @brief 构造函数,初始化线程池大小和停止标志位。 + * + * 构造函数会创建 n_threads 个线程,并等待来自任务队列的任务分配。 + * + * @param n_threads 线程池大小 + */ + ThreadPool(std::size_t n_threads) : stop(false) { + for (std::size_t i = 0; i < n_threads; ++i) { + threads.emplace_back([this] { + for (;;) { + std::function task; + { + std::unique_lock lock(queue_mutex); + condition.wait( + lock, [this] { return stop || !tasks.empty(); }); + if (stop && tasks.empty()) { + return; + } + task = std::move(tasks.front()); + tasks.pop(); + } + task(); + } + }); + } + } + + /** + * @brief 析构函数,销毁所有线程并退出。 + * + * 析构函数会向任务队列中插入空任务,并等待所有线程完成该任务并退出。 + */ + ~ThreadPool() { + { + std::unique_lock lock(queue_mutex); + stop = true; + } + condition.notify_all(); + for (std::thread &thread : threads) { + thread.join(); + } + } + + /** + * @brief 将指定任务添加到任务队列中,并返回该任务的 future 对象。 + * + * 该函数用于将函数 f 和其参数 args 添加到任务队列中等待执行,并返回一 + * 个 std::future 对象,以便查询任务完成情况。当任务队列已满或线程池被 + * 停止时,将会抛出 std::runtime_error 异常。 + * + * @tparam F 函数类型 + * @tparam Args 参数类型 + * @param f 要执行的函数对象 + * @param args 函数参数 + * @return 返回一个 std::future 对象,用于查询任务完成情况 + */ + template + auto enqueue(F &&f, Args &&...args) + -> std::future::type> { + using return_type = typename std::result_of::type; + + auto task = std::make_shared>( + std::bind(std::forward(f), std::forward(args)...)); + + std::future res = task->get_future(); + { + std::unique_lock lock(queue_mutex); + + if (stop) { + throw std::runtime_error("enqueue on stopped ThreadPool"); + } + + tasks.emplace([task] { (*task)(); }); + } + condition.notify_one(); + return res; + } + +private: + std::vector threads; ///< 线程池中的线程列表 + std::queue> tasks; ///< 任务队列 + + std::mutex queue_mutex; ///< 任务队列的互斥锁 + std::condition_variable condition; ///< 任务队列的条件变量 + bool stop; ///< 停止标志位 +}; +} // namespace Atom::Async + +#endif \ No newline at end of file diff --git a/src/atom/async/queue.hpp b/src/atom/async/queue.hpp index 6eedf3ad..e94002f2 100644 --- a/src/atom/async/queue.hpp +++ b/src/atom/async/queue.hpp @@ -1,3 +1,17 @@ +/* + * queue.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-2-13 + +Description: A simple thread safe queue + +**************************************************/ + #ifndef ATOM_ASYNC_QUEUE_HPP #define ATOM_ASYNC_QUEUE_HPP @@ -13,184 +27,132 @@ #include "atom/experiment/noncopyable.hpp" namespace Atom::Async { +/** + * @brief A thread-safe queue data structure that supports concurrent access + * from multiple threads. + * + * This class provides a thread-safe implementation of a queue, allowing + * elements to be added and removed concurrently from multiple threads without + * causing data races or undefined behavior. + * + * @tparam T The type of elements stored in the queue. + */ template -struct ThreadSafeQueue : public NonCopyable { +class ThreadSafeQueue : public NonCopyable { +public: + /** + * @brief Default constructor. + */ ThreadSafeQueue() = default; - void put(T element) { - { - std::lock_guard lock(m_mutex); - m_queue.push(std::move(element)); - } - m_conditionVariable.notify_one(); - } - - std::optional take() { - std::unique_lock lock(m_mutex); - m_conditionVariable.wait( - lock, [this] { return m_mustReturnNullptr || !m_queue.empty(); }); - - if (m_mustReturnNullptr) - return {}; - - T ret = std::move(m_queue.front()); - m_queue.pop(); - - return ret; - } - - std::queue destroy() { - { - std::lock_guard lock(m_mutex); - m_mustReturnNullptr = true; - } - m_conditionVariable.notify_all(); - - std::queue result; - { - std::lock_guard lock(m_mutex); - std::swap(result, m_queue); - } - return result; - } - - size_t size() const { - std::lock_guard lock(m_mutex); - return m_queue.size(); - } - - bool empty() const { - std::lock_guard lock(m_mutex); - return m_queue.empty(); - } - - void clear() { - std::lock_guard lock(m_mutex); - while (!m_queue.empty()) { - m_queue.pop(); - } - } - - std::optional front() { - std::lock_guard lock(m_mutex); - if (m_queue.empty()) { - return {}; - } - return m_queue.front(); - } - - std::optional back() { - std::lock_guard lock(m_mutex); - if (m_queue.empty()) { - return {}; - } - return m_queue.back(); - } - + /** + * @brief Adds an element to the end of the queue. + * @param element The element to be added to the queue. + */ + void put(T element); + + /** + * @brief Removes and returns an element from the front of the queue. + * @return An optional containing the removed element, or empty if the queue + * is empty. + */ + std::optional take(); + + /** + * @brief Removes all elements from the queue and returns them in a + * std::queue. + * @return A std::queue containing all the elements removed from the queue. + */ + std::queue destroy(); + + /** + * @brief Returns the number of elements currently in the queue. + * @return The number of elements in the queue. + */ + [[nodiscard]] size_t size() const; + + /** + * @brief Checks if the queue is empty. + * @return True if the queue is empty, false otherwise. + */ + [[nodiscard]] bool empty() const; + + /** + * @brief Removes all elements from the queue. + */ + void clear(); + + /** + * @brief Returns the element at the front of the queue without removing it. + * @return An optional containing the element at the front of the queue, or + * empty if the queue is empty. + */ + std::optional front(); + + /** + * @brief Returns the element at the back of the queue without removing it. + * @return An optional containing the element at the back of the queue, or + * empty if the queue is empty. + */ + std::optional back(); + + /** + * @brief Constructs and adds an element to the end of the queue. + * @tparam Args The types of arguments used to construct the element. + * @param args The arguments used to construct the element. + */ template - void emplace(Args &&...args) { - { - std::lock_guard lock(m_mutex); - m_queue.emplace(std::forward(args)...); - } - m_conditionVariable.notify_one(); - } - + void emplace(Args&&... args); + + /** + * @brief Waits until a predicate becomes true for an element in the queue, + * then removes and returns that element. + * @tparam Predicate The type of predicate function or functor. + * @param predicate The predicate to wait for. + * @return An optional containing the element that satisfied the predicate, + * or empty if the queue is destroyed or the timeout expires. + */ template - std::optional waitFor(Predicate predicate) { - std::unique_lock lock(m_mutex); - m_conditionVariable.wait(lock, [this, &predicate] { - return m_mustReturnNullptr || predicate(m_queue); - }); - - if (m_mustReturnNullptr) - return {}; - - T ret = std::move(m_queue.front()); - m_queue.pop(); - - return ret; - } - - void waitUntilEmpty() { - std::unique_lock lock(m_mutex); - m_conditionVariable.wait( - lock, [this] { return m_mustReturnNullptr || m_queue.empty(); }); - } - + std::optional waitFor(Predicate predicate); + + /** + * @brief Blocks until the queue becomes empty. + */ + void waitUntilEmpty(); + + /** + * @brief Removes and returns all elements from the queue that satisfy a + * given unary predicate. + * @tparam UnaryPredicate The type of unary predicate function or functor. + * @param pred The unary predicate used to test elements. + * @return A vector containing all elements removed from the queue that + * satisfy the predicate. + */ template - std::vector extractIf(UnaryPredicate pred) { - std::vector result; - { - std::lock_guard lock(m_mutex); - auto it = m_queue.begin(); - while (it != m_queue.end()) { - if (pred(*it)) { - result.push_back(std::move(*it)); - it = m_queue.erase(it); - } else { - ++it; - } - } - } - return result; - } - + std::vector extractIf(UnaryPredicate pred); + + /** + * @brief Sorts the elements in the queue using a custom comparison + * function. + * @tparam Compare The type of comparison function or functor. + * @param comp The comparison function used to sort elements. + */ template - void sort(Compare comp) { - std::lock_guard lock(m_mutex); - std::vector temp(std::make_move_iterator(m_queue.front()), - std::make_move_iterator(m_queue.back())); - std::sort(temp.begin(), temp.end(), comp); - std::queue newQueue; - for (auto &item : temp) { - newQueue.push(std::move(item)); - } - std::swap(m_queue, newQueue); - } - - template - ThreadSafeQueue transform(std::function func) { - ThreadSafeQueue resultQueue; - { - std::lock_guard lock(m_mutex); - while (!m_queue.empty()) { - T item = std::move(m_queue.front()); - m_queue.pop(); - resultQueue.put(func(std::move(item))); - } - } - return resultQueue; - } - - template - std::vector> groupBy(std::function func) { - std::unordered_map> resultMap; - { - std::lock_guard lock(m_mutex); - while (!m_queue.empty()) { - T item = std::move(m_queue.front()); - m_queue.pop(); - GroupKey key = func(item); - resultMap[key].put(std::move(item)); - } - } - - std::vector> resultQueues; - for (auto &pair : resultMap) { - resultQueues.push_back(std::move(pair.second)); - } - - return resultQueues; - } + void sort(Compare comp); private: - std::queue m_queue; - mutable std::mutex m_mutex; - std::condition_variable m_conditionVariable; - - std::atomic m_mustReturnNullptr{false}; + std::queue m_queue; ///< The underlying queue. + mutable std::mutex m_mutex; ///< Mutex for ensuring thread safety. + std::condition_variable + m_conditionVariable; ///< Condition variable for blocking and waking + ///< threads. + std::atomic m_mustReturnNullptr{ + false}; ///< Atomic flag indicating whether the queue should return + ///< nullptr on take() when empty. }; + } // namespace Atom::Async +#include "queue.inl" + #endif diff --git a/src/atom/async/queue.inl b/src/atom/async/queue.inl new file mode 100644 index 00000000..f026f202 --- /dev/null +++ b/src/atom/async/queue.inl @@ -0,0 +1,206 @@ +#ifndef ATOM_ASYNC_QUEUE_INL +#define ATOM_ASYNC_QUEUE_INL + +#include "queue.hpp" + +#include + +namespace Atom::Async { +template +void ThreadSafeQueue::put(T element) { + { + std::lock_guard lock(m_mutex); + m_queue.push(std::move(element)); + } + m_conditionVariable.notify_one(); +} + +template +std::optional ThreadSafeQueue::take() { + std::unique_lock lock(m_mutex); + m_conditionVariable.wait( + lock, [this] { return m_mustReturnNullptr || !m_queue.empty(); }); + + if (m_mustReturnNullptr) + return std::nullopt; + + T ret = std::move(m_queue.front()); + m_queue.pop(); + + return ret; +} + +template +std::queue ThreadSafeQueue::destroy() { + { + std::lock_guard lock(m_mutex); + m_mustReturnNullptr = true; + } + m_conditionVariable.notify_all(); + + std::queue result; + { + std::lock_guard lock(m_mutex); + std::swap(result, m_queue); + } + return result; +} + +template +size_t ThreadSafeQueue::size() const { + std::lock_guard lock(m_mutex); + return m_queue.size(); +} + +template +bool ThreadSafeQueue::empty() const { + std::lock_guard lock(m_mutex); + return m_queue.empty(); +} + +template +void ThreadSafeQueue::clear() { + std::lock_guard lock(m_mutex); + std::queue empty; + std::swap(m_queue, empty); +} + +template +std::optional ThreadSafeQueue::front() { + std::lock_guard lock(m_mutex); + if (m_queue.empty()) { + return std::nullopt; + } + return m_queue.front(); +} + +template +std::optional ThreadSafeQueue::back() { + std::lock_guard lock(m_mutex); + if (m_queue.empty()) { + return std::nullopt; + } + return m_queue.back(); +} + +template +template +void ThreadSafeQueue::emplace(Args&&... args) { + { + std::lock_guard lock(m_mutex); + m_queue.emplace(std::forward(args)...); + } + m_conditionVariable.notify_one(); +} + +template +template +std::optional ThreadSafeQueue::waitFor(Predicate predicate) { + std::unique_lock lock(m_mutex); + m_conditionVariable.wait(lock, [this, &predicate] { + return m_mustReturnNullptr || predicate(m_queue); + }); + + if (m_mustReturnNullptr) + return std::nullopt; + + T ret = std::move(m_queue.front()); + m_queue.pop(); + + return ret; +} + +template +void ThreadSafeQueue::waitUntilEmpty() { + std::unique_lock lock(m_mutex); + m_conditionVariable.wait( + lock, [this] { return m_mustReturnNullptr || m_queue.empty(); }); +} + +template +template +std::vector ThreadSafeQueue::extractIf(UnaryPredicate pred) { + std::vector result; + { + std::lock_guard lock(m_mutex); + auto it = + std::remove_if(m_queue.front(), m_queue.back(), [&](const T& item) { + if (pred(item)) { + result.push_back(std::move(const_cast(item))); + return true; + } + return false; + }); + m_queue.pop(); + } + return result; +} + +template +template +void ThreadSafeQueue::sort(Compare comp) { + std::lock_guard lock(m_mutex); + + // 移动元素到临时向量并排序 + std::vector temp; + while (!m_queue.empty()) { + temp.push_back(std::move(m_queue.front())); + m_queue.pop(); + } + std::sort(temp.begin(), temp.end(), comp); + + // 将排序后的元素移动到新的队列中 + std::queue newQueue; + for (auto& elem : temp) { + newQueue.push(std::move(elem)); + } + + // 交换新旧队列 + std::swap(m_queue, newQueue); +} + +/* +template +template +ThreadSafeQueue ThreadSafeQueue::transform( + std::function func) { + ThreadSafeQueue resultQueue; + { + std::lock_guard lock(m_mutex); + std::transform(std::make_move_iterator(m_queue.front()), + std::make_move_iterator(m_queue.back()), + std::back_inserter(resultQueue.m_queue), func); + std::queue empty; + std::swap(m_queue, empty); + } + // return resultQueue; +} + +template +template +std::vector> ThreadSafeQueue::groupBy( + std::function func) { + std::unordered_map> resultMap; + { + std::lock_guard lock(m_mutex); + while (!m_queue.empty()) { + T item = std::move(m_queue.front()); + m_queue.pop(); + GroupKey key = func(item); + resultMap[key].put(std::move(item)); + } + } + + std::vector> resultQueues; + resultQueues.reserve(resultMap.size()); + for (auto& pair : resultMap) { + resultQueues.push_back(std::move(pair.second)); + } + + return resultQueues; +} +*/ + +} // namespace Atom::Async + +#endif \ No newline at end of file diff --git a/src/atom/async/thread_wrapper.hpp b/src/atom/async/thread_wrapper.hpp new file mode 100644 index 00000000..e87ec8a3 --- /dev/null +++ b/src/atom/async/thread_wrapper.hpp @@ -0,0 +1,163 @@ +/* + * thread_wrapper.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-2-13 + +Description: A simple wrapper of std::jthread + +**************************************************/ + +#ifndef ATOM_ASYNC_THREAD_WRAPPER_HPP +#define ATOM_ASYNC_THREAD_WRAPPER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief A wrapper class for managing a C++20 jthread. + * + * This class provides a convenient interface for managing a C++20 jthread, + * allowing for starting, stopping, and joining threads easily. + */ +class Thread { +public: + /** + * @brief Default constructor. + */ + Thread() = default; + + /** + * @brief Move constructor. + * @param other The Thread object to move from. + */ + Thread(Thread&&) noexcept = default; + + /** + * @brief Move assignment operator. + * @param other The Thread object to move from. + * @return Reference to the assigned Thread object. + */ + Thread& operator=(Thread&&) noexcept = default; + + /** + * @brief Deleted copy constructor. + */ + Thread(const Thread&) = delete; + + /** + * @brief Deleted copy assignment operator. + */ + Thread& operator=(const Thread&) = delete; + + /** + * @brief Starts a new thread with the specified callable object and + * arguments. + * + * If the callable object is invocable with a std::stop_token and the + * provided arguments, it will be invoked with a std::stop_token as the + * first argument. Otherwise, it will be invoked with the provided + * arguments. + * + * @tparam Callable The type of the callable object. + * @tparam Args The types of the arguments. + * @param func The callable object to execute in the new thread. + * @param args The arguments to pass to the callable object. + */ + template + void start(Callable&& func, Args&&... args) { + thread_ = + std::jthread([func = std::forward(func), + ... args = std::forward(args), this]() mutable { + if constexpr (std::is_invocable_v) { + func(std::stop_token(thread_.get_stop_token()), + std::move(args)...); + } else { + func(std::move(args)...); + } + }); + } + + /** + * @brief Requests the thread to stop execution. + */ + void request_stop() { thread_.request_stop(); } + + /** + * @brief Waits for the thread to finish execution. + */ + void join() { thread_.join(); } + + /** + * @brief Checks if the thread is currently running. + * @return True if the thread is running, false otherwise. + */ + [[nodiscard]] bool running() const noexcept { return thread_.joinable(); } + + /** + * @brief Swaps the content of this Thread object with another Thread + * object. + * @param other The Thread object to swap with. + */ + void swap(Thread& other) noexcept { thread_.swap(other.thread_); } + + /** + * @brief Gets the underlying std::jthread object. + * @return Reference to the underlying std::jthread object. + */ + [[nodiscard]] std::jthread& get_thread() noexcept { return thread_; } + + /** + * @brief Gets the underlying std::jthread object (const version). + * @return Constant reference to the underlying std::jthread object. + */ + [[nodiscard]] const std::jthread& get_thread() const noexcept { + return thread_; + } + + /** + * @brief Gets the ID of the thread. + * @return The ID of the thread. + */ + [[nodiscard]] std::thread::id get_id() const noexcept { + return thread_.get_id(); + } + + /** + * @brief Gets the underlying std::stop_source object. + * @return The underlying std::stop_source object. + */ + [[nodiscard]] std::stop_source get_stop_source() noexcept { + return thread_.get_stop_source(); + } + + /** + * @brief Gets the underlying std::stop_token object. + * @return The underlying std::stop_token object. + */ + [[nodiscard]] std::stop_token get_stop_token() const noexcept { + return thread_.get_stop_token(); + } + + /** + * @brief Default destructor. + */ + ~Thread() = default; + +private: + std::jthread thread_; ///< The underlying jthread object. +}; + +#endif diff --git a/src/atom/async/trigger_impl.hpp b/src/atom/async/trigger_impl.hpp index 415af312..fa22d1dd 100644 --- a/src/atom/async/trigger_impl.hpp +++ b/src/atom/async/trigger_impl.hpp @@ -77,7 +77,6 @@ void Trigger::trigger(const std::string &event, try { callback.second(param); } catch (std::exception &e) { - throw std::throw_with_nested(std::nested_exception(e)); } } } @@ -86,7 +85,7 @@ template void Trigger::scheduleTrigger(const std::string &event, const ParamType ¶m, std::chrono::milliseconds delay) { - std::thread([=]() { + std::thread([this, event, param, delay]() { std::this_thread::sleep_for(delay); trigger(event, param); }).detach(); @@ -97,7 +96,7 @@ std::future Trigger::scheduleAsyncTrigger( const std::string &event, const ParamType ¶m) { auto promise = std::make_shared>(); auto future = promise->get_future(); - std::thread([=]() mutable { + std::thread([this, event, param, promise]() mutable { try { trigger(event, param); promise->set_value(); diff --git a/src/atom/async/xmake.lua b/src/atom/async/xmake.lua new file mode 100644 index 00000000..5bcc6bfd --- /dev/null +++ b/src/atom/async/xmake.lua @@ -0,0 +1,59 @@ +-- xmake.lua for Atom-Async +-- This project is licensed under the terms of the GPL3 license. +-- +-- Project Name: Atom-Async +-- Description: Async Implementation of Lithium Server and Driver +-- Author: Max Qian +-- License: GPL3 + +add_rules("mode.debug", "mode.release") + +set_project("atom-async") + +set_languages("cxx20") + +-- 设置CMake最低版本 +cmake_minimum_required("version" 3.20) + +-- 设置项目名称和描述 +set_project_name("atom-async") +set_project_description("Async Implementation of Lithium Server and Driver") + +-- 添加源文件 +add_files("lock.cpp") +add_files("timer.cpp") + +-- 添加头文件 +add_header_files("async.hpp") +add_header_files("async_impl.hpp") +add_header_files("lock.hpp") +add_header_files("queue.hpp") +add_header_files("queue.inl") +add_header_files("thread_wrapper.hpp") +add_header_files("timer.hpp") +add_header_files("trigger.hpp") +add_header_files("trigger_impl.hpp") + +-- 创建对象库 +obj_library("atom_async_object" srcs _all_) +set_property("atom_async_object", {position_independent = true}) +target_link_libraries("atom_async_object" "loguru") + +-- 创建静态库 +static_library("atom_async" srcs _all_ dependencies "atom_async_object") + +-- 链接必要的库 +target_link_libraries("atom_async" "atom_async_object" ${CMAKE_THREAD_LIBS_INIT}) +target_include_directories("atom_async" include_path ".") + +-- 设置版本和输出文件名 +set_property("atom_async", {version = CMAKE_HYDROGEN_VERSION_STRING, so_version = HYDROGEN_SOVERSION, output_name = "atom_async"}) + +-- 安装目标 +install_target("atom_async" arch "lib" dest "lib") + +-- 如果构建Python模块 +if (ATOM_BUILD_PYTHON) + pybind11_add_module("atom_async_py" srcs "_pybind.cpp") + target_link_libraries("atom_async_py" "atom_async") +endif() \ No newline at end of file diff --git a/src/atom/components/component.cpp b/src/atom/components/component.cpp index 162e2704..3eb15494 100644 --- a/src/atom/components/component.cpp +++ b/src/atom/components/component.cpp @@ -28,240 +28,83 @@ namespace fs = std::filesystem; #include #endif -Component::Component(const std::string &name) - : m_CommandDispatcher(std::make_unique>()), - m_VariableRegistry(std::make_unique(name)), - m_name(name) {} - -Component::~Component() { destroy(); } - -bool Component::initialize() { - DLOG_F(INFO, "Component is initializing ..."); - registerFunc("registerVariable", &Component::_registerVariable, this); - DLOG_F(INFO, "Component is initialized"); - return true; +Component::Component(const std::string& name) + : m_name(name), + m_ConfigManager(std::make_unique()), + m_CommandDispatcher(std::make_unique()), + m_VariableManager(std::make_unique()), + m_typeInfo(user_type()) { + // Empty } -bool Component::destroy() { - if (m_CommandDispatcher) { - m_CommandDispatcher->removeAll(); - m_CommandDispatcher.reset(); - } - if (m_VariableRegistry) { - m_VariableRegistry->RemoveAll(); - m_VariableRegistry.reset(); - } - return true; +Component::~Component() { + // Empty } +bool Component::initialize() { return true; } + +bool Component::destroy() { return true; } + std::string Component::getName() const { return m_name; } -bool Component::loadConfig(const std::string &path) { - std::unique_lock lock(m_mutex); - try { - std::ifstream ifs(path); - if (!ifs.is_open()) { - LOG_F(ERROR, "Failed to open file: {}", path); - return false; - } - json j = json::parse(ifs); - const std::string basename = fs::path(path).stem().string(); - m_config[basename] = j["config"]; - m_ConfigPath = path; - DLOG_F(INFO, "Loaded config file {} successfully", path); - return true; - } catch (const json::exception &e) { - LOG_F(ERROR, "Failed to parse file: {}, error message: {}", path, - e.what()); - } catch (const std::exception &e) { - LOG_F(ERROR, "Failed to load config file: {}, error message: {}", path, - e.what()); - } - return false; +std::optional Component::getValue(const std::string& key_path) const { + return m_ConfigManager->getValue(key_path); } -bool Component::saveConfig() { - if (m_ConfigPath.empty()) { - LOG_F(ERROR, "No path provided, will not save {}'s config", m_name); - return false; - } - std::ofstream ofs(m_ConfigPath); - if (!ofs.is_open()) { - LOG_F(ERROR, "Failed to open file: {}", m_ConfigPath); - return false; - } - try { - ofs << m_config.dump(4); - } catch (const json::parse_error &e) { - LOG_F(ERROR, "Failed to sace config {} for JSON error: {}", - m_ConfigPath, e.what()); - ofs.close(); - return false; - } catch (const std::exception &e) { - LOG_F(ERROR, "Failed to save config to file: {}, error message: {}", - m_ConfigPath, e.what()); - ofs.close(); - return false; - } - ofs.close(); - DLOG_F(INFO, "Save config to file: {}", m_ConfigPath); - return true; +bool Component::setValue(const std::string& key_path, const json& value) { + return m_ConfigManager->setValue(key_path, value); } -json Component::getValue(const std::string &key_path) const { - // std::lock_guard lock(rw_m_mutex); - try { - const json *p = &m_config; - for (const auto &key : Atom::Utils::splitString(key_path, '/')) { - if (p->is_object() && p->contains(key)) { - p = &(*p)[key]; - } else { - LOG_F(ERROR, "Key not found: {}", key_path); - return nullptr; - } - } - return *p; - } catch (const std::exception &e) { - LOG_F(ERROR, "Failed to get value: {} {}", key_path, e.what()); - return nullptr; - } - return nullptr; +bool Component::hasValue(const std::string& key_path) const { + return getValue(key_path).has_value(); } -std::string Component::getVariableInfo(const std::string &name) const { - if (!m_VariableRegistry->HasVariable(name)) { - return ""; - } - return m_VariableRegistry->GetDescription(name); +bool Component::loadFromFile(const fs::path& path) { + return m_ConfigManager->loadFromFile(path); } -bool Component::registerVariableRanges(const std::string &name, - const double &low, const double &high) { - if (name.empty()) - return false; - m_VariableRegistry->SetVariableRange(name, low, high); - return true; +bool Component::saveToFile(const fs::path& file_path) const { + return m_ConfigManager->saveToFile(file_path); } -bool Component::runFunc(const std::string &name, const json ¶ms) { - if (!m_CommandDispatcher->hasHandler(name)) { - return false; - } - m_CommandDispatcher->dispatch(name, params); - return true; +void Component::addAlias(const std::string& name, const std::string& alias) { + m_CommandDispatcher->addAlias(name, alias); } -json Component::getFuncInfo(const std::string &name) { - if (m_CommandDispatcher->hasHandler(name)) { - json args; - args = { - {"name", name}, - {"description", m_CommandDispatcher->getFunctionDescription(name)}}; - return args; - } - return {}; +void Component::addGroup(const std::string& name, const std::string& group) { + m_CommandDispatcher->addGroup(name, group); } -std::function Component::getFunc(const std::string &name) { - if (!m_CommandDispatcher->hasHandler(name)) { - throw Atom::Error::InvalidArgument("Function not found"); - } - return m_CommandDispatcher->getHandler(name); +void Component::setTimeout(const std::string& name, + std::chrono::milliseconds timeout) { + m_CommandDispatcher->setTimeout(name, timeout); } -json Component::createSuccessResponse(const std::string &command, - const json &value) { - json res; - res["command"] = command; - res["value"] = value; - res["status"] = "ok"; - res["code"] = 200; -#if __cplusplus >= 202003L - res["message"] = std::format("{} operated on success", command); -#else - res["message"] = fmt::format("{} operated on success", command); -#endif - return res; +void Component::clearCache() { m_CommandDispatcher->clearCache(); } + +void Component::removeCommand(const std::string& name) { + m_CommandDispatcher->removeCommand(name); } -json Component::createErrorResponse(const std::string &command, - const json &error, - const std::string &message = "") { - json res; - res["command"] = command; - res["status"] = "error"; - res["code"] = 500; -#if __cplusplus >= 202003L - res["message"] = - std::format("{} operated on failure, message: {}", command, message); -#else - res["message"] = - std::format("{} operated on failure, message: {}", command, message); -#endif - if (!error.empty()) { - res["error"] = error; - } else { - res["error"] = "Unknown Error"; - } - return res; +std::vector Component::getCommandsInGroup( + const std::string& group) const { + return m_CommandDispatcher->getCommandsInGroup(group); +} + +std::string Component::getCommandDescription(const std::string& name) const + +{ + return m_CommandDispatcher->getCommandDescription(name); } -json Component::createWarningResponse(const std::string &command, - const json &warning, - const std::string &message = "") { - json res; - res["command"] = command; - res["status"] = "warning"; - res["code"] = 400; -#if __cplusplus >= 202003L - res["message"] = - std::format("{} operated on warning, message: {}", command, message); +#if ENABLE_FASTHASH +emhash::HashSet Component::getCommandAliases( + const std::string& name) const #else - res["message"] = - std::format("{} operated on warning, message: {}", command, message); +std::unordered_set Component::getCommandAliases( + const std::string& name) const #endif - if (!warning.empty()) { - res["warning"] = warning; - } else { - res["warning"] = "Unknown Warning"; - } - return res; -} -json Component::_registerVariable(const json ¶ms) { - // Check Parameters - if (!params.contains("name") || !params.contains("value")) { - return createErrorResponse(__func__, {{"error", "Invalid Parameters"}}, - "Missing 'name' or 'value'"); - } - if (!params["name"].is_string()) { - return createErrorResponse(__func__, {{"error", "Invalid Parameters"}}, - "'name' must be a string value"); - } - std::string name = params["name"].get(); - std::string description = ""; - // Get description if have - if (params.contains("description") && params["description"].is_string()) { - description = params["description"]; - } - bool status = false; - if (params["value"].is_string()) { - status = registerVariable(name, params["value"].get(), - description); - } else if (params["value"].is_number()) { - status = - registerVariable(name, params["value"].get(), description); - } else if (params["value"].is_boolean()) { - status = - registerVariable(name, params["value"].get(), description); - } else { - return createErrorResponse( - __func__, {{"error", "Unknown Type"}}, - "Value type must be one of number|boolean|string"); - } - if (!status) { - return createErrorResponse(__func__, {{"error", "Operation Failed"}, - "Failed to register value"}); - } - return createSuccessResponse(__func__, {}); +{ + return m_CommandDispatcher->getCommandAliases(name); } \ No newline at end of file diff --git a/src/atom/components/component.hpp b/src/atom/components/component.hpp index f91fdb50..b07a0242 100644 --- a/src/atom/components/component.hpp +++ b/src/atom/components/component.hpp @@ -12,45 +12,29 @@ Description: Basic Component Definition **************************************************/ -// Max: Obviously, directly use json.hpp is much simpler than self-implement -// type - #ifndef ATOM_COMPONENT_HPP #define ATOM_COMPONENT_HPP #include #include +#include #include "types.hpp" -#include "atom/server/commander.hpp" -#include "atom/server/variables.hpp" - -#include "atom/type/json.hpp" -using json = nlohmann::json; - -#define SETVAR_STR(name, value) \ - m_VariableRegistry->RegisterVariable(name); \ - m_VariableRegistry->SetVariable(name, value); +#include "dispatch.hpp" +#include "var.hpp" -#define SETVAR_INT(name, value) \ - m_VariableRegistry->RegisterVariable(name); \ - m_VariableRegistry->SetVariable(name, value); +#include "configor.hpp" -#define SETVAR_BOOL(name, value) \ - m_VariableRegistry->RegisterVariable(name); \ - m_VariableRegistry->SetVariable(name, value); +#include "atom/experiment/noncopyable.hpp" +#include "atom/experiment/type_info.hpp" -#define SETVAR_DOUBLE(name, value) \ - m_VariableRegistry->RegisterVariable(name); \ - m_VariableRegistry->SetVariable(name, value); - -class Component : public std::enable_shared_from_this { +class Component : public std::enable_shared_from_this, public NonCopyable { public: /** * @brief Constructs a new Component object. */ - explicit Component(const std::string &name); + explicit Component(const std::string& name); /** * @brief Destroys the Component object. @@ -81,164 +65,217 @@ class Component : public std::enable_shared_from_this { */ virtual bool destroy(); + /** + * @brief Gets the name of the plugin. + * + * @return The name of the plugin. + */ std::string getName() const; // ------------------------------------------------------------------- // Component Configuration methods // ------------------------------------------------------------------- - // Max: Should we use JSON here? - // 2024/02/13 Max: Obviously, directly use json.hpp is much simpler than - // self-implement type + [[nodiscard("config value should not be ignored!")]] std::optional + getValue(const std::string& key_path) const; /** - * @brief Loads the component configuration from a file. + * @brief 添加或更新一个配置项 * - * @param path The path to the component configuration file. - * @return True if the component configuration was loaded successfully, - * false otherwise. - * @note Usually, the component configuration file is stored in the plugin's - * directory. + * Add or update a configuration item. + * + * @param key_path 配置项的键路径,使用斜杠 / 进行分隔,如 + * "database/username" + * @param value 配置项的值,使用 JSON 格式进行表示 + * @return bool 成功返回 true,失败返回 false */ - bool loadConfig(const std::string &path); + bool setValue(const std::string& key_path, const json& value); - bool saveConfig(); + /** + * @brief 判断一个配置项是否存在 + * + * Determine if a configuration item exists. + * + * @param key_path 配置项的键路径,使用斜杠 / 进行分隔,如 + * "database/username" + * @return bool 存在返回 true,不存在返回 false + */ + [[nodiscard("status of the value should not be ignored")]] bool hasValue( + const std::string& key_path) const; + + /** + * @brief 从指定文件中加载JSON配置,并与原有配置进行合并 + * + * Load JSON configuration from the specified file and merge with the + * existing configuration. + * + * @param path 配置文件路径 + */ + bool loadFromFile(const fs::path& path); - json getValue(const std::string &key_path) const; + /** + * @brief 将当前配置保存到指定文件 + * + * Save the current configuration to the specified file. + * + * @param file_path 目标文件路径 + */ + bool saveToFile(const fs::path& file_path) const; // ------------------------------------------------------------------- // Variable methods // ------------------------------------------------------------------- - json _registerVariable(const json ¶ms); + template + void addVariable(const std::string& name, T initialValue, + const std::string& description = "", + const std::string& alias = "", + const std::string& group = "") { + m_VariableManager->addVariable(name, initialValue, description, alias, + group); + } - /** - * @brief Registers a member function with a specific name and handler. - * - * This function allows plugins to register member functions that can be - * called by other plugins. - * - * @tparam ClassType The type of the class that owns the handler function. - * @param name The name of the function. - * @param handler The handler function for the function. - * @param object The object instance that owns the handler function. - */ template - bool registerVariable(const std::string &name, const T &value, - const std::string &description = "") { - return m_VariableRegistry->RegisterVariable(name, value, - description); + void setRange(const std::string& name, T min, T max) { + m_VariableManager->setRange(name, min, max); } - bool registerVariableRanges(const std::string &name, const double &low, const double &high); + void setStringOptions(const std::string& name, + std::vector options) { + m_VariableManager->setStringOptions(name, options); + } /** - * @brief Gets the value of the variable with the specified name. - * - * @tparam T The type of the variable. + * @brief Gets a variable by name. * @param name The name of the variable. - * @return An optional containing the value of the variable, or empty if the - * variable does not exist. + * @return A shared pointer to the variable. */ template - bool setVariable(const std::string &name, const T &value) { - return m_VariableRegistry->SetVariable(name, value); + std::shared_ptr> getVariable(const std::string& name) { + return m_VariableManager->getVariable(name); } /** - * @brief Gets the value of the variable with the specified name. - * - * @tparam T The type of the variable. + * @brief Sets the value of a variable. * @param name The name of the variable. - * @return An optional containing the value of the variable, or empty if the - * variable does not exist - * @note When you try to get a variable that does not exist, an exception - * will be thrown - * @note This function is thread-safe - * @note This function is for the server to get the value of a variable of - * the plugin. + * @param newValue The new value of the variable. + * @note const char * is not equivalent to std::string, please use + * std::string */ template - [[nodiscard]] std::optional getVariable(const std::string &name) const { - return m_VariableRegistry->GetVariable(name); + void setValue(const std::string& name, T newValue) { + m_VariableManager->setValue(name, newValue); } - /** - * @brief Gets the information about the variable with the specified name. - * - * @param name The name of the variable. - * @return The information about the variable. - */ - std::string getVariableInfo(const std::string &name) const; - // ------------------------------------------------------------------- // Function methods // ------------------------------------------------------------------- - /** - * @brief Sets the value of the variable with the specified name. - * - * @tparam T The type of the variable. - * @param name The name of the variable. - * @param value The value to set. - * @return True if the variable was set successfully, false otherwise. - */ - template - void registerFunc(const std::string &name, - json (ClassType::*handler)(const json &), - ClassType *object); + template + void registerCommand( + const std::string& name, const std::string& group, + const std::string& description, std::function func, + std::optional> precondition = std::nullopt, + std::optional> postcondition = std::nullopt) + { + m_CommandDispatcher->registerCommand(name, group, description, func, + precondition, postcondition); + } - /** - * @brief Gets the information about the function with the specified name. - * - * @param name The name of the function. - * @return The information about the function in JSON format. - */ - json getFuncInfo(const std::string &name); + template + void registerCommand(const std::string& name, Callable&& func, + const std::string& group = "", + const std::string& description = "") { + m_CommandDispatcher->registerCommand(name, func, group, description); + } - /** - * @brief Runs the function with the specified name and parameters. - * - * This function calls a registered member function with the specified name - * and parameters. - * - * @param name The name of the function. - * @param params The parameters for the function. - * @return True if the function was executed successfully, false otherwise. - */ - bool runFunc(const std::string &name, const json ¶ms); + template + void registerCommand(const std::string& name, Ret (*func)(Args...), + const std::string& group = "", + const std::string& description = "") { + m_CommandDispatcher->registerCommand(name, func, group, description); + } - std::function getFunc(const std::string &name); + template + void registerCommand(const std::string& name, Ret (Class::*func)(Args...), + const PointerSentinel& instance, + const std::string& group = "", + const std::string& description = "") - json createSuccessResponse(const std::string &command, const json &value); + { + m_CommandDispatcher->registerCommand(name, func, instance, group, + description); + } - json createErrorResponse(const std::string &command, const json &error, - const std::string &message); + template + void registerCommand(const std::string& name, + Ret (Class::*func)(Args...) const, + const PointerSentinel& instance, + const std::string& group = "", + const std::string& description = "") { + m_CommandDispatcher->registerCommand(name, func, instance, group, + description); + } - json createWarningResponse(const std::string &command, const json &warning, - const std::string &message); + template + void registerCommand(const std::string& name, + Ret (Class::*func)(Args...) noexcept, + const PointerSentinel& instance, + const std::string& group = "", + const std::string& description = "") { + m_CommandDispatcher->registerCommand(name, func, instance, group, + description); + } -private: - std::string m_name; - std::string m_ConfigPath; - std::string m_InfoPath; + template + void registerCommand(const std::string& name, Ret (*func)(Args...), + const std::string& group = "", + const std::string& description = "") { + m_CommandDispatcher->registerCommand(name, func, group, description); + } + + void addAlias(const std::string& name, const std::string& alias); + + void addGroup(const std::string& name, const std::string& group); + + void setTimeout(const std::string& name, std::chrono::milliseconds timeout); + + template + std::any dispatch(const std::string& name, Args&&... args) + { + return m_CommandDispatcher->dispatch(name, std::forward(args)...); + } - std::unique_ptr> - m_CommandDispatcher; ///< The command dispatcher for handling - ///< functions. - std::unique_ptr - m_VariableRegistry; ///< The variable registry for managing variables. + void clearCache(); - json m_config; + void removeCommand(const std::string& name); + + std::vector getCommandsInGroup(const std::string& group) const; + + std::string getCommandDescription(const std::string& name) const; + +#if ENABLE_FASTHASH + emhash::HashSet getCommandAliases( + const std::string& name) const; +#else + std::unordered_set getCommandAliases( + const std::string& name) const; +#endif + +private: + std::string m_name; + std::string m_configPath; + std::string m_infoPath; + Type_Info m_typeInfo; + + std::unique_ptr + m_CommandDispatcher; ///< The command dispatcher for managing commands. + std::unique_ptr + m_VariableManager; ///< The variable registry for managing variables. + std::unique_ptr + m_ConfigManager; + std::mutex m_mutex; }; -template -void Component::registerFunc(const std::string &name, - json (ClassType::*handler)(const json &), - ClassType *object) { - if (!m_CommandDispatcher->hasHandler(name)) - m_CommandDispatcher->registerMemberHandler(name, object, handler); -} - #endif diff --git a/src/atom/components/component.inl b/src/atom/components/component.inl new file mode 100644 index 00000000..1edf9772 --- /dev/null +++ b/src/atom/components/component.inl @@ -0,0 +1,18 @@ +#ifndef ATOM_COMPONENT_INL +#define ATOM_COMPONENT_INL + +#include + +template +struct has_getValue { + template + static constexpr auto check(int) -> decltype(std::declval().getValue(), + std::true_type{}); + + template + static constexpr std::false_type check(...); + + static constexpr bool value = decltype(check(0))::value; +}; + +#endif diff --git a/src/atom/components/component_impl.hpp b/src/atom/components/component_impl.hpp deleted file mode 100644 index 4cea6d40..00000000 --- a/src/atom/components/component_impl.hpp +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef ATOM_COMPONENT_IMPL_HPP -#define ATOM_COMPONENT_IMPL_HPP - -#include "component.hpp" - -#endif \ No newline at end of file diff --git a/src/atom/components/configor.cpp b/src/atom/components/configor.cpp new file mode 100644 index 00000000..d2ca6a15 --- /dev/null +++ b/src/atom/components/configor.cpp @@ -0,0 +1,209 @@ +/* + * configor.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-4-30 + +Description: Configor + +**************************************************/ + +#include "configor.hpp" + +#include +#include +#include +#include +#if ENABLE_FASTHASH +#include "emhash/hash_table8.hpp" +#else +#include +#endif +#include "atom/utils/string.hpp" + +#include "atom/log/loguru.hpp" + +namespace fs = std::filesystem; + +class ConfigManagerImpl { +public: + mutable std::shared_mutex rw_mutex_; + json config_; +}; + +ConfigManager::ConfigManager() : m_impl(std::make_unique()) { + if (loadFromFile("config.json")) { + DLOG_F(INFO, "Config loaded successfully."); + } +} + +ConfigManager::~ConfigManager() { saveToFile("config.json"); } + +std::shared_ptr ConfigManager::createShared() { + static std::shared_ptr instance = + std::make_shared(); + return instance; +} + +std::unique_ptr ConfigManager::createUnique() { + return std::make_unique(); +} + +bool ConfigManager::loadFromFile(const fs::path& path) { + std::shared_lock lock(m_impl->rw_mutex_); + try { + std::ifstream ifs(path); + if (!ifs || ifs.peek() == std::ifstream::traits_type::eof()) { + LOG_F(ERROR, "Failed to open file: {}", path.string()); + return false; + } + json j = json::parse(ifs); + if (j.empty()) { + return false; + } + + mergeConfig(j); + return true; + } catch (const json::exception& e) { + LOG_F(ERROR, "Failed to parse file: {}, error message: {}", path.string(), + e.what()); + } catch (const std::exception& e) { + LOG_F(ERROR, "Failed to load config file: {}, error message: {}", path.string(), + e.what()); + } + return false; +} + +bool ConfigManager::loadFromDir(const fs::path& dir_path, bool recursive) { + std::shared_lock lock(m_impl->rw_mutex_); + try { + for (const auto& entry : fs::directory_iterator(dir_path)) { + if (entry.is_regular_file() && + entry.path().extension() == ".json") { + loadFromFile(entry.path()); + } else if (recursive && entry.is_directory()) { + loadFromDir(entry.path(), true); + } + } + } catch (const std::exception& e) { + LOG_F(ERROR, "Failed to load config file from: {}, error message: {}", + dir_path.string(), e.what()); + return false; + } + return true; +} + +std::optional ConfigManager::getValue(const std::string& key_path) const { + std::shared_lock lock(m_impl->rw_mutex_); + const json* p = &m_impl->config_; + for (const auto& key : key_path | std::views::split('/')) { + if (p->is_object() && + p->contains(std::string(key.begin(), key.end()))) { + p = &(*p)[std::string(key.begin(), key.end())]; + } else { + return std::nullopt; + } + } + return *p; +} + +bool ConfigManager::setValue(const std::string& key_path, const json& value) { + std::unique_lock lock(m_impl->rw_mutex_); + json* p = &m_impl->config_; + std::vector keys; + for (auto sub_range : key_path | std::views::split('/')) { + keys.emplace_back(sub_range.data(), sub_range.size()); + } + + for (auto it = keys.begin(); it != keys.end(); ++it) { + if (it + 1 == keys.end()) { + (*p)[*it] = value; + return true; + } + + if (!p->is_object()) { + return false; + } + + if (!p->contains(*it)) { + (*p)[*it] = json::object(); + } + + p = &(*p)[*it]; + } + + return true; +} + +bool ConfigManager::deleteValue(const std::string& key_path) { + std::unique_lock lock(m_impl->rw_mutex_); + std::vector keys; + for (auto sub_range : key_path | std::views::split('/')) { + keys.emplace_back(sub_range.data(), sub_range.size()); + } + json* p = &m_impl->config_; + for (auto it = keys.begin(); it != keys.end(); ++it) { + if (it + 1 == keys.end()) { // Last key + if (p->contains(*it)) { + p->erase(*it); + return true; + } + return false; + } + if (!p->contains(*it) || !(*p)[*it].is_object()) { + return false; + } + p = &(*p)[*it]; + } + return false; +} + +bool ConfigManager::hasValue(const std::string& key_path) const { + return getValue(key_path).has_value(); +} + +bool ConfigManager::saveToFile(const fs::path& file_path) const { + std::unique_lock lock(m_impl->rw_mutex_); + std::ofstream ofs(file_path); + if (!ofs) { + LOG_F(ERROR, "Failed to open file: {}", file_path.string()); + return false; + } + try { + ofs << m_impl->config_.dump(4); + ofs.close(); + return true; + } catch (const std::exception& e) { + LOG_F(ERROR, "Failed to save config to file: {}, error message: {}", + file_path.string(), e.what()); + return false; + } +} + +void ConfigManager::tidyConfig() { + std::unique_lock lock(m_impl->rw_mutex_); + json updated_config; + for (const auto& [key, value] : m_impl->config_.items()) { + json* p = &updated_config; + for (auto sub_key : key | std::views::split('/')) { + if (!p->contains(std::string(sub_key.begin(), sub_key.end()))) { + (*p)[std::string(sub_key.begin(), sub_key.end())] = + json::object(); + } + p = &(*p)[std::string(sub_key.begin(), sub_key.end())]; + } + *p = value; + } + m_impl->config_ = std::move(updated_config); +} + +void ConfigManager::mergeConfig(const json& src) { + m_impl->config_.merge_patch(src); +} + +void ConfigManager::clearConfig() { m_impl->config_.clear(); } + diff --git a/src/atom/components/configor.hpp b/src/atom/components/configor.hpp new file mode 100644 index 00000000..3f95a574 --- /dev/null +++ b/src/atom/components/configor.hpp @@ -0,0 +1,199 @@ +/* + * configor.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-4-4 + +Description: Configor + +**************************************************/ + +#ifndef ATOM_COMPONENT_CONFIG_HPP +#define ATOM_COMPONENT_CONFIG_HPP + +#include +#include +#include +namespace fs = std::filesystem; + +#include "error/error_code.hpp" + +#include "atom/type/json.hpp" +using json = nlohmann::json; + +#define GetIntConfig(path) \ + GetPtr("lithium.config") \ + .value() \ + ->getValue(path) \ + .value() \ + .get() + +#define GetFloatConfig(path) \ + GetPtr("lithium.config") \ + .value() \ + ->getValue(path) \ + .value() \ + .get() + +#define GetBoolConfig(path) \ + GetPtr("lithium.config") \ + .value() \ + ->getValue(path) \ + .value() \ + .get() + +#define GetDoubleConfig(path) \ + GetPtr("lithium.config") \ + .value() \ + ->getValue(path) \ + .value() \ + .get() + +#define GetStringConfig(path) \ + GetPtr("lithium.config") \ + .value() \ + ->getValue(path) \ + .value() \ + .get() + +class ConfigManagerImpl; + +class ConfigManager { +public: + /** + * @brief 构造函数 + * + * Constructor. + */ + ConfigManager(); + + /** + * @brief 析构函数 + * + * Destructor. + */ + ~ConfigManager(); + + // ------------------------------------------------------------------- + // Common methods + // ------------------------------------------------------------------- + + /** + * @brief 创建ConfigManager的共享指针,但是全局唯一 + * + * Create a shared pointer of ConfigManager. But global only + */ + static std::shared_ptr createShared(); + + static std::unique_ptr createUnique(); + + // ------------------------------------------------------------------- + // Config methods + // ------------------------------------------------------------------- + + /** + * @brief 获取一个配置项的值 + * + * Get the value of a configuration item. + * + * @param key_path 配置项的键路径,使用斜杠 / 进行分隔,如 + * "database/username" + * @return json 配置项对应的值,如果键不存在则返回 nullptr + */ + [[nodiscard("config value should not be ignored!")]] std::optional + getValue(const std::string& key_path) const; + + /** + * @brief 添加或更新一个配置项 + * + * Add or update a configuration item. + * + * @param key_path 配置项的键路径,使用斜杠 / 进行分隔,如 + * "database/username" + * @param value 配置项的值,使用 JSON 格式进行表示 + * @return bool 成功返回 true,失败返回 false + */ + bool setValue(const std::string& key_path, const json& value); + /** + * @brief 删除一个配置项 + * + * Delete a configuration item. + * + * @param key_path 配置项的键路径,使用斜杠 / 进行分隔,如 + * "database/username" + */ + + bool deleteValue(const std::string& key_path); + + /** + * @brief 判断一个配置项是否存在 + * + * Determine if a configuration item exists. + * + * @param key_path 配置项的键路径,使用斜杠 / 进行分隔,如 + * "database/username" + * @return bool 存在返回 true,不存在返回 false + */ + [[nodiscard("status of the value should not be ignored")]] bool hasValue( + const std::string& key_path) const; + + /** + * @brief 从指定文件中加载JSON配置,并与原有配置进行合并 + * + * Load JSON configuration from the specified file and merge with the + * existing configuration. + * + * @param path 配置文件路径 + */ + bool loadFromFile(const fs::path& path); + + /** + * @brief 加载指定目录下的所有JSON配置文件 + * + * Load all JSON configuration files in the specified directory. + * + * @param dir_path 配置文件所在目录的路径 + */ + bool loadFromDir(const fs::path& dir_path, bool recursive = false); + + /** + * @brief 将当前配置保存到指定文件 + * + * Save the current configuration to the specified file. + * + * @param file_path 目标文件路径 + */ + bool saveToFile(const fs::path& file_path) const; + + /** + * @brief 清理配置项 + * + * Clean up configuration items. + */ + void tidyConfig(); + + /** + * @brief 清除所有配置(测试用) + * + * Clear all of the configurations, only used for test + */ + void clearConfig(); + +private: + std::unique_ptr m_impl; + + /** + * @brief 将 JSON 配置合并到当前配置中 + * + * Merge JSON configuration to the current configuration. + * + * @param j JSON 配置 + */ + void mergeConfig(const json& j); +}; + +#endif diff --git a/src/atom/components/dispatch.cpp b/src/atom/components/dispatch.cpp new file mode 100644 index 00000000..7be45524 --- /dev/null +++ b/src/atom/components/dispatch.cpp @@ -0,0 +1,59 @@ +#include "dispatch.hpp" + +void CommandDispatcher::addAlias(const std::string& name, + const std::string& alias) { + auto it = commands.find(name); + if (it != commands.end()) { + it->second.aliases.insert(alias); + commands[alias] = it->second; + groupMap[alias] = groupMap[name]; + } +} + +void CommandDispatcher::addGroup(const std::string& name, + const std::string& group) { + groupMap[name] = group; +} + +void CommandDispatcher::setTimeout(const std::string& name, + std::chrono::milliseconds timeout) { + timeoutMap[name] = timeout; +} + +void CommandDispatcher::clearCache() { cacheMap.clear(); } + +void CommandDispatcher::removeCommand(const std::string& name) { + commands.erase(name); + groupMap.erase(name); + timeoutMap.erase(name); + cacheMap.erase(name); +} + +std::vector CommandDispatcher::getCommandsInGroup( + const std::string& group) const { + std::vector result; + for (const auto& pair : groupMap) { + if (pair.second == group) { + result.push_back(pair.first); + } + } + return result; +} + +std::string CommandDispatcher::getCommandDescription( + const std::string& name) const { + auto it = commands.find(name); + if (it != commands.end()) { + return it->second.description; + } + return ""; +} + +std::unordered_set CommandDispatcher::getCommandAliases( + const std::string& name) const { + auto it = commands.find(name); + if (it != commands.end()) { + return it->second.aliases; + } + return {}; +} \ No newline at end of file diff --git a/src/atom/components/dispatch.hpp b/src/atom/components/dispatch.hpp new file mode 100644 index 00000000..1c05edd2 --- /dev/null +++ b/src/atom/components/dispatch.hpp @@ -0,0 +1,130 @@ +#ifndef ATOM_COMMAND_DISPATCH_HPP +#define ATOM_COMMAND_DISPATCH_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ENABLE_FASTHASH +#include "emhash/hash_set8.hpp" +#include "emhash/hash_table8.hpp" +#else +#include +#include +#endif +#include +#include + +#include "atom/experiment/noncopyable.hpp" +#include "atom/type/pointer.hpp" + +#include "proxy.hpp" + +class CommandDispatcher : public NonCopyable { +public: + template + void registerCommand( + const std::string& name, const std::string& group, + const std::string& description, std::function func, + std::optional> precondition = std::nullopt, + std::optional> postcondition = std::nullopt); + + template + void registerCommand(const std::string& name, Callable&& func, + const std::string& group = "", + const std::string& description = ""); + + template + void registerCommand(const std::string& name, Ret (*func)(Args...), + const std::string& group = "", + const std::string& description = ""); + + template + void registerCommand(const std::string& name, Ret (Class::*func)(Args...), + const PointerSentinel& instance, + const std::string& group = "", + const std::string& description = ""); + + template + void registerCommand(const std::string& name, + Ret (Class::*func)(Args...) const, + const PointerSentinel& instance, + const std::string& group = "", + const std::string& description = ""); + + template + void registerCommand(const std::string& name, + Ret (Class::*func)(Args...) noexcept, + const PointerSentinel& instance, + const std::string& group = "", + const std::string& description = ""); + + template + void registerCommand(const std::string& name, Ret (*func)(Args...), + const std::string& group = "", + const std::string& description = ""); + + void addAlias(const std::string& name, const std::string& alias); + + void addGroup(const std::string& name, const std::string& group); + + void setTimeout(const std::string& name, std::chrono::milliseconds timeout); + + template + std::any dispatch(const std::string& name, Args&&... args); + + void clearCache(); + + void removeCommand(const std::string& name); + + std::vector getCommandsInGroup(const std::string& group) const; + + std::string getCommandDescription(const std::string& name) const; + +#if ENABLE_FASTHASH + emhash::HashSet getCommandAliases( + const std::string& name) const; +#else + std::unordered_set getCommandAliases( + const std::string& name) const; +#endif + +private: + struct Command { + std::vector&)>> + funcs; + std::vector funcs_info; + std::string description; +#if ENABLE_FASTHASH + emhash::HashSet aliases; +#else + std::unordered_set aliases; +#endif + std::optional> precondition; + std::optional> postcondition; + }; + +#if ENABLE_FASTHASH + emhash8::HashMap commands; + emhash8::HashMap groupMap; + emhash8::HashMap timeoutMap; + emhash8::HashMap cacheMap; +#else + std::unordered_map commands; + std::unordered_map groupMap; + std::unordered_map timeoutMap; + std::unordered_map cacheMap; +#endif +}; + +#include "dispatch.inl" + +#endif diff --git a/src/atom/components/dispatch.inl b/src/atom/components/dispatch.inl new file mode 100644 index 00000000..7c82f308 --- /dev/null +++ b/src/atom/components/dispatch.inl @@ -0,0 +1,175 @@ +#ifndef ATOM_COMPONENT_DISPATCH_INL +#define ATOM_COMPONENT_DISPATCH_INL + +#include "dispatch.hpp" + +#include "atom/error/exception.hpp" +#include "atom/type/abi.hpp" + +template +void CommandDispatcher::registerCommand( + const std::string& name, const std::string& group, + const std::string& description, std::function func, + std::optional> precondition = std::nullopt, + std::optional> postcondition = std::nullopt) { + auto it = commands.find(name); + if (it == commands.end()) { + Command cmd{{ProxyFunction(std::move(func))}, + description, + {}, + std::move(precondition), + std::move(postcondition)}; + commands[name] = std::move(cmd); + groupMap[name] = group; + } else { + it->second.funcs.emplace_back(ProxyFunction(std::move(func))); + } +} + +template +void CommandDispatcher::registerCommand(const std::string& name, + Callable&& func, + const std::string& group = "", + const std::string& description = "") { + registerCommand(name, group, description, + std::function(std::forward(func))); +} + +template +void CommandDispatcher::registerCommand(const std::string& name, + Ret (*func)(Args...), + const std::string& group = "", + const std::string& description = "") { + registerCommand(name, group, description, + std::function([func](Args... args) { + return func(std::forward(args)...); + })); +} + +template +void CommandDispatcher::registerCommand(const std::string& name, + Ret (Class::*func)(Args...), + const PointerSentinel& instance, + const std::string& group = "", + const std::string& description = "") { + registerCommand(name, group, description, + std::function([instance, func](Args... args) { + return std::invoke(func, instance.get(), + std::forward(args)...); + })); +} + +template +void CommandDispatcher::registerCommand(const std::string& name, + Ret (Class::*func)(Args...) const, + const PointerSentinel& instance, + const std::string& group = "", + const std::string& description = "") { + registerCommand(name, group, description, + std::function([instance, func](Args... args) { + return std::invoke(func, instance.get(), + std::forward(args)...); + })); +} + +template +void CommandDispatcher::registerCommand(const std::string& name, + Ret (Class::*func)(Args...) noexcept, + const PointerSentinel& instance, + const std::string& group = "", + const std::string& description = "") { + registerCommand(name, group, description, + std::function([instance, func](Args... args) { + return std::invoke(func, instance.get(), + std::forward(args)...); + })); +} + +template +void CommandDispatcher::registerCommand(const std::string& name, + Ret (*func)(Args...), + const std::string& group = "", + const std::string& description = "") { + registerCommand(name, group, description, + std::function([func](Args... args) { + return func(std::forward(args)...); + })); +} + +template +std::any CommandDispatcher::dispatch(const std::string& name, Args&&... args) { + auto it = commands.find(name); + if (it == commands.end()) { + for (const auto& cmd : commands) { + for (const auto& alias : cmd.second.aliases) { + if (alias == name) { +#if ENABLE_DEBUG + std::cout << "Command '" << name + << "' not found, did you mean '" << cmd.first + << "'?\n"; +#endif + it = commands.find(cmd.first); + } + } + } + throw std::runtime_error("Unknown command: " + name); + } + + const auto& cmd = it->second; + if (cmd.precondition.has_value() && !cmd.precondition.value()()) { + throw std::runtime_error("Precondition failed for command: " + name); + } + + auto argsTuple = std::make_tuple(std::forward(args)...); + std::vector argsVec; + argsVec.reserve(sizeof...(Args)); + std::apply( + [&argsVec](auto&&... args) { + ((argsVec.emplace_back(std::forward(args))), ...); + }, + argsTuple); + + auto cacheIt = cacheMap.find(name); + if (cacheIt != cacheMap.end()) { + return cacheIt->second; + } + + auto timeoutIt = timeoutMap.find(name); + if (timeoutIt != timeoutMap.end()) { + auto future = std::async(std::launch::async, [&]() { + for (const auto& func : cmd.funcs) { + try { + // Max: 匹配找到的第一个,但是理论上来说不会有重复 + return func(argsVec); + } catch (const std::bad_any_cast&) { + // 参数类型不匹配,尝试下一个重载函数 + } + } + return std::any{}; + }); + if (future.wait_for(timeoutIt->second) == std::future_status::timeout) { + throw std::runtime_error("Command timed out: " + name); + } + auto result = future.get(); + cacheMap[name] = result; + if (cmd.postcondition.has_value()) + cmd.postcondition.value()(); + return result; + } else { + for (const auto& func : cmd.funcs) { + try { + auto result = func(argsVec); + cacheMap[name] = result; + if (cmd.postcondition.has_value()) + cmd.postcondition.value()(); + return result; + } catch (const std::bad_any_cast&) { + // 参数类型不匹配,尝试下一个重载函数 + } + } + throw std::runtime_error("No matching overload found for command: " + + name); + } +} + +#endif \ No newline at end of file diff --git a/src/atom/components/proxy.hpp b/src/atom/components/proxy.hpp new file mode 100644 index 00000000..da804b94 --- /dev/null +++ b/src/atom/components/proxy.hpp @@ -0,0 +1,112 @@ +/* + * proxy.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-3-1 + +Description: Proxy Function Implementation + +**************************************************/ + +#ifndef ATOM_COMPONENT_PROXY_HPP +#define ATOM_COMPONENT_PROXY_HPP + +#include +#include +#include +#include +#include +#include + +template +struct ProxyFunction { + std::function func; + static constexpr std::size_t N = sizeof...(Args); + + ProxyFunction(std::function func) : func(std::move(func)) {} + + std::any operator()(const std::vector& args) { + if (args.size() != N) + throw std::runtime_error("Number of arguments does not match"); + return call(args, std::make_index_sequence()); + } + +private: + template + std::any call(const std::vector& args, + std::index_sequence) { + if constexpr (std::is_void_v) { + std::invoke(func, + std::any_cast>(args[Is])...); + return {}; + } else { + return std::make_any(std::invoke( + func, std::any_cast>(args[Is])...)); + } + } +}; + +template +struct ProxyFunction { + using MemFuncPtr = Ret (Class::*)(Args...); + MemFuncPtr memFunc; + static constexpr std::size_t N = sizeof...(Args) + 1; + + ProxyFunction(MemFuncPtr func) : memFunc(func) {} + + std::any operator()(const std::vector& args) { + if (args.size() != N) + throw std::runtime_error("Number of arguments does not match"); + return call(args, std::make_index_sequence()); + } + +private: + template + std::any call(const std::vector& args, + std::index_sequence) { + if constexpr (std::is_void_v) { + (std::any_cast(args[0]).*memFunc)( + std::any_cast>(args[Is + 1])...); + return {}; + } else { + return std::make_any((std::any_cast(args[0]).*memFunc)( + std::any_cast>(args[Is + 1])...)); + } + } +}; + +template +struct ProxyFunction { + using ConstMemFuncPtr = Ret (Class::*)(Args...) const; + ConstMemFuncPtr memFunc; + static constexpr std::size_t N = sizeof...(Args) + 1; + + ProxyFunction(ConstMemFuncPtr func) : memFunc(func) {} + + std::any operator()(const std::vector& args) { + if (args.size() != N) + throw std::runtime_error("Number of arguments does not match"); + return call(args, std::make_index_sequence()); + } + +private: + template + std::any call(const std::vector& args, + std::index_sequence) { + if constexpr (std::is_void_v) { + (std::any_cast(args[0]).*memFunc)( + std::any_cast>(args[Is + 1])...); + return {}; + } else { + return std::make_any( + (std::any_cast(args[0]).*memFunc)( + std::any_cast>(args[Is + 1])...)); + } + } +}; + +#endif diff --git a/src/atom/components/templates/shared.cpp b/src/atom/components/templates/shared.cpp new file mode 100644 index 00000000..35aa75c4 --- /dev/null +++ b/src/atom/components/templates/shared.cpp @@ -0,0 +1,91 @@ +#include "shared.hpp" + +#include "atom/error/exception.hpp" +#include "atom/log/loguru.hpp" +#include "atom/server/message_bus.hpp" +#include "atom/type/message.hpp" +#include "atom/utils/string.hpp" + +using namespace Atom::Error; + +SharedComponent::SharedComponent(const std::string &name) : Component(name) { + m_handleFunction = [shared_this = + shared_from_this()](const std::any &message) { + LOG_F(INFO, "SharedComponent::handleFunction"); + if (message.has_value()) { + if (message.type() == typeid(std::shared_ptr)) { + shared_this->dispatch( + "handleVoid", + std::any_cast>(message)); + } else if (message.type() == + typeid(std::shared_ptr)) { + shared_this->dispatch( + "handleNumber", + std::any_cast>(message)); + } else if (message.type() == typeid(std::shared_ptr)) { + shared_this->dispatch( + "handleText", + std::any_cast>(message)); + } else if (message.type() == + typeid(std::shared_ptr)) { + shared_this->dispatch( + "handleBoolean", + std::any_cast>(message)); + } else if (message.type() == + typeid(std::shared_ptr)) { + shared_this->dispatch( + "handleParams", + std::any_cast>(message)); + } else { + LOG_F(ERROR, + "SharedComponent::handleFunction: unknown message type"); + THROW_EXCEPTION( + "SharedComponent::handleFunction: unknown message type"); + } + } else { + LOG_F(ERROR, "SharedComponent::handleFunction: message is null"); + THROW_EXCEPTION("SharedComponent::handleFunction: message is null"); + } + }; + + registerCommand("handleNumber", handleNumberMessage); + registerCommand("handleText", handleTextMessage); + registerCommand("handleBoolean", handleBooleanMessage); + registerCommand("handleVoid", handleVoidMessage); +} + +SharedComponent::~SharedComponent() {} + +bool SharedComponent::initialize() { return true; } + +bool SharedComponent::destroy() { return true; } + +void SharedComponent::handleVoidMessage( + const std::shared_ptr &message) { + LOG_F(INFO, "SharedComponent::handleVoid"); + auto name = message->name(); + try + { + + } + catch(const std::exception& e) + { + std::cerr << e.what() << '\n'; + } + +} + +void SharedComponent::handleTextMessage( + const std::shared_ptr &message) { + LOG_F(INFO, "SharedComponent::handleText"); +} + +void SharedComponent::handleBooleanMessage( + const std::shared_ptr &message) { + LOG_F(INFO, "SharedComponent::handleBoolean"); +} + +void SharedComponent::handleNumberMessage( + const std::shared_ptr &message) { + LOG_F(INFO, "SharedComponent::handleNumber"); +} \ No newline at end of file diff --git a/src/atom/components/templates/shared.hpp b/src/atom/components/templates/shared.hpp new file mode 100644 index 00000000..ba51cb50 --- /dev/null +++ b/src/atom/components/templates/shared.hpp @@ -0,0 +1,24 @@ +#ifndef ATOM_COMPONENT_SHARED_HPP +#define ATOM_COMPONENT_SHARED_HPP + +#include "atom/components/component.hpp" + +#include "atom/type/message.hpp" + +class SharedComponent : public Component { + explicit SharedComponent(const std::string &name); + virtual ~SharedComponent() override; + + virtual bool initialize() override; + virtual bool destroy() override; + + void handleVoidMessage(const std::shared_ptr &message); + void handleTextMessage(const std::shared_ptr &message); + void handleNumberMessage(const std::shared_ptr &message); + void handleBooleanMessage(const std::shared_ptr &message); + +private: + std::function m_handleFunction; +}; + +#endif \ No newline at end of file diff --git a/src/atom/components/templates/shared_component.cpp b/src/atom/components/templates/shared_component.cpp deleted file mode 100644 index a992b348..00000000 --- a/src/atom/components/templates/shared_component.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include "shared_component.hpp" - -#include "atom/log/loguru.hpp" -#include "atom/server/message_bus.hpp" -#include "atom/utils/string.hpp" - -//#include "config.h" - -#include "macro.hpp" - -#define GET_ARGUMENT_S(command, type, name) \ - if (!args.get(#name).has_value()) { \ - this->SendTextMessage(#command, \ - fmt::format("Missing arguments: {}", #name)); \ - return; \ - } \ - type name = args.get(#name).value(); - -SharedComponent::SharedComponent(const std::string &name) : Component(name) { - DLOG_F(INFO, "Shared component is created."); -} - -SharedComponent::~SharedComponent() { - DLOG_F(INFO, "Shared component is destroyed."); -} - -bool SharedComponent::initialize() { - Component::initialize(); - DLOG_F(INFO, "Shared component is initializing ..."); - - m_handleFunction = [this](std::shared_ptr message) { - if (message) { - switch (message->type()) { - case Message::Type::kText: { - auto textMessage = - std::dynamic_pointer_cast(message); - if (textMessage) { - DLOG_F(INFO, "Text message is received: {}", - textMessage->value()); - m_handleText->match(textMessage->name(), textMessage); - } - break; - } - case Message::Type::kNumber: { - auto numberMessage = - std::dynamic_pointer_cast(message); - if (numberMessage) { - DLOG_F(INFO, "Number message is received: {}", - numberMessage->value()); - m_handleNumber->match(numberMessage->name(), - numberMessage); - } - } - case Message::Type::kBoolean: { - auto booleanMessage = - std::dynamic_pointer_cast(message); - if (booleanMessage) { - DLOG_F(INFO, "Boolean message is received: {}", - booleanMessage->value()); - m_handleBoolean->match(booleanMessage->name(), - booleanMessage); - } - break; - } - case Message::Type::kParams: { - auto paramsMessage = - std::dynamic_pointer_cast(message); - if (paramsMessage) { - DLOG_F(INFO, "Params message is received: {}", - paramsMessage->value().toJson()); - m_handleParams->match(paramsMessage->name(), - paramsMessage); - } - break; - } - default: - break; - } - } - }; - - // Initialize message handlers - m_handleVoid = std::make_unique< - Atom::Utils::StringSwitch &>>(); - m_handleText = std::make_unique< - Atom::Utils::StringSwitch &>>(); - m_handleNumber = std::make_unique< - Atom::Utils::StringSwitch &>>(); - m_handleBoolean = std::make_unique< - Atom::Utils::StringSwitch &>>(); - m_handleParams = std::make_unique< - Atom::Utils::StringSwitch &>>(); - - DLOG_F(INFO, "Shared component is initialized"); - return true; -} - -bool SharedComponent::destroy() { - Component::destroy(); - return true; } - -bool SharedComponent::NeedMessageBus() { return true; } - -bool SharedComponent::InjectMessageBus( - std::shared_ptr messageBus) { - m_MessageBus = messageBus; - if (!m_MessageBus) { - LOG_F(ERROR, "Message bus is null."); - return false; - } - DLOG_F(INFO, "Message bus is injected."); - return true; -} - -bool SharedComponent::ConnectMessageBus() { - if (!m_MessageBus) { - LOG_F(ERROR, "Message bus is null."); - return false; - } - m_MessageBus->Subscribe>("lithium.app", - m_handleFunction); - DLOG_F(INFO, "Message bus is connected."); - return true; -} - -bool SharedComponent::DisconnectMessageBus() { - if (!m_MessageBus) { - LOG_F(ERROR, "Message bus is null."); - return false; - } - // There is a very severe bug in the message bus. - // It will cause a crash when the message bus is disconnected. - // How should we identify which connection is the one we want to disconnect? - m_MessageBus->Unsubscribe>("lithium.app", - m_handleFunction); - DLOG_F(INFO, "Message bus is disconnected."); - return true; -} - -bool SharedComponent::SendTextMessage(const std::string &message, - const std::string &text) { - if (!m_MessageBus) { - LOG_F(ERROR, "Message bus is null."); - return false; - } - m_MessageBus->Publish>( - message, - std::make_shared(message, text, "lithium.app", getName())); - return true; -} - -bool SharedComponent::SendNumberMessage(const std::string &message, - const double &number) { - if (!m_MessageBus) { - LOG_F(ERROR, "Message bus is null."); - return false; - } - m_MessageBus->Publish>( - message, std::make_shared(message, number, "lithium.app", - getName())); - return true; -} - -bool SharedComponent::SendBooleanMessage(const std::string &message, - const bool &boolean) { - if (!m_MessageBus) { - LOG_F(ERROR, "Message bus is null."); - return false; - } - m_MessageBus->Publish>( - message, std::make_shared(message, boolean, - "lithium.app", getName())); - return true; -} - -bool SharedComponent::SendParamsMessage(const std::string &message, - const Args ¶ms) { - if (!m_MessageBus) { - LOG_F(ERROR, "Message bus is null."); - return false; - } - m_MessageBus->Publish>( - message, std::make_shared(message, params, "lithium.app", - getName())); - return true; -} diff --git a/src/atom/components/templates/shared_component.hpp b/src/atom/components/templates/shared_component.hpp deleted file mode 100644 index f2199070..00000000 --- a/src/atom/components/templates/shared_component.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include "atom/components/component.hpp" -#include "atom/server/message_bus.hpp" -#include "atom/type/message.hpp" -#include "atom/utils/switch.hpp" - -class SharedComponent : public Component { -public: - explicit SharedComponent(const std::string &name); - ~SharedComponent(); - - // ------------------------------------------------------------------- - // Common methods - // ------------------------------------------------------------------- - - virtual bool initialize() override; - virtual bool destroy() override; - - // ------------------------------------------------------------------- - // Message methods - // ------------------------------------------------------------------- - - bool NeedMessageBus(); - bool InjectMessageBus(std::shared_ptr messageBus); - bool ConnectMessageBus(); - bool DisconnectMessageBus(); - bool SendTextMessage(const std::string &message, const std::string &text); - bool SendNumberMessage(const std::string &message, const double &number); - bool SendBooleanMessage(const std::string &message, const bool &boolean); - bool SendParamsMessage(const std::string &message, const Args ¶ms); - - // ------------------------------------------------------------------- - // Handbler methods - // ------------------------------------------------------------------- - - void SetHandleFunction( - std::function)> handleFunction) { - m_handleFunction = handleFunction; - } - -private: - // This is a little bit hacky, but it works. - std::shared_ptr m_MessageBus; - - std::function)> m_handleFunction; - - // Message handlers - std::unique_ptr< - Atom::Utils::StringSwitch &>> - m_handleVoid; - std::unique_ptr< - Atom::Utils::StringSwitch &>> - m_handleText; - std::unique_ptr< - Atom::Utils::StringSwitch &>> - m_handleNumber; - std::unique_ptr< - Atom::Utils::StringSwitch &>> - m_handleBoolean; - std::unique_ptr< - Atom::Utils::StringSwitch &>> - m_handleParams; -}; \ No newline at end of file diff --git a/src/atom/components/var.hpp b/src/atom/components/var.hpp new file mode 100644 index 00000000..d481c590 --- /dev/null +++ b/src/atom/components/var.hpp @@ -0,0 +1,114 @@ +#ifndef ATOM_COMPONENT_VAR_HPP +#define ATOM_COMPONENT_VAR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ENABLE_FASTHASH +#include "emhash/hash_table8.hpp" +#else +#include +#endif + +#include "atom/error/exception.hpp" +#include "atom/experiment/noncopyable.hpp" +#include "atom/type/trackable.hpp" +#include "atom/utils/cstring.hpp" + +class VariableManager : public NonCopyable { +public: + template + void addVariable(const std::string& name, T initialValue, + const std::string& description = "", + const std::string& alias = "", + const std::string& group = "") { + auto variable = std::make_shared>(std::move(initialValue)); + variables_[name] = {std::move(variable), description, alias, group}; + } + + template + void setRange(const std::string& name, T min, T max) { + if (auto variable = getVariable(name)) { + ranges_[name] = std::make_pair(std::move(min), std::move(max)); + } + } + + void setStringOptions(const std::string& name, + std::vector options) { + if (auto variable = getVariable(name)) { + stringOptions_[name] = std::move(options); + } + } + + template + std::shared_ptr> getVariable(const std::string& name) { + auto it = variables_.find(name); + if (it != variables_.end()) { + try { + return std::any_cast>>( + it->second.variable); + } catch (const std::bad_any_cast& e) { + THROW_EXCEPTION(concat("Type mismatch: ", name)); + } + } + return nullptr; + } + + void setValue(const std::string& name, const char* newValue) { + setValue(name, std::string(newValue)); + } + + template + void setValue(const std::string& name, T newValue) { + if (auto variable = getVariable(name)) { + if constexpr (std::is_arithmetic_v) { + if (ranges_.count(name)) { + auto [min, max] = + std::any_cast>(ranges_[name]); + if (newValue < min || newValue > max) { + THROW_EXCEPTION("Value out of range"); + } + } + } else if constexpr (std::is_same_v) { + if (stringOptions_.count(name)) { + auto& options = stringOptions_[name]; + if (std::find(options.begin(), options.end(), newValue) == + options.end()) { + THROW_EXCEPTION("Invalid string option"); + } + } + } + *variable = std::move(newValue); + } else { + THROW_EXCEPTION("Variable not found"); + } + } + +private: + struct VariableInfo { + std::any variable; + std::string description; + std::string alias; + std::string group; + }; + +#if ENABLE_FASTHASH + emhash8::HashMap variables_; + emhash8::HashMap ranges_; + emhash8::HashMap> stringOptions_; +#else + std::unordered_map variables_; + std::unordered_map ranges_; + std::unordered_map> stringOptions_; +#endif +}; + +#include "var.inl" + +#endif \ No newline at end of file diff --git a/src/atom/components/var.inl b/src/atom/components/var.inl new file mode 100644 index 00000000..7f842f31 --- /dev/null +++ b/src/atom/components/var.inl @@ -0,0 +1,73 @@ +#ifndef ATOM_COMPONENT_VAR_INL +#define ATOM_COMPONENT_VAR_INL + +#include "var.hpp" + +template +void VariableManager::addVariable(const std::string& name, T initialValue, + const std::string& description = "", + const std::string& alias = "", + const std::string& group = "") { + auto variable = std::make_shared>(std::move(initialValue)); + variables_[name] = {std::move(variable), description, alias, group}; +} + +template +void VariableManager::setRange(const std::string& name, T min, T max) { + if (auto variable = getVariable(name)) { + ranges_[name] = std::make_pair(std::move(min), std::move(max)); + } +} + +inline void VariableManager::setStringOptions( + const std::string& name, std::vector options) { + if (auto variable = getVariable(name)) { + stringOptions_[name] = std::move(options); + } +} + +template +std::shared_ptr> VariableManager::getVariable( + const std::string& name) { + auto it = variables_.find(name); + if (it != variables_.end()) { + try { + return std::any_cast>>( + it->second.variable); + } catch (const std::bad_any_cast& e) { + THROW_EXCEPTION(concat("Type mismatch: ", name)); + } + } + return nullptr; +} + +void VariableManager::setValue(const std::string& name, const char* newValue) { + setValue(name, std::string(newValue)); +} + +template +void VariableManager::setValue(const std::string& name, T newValue) { + if (auto variable = getVariable(name)) { + if constexpr (std::is_arithmetic_v) { + if (ranges_.count(name)) { + auto [min, max] = std::any_cast>(ranges_[name]); + if (newValue < min || newValue > max) { + THROW_EXCEPTION("Value out of range"); + } + } + } else if constexpr (std::is_same_v) { + if (stringOptions_.count(name)) { + auto& options = stringOptions_[name]; + if (std::find(options.begin(), options.end(), newValue) == + options.end()) { + THROW_EXCEPTION("Invalid string option"); + } + } + } + *variable = std::move(newValue); + } else { + THROW_EXCEPTION("Variable not found"); + } +} + +#endif \ No newline at end of file diff --git a/src/atom/connection/CMakeLists.txt b/src/atom/connection/CMakeLists.txt index 3e9343c4..02a59787 100644 --- a/src/atom/connection/CMakeLists.txt +++ b/src/atom/connection/CMakeLists.txt @@ -69,4 +69,12 @@ set_target_properties(${PROJECT_NAME} PROPERTIES install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} -) \ No newline at end of file +) + +if (ATOM_BUILD_PYTHON) +pybind11_add_module(${PROJECT_NAME}-py _pybind.cpp) +target_link_libraries(${PROJECT_NAME}-py PRIVATE ${PROJECT_NAME}) +if (WIN32) +target_link_libraries(${PROJECT_NAME}-py PRIVATE ws2_32) +endif() +endif() \ No newline at end of file diff --git a/src/atom/connection/_pybind.cpp b/src/atom/connection/_pybind.cpp new file mode 100644 index 00000000..5351d1a8 --- /dev/null +++ b/src/atom/connection/_pybind.cpp @@ -0,0 +1,92 @@ +/* + * _pybind.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-13 + +Description: Python Binding of Atom-Connection + +**************************************************/ + +#include + +#include "fifoclient.hpp" +#include "fifoserver.hpp" +#include "shared_memory.hpp" +#include "sockethub.hpp" +#include "udp_server.hpp" + +#if ENABLE_SSHCLIENT +#include "sshclient.hpp" +#endif +namespace py = pybind11; + +using namespace Atom::Connection; + +template +void bind_shared_memory(py::module &m, const std::string &type_name) { + py::class_>(m, ("shared_memory_" + type_name).c_str()) + .def(py::init()) + .def("write", &SharedMemory::write, py::arg("data"), + py::arg("timeout") = std::chrono::milliseconds(0)) + .def("read", &SharedMemory::read, + py::arg("timeout") = std::chrono::milliseconds(0)) + .def("clear", &SharedMemory::clear) + .def("isOccupied", &SharedMemory::isOccupied); +} + +PYBIND11_MODULE(atom_connection, m) { + m.doc() = "Atom Connection Python Binding"; + + py::class_(m, "FifoClient") + .def(py::init()) + .def("write", &FifoClient::write) + .def("read", &FifoClient::read); + + py::class_(m, "FIFOServer") + .def(py::init()) + .def("sendMessage", &FIFOServer::sendMessage); + + bind_shared_memory(m, "int"); + bind_shared_memory(m, "float"); + bind_shared_memory(m, "double"); + + py::class_(m, "UdpSocketHub") + .def(py::init<>()) + .def("start", &UdpSocketHub::start, py::arg("port")) + .def("stop", &UdpSocketHub::stop) + .def("addHandler", &UdpSocketHub::addHandler) + .def("sendTo", &UdpSocketHub::sendTo, py::arg("message"), py::arg("ip"), + py::arg("port")); + +#if ENABLE_SSHCLIENT + py::class_(m, "SSHClient") + .def(py::init()) + .def("Connect", &SSHClient::Connect, py::arg("username"), + py::arg("password"), py::arg("timeout") = 10) + .def("IsConnected", &SSHClient::IsConnected) + .def("Disconnect", &SSHClient::Disconnect) + .def("ExecuteCommand", &SSHClient::ExecuteCommand) + .def("ExecuteCommands", &SSHClient::ExecuteCommands) + .def("FileExists", &SSHClient::FileExists) + .def("CreateDirectory", &SSHClient::CreateDirectory, + py::arg("remote_path"), py::arg("mode") = S_NORMAL) + .def("RemoveFile", &SSHClient::RemoveFile) + .def("RemoveDirectory", &SSHClient::RemoveDirectory) + .def("ListDirectory", &SSHClient::ListDirectory) + .def("Rename", &SSHClient::Rename) + .def("GetFileInfo", &SSHClient::GetFileInfo) + .def("DownloadFile", &SSHClient::DownloadFile) + .def("UploadFile", &SSHClient::UploadFile); +#endif + + py::class_(m, "SocketHub") + .def(py::init<>()) + .def("start", &SocketHub::start) + .def("stop", &SocketHub::stop) + .def("addHandler", &SocketHub::addHandler); +} diff --git a/src/atom/connection/fifoclient.cpp b/src/atom/connection/fifoclient.cpp index dd6f1796..1df396a1 100644 --- a/src/atom/connection/fifoclient.cpp +++ b/src/atom/connection/fifoclient.cpp @@ -14,79 +14,66 @@ Description: FIFO CLient #include "fifoclient.hpp" +#include +#include #include - -#include "atom/log/loguru.hpp" +#include namespace Atom::Connection { +FifoClient::FifoClient(const std::string& fifoPath) : m_fifoPath(fifoPath) { #ifdef _WIN32 -FifoClient::FifoClient(const std::string &fifoPath) - : fifoPath(fifoPath), - pipeHandle(INVALID_HANDLE_VALUE) + m_fifo = CreateFile(m_fifoPath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); +#elif __APPLE__ + // 在 macOS 上使用命名管道 + m_fifo = open(m_fifoPath.c_str(), O_RDWR); #else -FifoClient::FifoClient(const std::string &fifoPath) - : fifoPath(fifoPath), - pipeFd() + m_fifo = open(m_fifoPath.c_str(), O_RDWR); #endif -{ } -void FifoClient::connect() { - DLOG_F(INFO, "Connecting to FIFO..."); - +FifoClient::~FifoClient() { #ifdef _WIN32 - if (!WaitNamedPipeA(fifoPath.c_str(), NMPWAIT_WAIT_FOREVER)) { - throw std::runtime_error("Failed to connect to FIFO"); - } - - pipeHandle = CreateFileA(fifoPath.c_str(), GENERIC_WRITE, 0, nullptr, - OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); - - if (pipeHandle == INVALID_HANDLE_VALUE) { - throw std::runtime_error("Failed to open FIFO"); - } + CloseHandle(m_fifo); #else - int fd = open(fifoPath.c_str(), O_WRONLY); - if (fd == -1) { - throw std::runtime_error("Failed to open FIFO"); - } - - pipeFd = fd; + close(m_fifo); #endif - - DLOG_F(INFO, "Connected to FIFO"); } -void FifoClient::sendMessage(const std::string &message) { - DLOG_F(INFO, "Sending message..."); +bool FifoClient::write(const std::string& data) { + std::vector buffer(data.begin(), data.end()); + buffer.push_back('\0'); // 添加字符串结束符 #ifdef _WIN32 - DWORD numBytesWritten; - if (!WriteFile(pipeHandle, message.c_str(), message.length(), - &numBytesWritten, nullptr) || - numBytesWritten != message.length()) { - throw std::runtime_error("Failed to write message to FIFO"); - } + DWORD bytesWritten; + return WriteFile(m_fifo, buffer.data(), static_cast(buffer.size()), + &bytesWritten, nullptr) != 0; #else - ssize_t numBytesWritten = write(pipeFd, message.c_str(), message.length()); - if (numBytesWritten == -1 || - static_cast(numBytesWritten) != message.length()) { - throw std::runtime_error("Failed to write message to FIFO"); - } + return write(m_fifo, buffer.data(), buffer.size()) != -1; #endif - - DLOG_F(INFO, "Message sent"); } -void FifoClient::disconnect() { - DLOG_F(INFO, "Disconnecting from FIFO..."); +std::string FifoClient::read() { + std::string data; + char buffer[1024]; #ifdef _WIN32 - CloseHandle(pipeHandle); + DWORD bytesRead; + while (ReadFile(m_fifo, buffer, sizeof(buffer) - 1, &bytesRead, nullptr) != + 0 && + bytesRead != 0) { + buffer[bytesRead] = '\0'; + data += buffer; + } #else - close(pipeFd); + ssize_t bytesRead; + while ((bytesRead = read(m_fifo, buffer, sizeof(buffer) - 1)) > 0) { + buffer[bytesRead] = '\0'; + data += buffer; + } #endif - DLOG_F(INFO, "Disconnected from FIFO"); + return data; } + } // namespace Atom::Connection diff --git a/src/atom/connection/fifoclient.hpp b/src/atom/connection/fifoclient.hpp index e6530c2b..640791bb 100644 --- a/src/atom/connection/fifoclient.hpp +++ b/src/atom/connection/fifoclient.hpp @@ -29,43 +29,46 @@ Description: FIFO CLient namespace Atom::Connection { /** * @brief The FifoClient class provides functionality to connect to a FIFO - * (First In First Out) pipe, send messages through the pipe, and disconnect - * from the pipe. + * (First In First Out) pipe, write data to the pipe, and read data from + * the pipe. */ class FifoClient { public: /** - * @brief Constructor for FifoClient. + * @brief Constructs a FifoClient object with the specified FIFO path. + * * @param fifoPath The path to the FIFO pipe. */ - explicit FifoClient(const std::string &fifoPath); + FifoClient(const std::string& fifoPath); /** - * @brief Connects to the FIFO pipe. + * @brief Destructor for the FifoClient object. */ - void connect(); + ~FifoClient(); /** - * @brief Sends a message through the FIFO pipe. - * @param message The message to send. + * @brief Writes data to the FIFO pipe. + * + * @param data The data to be written to the pipe. + * @return True if the data was successfully written, false otherwise. */ - void sendMessage(const std::string &message); + bool write(const std::string& data); /** - * @brief Disconnects from the FIFO pipe. + * @brief Reads data from the FIFO pipe. + * + * @return The data read from the pipe as a string. */ - void disconnect(); + std::string read(); private: - std::string fifoPath; /**< The path to the FIFO pipe. */ - #ifdef _WIN32 - HANDLE pipeHandle; /**< Handle to the pipe (Windows). */ + HANDLE m_fifo; /**< Handle to the FIFO pipe (Windows). */ #else - int pipeFd; /**< File descriptor for the pipe (Unix/Linux). */ + int m_fifo; /**< File descriptor for the FIFO pipe (Unix/Linux). */ #endif + std::string m_fifoPath; /**< The path to the FIFO pipe. */ }; - } // namespace Atom::Connection #endif // FIFOSERVER_H diff --git a/src/atom/connection/fifoserver.cpp b/src/atom/connection/fifoserver.cpp index 91f82a2a..870cfaed 100644 --- a/src/atom/connection/fifoserver.cpp +++ b/src/atom/connection/fifoserver.cpp @@ -14,98 +14,80 @@ Description: FIFO Server #include "fifoserver.hpp" -#include - -#include "atom/log/loguru.hpp" - -namespace Atom::Connection { +#include +#include +#include #ifdef _WIN32 -FifoServer::FifoServer(const std::string &fifoPath) - : fifoPath(fifoPath), - pipeHandle(INVALID_HANDLE_VALUE) +#include #else -FifoServer::FifoServer(const std::string &fifoPath) - : fifoPath(fifoPath), - pipeFd() +#include +#include +#include +#include #endif -{ -} -void FifoServer::start() { - DLOG_F(INFO, "Starting FIFO server..."); +namespace Atom::Connection { +FIFOServer::FIFOServer(const std::string& fifo_path) : fifo_path_(fifo_path) { +// 创建 FIFO 文件 #ifdef _WIN32 - if (!WaitNamedPipeA(fifoPath.c_str(), NMPWAIT_WAIT_FOREVER)) { - throw std::runtime_error("Failed to connect to FIFO"); - } - - pipeHandle = - CreateNamedPipeA(fifoPath.c_str(), PIPE_ACCESS_INBOUND, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, - bufferSize, bufferSize, 0, nullptr); - - if (pipeHandle == INVALID_HANDLE_VALUE) { - throw std::runtime_error("Failed to create FIFO"); - } - - if (!ConnectNamedPipe(pipeHandle, nullptr)) { - throw std::runtime_error("Failed to establish connection with client"); - } -#else - if (mkfifo(fifoPath.c_str(), 0666) == -1) { - throw std::runtime_error("Failed to create FIFO"); - } - - int fd = open(fifoPath.c_str(), O_RDONLY); - if (fd == -1) { - throw std::runtime_error("Failed to open FIFO"); - } - - pipeFd = fd; + CreateNamedPipeA(fifo_path_.c_str(), PIPE_ACCESS_DUPLEX, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, NULL); +#elif __APPLE__ || __linux__ + mkfifo(fifo_path_.c_str(), 0666); #endif - DLOG_F(INFO, "FIFO server started"); + // 启动服务器线程 + server_thread_ = std::thread([this] { serverLoop(); }); } -std::string FifoServer::receiveMessage() { - DLOG_F(INFO, "Receiving message..."); - - char buffer[bufferSize]; +FIFOServer::~FIFOServer() { + // 停止服务器线程 + stop_server_ = true; + server_thread_.join(); +// 删除 FIFO 文件 #ifdef _WIN32 - DWORD numBytesRead; - if (!ReadFile(pipeHandle, buffer, bufferSize - 1, &numBytesRead, nullptr) || - numBytesRead == 0) { - return ""; - } -#else - ssize_t numBytesRead = read(pipeFd, buffer, bufferSize - 1); - if (numBytesRead == -1 || numBytesRead == 0) { - return ""; - } + DeleteFileA(fifo_path_.c_str()); +#elif __APPLE__ || __linux__ + std::filesystem::remove(fifo_path_); #endif +} - buffer[numBytesRead] = '\0'; - std::string receivedMessage(buffer); - - DLOG_F(INFO, "Received message: %s", receivedMessage.c_str()); - - return receivedMessage; +void FIFOServer::sendMessage(const std::string& message) { + std::lock_guard lock(queue_mutex_); + message_queue_.push(message); + message_cv_.notify_one(); } -void FifoServer::stop() { - DLOG_F(INFO, "Stopping FIFO server..."); +void FIFOServer::serverLoop() { + while (!stop_server_) { + std::string message; + { + std::unique_lock lock(queue_mutex_); + message_cv_.wait(lock, [this] { return !message_queue_.empty(); }); + message = message_queue_.front(); + message_queue_.pop(); + } #ifdef _WIN32 - DisconnectNamedPipe(pipeHandle); - CloseHandle(pipeHandle); - DeleteFileA(fifoPath.c_str()); -#else - close(pipeFd); - unlink(fifoPath.c_str()); + HANDLE pipe = CreateFileA(fifo_path_.c_str(), GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, 0, NULL); + DWORD bytes_written; + WriteFile(pipe, message.c_str(), message.length(), &bytes_written, + NULL); + CloseHandle(pipe); +#elif __APPLE__ + int fd = open(fifo_path_.c_str(), O_WRONLY); + write(fd, message.c_str(), message.length()); + close(fd); +#elif __linux__ + int fd = open(fifo_path_.c_str(), O_WRONLY); + write(fd, message.c_str(), message.length()); + close(fd); #endif - - DLOG_F(INFO, "FIFO server stopped"); + } } } // namespace Atom::Connection diff --git a/src/atom/connection/fifoserver.hpp b/src/atom/connection/fifoserver.hpp index 14c0e2cd..a836b196 100644 --- a/src/atom/connection/fifoserver.hpp +++ b/src/atom/connection/fifoserver.hpp @@ -15,57 +15,51 @@ Description: FIFO Server #ifndef ATOM_CONNECTION_FIFOSERVER_HPP #define ATOM_CONNECTION_FIFOSERVER_HPP +#include +#include +#include #include - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#include -#endif +#include namespace Atom::Connection { + /** - * @brief The FifoServer class provides functionality to start a server that - * listens on a FIFO (First In First Out) pipe, receive messages from the pipe, - * and stop the server. + * @brief A class representing a server for handling FIFO messages. */ -class FifoServer { +class FIFOServer { public: /** - * @brief Constructor for FifoServer. - * @param fifoPath The path to the FIFO pipe. + * @brief Constructs a new FIFOServer object. + * + * @param fifo_path The path to the FIFO pipe. */ - FifoServer(const std::string &fifoPath); + explicit FIFOServer(const std::string& fifo_path); /** - * @brief Starts the FIFO server to listen for incoming messages. + * @brief Destroys the FIFOServer object. */ - void start(); + ~FIFOServer(); /** - * @brief Receives a message from the FIFO pipe. - * @return The received message as a string. + * @brief Sends a message through the FIFO pipe. + * + * @param message The message to be sent. */ - std::string receiveMessage(); + void sendMessage(const std::string& message); +private: /** - * @brief Stops the FIFO server. + * @brief The main server loop function. */ - void stop(); - -private: - std::string fifoPath; /**< The path to the FIFO pipe. */ - static const int bufferSize = - 1024; /**< The size of the buffer for receiving messages. */ + void serverLoop(); -#ifdef _WIN32 - HANDLE pipeHandle; /**< Handle to the pipe (Windows). */ -#else - int pipeFd; /**< File descriptor for the pipe (Unix/Linux). */ -#endif + std::string fifo_path_; /**< The path to the FIFO pipe. */ + std::thread server_thread_; /**< The server thread. */ + std::atomic_bool stop_server_ = false; /**< Flag to stop the server. */ + std::queue message_queue_; /**< Queue for storing messages. */ + std::mutex queue_mutex_; /**< Mutex for message queue synchronization. */ + std::condition_variable + message_cv_; /**< Condition variable for message synchronization. */ }; } // namespace Atom::Connection diff --git a/src/atom/connection/meson.build b/src/atom/connection/meson.build new file mode 100644 index 00000000..25b9cc12 --- /dev/null +++ b/src/atom/connection/meson.build @@ -0,0 +1,59 @@ +project('Atom-Connection', 'cpp') + +# Define sources and headers +sources = [ + 'fifoclient.cpp', + 'fifoserver.cpp', + 'sockethub.cpp', + 'udp_server.cpp' +] +headers = [ + 'fifoclient.hpp', + 'fifoserver.hpp', + 'shared_memory.hpp', + 'sockethub.hpp', + 'udp_server.hpp' +] + +# Check if LibSSH is enabled +enable_ssh = get_option('ENABLE_SSH') +enable_libssh = get_option('ENABLE_LIBSSH') + +if enable_ssh and enable_libssh + sources += 'sshclient.cpp' + headers += 'sshclient.hpp' +endif + +# Create object library +atom_connection_obj = library('atom_connection_obj', + sources + headers, + cpp_std: c_std +) + +# Link with necessary libraries +atom_connection_obj_link_libs = ['loguru'] +atom_connection_obj.link_with(atom_connection_obj_link_libs) + +# Create static library +atom_connection_static = static_library('atom_connection_static', atom_connection_obj) + +# Set version properties +version = '1.0.0' # You should define your version here +soversion = '1' + +atom_connection_static.version = version +atom_connection_static.soversion = soversion +atom_connection_static.basename = 'Atom-Connection' + +# Install target +install_targets(atom_connection_static) + +# Check if Python support is enabled +atom_build_python = get_option('ATOM_BUILD_PYTHON') + +if atom_build_python + pybind11_module('atom_connection_py' 'pybind.cpp' link_with : [atom_connection_static]) + if meson.get_build_option('WIN32') + pybind11_module_link_with('atom_connection_py', ['ws2_32']) + endif +endif diff --git a/src/atom/connection/sockethub.cpp b/src/atom/connection/sockethub.cpp index f602e2b8..488d614d 100644 --- a/src/atom/connection/sockethub.cpp +++ b/src/atom/connection/sockethub.cpp @@ -110,6 +110,10 @@ void SocketHub::stop() { DLOG_F(INFO, "SocketHub stopped."); } +void SocketHub::addHandler(std::function handler) { + this->handler = std::move(handler); +} + bool SocketHub::initWinsock() { #ifdef _WIN32 WSADATA wsaData; diff --git a/src/atom/connection/xmake.lua b/src/atom/connection/xmake.lua new file mode 100644 index 00000000..5dfc1701 --- /dev/null +++ b/src/atom/connection/xmake.lua @@ -0,0 +1,69 @@ +-- 设置项目信息 +set_project("atom-connection") +set_version("1.0.0") +set_description("Connection Between Lithium Drivers, TCP and IPC") +set_license("GPL3") + +-- 添加构建模式 +add_rules("mode.debug", "mode.release") + +-- 设置构建选项 +option("enable_ssh") + set_default(false) + set_showmenu(true) + set_description("Enable SSH support") +option_end() + +option("enable_libssh") + set_default(false) + set_showmenu(true) + set_description("Enable LibSSH support") +option_end() + +option("enable_python") + set_default(false) + set_showmenu(true) + set_description("Enable Python bindings") +option_end() + +-- 设置构建目标 +target("atom-connection") + set_kind("static") + add_files("*.cpp") + add_headerfiles("*.hpp") + add_packages("loguru") + if is_plat("windows") then + add_syslinks("ws2_32") + end + if has_config("enable_ssh") then + add_packages("libssh") + end + if has_config("enable_libssh") then + add_files("sshclient.cpp") + add_headerfiles("sshclient.hpp") + end + if has_config("enable_python") then + add_rules("python.pybind11_module") + add_files("_pybind.cpp") + add_deps("python") + end + +-- 安装目标文件 +target("install") + set_kind("phony") + add_deps("atom-connection") + on_install(function (target) + import("package.tools.install") + local installx = package.tools.install + installx.static("atom-connection", {destdir = "/usr/local/lib"}) + end) + +-- 构建项目 +target("build") + set_kind("phony") + add_deps("atom-connection") + +-- 清理构建产物 +target("clean") + set_kind("phony") + add_rules("utils.clean.clean") \ No newline at end of file diff --git a/src/atom/driver/_pybind.cpp b/src/atom/driver/_pybind.cpp new file mode 100644 index 00000000..2d3e410c --- /dev/null +++ b/src/atom/driver/_pybind.cpp @@ -0,0 +1,191 @@ +/* + * _pybind.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-13 + +Description: Python Binding of Atom-Driver + +**************************************************/ + +#include + +#include "camera.hpp" +#include "device.hpp" +#include "filterwheel.hpp" +#include "focuser.hpp" +#include "solver.hpp" +#include "telescope.hpp" + +#include "pid/pid.hpp" + +namespace py = pybind11; + +PYBIND11_MODULE(atom_driver, m) { + m.doc() = "Atom Driver Python Binding"; + + py::class_(m, "AtomDriver") + .def(py::init()) // 默认构造函数 + .def("initialize", &AtomDriver::initialize) // initialize 方法 + .def("connect", &AtomDriver::connect) // connect 方法 + .def("disconnect", &AtomDriver::disconnect) // disconnect 方法 + .def("reconnect", &AtomDriver::reconnect) // reconnect 方法 + .def("isConnected", &AtomDriver::isConnected) // isConnected 方法 + .def("Connect", &AtomDriver::Connect) // Connect 方法 + .def("Disconnect", &AtomDriver::Disconnect) // Disconnect 方法 + .def("Reconnect", &AtomDriver::Reconnect) // Reconnect 方法 + .def("IsConnected", &AtomDriver::IsConnected); // IsConnected 方法 + + py::class_(m, "AtomCamera") + .def(py::init()) // 默认构造函数 + .def("initialize", &AtomCamera::initialize) // initialize 方法 + .def("connect", &AtomCamera::connect) // connect 方法 + .def("disconnect", &AtomCamera::disconnect) // disconnect 方法 + .def("reconnect", &AtomCamera::reconnect) // reconnect 方法 + .def("isConnected", &AtomCamera::isConnected) // isConnected 方法 + .def("startExposure", &AtomCamera::startExposure) // startExposure 方法 + .def("abortExposure", &AtomCamera::abortExposure) // abortExposure 方法 + .def("getExposureStatus", + &AtomCamera::getExposureStatus) // getExposureStatus 方法 + .def("getExposureResult", + &AtomCamera::getExposureResult) // getExposureResult 方法 + .def("saveExposureResult", + &AtomCamera::saveExposureResult) // saveExposureResult 方法 + .def("startVideo", &AtomCamera::startVideo) // startVideo 方法 + .def("stopVideo", &AtomCamera::stopVideo) // stopVideo 方法 + .def("getVideoStatus", + &AtomCamera::getVideoStatus) // getVideoStatus 方法 + .def("getVideoResult", + &AtomCamera::getVideoResult) // getVideoResult 方法 + .def("saveVideoResult", + &AtomCamera::saveVideoResult) // saveVideoResult 方法 + .def("startCooling", &AtomCamera::startCooling) // startCooling 方法 + .def("stopCooling", &AtomCamera::stopCooling) // stopCooling 方法 + .def("getCoolingStatus", + &AtomCamera::getCoolingStatus) // getCoolingStatus 方法 + .def("isCoolingAvailable", + &AtomCamera::isCoolingAvailable) // isCoolingAvailable 方法 + .def("getTemperature", + &AtomCamera::getTemperature) // getTemperature 方法 + .def("getCoolingPower", + &AtomCamera::getCoolingPower) // getCoolingPower 方法 + .def("setTemperature", + &AtomCamera::setTemperature) // setTemperature 方法 + .def("setCoolingPower", + &AtomCamera::setCoolingPower) // setCoolingPower 方法 + .def("getGain", &AtomCamera::getGain) // getGain 方法 + .def("setGain", &AtomCamera::setGain) // setGain 方法 + .def("isGainAvailable", + &AtomCamera::isGainAvailable) // isGainAvailable 方法 + .def("getOffset", &AtomCamera::getOffset) // getOffset 方法 + .def("setOffset", &AtomCamera::setOffset) // setOffset 方法 + .def("isOffsetAvailable", + &AtomCamera::isOffsetAvailable) // isOffsetAvailable 方法 + .def("getISO", &AtomCamera::getISO) // getISO 方法 + .def("setISO", &AtomCamera::setISO) // setISO 方法 + .def("isISOAvailable", + &AtomCamera::isISOAvailable) // isISOAvailable 方法 + .def("getFrame", &AtomCamera::getFrame) // getFrame 方法 + .def("setFrame", &AtomCamera::setFrame) // setFrame 方法 + .def("isFrameSettingAvailable", + &AtomCamera::isFrameSettingAvailable) // isFrameSettingAvailable + // 方法 + .def("getBinning", &AtomCamera::getBinning) // getBinning 方法 + .def("setBinning", &AtomCamera::setBinning); // setBinning 方法 + + py::class_(m, "Telescope") + .def(py::init()) // 默认构造函数 + .def("connect", &Telescope::connect) // connect 方法 + .def("disconnect", &Telescope::disconnect) // disconnect 方法 + .def("reconnect", &Telescope::reconnect) // reconnect 方法 + .def("isConnected", &Telescope::isConnected) // isConnected 方法 + .def("SlewTo", &Telescope::SlewTo) // SlewTo 方法 + .def("Abort", &Telescope::Abort) // Abort 方法 + .def("isSlewing", &Telescope::isSlewing) // isSlewing 方法 + .def("getCurrentRA", &Telescope::getCurrentRA) // getCurrentRA 方法 + .def("getCurrentDec", &Telescope::getCurrentDec) // getCurrentDec 方法 + .def("StartTracking", &Telescope::StartTracking) // StartTracking 方法 + .def("StopTracking", &Telescope::StopTracking) // StopTracking 方法 + .def("setTrackingMode", + &Telescope::setTrackingMode) // setTrackingMode 方法 + .def("setTrackingSpeed", + &Telescope::setTrackingSpeed) // setTrackingSpeed 方法 + .def("getTrackingMode", + &Telescope::getTrackingMode) // getTrackingMode 方法 + .def("getTrackingSpeed", + &Telescope::getTrackingSpeed) // getTrackingSpeed 方法 + .def("Home", &Telescope::Home) // Home 方法 + .def("isAtHome", &Telescope::isAtHome) // isAtHome 方法 + .def("setHomePosition", + &Telescope::setHomePosition) // setHomePosition 方法 + .def("isHomeAvailable", + &Telescope::isHomeAvailable) // isHomeAvailable 方法 + .def("Park", &Telescope::Park) // Park 方法 + .def("Unpark", &Telescope::Unpark) // Unpark 方法 + .def("isAtPark", &Telescope::isAtPark) // isAtPark 方法 + .def("setParkPosition", + &Telescope::setParkPosition) // setParkPosition 方法 + .def("isParkAvailable", + &Telescope::isParkAvailable); // isParkAvailable 方法 + + // 继承 AtomDriver 的 Focuser 类 + py::class_(m, "Focuser") + .def(py::init()) // 默认构造函数 + .def("connect", &Focuser::connect) // connect 方法 + .def("disconnect", &Focuser::disconnect) // disconnect 方法 + .def("reconnect", &Focuser::reconnect) // reconnect 方法 + .def("isConnected", &Focuser::isConnected) // isConnected 方法 + .def("moveTo", &Focuser::moveTo) // moveTo 方法 + .def("moveToAbsolute", &Focuser::moveToAbsolute) // moveToAbsolute 方法 + .def("moveStep", &Focuser::moveStep) // moveStep 方法 + .def("moveStepAbsolute", + &Focuser::moveStepAbsolute) // moveStepAbsolute 方法 + .def("AbortMove", &Focuser::AbortMove) // AbortMove 方法 + .def("getMaxPosition", &Focuser::getMaxPosition) // getMaxPosition 方法 + .def("setMaxPosition", &Focuser::setMaxPosition) // setMaxPosition 方法 + .def("isGetTemperatureAvailable", + &Focuser::isGetTemperatureAvailable) // isGetTemperatureAvailable + // 方法 + .def("getTemperature", &Focuser::getTemperature) // getTemperature 方法 + .def("isAbsoluteMoveAvailable", + &Focuser::isAbsoluteMoveAvailable) // isAbsoluteMoveAvailable 方法 + .def("isManualMoveAvailable", + &Focuser::isManualMoveAvailable) // isManualMoveAvailable 方法 + .def("getCurrentPosition", + &Focuser::getCurrentPosition) // getCurrentPosition 方法 + .def("haveBacklash", &Focuser::haveBacklash) // haveBacklash 方法 + .def("setBacklash", &Focuser::setBacklash); // setBacklash 方法 + + py::class_(m, "Filterwheel") + .def(py::init()) // 默认构造函数 + .def("connect", &Filterwheel::connect) // connect 方法 + .def("disconnect", &Filterwheel::disconnect) // disconnect 方法 + .def("reconnect", &Filterwheel::reconnect) // reconnect 方法 + .def("isConnected", &Filterwheel::isConnected) // isConnected 方法 + .def("moveTo", &Filterwheel::moveTo) // moveTo 方法 + .def("getCurrentPosition", + &Filterwheel::getCurrentPosition); // getCurrentPosition 方法 + + py::class_(m, "PID") + .def(py::init()) // 构造函数 + .def("setIntegratorLimits", + &PID::setIntegratorLimits) // setIntegratorLimits 方法 + .def("setTau", &PID::setTau) // setTau 方法 + .def("calculate", &PID::calculate) // calculate 方法 + .def("propotionalTerm", &PID::propotionalTerm) // propotionalTerm 方法 + .def("integralTerm", &PID::integralTerm) // integralTerm 方法 + .def("derivativeTerm", &PID::derivativeTerm); // derivativeTerm 方法 + + py::class_(m, "Solver") + .def(py::init()) // 构造函数 + .def("solveImage", &Solver::solveImage) // solveImage 方法 + .def("getSolveResult", &Solver::getSolveResult) // getSolveResult 方法 + .def("getSolveStatus", &Solver::getSolveStatus) // getSolveStatus 方法 + .def("setSolveParams", &Solver::setSolveParams) // setSolveParams 方法 + .def("getSolveParams", &Solver::getSolveParams); // getSolveParams 方法 +} diff --git a/src/atom/driver/camera.cpp b/src/atom/driver/camera.cpp index 1b8707a6..257badc7 100644 --- a/src/atom/driver/camera.cpp +++ b/src/atom/driver/camera.cpp @@ -24,6 +24,22 @@ AtomCamera::~AtomCamera() {} bool AtomCamera::initialize() { AtomDriver::initialize(); + + // Primary CCD Exposure + registerVariable("CCD_EXPOSURE_VALUE", 1.0, "Duration (s)"); + registerVariableRanges("CCD_EXPOSURE_VALUE", 0.01, 3600); + registerVariable("CCD_EXPOSURE_STATUS", false, "Status"); + + // Primary CCD Abort + registerVariable("CCD_ABORT_EXPOSURE", false, "Abort"); + + registerVariable("CCD_VIDEO_STATUS", false, "Status"); + + registerVariable("CCD_GAIN", 20, "gain"); + registerVariableRanges("CCD_GAIN", 0, 256); + registerVariable("CCD_OFFSET", 0, "offset"); + registerVariableRanges("CCD_OFFSET", 0, 256); + // CCD Temperature registerVariable("CCD_TEMPERATURE_VALUE", 0.0, "Temperature (C)"); registerVariableRanges("CCD_TEMPERATURE_VALUE", -50.0, 50.0); @@ -41,7 +57,7 @@ bool AtomCamera::initialize() { // Primary CCD Region-Of-Interest (ROI) registerVariable("X", 0.0, "Left"); registerVariable("Y", 0.0, "Top"); - registerVariable("WIDTH", 0.0 ,"Width"); + registerVariable("WIDTH", 0.0, "Width"); registerVariable("HEIGHT", 0.0, "Height"); // Primary CCD Frame Type @@ -50,13 +66,6 @@ bool AtomCamera::initialize() { registerVariable("FRAME_DARK", false, "Dark"); registerVariable("FRAME_FLAT", false, "Flat"); - // Primary CCD Exposure - registerVariable("CCD_EXPOSURE_VALUE", 1.0 ,"Duration (s)"); - registerVariableRanges("CCD_EXPOSURE_VALUE", 0.01, 3600); - - // Primary CCD Abort - registerVariable("CCD_ABORT_EXPOSURE", false, "Abort"); - // Primary CCD Binning registerVariable("HOR_BIN", 1, "X"); registerVariableRanges("HOR_BIN", 1, 4); @@ -71,9 +80,9 @@ bool AtomCamera::initialize() { registerVariable("CCD_PIXEL_SIZE", 0, "Pixel size (um)"); registerVariableRanges("CCD_PIXEL_SIZE", 1, 40); - registerVariable("CCD_PIXEL_SIZE_X", 0.0 ,"Pixel size X"); + registerVariable("CCD_PIXEL_SIZE_X", 0.0, "Pixel size X"); registerVariableRanges("CCD_PIXEL_SIZE_X", 0, 40); - registerVariable("CCD_PIXEL_SIZE_Y", 0.0 ,"Pixel size Y"); + registerVariable("CCD_PIXEL_SIZE_Y", 0.0, "Pixel size Y"); registerVariableRanges("CCD_PIXEL_SIZE_Y", 0, 40); registerVariable("CCD_BITSPERPIXEL", 0, "Bits per pixel"); registerVariableRanges("CCD_BITSPERPIXEL", 8, 64); @@ -127,6 +136,30 @@ bool AtomCamera::initialize() { registerFunc("getExposureStatus", &AtomCamera::_getExposureStatus, this); registerFunc("getExposureResult", &AtomCamera::_getExposureResult, this); + registerFunc("startVideo", &AtomCamera::_startVideo, this); + registerFunc("stopVideo", &AtomCamera::_stopVideo, this); + registerFunc("getVideoStatus", &AtomCamera::_getVideoStatus, this); + registerFunc("getVideoResult", &AtomCamera::_getVideoResult, this); + + registerFunc("startCooling", &AtomCamera::_startCooling, this); + registerFunc("stopCooling", &AtomCamera::_stopCooling, this); + registerFunc("getCoolingStatus", &AtomCamera::_getCoolingStatus, this); + registerFunc("getCoolingPower", &AtomCamera::_getCoolingPower, this); + registerFunc("setCoolingPower", &AtomCamera::_setCoolingPower, this); + registerFunc("getTemperature", &AtomCamera::_getTemperature, this); + registerFunc("setTemperature", &AtomCamera::_setTemperature, this); + + registerFunc("getGain", &AtomCamera::_getGain, this); + registerFunc("setGain", &AtomCamera::_setGain, this); + registerFunc("getOffset", &AtomCamera::_getOffset, this); + registerFunc("setOffset", &AtomCamera::_setOffset, this); + registerFunc("getISO", &AtomCamera::_getISO, this); + registerFunc("setISO", &AtomCamera::_setISO, this); + registerFunc("getBinning", &AtomCamera::_getBinning, this); + registerFunc("setBinning", &AtomCamera::_setBinning, this); + + registerFunc("getFrame", &AtomCamera::_getFrame, this); + registerFunc("setFrame", &AtomCamera::_setFrame, this); return true; } @@ -194,7 +227,8 @@ bool AtomCamera::isISOAvailable() { return true; } bool AtomCamera::getFrame() { return true; } -bool AtomCamera::setFrame(const int &x, const int &y, const int &w, const int &h) { +bool AtomCamera::setFrame(const int &x, const int &y, const int &w, + const int &h) { return true; } @@ -280,6 +314,10 @@ json AtomCamera::_getISO(const json ¶ms) { return {}; } json AtomCamera::_setISO(const json ¶ms) { return {}; } +json AtomCamera::_getBinning(const json ¶ms) { return {}; } + +json AtomCamera::_setBinning(const json ¶ms) { return {}; } + json AtomCamera::_getFrame(const json ¶ms) { return {}; } json AtomCamera::_setFrame(const json ¶ms) { return {}; } \ No newline at end of file diff --git a/src/atom/driver/camera.hpp b/src/atom/driver/camera.hpp index a34c44df..673df1a1 100644 --- a/src/atom/driver/camera.hpp +++ b/src/atom/driver/camera.hpp @@ -192,4 +192,8 @@ class AtomCamera : public AtomDriver { json _getFrame(const json ¶ms); json _setFrame(const json ¶ms); + + json _getBinning(const json ¶ms); + + json _setBinning(const json ¶ms); }; diff --git a/src/atom/driver/device.cpp b/src/atom/driver/device.cpp index daa22a6c..a92e9eb2 100644 --- a/src/atom/driver/device.cpp +++ b/src/atom/driver/device.cpp @@ -34,8 +34,20 @@ bool AtomDriver::initialize() { SharedComponent::initialize(); Atom::Utils::UUIDGenerator generator; m_uuid = generator.generateUUID(); - setVariable("DEVICE_UUID", m_uuid); - setVariable("DEVICE_NAME", m_name); + + registerVariable("DEVICE_NAME", m_name); + registerVariable("DEVICE_ID", 0, "Device ID"); + registerVariable("DEVICE_UUID", m_uuid); + registerVariable("DEVICE_TYPE", "", "Device Type"); + registerVariable("DEVICE_VERSION", "1.0.0", "Device Version"); + registerVariable("DEVICE_MANUFACTURER", "Atom", "Device Manufacturer"); + registerVariable("DEVICE_MODEL", "Atom", "Device Model"); + registerVariable("DEVICE_SERIAL_NUMBER", "00000000", + "Device Serial Number"); + + registerVariable("DEVICE_CONNECTED", false, "Device Connected"); + registerVariable("DEVICE_CONNECTION_STATUS", "Disconnected", + "Device Connection Status"); registerFunc("connect", &AtomDriver::Connect, this); registerFunc("disconnect", &AtomDriver::Disconnect, this); diff --git a/src/atom/driver/marco.hpp b/src/atom/driver/marco.hpp index 9fd52401..5c86a690 100644 --- a/src/atom/driver/marco.hpp +++ b/src/atom/driver/marco.hpp @@ -11,4 +11,20 @@ "Missing value"); \ } +#define GET_STR_VARIABLE(x) \ + auto _ #x = getVariable(#x); \ + if (!_ #x.has_value()) { \ + LOG_F(ERROR, "{} {}: Missing {} value", getName(), __func__, #x); \ + return false; \ + } \ + auto x = _ #x.value(); + +#define GET_INT_VARIABLE(x) \ + auto _ #x = getVariable(#x); \ + if (!_ #x.has_value()) { \ + LOG_F(ERROR, "{} {}: Missing {} value", getName(), __func__, #x); \ + return false; \ + } \ + auto x = _ #x.value(); + #endif \ No newline at end of file diff --git a/src/atom/driver/meson.build b/src/atom/driver/meson.build new file mode 100644 index 00000000..0e00cc15 --- /dev/null +++ b/src/atom/driver/meson.build @@ -0,0 +1,66 @@ +project('atom-driver', 'cpp') + +# Set minimum Meson version required +meson_version = '>=0.59.0' + +# Define sources and headers +sources = [ + 'fitskeyword.cpp', + 'device.cpp', + 'camera.cpp', + 'telescope.cpp', + 'focuser.cpp', + 'filterwheel.cpp', + 'guider.cpp', + 'solver.cpp' +] +headers = [ + 'fitskeyword.h', + 'device.hpp', + 'camera.hpp', + 'telescope.hpp', + 'focuser.hpp', + 'filterwheel.hpp', + 'guider.hpp', + 'solver.hpp' +] + +# Check if fmt library is available +has_fmt = find_library('fmt') + +# Create object library +atom_driver_obj = library('atom_driver_obj', + sources + headers, + cpp_std: c_std +) + +# Link with necessary libraries +atom_driver_obj_link_libs = ['atom-component', 'atom-server'] +atom_driver_obj.link_with(atom_driver_obj_link_libs) + +if not has_fmt.found() + atom_driver_obj_link_libs += ['fmt'] +endif + +if host_machine.system() == 'linux' + atom_driver_obj_link_libs += ['uuid'] +endif + +atom_driver_obj.link_with(atom_driver_obj_link_libs) + +# Create shared library +atom_driver_shared = shared_library('atom_driver_shared', atom_driver_obj) + +# Set version properties +version = '1.0.0' # You should define your version here +soversion = '1' + +atom_driver_shared.version = version +atom_driver_shared.soversion = soversion +atom_driver_shared.basename = 'atom-driver' + +# Install target +install_targets(atom_driver_shared) + +# Create executable +shared_driver_test = executable('shared_driver_test', 'main.cpp', link_with : [atom_driver_shared]) diff --git a/src/atom/error/_script.hpp b/src/atom/error/_script.hpp new file mode 100644 index 00000000..87bd585f --- /dev/null +++ b/src/atom/error/_script.hpp @@ -0,0 +1,131 @@ +/* + * _script.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-23 + +Description: Carbon binding for Atom-ERROR + +**************************************************/ + +#ifndef ATOM_ERROR_SCRIPT_HPP +#define ATOM_ERROR_SCRIPT_HPP + +#include "carbon/carbon.hpp" + +#include "error_code.hpp" +#include "error_stack.hpp" +#include "exception.hpp" + +using namespace Atom::Error; + +namespace Atom::_Script::Error { +/** + * Adds the String Methods to the given Carbon module. + */ +Carbon::ModulePtr bootstrap( + Carbon::ModulePtr m = std::make_shared()) { + m->add( + Carbon::type_conversion([](const LIError &error) { + switch (error) { + case LIError::None: + return "None"; + case LIError::NotFound: + return "NotFound"; + case LIError::OpenError: + return "OpenError"; + case LIError::AccessDenied: + return "AccessDenied"; + case LIError::ReadError: + return "ReadError"; + case LIError::WriteError: + return "WriteError"; + case LIError::PermissionDenied: + return "PermissionDenied"; + case LIError::ParseError: + return "ParseError"; + case LIError::InvalidPath: + return "InvalidPath"; + case LIError::FileExists: + return "FileExists"; + case LIError::DirectoryNotEmpty: + return "DirectoryNotEmpty"; + case LIError::TooManyOpenFiles: + return "TooManyOpenFiles"; + case LIError::DiskFull: + return "DiskFull"; + case LIError::LoadError: + return "LoadError"; + case LIError::UnLoadError: + return "UnLoadError"; + } + })); + m->add(Carbon::type_conversion( + [](const std::string &str) { + if (str == "None") + return LIError::None; + else if (str == "NotFound") + return LIError::NotFound; + else if (str == "OpenError") + return LIError::OpenError; + else if (str == "AccessDenied") + return LIError::AccessDenied; + else if (str == "ReadError") + return LIError::ReadError; + else if (str == "WriteError") + return LIError::WriteError; + else if (str == "PermissionDenied") + return LIError::PermissionDenied; + else if (str == "ParseError") + return LIError::ParseError; + else if (str == "InvalidPath") + return LIError::InvalidPath; + else if (str == "FileExists") + return LIError::FileExists; + else if (str == "DirectoryNotEmpty") + return LIError::DirectoryNotEmpty; + else if (str == "TooManyOpenFiles") + return LIError::TooManyOpenFiles; + else if (str == "DiskFull") + return LIError::DiskFull; + else if (str == "LoadError") + return LIError::LoadError; + else if (str == "UnloadError") + return LIError::UnLoadError; + else + throw std::runtime_error("Invalid LIError string: " + str); + })); + + m->add(user_type(), "ErrorInfo"); + m->add(Carbon::fun(&ErrorInfo::errorMessage), "errorMessage"); + m->add(Carbon::fun(&ErrorInfo::moduleName), "moduleName"); + m->add(Carbon::fun(&ErrorInfo::functionName), "functionName"); + m->add(Carbon::fun(&ErrorInfo::line), "line"); + m->add(Carbon::fun(&ErrorInfo::fileName), "fileName"); + m->add(Carbon::fun(&ErrorInfo::timestamp), "timestamp"); + m->add(Carbon::fun(&ErrorInfo::uuid), "uuid"); + + m->add(user_type(), "ErrorStack"); + m->add(Carbon::fun(&ErrorStack::createShared), "create_error_stack"); + m->add(Carbon::fun(&ErrorStack::createUnique), "create_unique_error_stack"); + m->add(Carbon::fun(&ErrorStack::insertError), "insert_error"); + m->add(Carbon::fun(&ErrorStack::getFilteredErrorsByModule), + "get_filtered_errors_by_module"); + m->add(Carbon::fun(&ErrorStack::getCompressedErrors), + "get_compressed_errors"); + m->add(Carbon::fun(&ErrorStack::setFilteredModules), + "set_filtered_modules"); + m->add(Carbon::fun(&ErrorStack::clearFilteredModules), + "clear_filtered_modules"); + m->add(Carbon::fun(&ErrorStack::printFilteredErrorStack), + "print_filtered_error_stack"); + + return m; +} +} // namespace Atom::_Script::Error + +#endif \ No newline at end of file diff --git a/src/atom/error/error_code.hpp b/src/atom/error/error_code.hpp index 6b6100ac..8f8751a0 100644 --- a/src/atom/error/error_code.hpp +++ b/src/atom/error/error_code.hpp @@ -18,7 +18,7 @@ Description: All of the error code enum class LIError { None, // 无错误 NotFound, // 文件未找到 - OepnError, // 无法打开 + OpenError, // 无法打开 AccessDenied, // 访问被拒绝 ReadError, // 读取错误 WriteError, // 写入错误 diff --git a/src/atom/error/exception.cpp b/src/atom/error/exception.cpp new file mode 100644 index 00000000..5f19b1f3 --- /dev/null +++ b/src/atom/error/exception.cpp @@ -0,0 +1,136 @@ +/* + * exception.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-11-10 + +Description: Better Exception Library + +**************************************************/ + +#include "exception.hpp" + +#include +#include +#include +#include +#include +#include +#include + + +#ifdef _WIN32 +#include +#include +#pragma comment(lib, "dbghelp.lib") +#elif defined(__APPLE__) || defined(__linux__) +#include +#include +#include +#endif + +namespace Atom::Error +{ +const char* Exception::what() const noexcept { + if (full_message_.empty()) { + std::ostringstream oss; + oss << "[" << getCurrentTime() << "] "; + oss << "Exception at " << file_ << ":" << line_ << " in " << func_ + << "()"; + oss << " (thread " << thread_id_ << ")"; + oss << "\n\tMessage: " << message_; + oss << "\n\tStack trace:\n" << getStackTrace(); + full_message_ = oss.str(); + } + return full_message_.c_str(); +} + +const std::string& Exception::getFile() const { return file_; } +int Exception::getLine() const { return line_; } +const std::string& Exception::getFunction() const { return func_; } +const std::string& Exception::getMessage() const { return message_; } +std::thread::id Exception::getThreadId() const { return thread_id_; } + +std::string Exception::getCurrentTime() const { + auto now = std::chrono::system_clock::now(); + auto time = std::chrono::system_clock::to_time_t(now); + char buffer[80]; + std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", + std::localtime(&time)); + return buffer; +} + +std::string Exception::getStackTrace() const { + std::ostringstream oss; + +#ifdef _WIN32 + const int max_frames = 64; + void* frames[max_frames]; + HANDLE process = GetCurrentProcess(); + SymInitialize(process, NULL, TRUE); + + WORD num_frames = CaptureStackBackTrace(0, max_frames, frames, NULL); + std::unique_ptr symbol( + (SYMBOL_INFO*)std::calloc(sizeof(SYMBOL_INFO) + 256, 1)); + symbol->MaxNameLen = 255; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + + for (int i = 0; i < num_frames; ++i) { + SymFromAddr(process, (DWORD64)(frames[i]), 0, symbol.get()); + oss << "\t\t" << symbol->Name << " - 0x" << std::hex << symbol->Address + << "\n"; + } +#elif defined(__APPLE__) || defined(__linux__) + const int max_frames = 64; + void* frames[max_frames]; + int num_frames = backtrace(frames, max_frames); + std::unique_ptr symbols(backtrace_symbols(frames, num_frames)); + + for (int i = 0; i < num_frames; ++i) { + char* symbol_name = nullptr; + char* offset_begin = nullptr; + char* offset_end = nullptr; + + for (char* p = symbols.get()[i]; *p; ++p) { + if (*p == '(') { + symbol_name = p; + } else if (*p == '+') { + offset_begin = p; + } else if (*p == ')') { + offset_end = p; + break; + } + } + + if (symbol_name && offset_begin && offset_end && + symbol_name < offset_begin) { + *symbol_name++ = '\0'; + *offset_begin++ = '\0'; + *offset_end = '\0'; + + int status = 0; + char* demangled_name = + abi::__cxa_demangle(symbol_name, nullptr, 0, &status); + if (status == 0) { + oss << "\t\t" << demangled_name << " +" << offset_begin + << offset_end << "\n"; + std::free(demangled_name); + } else { + oss << "\t\t" << symbol_name << " +" << offset_begin + << offset_end << "\n"; + } + } else { + oss << "\t\t" << symbols.get()[i] << "\n"; + } + } +#else + oss << "\t\tStack trace not available on this platform.\n"; +#endif + + return oss.str(); +} +} diff --git a/src/atom/error/exception.hpp b/src/atom/error/exception.hpp index a6512647..a6e35245 100644 --- a/src/atom/error/exception.hpp +++ b/src/atom/error/exception.hpp @@ -15,143 +15,235 @@ Description: Better Exception Library #ifndef ATOM_ERROR_EXCEPTION_HPP #define ATOM_ERROR_EXCEPTION_HPP +#include +#include +#include #include #include +#include namespace Atom::Error { + +/** + * @brief Custom exception class with detailed information about the error. + */ +class Exception : public std::exception { +public: + /** + * @brief Constructs an Exception object. + * @param file The file where the exception occurred. + * @param line The line number in the file where the exception occurred. + * @param func The function where the exception occurred. + * @param args Additional arguments to provide context for the exception. + */ + template + Exception(const char *file, int line, const char *func, Args &&...args) + : file_(file), + line_(line), + func_(func), + thread_id_(std::this_thread::get_id()) { + std::ostringstream oss; + ((oss << std::forward(args)), ...); + message_ = oss.str(); + } + + /** + * @brief Returns a C-style string describing the exception. + * @return A pointer to a string describing the exception. + */ + const char *what() const noexcept override; + + /** + * @brief Gets the file where the exception occurred. + * @return The file where the exception occurred. + */ + const std::string &getFile() const; + + /** + * @brief Gets the line number where the exception occurred. + * @return The line number where the exception occurred. + */ + int getLine() const; + + /** + * @brief Gets the function where the exception occurred. + * @return The function where the exception occurred. + */ + const std::string &getFunction() const; + + /** + * @brief Gets the message associated with the exception. + * @return The message associated with the exception. + */ + const std::string &getMessage() const; + + /** + * @brief Gets the ID of the thread where the exception occurred. + * @return The ID of the thread where the exception occurred. + */ + std::thread::id getThreadId() const; + +private: + /** + * @brief Gets the current time as a formatted string. + * @return The current time as a formatted string. + */ + std::string getCurrentTime() const; + + /** + * @brief Gets the stack trace when the exception occurred. + * @return The stack trace when the exception occurred. + */ + std::string getStackTrace() const; + + std::string file_; /**< The file where the exception occurred. */ + int line_; /**< The line number in the file where the exception occurred. */ + std::string func_; /**< The function where the exception occurred. */ + std::string message_; /**< The message associated with the exception. */ + mutable std::string + full_message_; /**< The full message including additional context. */ + std::thread::id + thread_id_; /**< The ID of the thread where the exception occurred. */ +}; + +#define THROW_EXCEPTION(...) \ + throw Exception(__FILE__, __LINE__, __func__, __VA_ARGS__) + class ObjectAlreadyExist : public std::logic_error { public: explicit ObjectAlreadyExist(const std::string &msg) - : std::logic_error(msg){}; + : std::logic_error(msg) {}; }; class ObjectNotExist : public std::logic_error { public: - explicit ObjectNotExist(const std::string &msg) : std::logic_error(msg){}; + explicit ObjectNotExist(const std::string &msg) : std::logic_error(msg) {}; }; class ObjectUninitialized : public std::logic_error { public: explicit ObjectUninitialized(const std::string &msg) - : std::logic_error(msg){}; + : std::logic_error(msg) {}; }; class Uninitialization : public std::logic_error { public: - explicit Uninitialization(const std::string &msg) : std::logic_error(msg){}; + explicit Uninitialization(const std::string &msg) + : std::logic_error(msg) {}; }; class WrongArgument : public std::logic_error { public: - explicit WrongArgument(const std::string &msg) : std::logic_error(msg){}; + explicit WrongArgument(const std::string &msg) : std::logic_error(msg) {}; }; class InvalidArgument : public std::logic_error { public: - explicit InvalidArgument(const std::string &msg) : std::logic_error(msg){}; + explicit InvalidArgument(const std::string &msg) : std::logic_error(msg) {}; }; class MissingArgument : public std::logic_error { public: - explicit MissingArgument(const std::string &msg) : std::logic_error(msg){}; + explicit MissingArgument(const std::string &msg) : std::logic_error(msg) {}; }; class UnlawfulOperation : public std::logic_error { public: explicit UnlawfulOperation(const std::string &msg) - : std::logic_error(msg){}; + : std::logic_error(msg) {}; }; class Unkown : public std::logic_error { public: - explicit Unkown(const std::string &msg) : std::logic_error(msg){}; + explicit Unkown(const std::string &msg) : std::logic_error(msg) {}; }; class SystemCollapse : public std::runtime_error { public: - explicit SystemCollapse(const std::string &msg) : std::runtime_error(msg){}; + explicit SystemCollapse(const std::string &msg) + : std::runtime_error(msg) {}; }; class NullPointer : public std::logic_error { public: - explicit NullPointer(const std::string &msg) : std::logic_error(msg){}; + explicit NullPointer(const std::string &msg) : std::logic_error(msg) {}; }; class NotFound : public std::logic_error { public: - explicit NotFound(const std::string &msg) : std::logic_error(msg){}; + explicit NotFound(const std::string &msg) : std::logic_error(msg) {}; }; class FileNotFound : public std::logic_error { public: - explicit FileNotFound(const std::string &msg) : std::logic_error(msg){}; + explicit FileNotFound(const std::string &msg) : std::logic_error(msg) {}; }; class FileNotReadable : public std::logic_error { public: - explicit FileNotReadable(const std::string &msg) : std::logic_error(msg){}; + explicit FileNotReadable(const std::string &msg) : std::logic_error(msg) {}; }; class FileNotWritable : public std::logic_error { public: - explicit FileNotWritable(const std::string &msg) : std::logic_error(msg){}; + explicit FileNotWritable(const std::string &msg) : std::logic_error(msg) {}; }; class FileUnknown : public std::logic_error { public: - explicit FileUnknown(const std::string &msg) : std::logic_error(msg){}; + explicit FileUnknown(const std::string &msg) : std::logic_error(msg) {}; }; class Conflict : public std::logic_error { public: - explicit Conflict(const std::string &msg) : std::logic_error(msg){}; + explicit Conflict(const std::string &msg) : std::logic_error(msg) {}; }; class FailToLoadDll : public std::runtime_error { public: - explicit FailToLoadDll(const std::string &msg) : std::runtime_error(msg){}; + explicit FailToLoadDll(const std::string &msg) : std::runtime_error(msg) {}; }; class FailToUnloadDll : public std::runtime_error { public: explicit FailToUnloadDll(const std::string &msg) - : std::runtime_error(msg){}; + : std::runtime_error(msg) {}; }; class FailToGetFunction : public std::runtime_error { public: explicit FailToGetFunction(const std::string &msg) - : std::runtime_error(msg){}; + : std::runtime_error(msg) {}; }; class FailToCreateObject : public std::runtime_error { public: explicit FailToCreateObject(const std::string &msg) - : std::runtime_error(msg){}; + : std::runtime_error(msg) {}; }; class FailToDestroyObject : public std::runtime_error { public: explicit FailToDestroyObject(const std::string &msg) - : std::runtime_error(msg){}; + : std::runtime_error(msg) {}; }; class FailToCallFunction : public std::runtime_error { public: explicit FailToCallFunction(const std::string &msg) - : std::runtime_error(msg){}; + : std::runtime_error(msg) {}; }; class FailToCallMemberFunction : public std::runtime_error { public: explicit FailToCallMemberFunction(const std::string &msg) - : std::runtime_error(msg){}; + : std::runtime_error(msg) {}; }; class FailToCallStaticFunction : public std::runtime_error { public: explicit FailToCallStaticFunction(const std::string &msg) - : std::runtime_error(msg){}; + : std::runtime_error(msg) {}; }; } // namespace Atom::Error diff --git a/src/atom/experiment/CMakeLists.txt b/src/atom/experiment/CMakeLists.txt index 8ee0c214..adc20f41 100644 --- a/src/atom/experiment/CMakeLists.txt +++ b/src/atom/experiment/CMakeLists.txt @@ -11,18 +11,33 @@ project(atom-experiment C CXX) # Sources list(APPEND ${PROJECT_NAME}_SOURCES + platform.cpp string.cpp ) # Headers list(APPEND ${PROJECT_NAME}_HEADERS any.hpp + anyutils.hpp + bind_first.hpp + callable.hpp decorate.hpp + flatmap.hpp + func_traits.hpp invoke.hpp list.hpp + memory.hpp noncopyable.hpp + object.hpp optional.hpp + platform.hpp + ranges.hpp + short_alloc.hpp + stack_vector.hpp + static_vector.hpp string.hpp + to_string.hpp + type_info.hpp ) # Build Object Library diff --git a/src/atom/experiment/any.hpp b/src/atom/experiment/any.hpp index 14430009..621f3043 100644 --- a/src/atom/experiment/any.hpp +++ b/src/atom/experiment/any.hpp @@ -15,7 +15,9 @@ Description: A simple implementation of any type. #ifndef ATOM_EXPERIMENT_ANY_HPP #define ATOM_EXPERIMENT_ANY_HPP +#include #include +#include #include #include #include @@ -27,27 +29,69 @@ template concept Derived = std::is_base_of_v, Any>; class Any { +private: + static constexpr std::size_t BufferSize = 64; + static constexpr std::size_t BufferAlign = alignof(std::max_align_t); + + using Storage = std::aligned_storage_t; + public: - Any() : ptr(nullptr) {} + constexpr Any() noexcept : ptr(nullptr) {} template - requires(!Derived) - Any(T &&value) : ptr(new holder>(std::forward(value))) {} + requires(!Derived && + !(sizeof(T) <= BufferSize && std::is_trivially_copyable_v)) + constexpr Any(T &&value) + : ptr(new holder>(std::forward(value))) {} - Any(const Any &other) : ptr(other.ptr ? other.ptr->clone() : nullptr) {} + template + requires(!Derived && sizeof(T) <= BufferSize && + std::is_trivially_copyable_v) + constexpr Any(T &&value) { + new (&storage) holder>(std::forward(value)); + small = true; + } + + constexpr Any(const Any &other) + : ptr(other.small ? nullptr + : (other.ptr ? other.ptr->clone() : nullptr)), + small(other.small) { + if (other.small) { + std::copy_n(reinterpret_cast(&other.storage), + sizeof(Storage), reinterpret_cast(&storage)); + } + } - Any(Any &&other) noexcept : ptr(std::exchange(other.ptr, nullptr)) {} + constexpr Any(Any &&other) noexcept : small(other.small) { + if (other.small) { + std::memcpy(&storage, &other.storage, sizeof(Storage)); + } else { + ptr = std::exchange(other.ptr, nullptr); + } + } - ~Any() { delete ptr; } + ~Any() { + if (small) { + // If the stored object has a non-trivial destructor, it needs to be + // called manually + if (!std::is_trivially_destructible_v) { + // Assuming 'holder' has a virtual destructor and a method to + // properly destroy the contained object... + reinterpret_cast(&storage)->~placeholder(); + } + } else { + delete ptr; + } + } - Any &operator=(const Any &other) { + constexpr Any &operator=(const Any &other) { if (this != &other) { Any(other).swap(*this); } return *this; } - Any &operator=(Any &&other) noexcept { + constexpr Any &operator=(Any &&other) noexcept { if (this != &other) { Any(std::move(other)).swap(*this); } @@ -56,14 +100,14 @@ class Any { template requires(!Derived) - Any &operator=(T &&value) { + constexpr Any &operator=(T &&value) { Any(std::forward(value)).swap(*this); return *this; } - bool empty() const { return !ptr; } + [[nodiscard]] constexpr bool empty() const noexcept { return !ptr; } - const std::type_info &type() const { + [[nodiscard]] constexpr const std::type_info &type() const noexcept { return ptr ? ptr->type() : typeid(void); } @@ -73,25 +117,30 @@ class Any { private: class placeholder { public: - virtual ~placeholder() {} - virtual const std::type_info &type() const = 0; - virtual placeholder *clone() const = 0; + virtual ~placeholder() = default; + [[nodiscard]] virtual const std::type_info &type() const noexcept = 0; + [[nodiscard]] virtual placeholder *clone() const = 0; virtual void swap(placeholder &other) = 0; + virtual void destroy() = 0; }; template class holder : public placeholder { public: - holder(T &&value) : held(std::move(value)) {} + constexpr holder(T &&value) : held(std::move(value)) {} - holder(const T &value) : held(value) {} + constexpr holder(const T &value) : held(value) {} - const std::type_info &type() const { return typeid(T); } + [[nodiscard]] const std::type_info &type() const noexcept override { + return typeid(T); + } - placeholder *clone() const { return new holder(held); } + [[nodiscard]] placeholder *clone() const override { + return new holder(held); + } - void swap(placeholder &other) { - if (holder *other_holder = dynamic_cast(&other)) { + void swap(placeholder &other) override { + if (auto other_holder = dynamic_cast(&other)) { std::swap(held, other_holder->held); } } @@ -99,9 +148,15 @@ class Any { T held; }; + union { + placeholder *ptr; + Storage storage; + }; + bool small = false; + placeholder *ptr; - void swap(Any &other) { std::swap(ptr, other.ptr); } + constexpr void swap(Any &other) noexcept { std::swap(ptr, other.ptr); } }; template diff --git a/src/atom/experiment/anyutils.hpp b/src/atom/experiment/anyutils.hpp index 865191ee..1028967d 100644 --- a/src/atom/experiment/anyutils.hpp +++ b/src/atom/experiment/anyutils.hpp @@ -34,19 +34,35 @@ template concept CanBeStringifiedToJson = requires(T t) { { toJson(t) } -> std::convertible_to; }; + +template +concept IsBuiltIn = + std::is_fundamental_v || std::is_same_v || + std::is_same_v || std::is_same_v; + +template +concept ContainerLike = requires(const Container &c) { + { c.begin() } -> std::input_iterator; + { c.end() } -> std::input_iterator; +}; + #endif template [[nodiscard]] std::string toString(const T &value, bool prettyPrint = false); -template -[[nodiscard]] std::string toString(const std::vector &vec, +template +[[nodiscard]] std::string toString(const Container &container, bool prettyPrint = false) { std::string result = "["; - for (const auto &item : vec) { - result += toString(item, prettyPrint) + ", "; + for (const auto &item : container) { + if constexpr (IsBuiltIn) { + result += toString(item, prettyPrint) + ", "; + } else { + result += "\"" + toString(item, prettyPrint) + "\", "; + } } - if (!vec.empty()) { + if (!container.empty()) { result.erase(result.length() - 2, 2); } result += "]"; @@ -78,6 +94,7 @@ template template [[nodiscard]] std::string toString(const T &value, bool prettyPrint) { if constexpr (std::is_same_v || + std::is_same_v || std::is_same_v || std::is_same_v) { return value; @@ -282,4 +299,4 @@ template return result; } -#endif +#endif \ No newline at end of file diff --git a/src/atom/experiment/bind_first.hpp b/src/atom/experiment/bind_first.hpp index 8012cf72..c5b67197 100644 --- a/src/atom/experiment/bind_first.hpp +++ b/src/atom/experiment/bind_first.hpp @@ -15,7 +15,9 @@ Description: An easy way to bind a function to a object #ifndef ATOM_EXPERIMENT_BIND_FIRST_HPP #define ATOM_EXPERIMENT_BIND_FIRST_HPP +#include #include +#include template constexpr T *get_pointer(T *t) noexcept { @@ -27,29 +29,55 @@ T *get_pointer(const std::reference_wrapper &t) noexcept { return &t.get(); } +template +concept invocable = std::is_invocable_v; + +template +concept nothrow_invocable = std::is_nothrow_invocable_v; + +// 判断函数对象是否可以用给定的参数调用 +template +constexpr bool is_invocable_v = invocable; + +// 判断函数对象是否可以用给定的参数调用,且不抛出异常 +template +constexpr bool is_nothrow_invocable_v = std::is_nothrow_invocable_v; + template -constexpr auto bind_first(Ret (*f)(P1, Param...), O &&o) { +constexpr auto bind_first(Ret (*f)(P1, Param...), O &&o) + requires invocable +{ return [f, o = std::forward(o)](Param... param) -> Ret { return f(o, std::forward(param)...); }; } template -constexpr auto bind_first(Ret (Class::*f)(Param...), O &&o) { +constexpr auto bind_first(Ret (Class::*f)(Param...), O &&o) + requires invocable +{ return [f, o = std::forward(o)](Param... param) -> Ret { return (get_pointer(o)->*f)(std::forward(param)...); }; } template -constexpr auto bind_first(Ret (Class::*f)(Param...) const, O &&o) { +constexpr auto bind_first(Ret (Class::*f)(Param...) const, O &&o) + requires invocable +{ return [f, o = std::forward(o)](Param... param) -> Ret { - return (get_pointer(o)->*f)(std::forward(param)...); + if constexpr (std::is_pointer_v>) { + return (o->*f)(std::forward(param)...); + } else { + return (o.*f)(std::forward(param)...); + } }; } template -auto bind_first(const std::function &f, O &&o) { +auto bind_first(const std::function &f, O &&o) + requires invocable, O, Param...> +{ return [f, o = std::forward(o)](Param... param) -> Ret { return f(o, std::forward(param)...); }; @@ -58,15 +86,29 @@ auto bind_first(const std::function &f, O &&o) { template constexpr auto bind_first(const F &fo, O &&o, - Ret (Class::*f)(P1, Param...) const) { + Ret (Class::*f)(P1, Param...) const) + requires invocable +{ return [fo, o = std::forward(o), f](Param... param) -> Ret { return (fo.*f)(o, std::forward(param)...); }; } template -constexpr auto bind_first(const F &f, O &&o) { +constexpr auto bind_first(const F &f, O &&o) + requires invocable +{ return bind_first(f, std::forward(o), &F::operator()); } +template +constexpr auto bind_first(F &&f, O &&o) + requires std::invocable +{ + return [f = std::forward(f), + o = std::forward(o)](auto &&...param) -> decltype(auto) { + return std::invoke(f, o, std::forward(param)...); + }; +} + #endif // ATOM_EXPERIMENT_BIND_FIRST_HPP diff --git a/src/atom/experiment/callable.hpp b/src/atom/experiment/callable.hpp index 7b5c471c..f67c5519 100644 --- a/src/atom/experiment/callable.hpp +++ b/src/atom/experiment/callable.hpp @@ -1,23 +1,42 @@ +/* + * callable.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-3-1 + +Description: make a callabel object + +**************************************************/ + #ifndef ATOM_EXPERIMENT_CALLABLE_HPP #define ATOM_EXPERIMENT_CALLABLE_HPP #include +#include +#include +#include template struct Constructor { template - std::shared_ptr operator()(Inner &&...inner) const { + auto operator()(Inner &&...inner) const -> std::shared_ptr { return std::make_shared(std::forward(inner)...); } }; template struct Const_Caller { - explicit Const_Caller(Ret (Class::*t_func)(Param...) const) + explicit constexpr Const_Caller(Ret (Class::*t_func)(Param...) + const) noexcept : m_func(t_func) {} template - Ret operator()(const Class &o, Inner &&...inner) const { + constexpr auto operator()(const Class &o, Inner &&...inner) const + noexcept(noexcept((o.*m_func)(std::forward(inner)...))) -> Ret { return (o.*m_func)(std::forward(inner)...); } @@ -26,11 +45,13 @@ struct Const_Caller { template struct Fun_Caller { - explicit Fun_Caller(Ret (*t_func)(Param...)) : m_func(t_func) {} + explicit constexpr Fun_Caller(Ret (*t_func)(Param...)) noexcept + : m_func(t_func) {} template - Ret operator()(Inner &&...inner) const { - return (m_func)(std::forward(inner)...); + constexpr auto operator()(Inner &&...inner) const + noexcept(noexcept(m_func(std::forward(inner)...))) -> Ret { + return m_func(std::forward(inner)...); } Ret (*m_func)(Param...); @@ -38,10 +59,12 @@ struct Fun_Caller { template struct Caller { - explicit Caller(Ret (Class::*t_func)(Param...)) : m_func(t_func) {} + explicit constexpr Caller(Ret (Class::*t_func)(Param...)) noexcept + : m_func(t_func) {} template - Ret operator()(Class &o, Inner &&...inner) const { + constexpr auto operator()(Class &o, Inner &&...inner) const + noexcept(noexcept((o.*m_func)(std::forward(inner)...))) -> Ret { return (o.*m_func)(std::forward(inner)...); } @@ -49,34 +72,30 @@ struct Caller { }; template -struct Arity {}; - -template -struct Arity { - static const size_t arity = sizeof...(Params); -}; +struct Arity : std::integral_constant>::value> {}; template -struct Function_Signature {}; +struct Function_Signature; template struct Function_Signature { using Return_Type = Ret; - using Signature = Ret (*)(Params...); // 修改成这样 + using Signature = Ret (*)(Params...); }; template struct Function_Signature { using Return_Type = Ret; - using Signature = Ret (*)(Params...); // 修改成这样 + using Signature = Ret (*)(Params...); }; template struct Callable_Traits { - using Signature = - typename Function_Signature::Signature; - using Return_Type = - typename Function_Signature::Return_Type; + using Signature = typename Function_Signature< + decltype(&std::remove_reference_t::operator())>::Signature; + using Return_Type = typename Function_Signature< + decltype(&std::remove_reference_t::operator())>::Return_Type; }; -#endif +#endif // ATOM_EXPERIMENT_CALLABLE_HPP \ No newline at end of file diff --git a/src/atom/experiment/decorate.hpp b/src/atom/experiment/decorate.hpp index 85179419..3b4686d8 100644 --- a/src/atom/experiment/decorate.hpp +++ b/src/atom/experiment/decorate.hpp @@ -24,6 +24,22 @@ decorator. #include #include +template +class Switchable { +public: + Switchable(std::function f) : f(f) {} + + template + void switch_to(F new_f) { + f = new_f; + } + + auto operator()(Args... args) const -> R { return f(args...); } + +private: + std::function f; +}; + template struct decorator; @@ -46,25 +62,26 @@ struct decorator> { template > decorator with_hooks( - Before b, Callback c = Callback(), After a = [](long long) {}) const { - decorator copy(func); - copy.before = b; - copy.callback = c; - copy.after = a; + Before &&b, Callback &&c = CallbackType(), + After &&a = [](long long) {}) const { + decorator copy(std::move(func)); + copy.before = std::forward(b); + copy.callback = std::forward(c); + copy.after = std::forward(a); return copy; } template - auto operator()(TArgs&&... args) const -> decltype(func(args...)) { + auto operator()(TArgs &&...args) const { if (before) before(); auto start = std::chrono::high_resolution_clock::now(); - if constexpr (std::is_void::value) { - func(std::forward(args)...); + if constexpr (std::is_void_v) { + std::invoke(func, std::forward(args)...); if (callback) callback(); } else { - auto result = func(std::forward(args)...); + auto result = std::invoke(func, std::forward(args)...); if (callback) callback(result); auto end = std::chrono::high_resolution_clock::now(); @@ -82,11 +99,11 @@ struct decorator> { } template - auto operator()(T& obj, TArgs&&... args) const { + auto operator()(T &obj, TArgs &&...args) const { if (before) before(); auto start = std::chrono::high_resolution_clock::now(); - if constexpr (std::is_void::value) { + if constexpr (std::is_void_v) { std::invoke(func, obj, std::forward(args)...); if (callback) callback(); @@ -120,8 +137,8 @@ struct LoopDecorator : public decorator { using Base::Base; template - auto operator()(int loopCount, TArgs&&... args) const - -> decltype(this->func(args...)) { + auto operator()(int loopCount, + TArgs &&...args) const -> decltype(this->func(args...)) { decltype(this->func(args...)) result; for (int i = 0; i < loopCount; ++i) { result = Base::operator()(std::forward(args)...); @@ -130,7 +147,7 @@ struct LoopDecorator : public decorator { } template - auto operator()(T& obj, int loopCount, TArgs&&... args) const { + auto operator()(T &obj, int loopCount, TArgs &&...args) const { for (int i = 0; i < loopCount; ++i) { std::invoke(this->func, obj, std::forward(args)...); } @@ -148,8 +165,8 @@ struct ConditionCheckDecorator : public decorator { using Base::Base; // Inherit constructor template - auto operator()(ConditionFunc condition, TArgs&&... args) const - -> decltype(this->func(args...)) { + auto operator()(ConditionFunc condition, + TArgs &&...args) const -> decltype(this->func(args...)) { if (condition()) { return Base::operator()(std::forward(args)...); } else { @@ -166,15 +183,15 @@ ConditionCheckDecorator make_condition_check_decorator(FuncType f) { struct DecoratorError : public std::exception { std::string message; - explicit DecoratorError(const std::string& msg) : message(msg) {} - const char* what() const noexcept override { return message.c_str(); } + explicit DecoratorError(const std::string &msg) : message(msg) {} + const char *what() const noexcept override { return message.c_str(); } }; template class BaseDecorator { public: using FuncType = std::function; - virtual R operator()(FuncType func, Args&&... args) = 0; + virtual R operator()(FuncType func, Args &&...args) = 0; }; template @@ -190,7 +207,7 @@ class DecorateStepper { // 添加装饰器 template - void addDecorator(DArgs&&... args) { + void addDecorator(DArgs &&...args) { decorators.emplace_back( std::make_unique(std::forward(args)...)); } @@ -199,7 +216,7 @@ class DecorateStepper { try { FuncType currentFunction = baseFunction; - for (auto& decorator : decorators) { + for (auto &decorator : decorators) { currentFunction = [&, nextFunction = std::move(currentFunction)]( Args... innerArgs) -> R { @@ -209,10 +226,10 @@ class DecorateStepper { } return currentFunction(std::forward(args)...); - } catch (const DecoratorError& e) { + } catch (const DecoratorError &e) { return R(); // 返回默认值 } } }; -#endif +#endif \ No newline at end of file diff --git a/src/atom/experiment/flatmap.hpp b/src/atom/experiment/flatmap.hpp new file mode 100644 index 00000000..08e39323 --- /dev/null +++ b/src/atom/experiment/flatmap.hpp @@ -0,0 +1,177 @@ +/* + * flatmap.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-2 + +Description: QuickFlatMap for C++20 + +**************************************************/ + +#ifndef ATOM_EXPERIMENT_FLATMAP_HPP +#define ATOM_EXPERIMENT_FLATMAP_HPP + +#include +#include +#include +#include +#include + +template > +struct QuickFlatMap { + Comparator comparator; + + template + auto find(const Lookup &s) noexcept { + return std::find_if( + std::begin(data), std::end(data), + [&s, this](const auto &d) { return comparator(d.first, s); }); + } + + template + auto find(const Lookup &s) const noexcept { + return std::find_if( + std::cbegin(data), std::cend(data), + [&s, this](const auto &d) { return comparator(d.first, s); }); + } + + template + auto find(const Lookup &s, const std::size_t t_hint) const noexcept { + if constexpr (std::is_invocable_v) { + if (data.size() > t_hint && comparator(data[t_hint].first, s)) { + const auto begin = std::cbegin(data); + return std::next( + begin, static_cast>::difference_type>( + t_hint)); + } else { + return find(s); + } + } else { + // Fallback to the original implementation if Comparator is not + // invocable with const Key & and const Lookup & + if (data.size() > t_hint && comparator(s, data[t_hint].first)) { + const auto begin = std::cbegin(data); + return std::next( + begin, static_cast>::difference_type>( + t_hint)); + } else { + return find(s); + } + } + } + + auto size() const noexcept { return data.size(); } + + auto begin() const noexcept { return data.begin(); } + + auto end() const noexcept { return data.end(); } + + auto begin() noexcept { return data.begin(); } + + auto end() noexcept { return data.end(); } + + auto &back() noexcept { return data.back(); } + + const auto &back() const noexcept { return data.back(); } + + Value &operator[](const Key &s) { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + grow(); + return data.emplace_back(s, Value()).second; + } + } + + Value &at_index(const std::size_t idx) noexcept { return data[idx].second; } + + const Value &at_index(const std::size_t idx) const noexcept { + return data[idx].second; + } + + bool empty() const noexcept { return data.empty(); } + + template + void assign(Itr begin, Itr end) { + data.assign(begin, end); + } + + Value &at(const Key &s) { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + throw std::out_of_range("Unknown key: " + s); + } + } + + template + auto insert_or_assign(Key &&key, M &&m) { + if (auto itr = find(key); itr != data.end()) { + itr->second = std::forward(m); + return std::pair{itr, false}; + } else { + grow(); + return std::pair{ + data.emplace(data.end(), std::move(key), std::forward(m)), + true}; + } + } + + template + auto insert_or_assign(const Key &key, M &&m) { + if (auto itr = find(key); itr != data.end()) { + itr->second = std::forward(m); + return std::pair{itr, false}; + } else { + grow(); + return std::pair{data.emplace(data.end(), key, std::forward(m)), + true}; + } + } + + const Value &at(const Key &s) const { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + throw std::out_of_range("Unknown key: " + s); + } + } + + template + size_t count(const Lookup &s) const noexcept { + return (find(s) != data.end()) ? 1 : 0; + } + + std::vector> data; + + using value_type = std::pair; + using iterator = typename decltype(data)::iterator; + using const_iterator = typename decltype(data)::const_iterator; + + std::pair insert(value_type &&value) { + if (const auto itr = find(value.first); itr != data.end()) { + return std::pair{itr, false}; + } else { + grow(); + return std::pair{data.insert(data.end(), std::move(value)), true}; + } + } + + void grow() { + if ((data.capacity() - data.size()) == 0) { + data.reserve(data.size() + 2); + } + } +}; + +#endif \ No newline at end of file diff --git a/src/atom/experiment/func_traits.hpp b/src/atom/experiment/func_traits.hpp new file mode 100644 index 00000000..4b93d134 --- /dev/null +++ b/src/atom/experiment/func_traits.hpp @@ -0,0 +1,89 @@ +/* + * func_traits.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-2 + +Description: Func Traits for C++20 + +**************************************************/ + +#ifndef ATOM_EXPERIMENT_FUNC_TRAITS_HPP +#define ATOM_EXPERIMENT_FUNC_TRAITS_HPP + +#include + +template +struct FunctionTraits; + +template +struct FunctionTraits { + using return_type = Return; + using argument_types = std::tuple; + + static constexpr std::size_t arity = sizeof...(Args); + + template + struct argument { + static_assert(N < arity, "Invalid argument index."); + using type = typename std::tuple_element::type; + }; + + template + using argument_t = typename argument::type; + + static constexpr bool is_member_function = false; + static constexpr bool is_const_member_function = false; + static constexpr bool is_volatile_member_function = false; + static constexpr bool is_noexcept = false; +}; + +template +struct FunctionTraits : FunctionTraits {}; + +template +struct FunctionTraits + : FunctionTraits { + static constexpr bool is_member_function = true; + using class_type = Class; +}; + +template +struct FunctionTraits + : FunctionTraits { + static constexpr bool is_member_function = true; + static constexpr bool is_const_member_function = true; + using class_type = Class; +}; + +template +struct FunctionTraits + : FunctionTraits { + static constexpr bool is_member_function = true; + static constexpr bool is_volatile_member_function = true; + using class_type = Class; +}; + +template +struct FunctionTraits + : FunctionTraits { + static constexpr bool is_member_function = true; + static constexpr bool is_const_member_function = true; + static constexpr bool is_volatile_member_function = true; + using class_type = Class; +}; + +template +struct FunctionTraits + : FunctionTraits { + static constexpr bool is_noexcept = true; +}; + +template +struct FunctionTraits : FunctionTraits {}; + +#endif diff --git a/src/atom/experiment/invoke.hpp b/src/atom/experiment/invoke.hpp index 8804a858..83a938f0 100644 --- a/src/atom/experiment/invoke.hpp +++ b/src/atom/experiment/invoke.hpp @@ -15,27 +15,81 @@ Description: An implementation of invoke function. Support C++11 and C++17. #ifndef ATOM_EXPERIMENTAL_INVOKE_HPP #define ATOM_EXPERIMENTAL_INVOKE_HPP +#include #include +#include #include #include #if __cplusplus >= 201703L #include +#include template -using is_invocable_with_args = std::is_invocable; +concept Invocable = std::is_invocable_v, std::decay_t...>; -template +template + requires Invocable auto delay_invoke(F &&f, Args &&...args) { - static_assert(is_invocable_with_args::value, - "F must be callable with Args..."); - return [f = std::forward(f), args = std::make_tuple(std::forward(args)...)]() mutable { - return std::apply(f, args); + return std::apply(std::move(f), std::move(args)); }; } + +template + requires Invocable +auto safe_call(Func &&func, Args &&...args) { + try { + return std::invoke(std::forward(func), + std::forward(args)...); + } catch (...) { + using ReturnType = + std::invoke_result_t, std::decay_t...>; + if constexpr (std::is_default_constructible_v) { + return ReturnType{}; + } else { + throw std::runtime_error("An exception occurred in safe_call"); + } + } +} + +template + requires std::is_invocable_v, std::decay_t...> +auto safe_try_catch(F &&func, Args &&...args) { + using ReturnType = + std::invoke_result_t, std::decay_t...>; + using ResultType = std::variant; + + try { + if constexpr (std::is_same_v) { + std::invoke(std::forward(func), std::forward(args)...); + return ResultType{}; // Empty variant for void functions + } else { + return ResultType{std::invoke(std::forward(func), + std::forward(args)...)}; + } + } catch (...) { + return ResultType{std::current_exception()}; + } +} + +template + requires Invocable +auto safe_try_catch_or_default( + Func &&func, + std::invoke_result_t, std::decay_t...> + default_value, + Args &&...args) { + try { + return std::invoke(std::forward(func), + std::forward(args)...); + } catch (...) { + return default_value; + } +} + #else template struct index_sequence {}; @@ -74,6 +128,44 @@ T delay_invoke(F &&f, Args &&...args) { return DelayInvoke(std::forward(f), std::forward(args)...)(); } + +template +auto safe_try_catch(Func &&func, Args &&...args) -> + typename std::enable_if< + !std::is_void >::value, + std::tuple::type, + std::exception_ptr> >::type { + using ReturnType = typename std::result_of::type; + try { + return std::make_tuple( + std::forward(func)(std::forward(args)...), nullptr); + } catch (...) { + return std::make_tuple(ReturnType(), std::current_exception()); + } +} + +template +auto safe_try_catch(Func &&func, Args &&...args) -> + typename std::enable_if >::value, + std::tuple >::type { + try { + std::forward(func)(std::forward(args)...); + return std::make_tuple(nullptr); + } catch (...) { + return std::make_tuple(std::current_exception()); + } +} + +template +auto safe_try_catch_or_default( + Func &&func, typename std::result_of::type default_value, + Args &&...args) -> typename std::result_of::type { + try { + return std::forward(func)(std::forward(args)...); + } catch (...) { + return default_value; + } +} #endif #endif \ No newline at end of file diff --git a/src/atom/experiment/iter.hpp b/src/atom/experiment/iter.hpp new file mode 100644 index 00000000..16e60bd0 --- /dev/null +++ b/src/atom/experiment/iter.hpp @@ -0,0 +1,286 @@ +/* + * iter.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-26 + +Description: Some iterators + +**************************************************/ + +#ifndef ATOM_EXPERIMENTAL_ITERATOR_HPP +#define ATOM_EXPERIMENTAL_ITERATOR_HPP + +#include +#include +#include +#include +#include +#include + +#if ENABLE_DEBUG +#include +#endif + +template +class pointer_iterator { +public: + using iterator_category = std::forward_iterator_tag; + using value_type = decltype(&*std::declval()); + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + +private: + IteratorT it_; + +public: + pointer_iterator() = default; + + explicit pointer_iterator(IteratorT it) : it_(std::move(it)) {} + + value_type operator*() const { return &*it_; } + + pointer_iterator& operator++() { + ++it_; + return *this; + } + + pointer_iterator operator++(int) { + pointer_iterator tmp = *this; + ++(*this); + return tmp; + } + + bool operator==(const pointer_iterator& other) const = default; + bool operator!=(const pointer_iterator& other) const = default; +}; + +template +auto make_pointer_range(IteratorT begin, IteratorT end) { + return std::make_pair(pointer_iterator(begin), + pointer_iterator(end)); +} + +template +void process_container(ContainerT& container) { + auto beginIter = std::next(container.begin()); + auto endIter = std::prev(container.end()); + + std::vector> ptrs; + auto ptrPair = make_pointer_range(beginIter, endIter); + for (auto it = ptrPair.first; it != ptrPair.second; ++it) { + ptrs.push_back(*it); + } + + for (auto& ptrOpt : ptrs) { + if (ptrOpt) { + auto ptr = *ptrOpt; +#if ENABLE_DEBUG + std::cout << "pointer addr: " << static_cast(&ptr) + << '\n'; + std::cout << "point to: " << static_cast(ptr) << '\n'; + std::cout << "value: " << *ptr << '\n'; +#endif + container.erase( + std::find(container.begin(), container.end(), *ptr)); + } + } +} + +template +class early_inc_iterator { +public: + using iterator_category = std::output_iterator_tag; + using value_type = void; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = void; + + early_inc_iterator() = default; + explicit early_inc_iterator(I x) : current(x) {} + + early_inc_iterator& operator++() { + ++current; + return *this; + } + + early_inc_iterator operator++(int) { + early_inc_iterator tmp = *this; + ++current; + return tmp; + } + + friend bool operator==(const early_inc_iterator& x, + const early_inc_iterator& y) { + return x.current == y.current; + } + + friend bool operator!=(const early_inc_iterator& x, + const early_inc_iterator& y) { + return !(x == y); + } + + auto operator*() const { return *current; } + +private: + I current{}; +}; + +template +early_inc_iterator make_early_inc_iterator(I x) { + return early_inc_iterator(x); +} + +template +class transform_iterator { +public: + using iterator_category = + typename std::iterator_traits::iterator_category; + using value_type = std::invoke_result_t< + FuncT, typename std::iterator_traits::reference>; + using difference_type = + typename std::iterator_traits::difference_type; + using pointer = value_type*; + using reference = value_type; + +private: + IteratorT it_; + FuncT func_; + +public: + transform_iterator() : it_(), func_() {} + transform_iterator(IteratorT it, FuncT func) : it_(it), func_(func) {} + + reference operator*() const { return func_(*it_); } + pointer operator->() const { return &(operator*()); } + + transform_iterator& operator++() { + ++it_; + return *this; + } + transform_iterator operator++(int) { + transform_iterator tmp = *this; + ++it_; + return tmp; + } + + bool operator==(const transform_iterator& other) const { + return it_ == other.it_; + } + bool operator!=(const transform_iterator& other) const { + return !(*this == other); + } +}; + +template +transform_iterator make_transform_iterator(IteratorT it, + FuncT func) { + return transform_iterator(it, func); +} + +template +class filter_iterator { +public: + using iterator_category = std::forward_iterator_tag; + using value_type = typename std::iterator_traits::value_type; + using difference_type = + typename std::iterator_traits::difference_type; + using pointer = typename std::iterator_traits::pointer; + using reference = typename std::iterator_traits::reference; + +private: + IteratorT it_; + IteratorT end_; + PredicateT pred_; + + void satisfy_predicate() { + while (it_ != end_ && !pred_(*it_)) { + ++it_; + } + } + +public: + filter_iterator() : it_(), end_(), pred_() {} + filter_iterator(IteratorT it, IteratorT end, PredicateT pred) + : it_(it), end_(end), pred_(pred) { + satisfy_predicate(); + } + + reference operator*() const { return *it_; } + pointer operator->() const { return &(operator*()); } + + filter_iterator& operator++() { + ++it_; + satisfy_predicate(); + return *this; + } + + filter_iterator operator++(int) { + filter_iterator tmp = *this; + ++*this; + return tmp; + } + + bool operator==(const filter_iterator& other) const { + return it_ == other.it_; + } + bool operator!=(const filter_iterator& other) const { + return !(*this == other); + } +}; + +template +class reverse_iterator { +public: + using iterator_category = + typename std::iterator_traits::iterator_category; + using value_type = typename std::iterator_traits::value_type; + using difference_type = + typename std::iterator_traits::difference_type; + using pointer = typename std::iterator_traits::pointer; + using reference = typename std::iterator_traits::reference; + +private: + IteratorT current; + +public: + reverse_iterator() : current() {} + explicit reverse_iterator(IteratorT x) : current(x) {} + + IteratorT base() const { return current; } + reference operator*() const { + IteratorT tmp = current; + return *--tmp; + } + pointer operator->() const { return &(operator*()); } + reverse_iterator& operator++() { + --current; + return *this; + } + reverse_iterator operator++(int) { + reverse_iterator tmp = *this; + --current; + return tmp; + } + reverse_iterator& operator--() { + ++current; + return *this; + } + reverse_iterator operator--(int) { + reverse_iterator tmp = *this; + ++current; + return tmp; + } + + bool operator==(const reverse_iterator& x) const { + return current == x.current; + } + bool operator!=(const reverse_iterator& x) const { return !(*this == x); } +}; + +#endif \ No newline at end of file diff --git a/src/atom/experiment/memory.hpp b/src/atom/experiment/memory.hpp new file mode 100644 index 00000000..90458107 --- /dev/null +++ b/src/atom/experiment/memory.hpp @@ -0,0 +1,142 @@ +/* + * memory.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-3-29 + +Description: A simple implementation of memory pool + +**************************************************/ + +#ifndef ATOM_EXPERIMENT_MEMORY_POOL_HPP +#define ATOM_EXPERIMENT_MEMORY_POOL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "noncopyable.hpp" + +template +class MemoryPool : public std::pmr::memory_resource, NonCopyable { +public: + T *allocate(size_t n) { + if (n > max_size()) { + throw std::bad_alloc(); + } + std::lock_guard lock(mutex_); + if (auto p = allocate_from_pool(n)) { + return p; + } + size_t chunk_size = (n + chunk_space_ - 1) / chunk_space_ * chunk_space_; + return allocate_from_chunk(chunk_size); + } + + void deallocate(T *p, size_t n) { + std::lock_guard lock(mutex_); + if (is_from_pool(p)) { + deallocate_to_pool(p, n); + } else { + deallocate_to_chunk(p, n); + } + } + + bool is_equal(const memory_resource &other) const noexcept { + return this == &other; + } + + size_t block_size() const { return BlockSize; } + +private: + struct Chunk { + size_t size; + size_t used; + std::unique_ptr> memory; + Chunk(size_t s) + : size(s), used(0), memory(static_cast(::operator new(s)), + [](uint8_t *p) { ::operator delete(p); }) {} + }; + + size_t max_size() const { return BlockSize - sizeof(T); } + size_t chunk_space() const { return BlockSize - sizeof(Chunk); } + + T *allocate_from_pool(size_t n) { + if (pool_.empty() || pool_.back().used + n > pool_.back().size) { + return nullptr; + } + auto &chunk = pool_.back(); + auto p = reinterpret_cast(chunk.memory.get() + chunk.used); + chunk.used += n; + return p; + } + + void deallocate_to_pool(T *p, size_t n) { + auto it = std::find_if(pool_.begin(), pool_.end(), [p](const Chunk &chunk) { + return chunk.memory.get() <= reinterpret_cast(p) && + reinterpret_cast(p) < chunk.memory.get() + chunk.size; + }); + assert(it != pool_.end()); + if (reinterpret_cast(p) + n == it->memory.get() + it->used) { + it->used -= n; + } + } + + T *allocate_from_chunk(size_t chunk_size) { + pool_.emplace_back(chunk_size); + auto &chunk = pool_.back(); + auto p = reinterpret_cast(chunk.memory.get()); + chunk.used += chunk_size; + return p; + } + + void deallocate_to_chunk(T *p, [[maybe_unused]] size_t n) { + auto it = std::find_if(pool_.begin(), pool_.end(), [p](const Chunk &chunk) { + return chunk.memory.get() <= reinterpret_cast(p) && + reinterpret_cast(p) < chunk.memory.get() + chunk.size; + }); + assert(it != pool_.end()); + if (it->used == it->size) { + pool_.erase(it); + } + } + + bool is_from_pool(T *p) { + for (const auto &chunk : pool_) { + if (chunk.memory.get() <= reinterpret_cast(p) && + reinterpret_cast(p) < chunk.memory.get() + chunk.size) { + return true; + } + } + return false; + } + + void *do_allocate(size_t bytes, size_t alignment) override { + size_t space = (bytes + alignment - 1) / alignment * alignment; + return allocate(space); + } + + void do_deallocate(void *p, size_t bytes, size_t alignment) override { + size_t space = (bytes + alignment - 1) / alignment * alignment; + deallocate(static_cast(p), space); + } + + bool do_is_equal(const memory_resource &other) const noexcept override { + return this == &other; + } + + size_t chunk_space_ = chunk_space(); + std::vector pool_; + std::mutex mutex_; +}; + +#endif \ No newline at end of file diff --git a/src/atom/experiment/meson.build b/src/atom/experiment/meson.build new file mode 100644 index 00000000..ccb5965f --- /dev/null +++ b/src/atom/experiment/meson.build @@ -0,0 +1,50 @@ +project('Atom-Experiment', 'cpp') + +# Define sources and headers +sources = [ + 'platform.cpp', + 'string.cpp' +] +headers = [ + 'any.hpp', + 'anyutils.hpp', + 'bind_first.hpp', + 'callable.hpp', + 'decorate.hpp', + 'flatmap.hpp', + 'func_traits.hpp', + 'invoke.hpp', + 'list.hpp', + 'memory.hpp', + 'noncopyable.hpp', + 'object.hpp', + 'optional.hpp', + 'platform.hpp', + 'ranges.hpp', + 'short_alloc.hpp', + 'stack_vector.hpp', + 'static_vector.hpp', + 'string.hpp', + 'to_string.hpp', + 'type_info.hpp' +] + +# Create object library +atom_experiment_obj = library('atom_experiment', + sources + headers, + cpp_std: c_std +) + +# Create static library +atom_experiment_static = static_library('atom_experiment_static', atom_experiment_obj) + +# Set version properties +version = '1.0.0' # You should define your version here +soversion = '1' + +atom_experiment_static.version = version +atom_experiment_static.soversion = soversion +atom_experiment_static.basename = 'Atom-Experiment' + +# Install target +install_targets(atom_experiment_static) diff --git a/src/atom/experiment/object.hpp b/src/atom/experiment/object.hpp new file mode 100644 index 00000000..9da72403 --- /dev/null +++ b/src/atom/experiment/object.hpp @@ -0,0 +1,75 @@ +/* + * object.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-5 + +Description: A simple implementation of object pool + +**************************************************/ + +#ifndef ATOM_EXPERIMENT_OBJECT_HPP +#define ATOM_EXPERIMENT_OBJECT_HPP + +#include +#include +#include +#include +#include +#include + +template +concept Resettable = requires(T &obj) { obj.reset(); }; + +template class ObjectPool { +public: + ObjectPool(size_t max_size) : max_size_(max_size), available_(max_size) {} + + std::shared_ptr acquire() { + std::unique_lock lock(mutex_); + cv_.wait(lock, [this] { return !pool_.empty() || available_ > 0; }); + if (!pool_.empty()) { + auto obj = std::move(pool_.back()); + pool_.pop_back(); + return obj; + } + assert(available_ > 0); + --available_; + return std::make_shared(); + } + + void release(std::shared_ptr obj) { + std::unique_lock lock(mutex_); + if (pool_.size() < max_size_) { + obj->reset(); + pool_.push_back(std::move(obj)); + cv_.notify_one(); + } else { + ++available_; + cv_.notify_one(); + } + } + + size_t available() const { + std::unique_lock lock(mutex_); + return available_ + pool_.size(); + } + + size_t size() const { + std::unique_lock lock(mutex_); + return max_size_ - available_; + } + +private: + size_t max_size_; + size_t available_; + std::vector> pool_; + mutable std::mutex mutex_; + std::condition_variable cv_; +}; + +#endif diff --git a/src/atom/experiment/platform.cpp b/src/atom/experiment/platform.cpp index 01143e16..a475bac3 100644 --- a/src/atom/experiment/platform.cpp +++ b/src/atom/experiment/platform.cpp @@ -41,7 +41,7 @@ std::string getWindowsVersion() { } else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) [[unlikely]] { return "Windows XP"; - } else [[unlikely]] { + } else { return "Unknown Windows version"; } } diff --git a/src/atom/experiment/platform.hpp b/src/atom/experiment/platform.hpp index 5a5bce89..1cadff85 100644 --- a/src/atom/experiment/platform.hpp +++ b/src/atom/experiment/platform.hpp @@ -15,6 +15,8 @@ Description: A platform information collection. #ifndef ATOM_EXPERIMENT_PLATFORM_HPP #define ATOM_EXPERIMENT_PLATFORM_HPP +#include + // 获取系统平台 #if defined(_WIN32) const std::string platform = "Windows"; diff --git a/src/atom/experiment/ranges.hpp b/src/atom/experiment/ranges.hpp new file mode 100644 index 00000000..d70955fe --- /dev/null +++ b/src/atom/experiment/ranges.hpp @@ -0,0 +1,247 @@ +/* + * ranges.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-12 + +Description: Some ranges functions for C++20 + +**************************************************/ + +#ifndef ATOM_EXPERIMENT_RANGES_HPP +#define ATOM_EXPERIMENT_RANGES_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Filters elements in a range satisfying a predicate and transforms them + * using a function. + * + * @tparam Range The type of the range. + * @tparam Pred The type of the predicate. + * @tparam Func The type of the function. + * @param range The input range. + * @param pred The predicate function. + * @param func The transformation function. + * @return The transformed range. + * + * @usage + * std::vector numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + * auto result = filter_and_transform( + * numbers, [](int x) { return x % 2 == 0; }, [](int x) { return x * 2; }); + * for (auto x : result) { + * std::cout << x << " "; + * } + */ +template +auto filter_and_transform(Range&& range, Pred&& pred, Func&& func) { + return std::forward(range) | + std::views::filter(std::forward(pred)) | + std::views::transform(std::forward(func)); +} + +/** + * @brief Finds an element in a range. + * + * @tparam Range The type of the range. + * @tparam T The type of the value to find. + * @param range The input range. + * @param value The value to find. + * @return An optional containing the found value, or nullopt if not found. + * + * @usage + * std::vector numbers = {1, 2, 3, 4, 5}; + * auto result = find_element(numbers, 3); + * if (result) { + * std::cout << "Found: " << *result << std::endl; + * } else { + * std::cout << "Element not found" << std::endl; + * } + */ +template +auto find_element(Range&& range, const T& value) { + auto it = std::ranges::find(std::forward(range), value); + return it != std::ranges::end(range) + ? std::optional>{*it} + : std::nullopt; +} + +/** + * @brief Groups elements in a range by a key and aggregates values using an + * aggregator function. + * + * @tparam Range The type of the range. + * @tparam KeySelector The type of the key selector function. + * @tparam Aggregator The type of the aggregator function. + * @param range The input range. + * @param key_selector The function to extract keys from elements. + * @param aggregator The function to aggregate values. + * @return A map containing grouped and aggregated results. + * + * @usage + * std::vector> data = {{"apple", 2}, + * {"banana", 3}, + * {"apple", 1}, + * {"cherry", 4}, + * {"banana", 1}}; + * auto fruit_counts = group_and_aggregate( + * data, [](const auto& pair) { return pair.first; }, + * [](const auto& pair) { return pair.second; }); + * for (const auto& [fruit, count] : fruit_counts) { + * std::cout << fruit << ": " << count << std::endl; + * } + */ +template +auto group_and_aggregate(Range&& range, KeySelector&& key_selector, + Aggregator&& aggregator) { + using Key = std::invoke_result_t>; + using Value = + std::invoke_result_t>; + + std::map result; + for (auto&& item : std::forward(range)) { + Key key = std::invoke(std::forward(key_selector), item); + Value value = std::invoke(std::forward(aggregator), item); + result[key] += value; + } + return result; +} + +/** + * @brief Drops the first n elements from a range. + * + * @tparam Range The type of the range. + * @param range The input range. + * @param n The number of elements to drop. + * @return The range with the first n elements dropped. + * + * @usage + * auto remaining_numbers = drop(numbers, 3); + * for (auto x : remaining_numbers) { + * std::cout << x << " "; + * } + * std::cout << std::endl; + */ +template +auto drop(Range&& range, std::ranges::range_difference_t n) { + return std::forward(range) | std::views::drop(n); +} + +/** + * @brief Takes the first n elements from a range. + * + * @tparam Range The type of the range. + * @param range The input range. + * @param n The number of elements to take. + * @return The range with the first n elements taken. + * + * @usage + * auto first_three = take(numbers, 3); + * for (auto x : first_three) { + * std::cout << x << " "; + * } + * std::cout << std::endl; + */ +template +auto take(Range&& range, std::ranges::range_difference_t n) { + return std::forward(range) | std::views::take(n); +} + +/** + * @brief Takes elements from a range while a predicate is true. + * + * @tparam Range The type of the range. + * @tparam Pred The type of the predicate function. + * @param range The input range. + * @param pred The predicate function. + * @return The range with elements taken while the predicate is true. + * + * @usage + * auto less_than_six = take_while(numbers, [](int x) { return x < 6; }); + * for (auto x : less_than_six) { + * std::cout << x << " "; + * } + * std::cout << std::endl; + */ +template +auto take_while(Range&& range, Pred&& pred) { + return std::forward(range) | + std::views::take_while(std::forward(pred)); +} + +/** + * @brief Drops elements from a range while a predicate is true. + * + * @tparam Range The type of the range. + * @tparam Pred The type of the predicate function. + * @param range The input range. + * @param pred The predicate function. + * @return The range with elements dropped while the predicate is true. + * + * @usage + * auto more_than_two = drop_while(numbers, [](int x) { return x <= 2; }); + * for (auto x : more_than_two) { + * std::cout << x << " "; + * } + * std::cout << std::endl; + */ +template +auto drop_while(Range&& range, Pred&& pred) { + return std::forward(range) | + std::views::drop_while(std::forward(pred)); +} + +/** + * @brief Reverses the elements in a range. + * + * @tparam Range The type of the range. + * @param range The input range. + * @return The reversed range. + * + * @usage + * auto reversed_numbers = reverse(numbers); + * for (auto x : reversed_numbers) { + * std::cout << x << " "; + * } + * std::cout << std::endl; + */ +template +auto reverse(Range&& range) { + return std::forward(range) | std::views::reverse; +} + +/** + * @brief Accumulates the elements in a range using a binary operation. + * + * @tparam Range The type of the range. + * @tparam T The type of the initial value. + * @tparam BinaryOp The type of the binary operation function. + * @param range The input range. + * @param init The initial value for accumulation. + * @param op The binary operation function. + * @return The result of the accumulation. + * + * @usage + * auto sum = accumulate(numbers, 0, std::plus<>{}); + * std::cout << "Sum: " << sum << std::endl; + */ +template +auto accumulate(Range&& range, T init, BinaryOp&& op) { + return std::accumulate(std::begin(range), std::end(range), std::move(init), + std::forward(op)); +} + +#endif \ No newline at end of file diff --git a/src/atom/experiment/short_alloc.hpp b/src/atom/experiment/short_alloc.hpp index dce05e3d..340bfc12 100644 --- a/src/atom/experiment/short_alloc.hpp +++ b/src/atom/experiment/short_alloc.hpp @@ -18,116 +18,206 @@ Description: Short Alloc from Howard Hinnant #include #include +/** + * @brief A fixed-size memory arena for allocating memory with aligned + * addresses. + * + * @tparam N The size of the arena buffer. + * @tparam alignment The alignment requirement for memory allocation. + */ template class arena { - alignas(alignment) char buf_[N]; - char *ptr_; + alignas(alignment) char buf_[N]; /**< The buffer for memory allocation. */ + char *ptr_; /**< Pointer to the next available memory location. */ public: - ~arena() { ptr_ = nullptr; } - arena() noexcept : ptr_(buf_) {} - arena(const arena &) = delete; - arena &operator=(const arena &) = delete; - + ~arena() { ptr_ = nullptr; } /**< Destructor. */ + arena() noexcept : ptr_(buf_) {} /**< Default constructor. */ + arena(const arena &) = delete; /**< Copy constructor (deleted). */ + arena &operator=(const arena &) = + delete; /**< Copy assignment operator (deleted). */ + + /** + * @brief Allocates memory from the arena with the specified alignment. + * + * @tparam ReqAlign The required alignment for the allocated memory. + * @param n The number of bytes to allocate. + * @return Pointer to the allocated memory. + */ template char *allocate(std::size_t n); + + /** + * @brief Deallocates memory previously allocated from the arena. + * + * @param p Pointer to the memory block to deallocate. + * @param n The number of bytes to deallocate. + */ void deallocate(char *p, std::size_t n) noexcept; + /** + * @brief Gets the total size of the arena buffer. + * + * @return The size of the arena buffer. + */ static constexpr std::size_t size() noexcept { return N; } + + /** + * @brief Gets the amount of memory used in the arena. + * + * @return The number of bytes used in the arena. + */ std::size_t used() const noexcept { return static_cast(ptr_ - buf_); } + + /** + * @brief Resets the arena, making all allocated memory available for reuse. + */ void reset() noexcept { ptr_ = buf_; } private: - static std::size_t align_up(std::size_t n) noexcept { + /** + * @brief Rounds up the size to the nearest multiple of the alignment. + * + * @param n The size to round up. + * @return The rounded-up size. + */ + static constexpr std::size_t align_up(std::size_t n) noexcept { return (n + (alignment - 1)) & ~(alignment - 1); } + /** + * @brief Checks if a pointer is within the arena buffer. + * + * @param p Pointer to check. + * @return True if the pointer is within the arena buffer, otherwise false. + */ bool pointer_in_buffer(char *p) noexcept { return buf_ <= p && p <= buf_ + N; } }; -template -template -char *arena::allocate(std::size_t n) { - static_assert(ReqAlign <= alignment, - "alignment is too small for this arena"); - assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena"); - auto const aligned_n = align_up(n); - if (static_cast(buf_ + N - ptr_) >= aligned_n) { - char *r = ptr_; - ptr_ += aligned_n; - return r; - } - - static_assert( - alignment <= alignof(std::max_align_t), - "you've chosen an " - "alignment that is larger than alignof(std::max_align_t), and " - "cannot be guaranteed by normal operator new"); - return static_cast(::operator new(n)); -} - -template -void arena::deallocate(char *p, std::size_t n) noexcept { - assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena"); - if (pointer_in_buffer(p)) { - n = align_up(n); - if (p + n == ptr_) { - ptr_ = p; - } - } else { - ::operator delete(p); - } -} - +/** + * @brief A memory allocator adapter for using the arena as a memory pool. + * + * @tparam T The type of objects to allocate. + * @tparam N The size of the arena buffer. + * @tparam Align The alignment requirement for memory allocation. + */ template class short_alloc { public: - using value_type = T; - static auto constexpr alignment = Align; - static auto constexpr size = N; - using arena_type = arena; + using value_type = T; /**< The type of objects to allocate. */ + static auto constexpr alignment = + Align; /**< The alignment requirement for memory allocation. */ + static auto constexpr size = N; /**< The size of the arena buffer. */ + using arena_type = + arena; /**< The type of the underlying arena. */ private: - arena_type &a_; + arena_type &a_; /**< Reference to the underlying arena. */ public: - short_alloc(const short_alloc &) = default; - short_alloc &operator=(const short_alloc &) = delete; - + short_alloc(const short_alloc &) = + default; /**< Copy constructor (defaulted). */ + short_alloc &operator=(const short_alloc &) = + delete; /**< Copy assignment operator (deleted). */ + + /** + * @brief Constructs the short_alloc with the specified arena. + * + * @param a Reference to the arena to use for allocation. + */ explicit short_alloc(arena_type &a) noexcept : a_(a) { static_assert(size % alignment == 0, "size N needs to be a multiple of alignment Align"); } + + /** + * @brief Constructs the short_alloc with the specified arena of a different + * type. + * + * @tparam U The type of objects allocated by the other short_alloc. + * @param a Reference to the other short_alloc's arena. + */ template explicit short_alloc(const short_alloc &a) noexcept : a_(a.a_) {} - template - struct rebind { - using other = short_alloc<_Up, N, alignment>; - }; - + /** + * @brief Allocates memory for an object of type T. + * + * @param n Number of objects to allocate memory for. + * @return Pointer to the allocated memory. + */ T *allocate(std::size_t n) { return reinterpret_cast( a_.template allocate(n * sizeof(T))); } + + /** + * @brief Deallocates memory previously allocated for objects of type T. + * + * @param p Pointer to the memory block to deallocate. + * @param n Number of objects to deallocate memory for. + */ void deallocate(T *p, std::size_t n) noexcept { a_.deallocate(reinterpret_cast(p), n * sizeof(T)); } + /** + * @brief Nested type alias for rebind. + * + * @tparam _Up The new type to rebind to. + */ + template + struct rebind { + using other = short_alloc<_Up, N, alignment>; + }; + + /** + * @brief Equality comparison operator. + * + * @tparam T1 The type of objects allocated by the left short_alloc. + * @tparam N1 The size of the arena buffer for the left short_alloc. + * @tparam A1 The alignment requirement for the left short_alloc. + * @tparam U The type of objects allocated by the right short_alloc. + * @tparam M The size of the arena buffer for the right short_alloc. + * @tparam A2 The alignment requirement for the right short_alloc. + * @param x The left short_alloc. + * @param y The right short_alloc. + * @return True if the short_allocs are equal, otherwise false. + */ template friend bool operator==(const short_alloc &x, const short_alloc &y) noexcept; - template + /** + * @brief Friendship declaration for the rebind struct. + * + * @tparam U The type of objects allocated by the other short_alloc. + * @tparam M The size of the arena buffer for the other short_alloc. + * @tparam A2 The alignment requirement for the other short_alloc. + */ + template friend class short_alloc; }; +/** + * @brief Equality comparison operator for short_alloc. + * + * @tparam T The type of objects allocated by the left short_alloc. + * @tparam N The size of the arena buffer for the left short_alloc. + * @tparam A1 The alignment requirement for the left short_alloc. + * @tparam U The type of objects allocated by the right short_alloc. + * @tparam M The size of the arena buffer for the right short_alloc. + * @tparam A2 The alignment requirement for the right short_alloc. + * @param x The left short_alloc. + * @param y The right short_alloc. + * @return True if the short_allocs are equal, otherwise false. + */ template inline bool operator==(const short_alloc &x, @@ -135,6 +225,19 @@ inline bool operator==(const short_alloc &x, return N == M && A1 == A2 && &x.a_ == &y.a_; } +/** + * @brief Inequality comparison operator for short_alloc. + * + * @tparam T The type of objects allocated by the left short_alloc. + * @tparam N The size of the arena buffer for the left short_alloc. + * @tparam A1 The alignment requirement for the left short_alloc. + * @tparam U The type of objects allocated by the right short_alloc. + * @tparam M The size of the arena buffer for the right short_alloc. + * @tparam A2 The alignment requirement for the right short_alloc. + * @param x The left short_alloc. + * @param y The right short_alloc. + * @return True if the short_allocs are not equal, otherwise false. + */ template inline bool operator!=(const short_alloc &x, diff --git a/src/atom/experiment/short_string.cpp b/src/atom/experiment/short_string.cpp new file mode 100644 index 00000000..9dc15417 --- /dev/null +++ b/src/atom/experiment/short_string.cpp @@ -0,0 +1,119 @@ +#include "short_string.hpp" + +#include + +ShortString::ShortString(const std::string& s) { + if (s.length() > MAX_LENGTH) { + throw std::invalid_argument("String too long for ShortString"); + } + str = s; +} + +ShortString::ShortString(std::string_view s) { + if (s.length() > MAX_LENGTH) { + throw std::invalid_argument("String too long for ShortString"); + } + str = s; +} + +ShortString::ShortString(const char* s) : ShortString(std::string(s)) {} + +ShortString::ShortString(const ShortString& other) : str(other.str) {} + +ShortString& ShortString::operator=(const ShortString& other) { + if (this != &other) { + str = other.str; + } + return *this; +} + +ShortString& ShortString::operator=(const std::string& s) { + if (s.length() > MAX_LENGTH) { + throw std::invalid_argument("String too long for ShortString"); + } + str = s; + return *this; +} + +ShortString& ShortString::operator=(const char* s) { + *this = ShortString(s); + return *this; +} + +ShortString& ShortString::operator=(std::string_view s) { + if (s.length() > MAX_LENGTH) { + throw std::invalid_argument("String too long for ShortString"); + } + str = s; + return *this; +} + +std::ostream& operator<<(std::ostream& os, const ShortString& ss) { + return os << ss.str; +} + +ShortString ShortString::operator+(const ShortString& other) const { + if (str.length() + other.str.length() > MAX_LENGTH) { + throw std::invalid_argument( + "Resulting string too long for ShortString"); + } + return ShortString(str + other.str); +} + +ShortString& ShortString::operator+=(const ShortString& other) { + *this = *this + other; + return *this; +} + +ShortString& ShortString::operator+=(std::string_view other) { + if (str.length() + other.length() > MAX_LENGTH) { + throw std::invalid_argument( + "Resulting string too long for ShortString"); + } + str += other; + return *this; +} + +bool ShortString::operator==(const ShortString& other) const noexcept { + return str == other.str; +} + +bool ShortString::operator!=(const ShortString& other) const noexcept { + return !(*this == other); +} + +bool ShortString::operator<(const ShortString& other) const noexcept { + return str < other.str; +} + +bool ShortString::operator>(const ShortString& other) const noexcept { + return str > other.str; +} + +bool ShortString::operator<=(const ShortString& other) const noexcept { + return !(*this > other); +} + +bool ShortString::operator>=(const ShortString& other) const noexcept { + return !(*this < other); +} + +char& ShortString::operator[](size_t index) noexcept { return str[index]; } + +const char& ShortString::operator[](size_t index) const noexcept { + return str[index]; +} + +size_t ShortString::length() const noexcept { return str.length(); } + +ShortString ShortString::substr(size_t pos = 0, + size_t count = std::string::npos) const { + if (pos > str.length()) { + throw std::out_of_range("Starting position out of range"); + } + return ShortString(str.substr(pos, count)); +} + +void ShortString::clear() noexcept { str.clear(); } + +void ShortString::swap(ShortString& other) noexcept { str.swap(other.str); } \ No newline at end of file diff --git a/src/atom/experiment/short_string.hpp b/src/atom/experiment/short_string.hpp new file mode 100644 index 00000000..64b477b9 --- /dev/null +++ b/src/atom/experiment/short_string.hpp @@ -0,0 +1,95 @@ +#include +#include + +class ShortString { +private: + static constexpr size_t MAX_LENGTH = + 15; ///< Maximum length of the short string + std::string str; ///< Actual string stored + +public: + /** + * @brief Default constructor for ShortString. + */ + ShortString() = default; + + /** + * @brief Constructor taking a std::string. + * @param s The input std::string. + */ + explicit ShortString(const std::string& s); + + /** + * @brief Constructor taking a std::string_view. + * @param s The input std::string_view. + */ + explicit ShortString(std::string_view s); + + /** + * @brief Constructor taking a C-style string. + * @param s The input C-style string. + */ + ShortString(const char* s); + + /** + * @brief Copy constructor. + * @param other The ShortString to copy from. + */ + ShortString(const ShortString& other); + + /** + * @brief Copy assignment operator. + * @param other The ShortString to assign from. + * @return Reference to the modified ShortString. + */ + ShortString& operator=(const ShortString& other); + + ShortString& operator=(const std::string& s); + + ShortString& operator=(const char* s); + + ShortString& operator=(std::string_view s); + + /** + * @brief Overloaded stream insertion operator for ShortString. + * @param os The output stream to write to. + * @param ss The ShortString to write. + * @return Reference to the output stream. + */ + friend std::ostream& operator<<(std::ostream& os, const ShortString& ss); + + /** + * @brief Overloaded addition operator for ShortString. + * @param other The ShortString to add. + * @return The result of concatenating two ShortStrings. + */ + ShortString operator+(const ShortString& other) const; + + ShortString& operator+=(const ShortString& other); + + ShortString& operator+=(std::string_view other); + + [[nodiscard]] bool operator==(const ShortString& other) const noexcept; + + [[nodiscard]] bool operator!=(const ShortString& other) const noexcept; + + [[nodiscard]] bool operator<(const ShortString& other) const noexcept; + + [[nodiscard]] bool operator>(const ShortString& other) const noexcept; + + [[nodiscard]] bool operator<=(const ShortString& other) const noexcept; + + [[nodiscard]] bool operator>=(const ShortString& other) const noexcept; + + [[nodiscard]] char& operator[](size_t index) noexcept; + + [[nodiscard]] const char& operator[](size_t index) const noexcept; + + [[nodiscard]] size_t length() const noexcept; + + ShortString substr(size_t pos = 0, size_t count = std::string::npos) const; + + void clear() noexcept; + + void swap(ShortString& other) noexcept; +}; \ No newline at end of file diff --git a/src/atom/experiment/sstring.hpp b/src/atom/experiment/sstring.hpp new file mode 100644 index 00000000..d6c691e5 --- /dev/null +++ b/src/atom/experiment/sstring.hpp @@ -0,0 +1,176 @@ +/* + * sstring.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-2 + +Description: A simple static string class + +**************************************************/ + +#ifndef ATOM_EXPERIMENT_SSTRING_HPP +#define ATOM_EXPERIMENT_SSTRING_HPP + +#include +#include + +class Static_String; +template +concept Stringable = std::is_convertible_v || + std::is_convertible_v || + std::is_convertible_v || + std::is_convertible_v; + +class Static_String { +public: + /** + * @brief Constructs a Static_String object from a string literal. + * + * @tparam N Size of the string literal. + * @param str The string literal. + */ + template + constexpr Static_String(const char (&str)[N]) noexcept + : m_size(N - 1), data(str) {} + + /** + * @brief Gets the size of the Static_String object. + * + * @return Size of the Static_String object. + */ + constexpr size_t size() const noexcept { return m_size; } + + /** + * @brief Gets a pointer to the C-style string stored in the Static_String + * object. + * + * @return Pointer to the C-style string. + */ + constexpr const char *c_str() const noexcept { return data; } + + /** + * @brief Gets an iterator to the beginning of the Static_String object. + * + * @return Iterator to the beginning of the Static_String object. + */ + constexpr const char *begin() const noexcept { return data; } + + /** + * @brief Gets an iterator to the end of the Static_String object. + * + * @return Iterator to the end of the Static_String object. + */ + constexpr const char *end() const noexcept { return data + m_size; } + + /** + * @brief Checks if the Static_String object is equal to the provided + * std::string_view. + * + * @param other The std::string_view to compare with. + * @return true if the Static_String object is equal to the + * std::string_view, otherwise false. + */ + constexpr bool operator==(const std::string_view &other) const noexcept { + return std::string_view(data, m_size) == other; + } + + /** + * @brief Checks if the Static_String object is equal to the provided + * convertible type. + * + * @tparam T The convertible type. + * @param other The convertible type to compare with. + * @return true if the Static_String object is equal to the convertible + * type, otherwise false. + */ + template + requires Stringable + constexpr bool operator==(T &&other) const noexcept { + return std::string_view(data, m_size) == std::forward(other); + } + + /** + * @brief Checks if the Static_String object is not equal to the provided + * convertible type. + * + * @tparam T The convertible type. + * @param other The convertible type to compare with. + * @return true if the Static_String object is not equal to the convertible + * type, otherwise false. + */ + template + requires Stringable + constexpr bool operator!=(T &&other) const noexcept { + return !(*this == std::forward(other)); + } + + /** + * @brief Checks if the Static_String object is less than the provided + * convertible type. + * + * @tparam T The convertible type. + * @param other The convertible type to compare with. + * @return true if the Static_String object is less than the convertible + * type, otherwise false. + */ + template + requires Stringable + constexpr bool operator<(T &&other) const noexcept { + return std::string_view(data, m_size) < std::forward(other); + } + + /** + * @brief Checks if the Static_String object is less than or equal to the + * provided convertible type. + * + * @tparam T The convertible type. + * @param other The convertible type to compare with. + * @return true if the Static_String object is less than or equal to the + * convertible type, otherwise false. + */ + template + requires Stringable + constexpr bool operator<=(T &&other) const noexcept { + return std::string_view(data, m_size) <= std::forward(other); + } + + /** + * @brief Checks if the Static_String object is greater than the provided + * convertible type. + * + * @tparam T The convertible type. + * @param other The convertible type to compare with. + * @return true if the Static_String object is greater than the convertible + * type, otherwise false. + */ + template + requires Stringable + constexpr bool operator>(T &&other) const noexcept { + return std::string_view(data, m_size) > std::forward(other); + } + + /** + * @brief Checks if the Static_String object is greater than or equal to the + * provided convertible type. + * + * @tparam T The convertible type. + * @param other The convertible type to compare with. + * @return true if the Static_String object is greater than or equal to the + * convertible type, otherwise false. + */ + template + requires Stringable + constexpr bool operator>=(T &&other) const noexcept { + return std::string_view(data, m_size) >= std::forward(other); + } + +private: + const size_t m_size; /**< Size of the Static_String object. */ + const char *data; /**< Pointer to the C-style string data. */ +}; + +#endif diff --git a/src/atom/experiment/stack_vector.hpp b/src/atom/experiment/stack_vector.hpp new file mode 100644 index 00000000..17d7f7e7 --- /dev/null +++ b/src/atom/experiment/stack_vector.hpp @@ -0,0 +1,181 @@ +/* + * stack_vector.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-2 + +Description: A simple stack vector implementation + +**************************************************/ + +#ifndef ATOM_EXPERIMENT_STACK_VECTOR_HPP +#define ATOM_EXPERIMENT_STACK_VECTOR_HPP + +#include +#include +#include +#include + +/** + * @brief A stack-allocated vector that stores elements of type T with a maximum + * capacity. + * + * @tparam T The type of elements stored in the Stack_Vector. + * @tparam MaxSize The maximum capacity of the Stack_Vector. + */ +template +struct Stack_Vector { + /** + * @brief Default constructor. + */ + Stack_Vector() noexcept = default; + + /** + * @brief Copy constructor. + * + * @param other The Stack_Vector to copy from. + */ + Stack_Vector(const Stack_Vector &other) { + m_size = other.m_size; + for (std::size_t i = 0; i < m_size; ++i) { + new (&(*this)[i]) T(other[i]); + } + } + + /** + * @brief Move constructor. + * + * @param other The Stack_Vector to move from. + */ + Stack_Vector(Stack_Vector &&other) noexcept + : data{other.data}, m_size{other.m_size} { + other.m_size = 0; + } + + /** + * @brief Destructor. + */ + ~Stack_Vector() noexcept(std::is_nothrow_destructible_v) { + for (std::size_t pos = 0; pos < m_size; ++pos) { + (*this)[pos].~T(); + } + } + + /** + * @brief Operator to access elements by index. + * + * @param idx The index of the element. + * @return Reference to the element at the specified index. + */ + [[nodiscard]] T &operator[](const std::size_t idx) noexcept { + return *reinterpret_cast(&data + aligned_size * idx); + } + + /** + * @brief Operator to access elements by index (const version). + * + * @param idx The index of the element. + * @return Const reference to the element at the specified index. + */ + [[nodiscard]] const T &operator[](const std::size_t idx) const noexcept { + return *reinterpret_cast(&data + aligned_size * idx); + } + + /** + * @brief Adds a new element to the end of the Stack_Vector. + * + * @tparam Param The parameter types for constructing the new element. + * @param param The arguments to forward to the constructor of the new + * element. + * @return Reference to the newly added element. + */ + template + T &emplace_back(Param &&...param) { + auto *p = new (&(*this)[m_size++]) T(std::forward(param)...); + return *p; + }; + + /** + * @brief Gets the number of elements stored in the Stack_Vector. + * + * @return The number of elements. + */ + auto size() const noexcept { return m_size; }; + + /** + * @brief Gets the maximum capacity of the Stack_Vector. + * + * @return The maximum capacity. + */ + auto capacity() const noexcept { return MaxSize; }; + + /** + * @brief Removes the last element from the Stack_Vector. + */ + void pop_back() noexcept(std::is_nothrow_destructible_v) { + (*this)[--m_size].~T(); + } + + /** + * @brief Resizes the Stack_Vector to contain the specified number of + * elements. + * + * @param new_size The new size of the Stack_Vector. + */ + void resize(std::size_t new_size) { m_size = new_size; } + + /** + * @brief Copy assignment operator. + * + * @param other The Stack_Vector to copy from. + * @return Reference to the modified Stack_Vector. + */ + Stack_Vector &operator=(const Stack_Vector &other) { + if (this != &other) { + for (std::size_t i = 0; i < m_size; ++i) { + (*this)[i].~T(); + } + m_size = other.m_size; + for (std::size_t i = 0; i < m_size; ++i) { + new (&(*this)[i]) T(other[i]); + } + } + return *this; + } + + /** + * @brief Move assignment operator. + * + * @param other The Stack_Vector to move from. + * @return Reference to the modified Stack_Vector. + */ + Stack_Vector &operator=(Stack_Vector &&other) noexcept { + if (this != &other) { + for (std::size_t i = 0; i < m_size; ++i) { + (*this)[i].~T(); + } + std::swap(data, other.data); + std::swap(m_size, other.m_size); + } + return *this; + } + + /** + * @brief Size of each aligned element in the Stack_Vector. + */ + constexpr static auto aligned_size = + sizeof(T) + (sizeof(T) & std::alignment_of_v) > 0 + ? std::alignment_of_v + : 0; + + std::size_t m_size{ + 0}; /**< Current number of elements in the Stack_Vector. */ + alignas(std::alignment_of_v) char data + [aligned_size * MaxSize]; /**< Storage for the elements. */ +}; + +#endif \ No newline at end of file diff --git a/src/atom/experiment/static_vector.hpp b/src/atom/experiment/static_vector.hpp new file mode 100644 index 00000000..576ae381 --- /dev/null +++ b/src/atom/experiment/static_vector.hpp @@ -0,0 +1,204 @@ +/* + * static_vector.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-3-1 + +Description: A static vector + +**************************************************/ + +#ifndef ATOM_EXPERIMENT_STATIC_VECTOR_HPP +#define ATOM_EXPERIMENT_STATIC_VECTOR_HPP + +#include +#include +#include +#include +#include +#include + +template +class StaticVector { +public: + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + constexpr StaticVector() noexcept = default; + + constexpr StaticVector(std::initializer_list init) noexcept { + assert(init.size() <= Capacity); + std::copy(init.begin(), init.end(), begin()); + m_size = init.size(); + } + + constexpr void push_back(const T& value) noexcept { + assert(m_size < Capacity); + m_data[m_size++] = value; + } + + constexpr void push_back(T&& value) noexcept { + assert(m_size < Capacity); + m_data[m_size++] = std::move(value); + } + + template + constexpr reference emplace_back(Args&&... args) noexcept { + assert(m_size < Capacity); + return m_data[m_size++] = T(std::forward(args)...); + } + + constexpr void pop_back() noexcept { + assert(m_size > 0); + --m_size; + } + + constexpr void clear() noexcept { m_size = 0; } + + [[nodiscard]] constexpr bool empty() const noexcept { return m_size == 0; } + + [[nodiscard]] constexpr size_type size() const noexcept { return m_size; } + + [[nodiscard]] constexpr size_type capacity() const noexcept { + return Capacity; + } + + [[nodiscard]] constexpr reference operator[](size_type index) noexcept { + assert(index < m_size); + return m_data[index]; + } + + [[nodiscard]] constexpr const_reference operator[]( + size_type index) const noexcept { + assert(index < m_size); + return m_data[index]; + } + + [[nodiscard]] constexpr reference at(size_type index) { + if (index >= m_size) { + throw std::out_of_range("StaticVector::at"); + } + return m_data[index]; + } + + [[nodiscard]] constexpr const_reference at(size_type index) const { + if (index >= m_size) { + throw std::out_of_range("StaticVector::at"); + } + return m_data[index]; + } + + [[nodiscard]] constexpr reference front() noexcept { + assert(m_size > 0); + return m_data[0]; + } + + [[nodiscard]] constexpr const_reference front() const noexcept { + assert(m_size > 0); + return m_data[0]; + } + + [[nodiscard]] constexpr reference back() noexcept { + assert(m_size > 0); + return m_data[m_size - 1]; + } + + [[nodiscard]] constexpr const_reference back() const noexcept { + assert(m_size > 0); + return m_data[m_size - 1]; + } + + [[nodiscard]] constexpr pointer data() noexcept { return m_data.data(); } + + [[nodiscard]] constexpr const_pointer data() const noexcept { + return m_data.data(); + } + + [[nodiscard]] constexpr iterator begin() noexcept { return data(); } + + [[nodiscard]] constexpr const_iterator begin() const noexcept { + return data(); + } + + [[nodiscard]] constexpr const_iterator cbegin() const noexcept { + return begin(); + } + + [[nodiscard]] constexpr iterator end() noexcept { return data() + m_size; } + + [[nodiscard]] constexpr const_iterator end() const noexcept { + return data() + m_size; + } + + [[nodiscard]] constexpr const_iterator cend() const noexcept { + return end(); + } + + [[nodiscard]] constexpr reverse_iterator rbegin() noexcept { + return reverse_iterator(end()); + } + + [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator(end()); + } + + [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { + return rbegin(); + } + + [[nodiscard]] constexpr reverse_iterator rend() noexcept { + return reverse_iterator(begin()); + } + + [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { + return const_reverse_iterator(begin()); + } + + [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { + return rend(); + } + + constexpr void swap(StaticVector& other) noexcept { + using std::swap; + swap(m_data, other.m_data); + swap(m_size, other.m_size); + } + +private: + std::array m_data{}; + size_type m_size{0}; +}; + +template +constexpr bool operator==(const StaticVector& lhs, + const StaticVector& rhs) noexcept { + return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} + +template +constexpr auto operator<=>(const StaticVector& lhs, + const StaticVector& rhs) noexcept { + return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), + rhs.begin(), rhs.end()); +} + +template +constexpr void swap(StaticVector& lhs, + StaticVector& rhs) noexcept { + lhs.swap(rhs); +} + +#endif // ATOM_EXPERIMENT_STATIC_VECTOR_HPP \ No newline at end of file diff --git a/src/atom/experiment/stringutils.hpp b/src/atom/experiment/stringutils.hpp deleted file mode 100644 index 8b96a1eb..00000000 --- a/src/atom/experiment/stringutils.hpp +++ /dev/null @@ -1,198 +0,0 @@ -/* - * cmdline.hpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-4-5 - -Description: Implementation of command line generator. - -**************************************************/ - -#ifndef ATOM_UTILS_CMDLINE_HPP -#define ATOM_UTILS_CMDLINE_HPP - -#include -#include -#include -#include -#include -#include -#include -#include - -template -constexpr bool is_string_type = - std::is_same_v || std::is_same_v || - std::is_same_v; - -template -struct is_container : std::false_type {}; - -template -struct is_container_helper {}; - -template -struct is_container< - T, - std::conditional_t().begin()), - decltype(std::declval().end()), - decltype(std::declval().size()), - typename T::value_type>, - void>> - : public std::integral_constant> {}; - -template -struct is_map : std::false_type {}; - -template -struct is_map> : std::true_type {}; - -template -struct is_map> : std::true_type {}; - -#if __cplusplus >= 202002L - -#include - -template -concept BasicType = std::is_arithmetic_v; - -template -concept StringType = requires(T a) { - std::is_same_v || std::is_same_v || - std::is_same_v; -}; - -template -concept SequenceContainer = requires(T a) { - typename T::value_type; - { a.begin() } -> std::forward_iterator; - { a.end() } -> std::forward_iterator; -}; - -template -concept AssociativeContainer = requires(T a) { - typename T::key_type; - typename T::mapped_type; - { a.begin() } -> std::forward_iterator; - { a.end() } -> std::forward_iterator; -}; - -template -concept SmartPointer = requires(T a) { - { *a } -> std::convertible_to; -}; - -#endif - -template -auto toString(const T &value) - -> std::enable_if_t::value && !is_container::value, - std::string> { - if constexpr (is_string_type) { - return std::string(value); - } else { - std::ostringstream oss; - oss << value; - return oss.str(); - } -} - -template -auto toString(const std::pair &keyValue) { - return "(" + toString(keyValue.first) + ", " + toString(keyValue.second) + - ")"; -} - -template -auto toString(const std::pair &keyValue, - const std::string &separator) { - return toString(keyValue.first) + separator + toString(keyValue.second); -} - -template -std::enable_if_t::value, std::string> toString( - const Container &container) { - std::ostringstream oss; - oss << "{"; - bool first = true; - for (const auto &elem : container) { - if (!first) { - oss << ", "; - } - oss << toString(elem.first) << ": " << toString(elem.second); - first = false; - } - oss << "}"; - return oss.str(); -} - -template -auto toString(const Container &container) - -> std::enable_if_t::value && - !is_map::value && - !is_string_type, - std::string> { - std::ostringstream oss; - oss << "["; - auto it = container.begin(); - while (it != container.end()) { - oss << toString(*it); - ++it; - if (it != container.end()) - oss << ", "; - } - oss << "]"; - return oss.str(); -} - -template -std::string toString(const std::vector &value) { - std::ostringstream oss; - oss << "["; - for (size_t i = 0; i < value.size(); ++i) { - oss << toString(value[i]); - if (i != value.size() - 1) { - oss << ", "; - } - } - oss << "]"; - return oss.str(); -} - -template -std::enable_if_t, std::string> joinKeyValuePair( - const std::string &key, const T &value, const std::string &separator = "") { - return key + separator + std::string(value); -} - -template -std::string joinKeyValuePair(const std::pair &keyValue, - const std::string &separator = "") { - return joinKeyValuePair(keyValue.first, keyValue.second, separator); -} - -template -[[nodiscard]] std::string joinCommandLine(const Args &...args) { - std::ostringstream oss; - bool firstArg = true; - ((oss << (!firstArg ? " " : " ") << toString(args)), ...); - return oss.str(); -} - -template -auto toStringArray(const std::vector &array) { - std::ostringstream oss; - for (const auto &elem : array) { - oss << toString(elem) << " "; - } - return oss.str(); -} - - -#endif diff --git a/src/atom/experiment/to_string.hpp b/src/atom/experiment/to_string.hpp new file mode 100644 index 00000000..1f86e9d4 --- /dev/null +++ b/src/atom/experiment/to_string.hpp @@ -0,0 +1,433 @@ +/* + * stringutils.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-4-5 + +Description: Implementation of command line generator. + +**************************************************/ + +#ifndef ATOM_EXPERIMENT_STRINGUTILS_HPP +#define ATOM_EXPERIMENT_STRINGUTILS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template +constexpr bool is_string_type = + std::is_same_v || std::is_same_v || + std::is_same_v; + +template +struct is_container : std::false_type {}; + +template +struct is_container_helper {}; + +template +struct is_container< + T, + std::conditional_t().begin()), + decltype(std::declval().end()), + decltype(std::declval().size()), + typename T::value_type>, + void>> + : public std::integral_constant> {}; + +template +struct is_map : std::false_type {}; + +template +struct is_map> : std::true_type {}; + +template +struct is_map> : std::true_type {}; + +#if __cplusplus >= 202002L + +template +concept BasicType = std::is_arithmetic_v; + +/** + * @brief Check if a type is a basic type. + * @tparam T The type to check. + * @return True if the type is a basic type, false otherwise. + */ +template +concept StringType = requires(T a) { + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v; +}; + +/** + * @brief Check if a type is a basic type. + * @tparam T The type to check. + * @return True if the type is a basic type, false otherwise. + */ +template +concept SequenceContainer = requires(T a) { + typename T::value_type; + { a.begin() } -> std::forward_iterator; + { a.end() } -> std::forward_iterator; +}; + +/** + * @brief Check if a type is an associative container. + * @tparam T The type to check. + * @return True if the type is an associative container, false otherwise. + */ +template +concept AssociativeContainer = requires(T a) { + typename T::key_type; + typename T::mapped_type; + { a.begin() } -> std::forward_iterator; + { a.end() } -> std::forward_iterator; +}; + +/** + * @brief Check if a type is a smart pointer. + * @tparam T The type to check. + * @return True if the type is a smart pointer, false otherwise. + */ +template +concept SmartPointer = requires(T a) { + { *a } -> std::convertible_to; +}; + +#endif + +/** + * @brief Convert a value to a string representation. + * @tparam T The type of the value. + * @param value The value to be converted. + * @return A string representation of the value. + */ +template +auto toString(const T &value) + -> std::enable_if_t::value && !is_container::value, + std::string> { + if constexpr (is_string_type) { + return std::string(value); + } else { + std::ostringstream oss; + oss << value; + return oss.str(); + } +} + +/** + * @brief Join a pair of key-value pairs into a string representation. + * @tparam Key The type of the key. + * @tparam Value The type of the value. + * @param keyValue The pair of key-value pairs. + * @return A string representation of the pair. + */ +template +auto toString(const std::pair &keyValue) { + return "(" + toString(keyValue.first) + ", " + toString(keyValue.second) + + ")"; +} + +/** + * @brief Join a pair of key-value pairs into a string representation. + * @tparam Key The type of the key. + * @tparam Value The type of the value. + * @param keyValue The pair of key-value pairs. + * @param separator The separator to use between the key and value. + * @return A string representation of the pair. + */ +template +auto toString(const std::pair &keyValue, + const std::string &separator) { + return toString(keyValue.first) + separator + toString(keyValue.second); +} + +/** + * @brief Join a map of key-value pairs into a string representation. + * @tparam Container The type of the map. + * @param container The map of key-value pairs. + * @return A string representation of the map. + */ +template +std::enable_if_t::value, std::string> toString( + const Container &container) { + std::ostringstream oss; + oss << "{"; + bool first = true; + for (const auto &elem : container) { + if (!first) { + oss << ", "; + } + oss << toString(elem.first) << ": " << toString(elem.second); + first = false; + } + oss << "}"; + return oss.str(); +} + +/** + * @brief Join a container of values into a string representation. + * @tparam Container The type of the container. + * @param container The container of values. + * @return A string representation of the container. + */ +template +auto toString(const Container &container) + -> std::enable_if_t::value && + !is_map::value && + !is_string_type, + std::string> { + std::ostringstream oss; + oss << "["; + auto it = container.begin(); + while (it != container.end()) { + oss << toString(*it); + ++it; + if (it != container.end()) + oss << ", "; + } + oss << "]"; + return oss.str(); +} + +/** + * @brief Join a vector of values into a string representation. + * @tparam T The type of the values in the vector. + * @param value The vector of values. + * @return A string representation of the vector. + */ +template +std::string toString(const std::vector &value) { + std::ostringstream oss; + oss << "["; + for (size_t i = 0; i < value.size(); ++i) { + oss << toString(value[i]); + if (i != value.size() - 1) { + oss << ", "; + } + } + oss << "]"; + return oss.str(); +} + +/** + * @brief Join a key-value pair into a string representation. + * @tparam T The type of the value. + * @param key The key. + * @param value The value. + * @param separator The separator to use between the key and value. + * @return A string representation of the key-value pair. + */ +template +std::enable_if_t, std::string> joinKeyValuePair( + const std::string &key, const T &value, const std::string &separator = "") { + return key + separator + std::string(value); +} + +/** + * @brief Join a key-value pair into a string representation. + * @tparam Key The type of the key. + * @tparam Value The type of the value. + * @param keyValue The key-value pair to join. + * @param separator The separator to use between the key and value. + * @return A string representation of the key-value pair. + */ +template +std::string joinKeyValuePair(const std::pair &keyValue, + const std::string &separator = "") { + return joinKeyValuePair(keyValue.first, keyValue.second, separator); +} + +/** + * @brief Join command line arguments into a single string. + * @tparam Args The types of the command line arguments. + * @param args The command line arguments. + * @return A string representation of the command line arguments. + */ +template +[[nodiscard]] std::string joinCommandLine(const Args &...args) { + std::ostringstream oss; + bool firstArg = true; + ((oss << (!firstArg ? " " : " ") << toString(args)), ...); + return oss.str(); +} + +/** + * @brief Convert a vector of elements to a string representation. + * @tparam T The type of elements in the vector. + * @param array The vector to convert. + * @return A string representation of the vector. + */ +template +auto toStringArray(const std::vector &array) { + std::ostringstream oss; + for (const auto &elem : array) { + oss << toString(elem) << " "; + } + return oss.str(); +} + +/** + * @brief Concept to check if a type has begin() and end() member functions. + * @tparam T The type to check. + */ +template +concept HasIterator = requires(T t) { + t.begin(); /**< The begin() member function. */ + t.end(); /**< The end() member function. */ +}; + +/** + * @brief Implementation of string equality comparison for types supporting + * iterators. + * @tparam LHS Type of the left-hand side. + * @tparam RHS Type of the right-hand side. + * @param t_lhs The left-hand side operand. + * @param t_rhs The right-hand side operand. + * @return True if the strings are equal, false otherwise. + */ +template +[[nodiscard]] constexpr bool str_equal_impl(const LHS &t_lhs, + const RHS &t_rhs) noexcept { + return std::equal(t_lhs.begin(), t_lhs.end(), t_rhs.begin(), t_rhs.end()); +} + +/** + * @brief Functor for string equality comparison. + */ +struct str_equal { + /** + * @brief Compares two std::string objects for equality. + * @param t_lhs The left-hand side string. + * @param t_rhs The right-hand side string. + * @return True if the strings are equal, false otherwise. + */ + [[nodiscard]] bool operator()(const std::string &t_lhs, + const std::string &t_rhs) const noexcept { + return t_lhs == t_rhs; + } + + /** + * @brief Compares two objects for equality using iterators. + * @tparam LHS Type of the left-hand side. + * @tparam RHS Type of the right-hand side. + * @param t_lhs The left-hand side operand. + * @param t_rhs The right-hand side operand. + * @return True if the strings are equal, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator()(const LHS &t_lhs, + const RHS &t_rhs) const noexcept { + return str_equal_impl(t_lhs, t_rhs); + } + + struct is_transparent {}; /**< Enables transparent comparison. */ +}; + +/** + * @brief Implementation of string less-than comparison for types supporting + * iterators. + * @tparam T Type of the operands. + * @param t_lhs The left-hand side operand. + * @param t_rhs The right-hand side operand. + * @return True if t_lhs is less than t_rhs, false otherwise. + */ +template +[[nodiscard]] constexpr bool str_less_impl(const T &t_lhs, + const T &t_rhs) noexcept { + return t_lhs < t_rhs; +} + +/** + * @brief Implementation of string less-than comparison for types supporting + * iterators. + * @tparam LHS Type of the left-hand side. + * @tparam RHS Type of the right-hand side. + * @param t_lhs The left-hand side operand. + * @param t_rhs The right-hand side operand. + * @return True if t_lhs is less than t_rhs, false otherwise. + */ +template +[[nodiscard]] constexpr bool str_less_impl(const LHS &t_lhs, + const RHS &t_rhs) noexcept { + return std::lexicographical_compare(t_lhs.begin(), t_lhs.end(), + t_rhs.begin(), t_rhs.end()); +} + +/** + * @brief Functor for string less-than comparison. + */ +struct str_less { + /** + * @brief Compares two std::string objects. + * @param t_lhs The left-hand side string. + * @param t_rhs The right-hand side string. + * @return True if t_lhs is less than t_rhs, false otherwise. + */ + [[nodiscard]] bool operator()(const std::string &t_lhs, + const std::string &t_rhs) const noexcept { + return t_lhs < t_rhs; + } + + /** + * @brief Compares two objects using iterators. + * @tparam LHS Type of the left-hand side. + * @tparam RHS Type of the right-hand side. + * @param t_lhs The left-hand side operand. + * @param t_rhs The right-hand side operand. + * @return True if t_lhs is less than t_rhs, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator()(const LHS &t_lhs, + const RHS &t_rhs) const noexcept { + return str_less_impl(t_lhs, t_rhs); + } + + struct is_transparent {}; /**< Enables transparent comparison. */ +}; + +struct str_more { + /** + * @brief Compares two std::string objects. + * @param t_lhs The left-hand side string. + * @param t_rhs The right-hand side string. + * @return True if t_lhs is greater than t_rhs, false otherwise. + */ + [[nodiscard]] bool operator()(const std::string &t_lhs, + const std::string &t_rhs) const noexcept { + return t_lhs > t_rhs; + } + + /** + * @brief Compares two objects using iterators. + * @tparam LHS Type of the left-hand side. + * @tparam RHS Type of the right-hand side. + * @param t_lhs The left-hand side operand. + * @param t_rhs The right-hand side operand. + * @return True if t_lhs is greater than t_rhs, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator()(const LHS &t_lhs, + const RHS &t_rhs) const noexcept { + return str_more_impl(t_lhs, t_rhs); + } + + struct is_transparent {}; /**< Enables transparent comparison. */ +}; + +#endif // ATOM_STRINGUTILS_HPP diff --git a/src/atom/experiment/type_info.hpp b/src/atom/experiment/type_info.hpp index ba0891f7..fd0610dd 100644 --- a/src/atom/experiment/type_info.hpp +++ b/src/atom/experiment/type_info.hpp @@ -259,28 +259,11 @@ struct Get_Type_Info &> { } }; -/// \brief Creates a Type_Info object representing the type passed in -/// \tparam T Type of object to get a Type_Info for, derived from the passed in -/// parameter \return Type_Info for T -/// -/// \b Example: -/// \code -/// int i; -/// chaiscript::Type_Info ti = chaiscript::user_type(i); -/// \endcode template constexpr Type_Info user_type(const T & /*t*/) noexcept { return Get_Type_Info::get(); } -/// \brief Creates a Type_Info object representing the templated type -/// \tparam T Type of object to get a Type_Info for -/// \return Type_Info for T -/// -/// \b Example: -/// \code -/// chaiscript::Type_Info ti = chaiscript::user_type(); -/// \endcode template constexpr Type_Info user_type() noexcept { return Get_Type_Info::get(); diff --git a/src/atom/experiment/xmake.lua b/src/atom/experiment/xmake.lua new file mode 100644 index 00000000..0930b4f7 --- /dev/null +++ b/src/atom/experiment/xmake.lua @@ -0,0 +1,65 @@ +-- xmake.lua for Atom-Experiment +-- This project is licensed under the terms of the GPL3 license. +-- +-- Project Name: Atom-Experiment +-- Description: A collection of experiments for the Atom project +-- Author: Max Qian +-- License: GPL3 + +add_rules("mode.debug", "mode.release") + +set_project("atom-experiment") +set_version("1.0.0") +set_license("GPL3") + +-- Sources +local sources = { + "platform.cpp", + "string.cpp" +} + +-- Headers +local headers = { + "any.hpp", + "anyutils.hpp", + "bind_first.hpp", + "callable.hpp", + "decorate.hpp", + "flatmap.hpp", + "func_traits.hpp", + "invoke.hpp", + "list.hpp", + "memory.hpp", + "noncopyable.hpp", + "object.hpp", + "optional.hpp", + "platform.hpp", + "ranges.hpp", + "short_alloc.hpp", + "stack_vector.hpp", + "static_vector.hpp", + "string.hpp", + "to_string.hpp", + "type_info.hpp" +} + +-- Build Object Library +target("atom-experiment-object") + set_kind("object") + add_headerfiles(headers, {public = true}) + add_files(sources, {public = false}) + +-- Build Static Library +target("atom-experiment") + set_kind("static") + add_deps("atom-experiment-object") + add_packages("threads") + add_includedirs(".", {public = true}) + + set_targetdir("$(buildir)/lib") + set_objectdir("$(buildir)/obj") + + after_build(function (target) + os.cp("$(buildir)/lib", "$(projectdir)/lib") + os.cp("$(projectdir)/*.hpp", "$(projectdir)/include") + end) \ No newline at end of file diff --git a/src/atom/io/_script.hpp b/src/atom/io/_script.hpp new file mode 100644 index 00000000..9fd62d6b --- /dev/null +++ b/src/atom/io/_script.hpp @@ -0,0 +1,147 @@ +/* + * _script.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-23 + +Description: Carbon binding for Atom-IO + +**************************************************/ + +#ifndef ATOM_IO_SCRIPT_HPP +#define ATOM_IO_SCRIPT_HPP + +#include "carbon/carbon.hpp" + +#include "compress.hpp" +#include "file.hpp" +#include "glob.hpp" +#include "idirectory.hpp" +#include "ifile.hpp" +#include "io.hpp" + +using namespace Atom::IO; + +namespace Atom::_Script::IO { +/** + * Adds the String Methods to the given Carbon module. + */ +Carbon::ModulePtr bootstrap( + Carbon::ModulePtr m = std::make_shared()) { + m->add(Carbon::fun(&compress_file), "compress_file"); + m->add(Carbon::fun(&decompress_file), "decompress_file"); + m->add(Carbon::fun(&compress_folder), "compress_folder"); + m->add(Carbon::fun(&create_zip), "create_zip"); + m->add(Carbon::fun(&extract_zip), "extract_zip"); + + m->add(user_type(), "FileManager"); + //m->add(Carbon::constructor(), "FileManager"); + m->add(Carbon::fun(&FileManager::createFile), "createFile"); + m->add(Carbon::fun(&FileManager::openFile), "openFile"); + m->add(Carbon::fun(&FileManager::readFile), "readFile"); + m->add(Carbon::fun(&FileManager::writeFile), "writeFile"); + m->add(Carbon::fun(&FileManager::moveFile), "moveFile"); + m->add(Carbon::fun(&FileManager::deleteFile), "deleteFile"); + m->add(Carbon::fun(&FileManager::getFileSize), "getFileSize"); + m->add(Carbon::fun(&FileManager::getFileDirectory), "getFileDirectory"); + + m->add(Carbon::fun(&glob::translate), "translate"); + m->add(Carbon::fun(&glob::expand_tilde), "expand_tilde"); + m->add(Carbon::fun(&glob::has_magic), "has_magic"); + m->add(Carbon::fun(&glob::is_hidden), "is_hidden"); + m->add(Carbon::fun(&glob::string_replace), "string_replace"); + m->add(Carbon::fun(&glob::is_recursive), "is_recursive"); + m->add(Carbon::fun(&glob::filter), "filter"); + m->add(Carbon::fun(&glob::glob0), "glob0"); + m->add(Carbon::fun(&glob::compile_pattern), "compile_pattern"); + m->add(Carbon::fun(&glob::glob1), "glob1"); + m->add(Carbon::fun(&glob::glob2), "glob2"); + m->add(Carbon::fun(&glob::iter_directory), "iter_directory"); + m->add(Carbon::fun(&glob::rlistdir), "rlistdir"); + + m->add(user_type(), "FileWrapper"); + //m->add(Carbon::constructor(), "FileWrapper"); + m->add(Carbon::fun( + static_cast&)>( + &FileWrapper::write)), + "write"); + m->add(Carbon::fun(&FileWrapper::read), "read"); + m->add(Carbon::fun(&FileWrapper::exists), "exists"); + m->add(Carbon::fun(&FileWrapper::remove), "remove"); + m->add(Carbon::fun(&FileWrapper::get_path), "get_path"); + m->add(Carbon::fun(&FileWrapper::is_binary_file), "is_binary_file"); + m->add(Carbon::fun(&FileWrapper::get_size), "get_size"); + m->add(Carbon::fun(&FileWrapper::get_size_string), "get_size_string"); + m->add(Carbon::fun(&FileWrapper::get_last_write_time), + "get_last_write_time"); + m->add(Carbon::fun(&FileWrapper::rename), "rename"); + m->add(Carbon::fun(&FileWrapper::copy_to), "copy_to"); + m->add(Carbon::fun(&FileWrapper::move_to), "move_to"); + m->add(Carbon::fun(&FileWrapper::is_empty), "is_empty"); + m->add(Carbon::fun( + static_cast&)>( + &FileWrapper::append)), + "append"); + + m->add(user_type(), "DirectoryWrapper"); + //m->add(Carbon::constructor(), + // "DirectoryWrapper"); + //m->add(Carbon::constructor(), + // "DirectoryWrapper"); + m->add(Carbon::fun(&DirectoryWrapper::exists), "exists"); + m->add(Carbon::fun(&DirectoryWrapper::get_path), "get_path"); + m->add(Carbon::fun(&DirectoryWrapper::get_size), "get_size"); + m->add(Carbon::fun(&DirectoryWrapper::get_size_string), "get_size_string"); + m->add(Carbon::fun(&DirectoryWrapper::remove), "remove"); + m->add(Carbon::fun(&DirectoryWrapper::create_directory), + "create_directory"); + m->add(Carbon::fun(&DirectoryWrapper::list_files), "list_files"); + m->add(Carbon::fun(&DirectoryWrapper::list_directories), + "list_directories"); + + m->add(Carbon::fun( + static_cast(&createDirectory)), + "createDirectory"); + m->add(Carbon::fun(&createDirectoriesRecursive), + "createDirectoriesRecursive"); + m->add(Carbon::fun(&removeDirectory), "removeDirectory"); + m->add(Carbon::fun(&renameDirectory), "renameDirectory"); + m->add(Carbon::fun(&moveDirectory), "moveDirectory"); + m->add(Carbon::fun(©File), "copyFile"); + m->add(Carbon::fun(&moveFile), "moveFile"); + m->add(Carbon::fun(&removeFile), "removeFile"); + m->add(Carbon::fun(&createSymlink), "createSymlink"); + m->add(Carbon::fun(&removeSymlink), "removeSymlink"); + m->add(Carbon::fun(&fileSize), "fileSize"); + m->add(Carbon::fun(&traverseDirectories), "traverseDirectories"); + m->add(Carbon::fun(&checkFileTypeInFolder), "checkFileTypeInFolder"); + m->add( + Carbon::fun(static_cast(&isFolderExists)), + "isFolderExists"); + m->add(Carbon::fun(static_cast(&isFolderExists)), + "isFolderExists"); + m->add( + Carbon::fun(static_cast(&isFileExists)), + "isFileExists"); + m->add(Carbon::fun(static_cast(&isFileExists)), + "isFileExists"); + m->add(Carbon::fun(&isFolderNameValid), "isFolderNameValid"); + m->add(Carbon::fun(&isFileNameValid), "isFileNameValid"); + m->add(Carbon::fun(&isAbsolutePath), "isAbsolutePath"); + m->add(Carbon::fun(&isExecutableFile), "isExecutableFile"); + m->add(Carbon::fun(&changeWorkingDirectory), "changeWorkingDirectory"); + m->add(Carbon::fun(&convertToLinuxPath), "convertToLinuxPath"); + m->add(Carbon::fun(&convertToWindowsPath), "convertToWindowsPath"); + m->add(Carbon::fun(&normPath), "normPath"); + m->add(Carbon::fun(&isFolderEmpty), "isFolderEmpty"); + m->add(Carbon::fun(&getFileTimes), "getFileTimes"); + m->add(Carbon::fun(&renameFile), "renameFile"); + return m; +} +} // namespace Atom::_Script::IO + +#endif \ No newline at end of file diff --git a/src/atom/io/compress.cpp b/src/atom/io/compress.cpp index e726deea..2dcd4c38 100644 --- a/src/atom/io/compress.cpp +++ b/src/atom/io/compress.cpp @@ -86,16 +86,26 @@ bool compress_file(const std::string &file_name, } char buf[CHUNK]; - while (in.read(buf, CHUNK)) { + while (in.read(buf, sizeof(buf))) { if (gzwrite(out, buf, static_cast(in.gcount())) == 0) { LOG_F(ERROR, "Failed to compress file {}", file_name); - in.close(); gzclose(out); return false; } } - in.close(); + if (in.eof()) { + if (gzwrite(out, buf, static_cast(in.gcount())) == 0) { + LOG_F(ERROR, "Failed to compress file {}", file_name); + gzclose(out); + return false; + } + } else if (in.bad()) { + LOG_F(ERROR, "Failed to read input file {}", file_name); + gzclose(out); + return false; + } + gzclose(out); DLOG_F(INFO, "Compressed file {} -> {}", file_name, output_path.string()); return true; @@ -109,17 +119,25 @@ bool compress_file_(const fs::path &file, gzFile out) { } char buf[CHUNK]; - while (in.read(buf, sizeof(buf)) || in.gcount()) { + while (in.read(buf, sizeof(buf))) { if (gzwrite(out, buf, static_cast(in.gcount())) != static_cast(in.gcount())) { - in.close(); - gzclose(out); - DLOG_F(ERROR, "Failed to compress file {}", file.string()); + LOG_F(ERROR, "Failed to compress file {}", file.string()); + return false; + } + } + + if (in.eof()) { + if (gzwrite(out, buf, static_cast(in.gcount())) != + static_cast(in.gcount())) { + LOG_F(ERROR, "Failed to compress file {}", file.string()); return false; } + } else if (in.bad()) { + LOG_F(ERROR, "Failed to read file {}", file.string()); + return false; } - in.close(); return true; } @@ -149,7 +167,7 @@ bool decompress_file(const std::string &file_name, char buf[CHUNK]; int bytesRead; - while ((bytesRead = gzread(in, buf, CHUNK)) > 0) { + while ((bytesRead = gzread(in, buf, sizeof(buf))) > 0) { if (fwrite(buf, 1, bytesRead, out) != static_cast(bytesRead)) { LOG_F(ERROR, "Failed to decompress file {}", file_name); fclose(out); @@ -158,6 +176,13 @@ bool decompress_file(const std::string &file_name, } } + if (bytesRead < 0) { + LOG_F(ERROR, "Failed to read compressed file {}", file_name); + fclose(out); + gzclose(in); + return false; + } + fclose(out); gzclose(in); DLOG_F(INFO, "Decompressed file {} -> {}", file_name, output_path.string()); @@ -174,36 +199,13 @@ bool compress_folder_(const fs::path &folder_name) { for (const auto &entry : fs::recursive_directory_iterator(folder_name)) { if (entry.is_directory()) { -#ifdef _WIN32 - std::string file_name = - fmt::format("{}\\{}", entry.path().string(), "*"); -#else - std::string file_name = - fmt::format("{}/{}", entry.path().string(), "*"); -#endif - for (const auto &sub_entry : fs::directory_iterator(file_name)) { + std::string file_pattern = entry.path().string() + "/*"; + for (const auto &sub_entry : fs::directory_iterator(file_pattern)) { if (sub_entry.is_regular_file()) { - std::ifstream in(sub_entry.path(), std::ios::binary); - if (!in) { - LOG_F(ERROR, "Failed to open file {}", - sub_entry.path().string()); - continue; + if (!compress_file_(sub_entry.path(), out)) { + gzclose(out); + return false; } - - char buf[CHUNK]; - while (in.read(buf, sizeof(buf)) || in.gcount()) { - if (gzwrite(out, buf, - static_cast(in.gcount())) != - static_cast(in.gcount())) { - in.close(); - gzclose(out); - LOG_F(ERROR, "Failed to compress file {}", - sub_entry.path().string()); - return false; - } - } - - in.close(); } } } else if (entry.is_regular_file()) { @@ -213,6 +215,7 @@ bool compress_folder_(const fs::path &folder_name) { } } } + gzclose(out); DLOG_F(INFO, "Compressed folder {} -> {}", folder_name.string(), outfile_name); diff --git a/src/atom/io/file.cpp b/src/atom/io/file.cpp index 640baba1..313bc514 100644 --- a/src/atom/io/file.cpp +++ b/src/atom/io/file.cpp @@ -21,19 +21,16 @@ Description: File Manager #include #include "atom/log/loguru.hpp" +#include "atom/utils/aes.hpp" #include -namespace Atom::IO { - -bool fileExists(const std::string &filename) { - return std::filesystem::exists(filename); -} +namespace fs = std::filesystem; -FileManager::FileManager() : m_file() {} +namespace Atom::IO { bool FileManager::createFile(const std::string &filename) { - if (fileExists(filename)) { + if (fs::exists(filename)) { LOG_F(ERROR, "File \"{}\" already exists!", filename); return false; } @@ -42,15 +39,12 @@ bool FileManager::createFile(const std::string &filename) { LOG_F(ERROR, "Error creating file \"{}\"!", filename); return false; } - outfile.close(); - std::fclose( - std::fopen(filename.c_str(), "a")); // create a link to the file DLOG_F(INFO, "Created file \"{}\"", filename); return true; } bool FileManager::openFile(const std::string &filename) { - if (!fileExists(filename)) { + if (!fs::exists(filename)) { LOG_F(ERROR, "File \"{}\" does not exist!", filename); return false; } @@ -69,9 +63,8 @@ bool FileManager::readFile(std::string &contents) { LOG_F(ERROR, "No file is currently open!"); return false; } - std::stringstream buffer; - buffer << m_file.rdbuf(); - contents = buffer.str(); + contents = std::string(std::istreambuf_iterator(m_file), + std::istreambuf_iterator()); DLOG_F(INFO, "Read contents of file \"{}\"", m_filename); return true; } @@ -88,16 +81,17 @@ bool FileManager::writeFile(const std::string &contents) { bool FileManager::moveFile(const std::string &oldFilename, const std::string &newFilename) { - if (!fileExists(oldFilename)) { + if (!fs::exists(oldFilename)) { LOG_F(ERROR, "File \"{}\" does not exist!", oldFilename); return false; } - if (fileExists(newFilename)) { + if (fs::exists(newFilename)) { LOG_F(ERROR, "File \"{}\" already exists!", newFilename); return false; } - int result = std::rename(oldFilename.c_str(), newFilename.c_str()); - if (result != 0) { + std::error_code ec; + fs::rename(oldFilename, newFilename, ec); + if (ec) { LOG_F(ERROR, "Could not move file from \"{}\" to \"{}\"!", oldFilename, newFilename); return false; @@ -107,11 +101,13 @@ bool FileManager::moveFile(const std::string &oldFilename, } bool FileManager::deleteFile(const std::string &filename) { - if (!fileExists(filename)) { + if (!fs::exists(filename)) { LOG_F(ERROR, "File \"{}\" does not exist!", filename); return false; } - if (std::remove(filename.c_str()) != 0) { + std::error_code ec; + fs::remove(filename, ec); + if (ec) { LOG_F(ERROR, "Could not delete file \"{}\"!", filename); return false; } @@ -124,74 +120,24 @@ long FileManager::getFileSize() { LOG_F(ERROR, "No file is currently open!"); return -1; } - m_file.seekg(0, m_file.end); - long fileSize = m_file.tellg(); - m_file.seekg(0, m_file.beg); - if (fileSize == -1) { + auto fileSize = fs::file_size(m_filename); + if (fileSize == static_cast(-1)) { LOG_F(ERROR, "Could not get file size of \"{}\"!", m_filename); } else { - DLOG_F(INFO, "File size of \"{}\" is %ld bytes", m_filename, fileSize); - } - return fileSize; -} - -std::string FileManager::calculateSHA256() { - if (!m_file.is_open()) { - LOG_F(ERROR, "No file is currently open!"); - return ""; - } - - EVP_MD_CTX *mdContext = EVP_MD_CTX_new(); - if (mdContext == nullptr) { - LOG_F(ERROR, "Failed to create EVP_MD_CTX"); - return ""; + DLOG_F(INFO, "File size of \"{}\" is {} bytes", m_filename, fileSize); } - - if (EVP_DigestInit_ex(mdContext, EVP_sha256(), nullptr) != 1) { - LOG_F(ERROR, "Failed to initialize EVP_MD_CTX"); - EVP_MD_CTX_free(mdContext); - return ""; - } - - char buffer[1024]; - while (m_file.read(buffer, sizeof(buffer))) { - if (EVP_DigestUpdate(mdContext, buffer, sizeof(buffer)) != 1) { - LOG_F(ERROR, "Failed to update EVP_MD_CTX"); - EVP_MD_CTX_free(mdContext); - return ""; - } - } - - unsigned char hash[EVP_MAX_MD_SIZE]; - unsigned int hashLength = 0; - if (EVP_DigestFinal_ex(mdContext, hash, &hashLength) != 1) { - LOG_F(ERROR, "Failed to finalize EVP_MD_CTX"); - EVP_MD_CTX_free(mdContext); - return ""; - } - - EVP_MD_CTX_free(mdContext); - - std::stringstream sha256Stream; - sha256Stream << std::hex << std::setfill('0'); - for (unsigned int i = 0; i < hashLength; ++i) { - sha256Stream << std::setw(2) << static_cast(hash[i]); - } - - DLOG_F(INFO, "SHA-256 value for file \"{}\" is {}", m_filename, - sha256Stream.str()); - return sha256Stream.str(); + return static_cast(fileSize); } std::string FileManager::getFileDirectory(const std::string &filename) { - size_t pos = filename.find_last_of("/\\"); - if (pos == std::string::npos) { + auto parentPath = fs::path(filename).parent_path(); + if (parentPath.empty()) { LOG_F(ERROR, "Could not get directory of file \"{}\"", filename); return ""; } else { - std::string directory = filename.substr(0, pos); - DLOG_F(INFO, "Directory of file \"{}\" is \"{}\"", filename, directory); - return directory; + DLOG_F(INFO, "Directory of file \"{}\" is \"{}\"", filename, + parentPath.string()); + return parentPath.string(); } } diff --git a/src/atom/io/file.hpp b/src/atom/io/file.hpp index 000f8b68..59ebe6bc 100644 --- a/src/atom/io/file.hpp +++ b/src/atom/io/file.hpp @@ -25,11 +25,6 @@ namespace Atom::IO { */ class FileManager { public: - /** - * 默认构造函数 - */ - FileManager(); - /** * 创建文件 * @param filename 文件名 @@ -80,12 +75,6 @@ class FileManager { */ long getFileSize(); - /** - * 计算文件的MD5值 - * @return 文件的MD5值 - */ - std::string calculateSHA256(); - /** * 获取文件所在目录路径 * @param filename 文件名 @@ -98,13 +87,6 @@ class FileManager { std::string m_filename; ///< 当前打开的文件名 }; -/** - * 检查文件是否存在 - * @param filename 文件名 - * @return 文件是否存在 - */ -bool fileExists(const std::string &filename); - } // namespace Atom::IO #endif diff --git a/src/atom/io/idirectory.cpp b/src/atom/io/idirectory.cpp new file mode 100644 index 00000000..f591bd65 --- /dev/null +++ b/src/atom/io/idirectory.cpp @@ -0,0 +1,85 @@ +/* + * idirectory.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-23 + +Description: Directory Wrapper + +**************************************************/ + +#include "idirectory.hpp" + +#include +#include + +namespace Atom::IO { +DirectoryWrapper::DirectoryWrapper(const fs::path& dir_path) + : dir_path_(dir_path) {} + +DirectoryWrapper::DirectoryWrapper(const std::string& dir_path) + : dir_path_(dir_path) {} + +bool DirectoryWrapper::exists() const { + return fs::exists(dir_path_) && fs::is_directory(dir_path_); +} + +void DirectoryWrapper::remove() { + if (exists()) { + fs::remove_all(dir_path_); + } +} + +fs::path DirectoryWrapper::get_path() const { return dir_path_; } + +uintmax_t DirectoryWrapper::get_size() const { + uintmax_t size = 0; + for (const auto& entry : fs::recursive_directory_iterator(dir_path_)) { + if (fs::is_regular_file(entry)) { + size += fs::file_size(entry); + } + } + return size; +} + +std::string DirectoryWrapper::get_size_string() const { + uintmax_t size = get_size(); + if (size < 1024) { + return std::to_string(size) + " B"; + } else if (size < 1024 * 1024) { + return std::to_string(size / 1024) + " KB"; + } else if (size < 1024 * 1024 * 1024) { + return std::to_string(size / (1024 * 1024)) + " MB"; + } else { + return std::to_string(size / (1024 * 1024 * 1024)) + " GB"; + } +} + +std::vector DirectoryWrapper::list_files() const { + std::vector files; + for (const auto& entry : fs::directory_iterator(dir_path_)) { + if (fs::is_regular_file(entry)) { + files.push_back(entry.path()); + } + } + return files; +} + +std::vector DirectoryWrapper::list_directories() const { + std::vector directories; + for (const auto& entry : fs::directory_iterator(dir_path_)) { + if (fs::is_directory(entry)) { + directories.push_back(entry.path()); + } + } + return directories; +} + +void DirectoryWrapper::create_directory(const std::string& name) const { + fs::create_directory(dir_path_ / name); +} +} // namespace Atom::IO diff --git a/src/atom/io/idirectory.hpp b/src/atom/io/idirectory.hpp new file mode 100644 index 00000000..13f6c469 --- /dev/null +++ b/src/atom/io/idirectory.hpp @@ -0,0 +1,97 @@ +/* + * idirectory.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-23 + +Description: Directory Wrapper + +**************************************************/ + +#ifndef ATOM_IO_IDIRECTORY_HPP +#define ATOM_IO_IDIRECTORY_HPP + +#include +#include +#include + +namespace fs = std::filesystem; + +namespace Atom::IO { +/** + * @brief A wrapper class for interacting with directories. + */ +class DirectoryWrapper { +public: + /** + * @brief Constructs a DirectoryWrapper object with the specified directory + * path. + * @param dir_path The path to the directory. + */ + explicit DirectoryWrapper(const fs::path& dir_path); + + /** + * @brief Constructs a DirectoryWrapper object with the specified directory + * path. + * @param dir_path The path to the directory. + */ + explicit DirectoryWrapper(const std::string& dir_path); + + /** + * @brief Checks if the directory exists. + * @return True if the directory exists, otherwise false. + */ + bool exists() const; + + /** + * @brief Removes the directory and its contents. + */ + void remove(); + + /** + * @brief Gets the path of the directory. + * @return The path of the directory. + */ + fs::path get_path() const; + + /** + * @brief Gets the size of the directory in bytes. + * @return The size of the directory. + */ + uintmax_t get_size() const; + + /** + * @brief Gets the size of the directory as a string representation. + * @return The size of the directory as a string. + */ + std::string get_size_string() const; + + /** + * @brief Lists all files in the directory. + * @return A vector containing paths to all files in the directory. + */ + std::vector list_files() const; + + /** + * @brief Lists all subdirectories in the directory. + * @return A vector containing paths to all subdirectories in the directory. + */ + std::vector list_directories() const; + + /** + * @brief Creates a new directory within the current directory. + * @param name The name of the new directory to create. + */ + void create_directory(const std::string& name) const; + +private: + fs::path dir_path_; ///< The path to the directory. +}; + +} // namespace Atom::IO + +#endif diff --git a/src/atom/io/ifile.cpp b/src/atom/io/ifile.cpp new file mode 100644 index 00000000..c5de7f77 --- /dev/null +++ b/src/atom/io/ifile.cpp @@ -0,0 +1,206 @@ +/* + * ifile.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-23 + +Description: File Wrapper + +**************************************************/ + +#include "ifile.hpp" + +#include +#include + +namespace Atom::IO { +FileWrapper::FileWrapper(const fs::path& file_path) : file_path_(file_path) {} + +void FileWrapper::write(const std::string& content) { write_file(content); } + +void FileWrapper::write(const std::vector& content) { + write_file(content); +} + +std::variant> FileWrapper::read() { + if (is_binary_file()) { + return read_file>(); + } else { + return read_file(); + } +} + +bool FileWrapper::exists() const { return fs::exists(file_path_); } + +void FileWrapper::remove() { + if (exists()) { + fs::remove(file_path_); + } +} + +fs::path FileWrapper::get_path() const { return file_path_; } + +bool FileWrapper::is_binary_file() const { + std::ifstream file(file_path_, std::ios::binary); + if (file.is_open()) { + char byte; + while (file.get(byte)) { + if (byte == '\0') { + return true; + } + } + } + return false; +} + +uintmax_t FileWrapper::get_size() const { return fs::file_size(file_path_); } + +std::string FileWrapper::get_size_string() const { + auto size = get_size(); + std::stringstream ss; + if (size < 1024) { + ss << size << " B"; + } else if (size < 1024 * 1024) { + ss << std::fixed << std::setprecision(2) << size / 1024.0 << " KB"; + } else if (size < 1024 * 1024 * 1024) { + ss << std::fixed << std::setprecision(2) << size / (1024.0 * 1024.0) + << " MB"; + } else { + ss << std::fixed << std::setprecision(2) + << size / (1024.0 * 1024.0 * 1024.0) << " GB"; + } + return ss.str(); +} + +std::string FileWrapper::get_last_write_time() const { + auto last_write_time = fs::last_write_time(file_path_); + auto time_point = + std::chrono::time_point_cast( + last_write_time - fs::file_time_type::clock::now() + + std::chrono::system_clock::now()); + auto time_t = std::chrono::system_clock::to_time_t(time_point); + std::stringstream ss; + ss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S"); + return ss.str(); +} + +void FileWrapper::rename(const fs::path& new_path) { + fs::rename(file_path_, new_path); + file_path_ = new_path; +} + +void FileWrapper::copy_to(const fs::path& destination) const { + fs::copy_file(file_path_, destination, + fs::copy_options::overwrite_existing); +} + +void FileWrapper::move_to(const fs::path& destination) { + fs::rename(file_path_, destination); + file_path_ = destination; +} + +bool FileWrapper::is_empty() const { return get_size() == 0; } + +void FileWrapper::append(const std::string& content) { + std::ofstream file(file_path_, std::ios::app); + if (!file) { + throw std::runtime_error("无法打开文件进行追加: " + + file_path_.string()); + } + file << content; +} + +void FileWrapper::append(const std::vector& content) { + std::ofstream file(file_path_, std::ios::app | std::ios::binary); + if (!file) { + throw std::runtime_error("无法打开文件进行追加: " + + file_path_.string()); + } + file.write(reinterpret_cast(content.data()), content.size()); +} + +void FileWrapper::write_at(const std::string& content, + std::streampos position) { + std::fstream file(file_path_, std::ios::in | std::ios::out); + if (!file) { + throw std::runtime_error("无法打开文件进行写入: " + + file_path_.string()); + } + file.seekp(position); + file << content; +} + +void FileWrapper::write_at(const std::vector& content, + std::streampos position) { + std::fstream file(file_path_, + std::ios::in | std::ios::out | std::ios::binary); + if (!file) { + throw std::runtime_error("无法打开文件进行写入: " + + file_path_.string()); + } + file.seekp(position); + file.write(reinterpret_cast(content.data()), content.size()); +} + +std::variant> FileWrapper::read_from( + std::streampos start, std::streamsize count) { + std::ifstream file(file_path_, std::ios::binary); + if (!file) { + throw std::runtime_error("无法打开文件进行读取: " + + file_path_.string()); + } + file.seekg(start); + if (is_binary_file()) { + std::vector content(count); + file.read(reinterpret_cast(content.data()), count); + return content; + } else { + std::string content(count, '\0'); + file.read(&content[0], count); + return content; + } +} + +std::string FileWrapper::get_extension() const { + return file_path_.extension().string(); +} + +std::string FileWrapper::get_stem() const { return file_path_.stem().string(); } + +std::string FileWrapper::get_parent_path() const { + return file_path_.parent_path().string(); +} + +bool FileWrapper::is_directory() const { return fs::is_directory(file_path_); } + +bool FileWrapper::is_regular_file() const { + return fs::is_regular_file(file_path_); +} + +bool FileWrapper::is_symlink() const { return fs::is_symlink(file_path_); } + +std::uintmax_t FileWrapper::get_hard_link_count() const { + return fs::hard_link_count(file_path_); +} + +void FileWrapper::create_symlink(const fs::path& target) { + fs::create_symlink(target, file_path_); +} + +void FileWrapper::create_hardlink(const fs::path& target) { + fs::create_hard_link(target, file_path_); +} + +void FileWrapper::permissions(fs::perms prms) { + fs::permissions(file_path_, prms); +} + +fs::perms FileWrapper::permissions() const { + return fs::status(file_path_).permissions(); +} + +} // namespace Atom::IO diff --git a/src/atom/io/ifile.hpp b/src/atom/io/ifile.hpp new file mode 100644 index 00000000..1bc60098 --- /dev/null +++ b/src/atom/io/ifile.hpp @@ -0,0 +1,250 @@ +/* + * ifile.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-23 + +Description: File Wrapper + +**************************************************/ + +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +namespace Atom::IO { + +class FileWrapper { +public: + /** + * @brief Constructs a FileWrapper object with the given file path. + * @param file_path The path to the file. + */ + explicit FileWrapper(const fs::path& file_path); + + /** + * @brief Writes string content to the file. + * @param content The string content to be written. + */ + void write(const std::string& content); + + /** + * @brief Writes binary content to the file. + * @param content The binary content to be written. + */ + void write(const std::vector& content); + + /** + * @brief Reads content from the file. + * @return A variant containing either a string or binary content. + */ + std::variant> read(); + + /** + * @brief Checks if the file exists. + * @return True if the file exists, false otherwise. + */ + bool exists() const; + + /** + * @brief Removes the file. + */ + void remove(); + + /** + * @brief Gets the path of the file. + * @return The path of the file. + */ + fs::path get_path() const; + + /** + * @brief Checks if the file is a binary file. + * @return True if the file is binary, false otherwise. + */ + bool is_binary_file() const; + + /** + * @brief Gets the size of the file. + * @return The size of the file in bytes. + */ + uintmax_t get_size() const; + + /** + * @brief Gets the size of the file as a human-readable string. + * @return The size of the file as a string (e.g., "10 KB"). + */ + std::string get_size_string() const; + + /** + * @brief Gets the last write time of the file. + * @return A string representing the last write time of the file. + */ + std::string get_last_write_time() const; + + /** + * @brief Renames the file. + * @param new_path The new path of the file. + */ + void rename(const fs::path& new_path); + + /** + * @brief Copies the file to a new location. + * @param destination The destination path. + */ + void copy_to(const fs::path& destination) const; + + /** + * @brief Moves the file to a new location. + * @param destination The destination path. + */ + void move_to(const fs::path& destination); + + /** + * @brief Checks if the file is empty. + * @return True if the file is empty, false otherwise. + */ + bool is_empty() const; + + /** + * @brief Appends content to the file. + * @param content The content to be appended. + */ + void append(const std::string& content); + + /** + * @brief Appends binary content to the file. + * @param content The binary content to be appended. + */ + void append(const std::vector& content); + + /** + * @brief Writes content to the file at a specific position. + * @param content The content to be written. + * @param position The position to write the content. + */ + void write_at(const std::string& content, std::streampos position); + + /** + * @brief Writes binary content to the file at a specific position. + * @param content The binary content to be written. + * @param position The position to write the content. + */ + void write_at(const std::vector& content, std::streampos position); + + /** + * @brief Reads content from the file at a specific position. + * @param start The starting position. + * @param count The number of bytes to read. + * @return A variant containing either a string or binary content. + */ + std::variant> read_from( + std::streampos start, std::streamsize count); + + /** + * @brief Gets the extension of the file. + * @return The extension of the file. + */ + std::string get_extension() const; + + /** + * @brief Gets the stem of the file. + * @return The stem of the file. + */ + std::string get_stem() const; + + /** + * @brief Gets the parent path of the file. + * @return The parent path of the file. + */ + std::string get_parent_path() const; + + /** + * @brief Checks if the file is a directory. + * @return True if the file is a directory, false otherwise. + */ + bool is_directory() const; + + /** + * @brief Checks if the file is a regular file. + * @return True if the file is a regular file, false otherwise. + */ + bool is_regular_file() const; + + /** + * @brief Checks if the file is a symbolic link. + * @return True if the file is a symbolic link, false otherwise. + */ + bool is_symlink() const; + + /** + * @brief Gets the hard link count of the file. + * @return The hard link count of the file. + */ + std::uintmax_t get_hard_link_count() const; + + /** + * @brief Creates a symbolic link to the file. + * @param target The target path of the symbolic link. + */ + void create_symlink(const fs::path& target); + + /** + * @brief Creates a hard link to the file. + * @param target The target path of the hard link. + */ + void create_hardlink(const fs::path& target); + + /** + * @brief Sets the permissions of the file. + * @param prms The permissions to set. + */ + void permissions(fs::perms prms); + + /** + * @brief Gets the permissions of the file. + * @return The permissions of the file. + */ + fs::perms permissions() const; + +private: + fs::path file_path_; + + /** + * @brief Writes content to the file. + * @tparam T The type of content to be written. + * @param content The content to be written. + */ + template + void write_file(const T& content) { + auto file = std::ofstream(file_path_, std::ios::binary); + if (!file) { + throw std::runtime_error("Failed to write " + file_path_.string()); + } + file.write(reinterpret_cast(content.data()), + content.size()); + } + + /** + * @brief Reads content from the file. + * @tparam T The type of content to be read. + * @return The content read from the file. + */ + template + T read_file() { + auto file = std::ifstream(file_path_, std::ios::binary); + if (!file) { + throw std::runtime_error("Failed to read " + file_path_.string()); + } + return T(std::istreambuf_iterator(file), {}); + } +}; +} // namespace Atom::IO diff --git a/src/atom/io/io.cpp b/src/atom/io/io.cpp index 6ba5dd92..2fd7b502 100644 --- a/src/atom/io/io.cpp +++ b/src/atom/io/io.cpp @@ -19,10 +19,15 @@ Description: IO #include #include #include +#include #include "atom/log/loguru.hpp" #include "atom/utils/string.hpp" +#if __cplusplus >= 202002L +#include +#endif + #ifdef _WIN32 #include const std::string PATH_SEPARATOR = "\\"; @@ -77,7 +82,7 @@ void createDirectory(const std::string &date, const std::string &rootDir) { return; } - std::vector tokens = Atom::Utils::splitString(date, '/'); + auto tokens = Atom::Utils::splitString(date, '/'); // Create directories fs::path currentDir = rootDir; @@ -86,8 +91,8 @@ void createDirectory(const std::string &date, const std::string &rootDir) { if (!fs::is_directory(currentDir)) { if (!fs::create_directory(currentDir)) { - LOG_F(ERROR, "Error: Failed to create directory - {}" - , currentDir.string()); + LOG_F(ERROR, "Error: Failed to create directory - {}", + currentDir.string()); return; } } else { @@ -97,6 +102,58 @@ void createDirectory(const std::string &date, const std::string &rootDir) { DLOG_F(INFO, "Directory creation completed: {}", currentDir.string()); } +bool createDirectoriesRecursive(const fs::path &basePath, + const std::vector &subdirs, + const CreateDirectoriesOptions &options = {}) { + for (size_t i = 0; i < subdirs.size(); ++i) { + const std::string &subdir = subdirs[i]; + +#if __cplusplus >= 202002L + std::string fullPath = std::format("{}/{}", basePath.string(), subdir); +#else + std::string fullPath = basePath.string() + "/" + subdir; +#endif + + if (!options.filter(subdir)) { + if (options.verbose) { + LOG_F(INFO, "Skipping directory (filtered out): {}", fullPath); + } + continue; + } + if (fs::exists(fullPath)) { + if (!fs::is_directory(fullPath)) { + LOG_F(ERROR, "Path exists but is not a directory: {}", + fullPath); + return false; + } + if (options.verbose) { + LOG_F(INFO, "Directory already exists: {}", fullPath); + } + continue; + } + + if (!options.dryRun) { + std::error_code ec; + if (!fs::create_directories(fullPath, ec)) { + std::cerr << "Failed to create directory: " << fullPath + << ", error: " << ec.message() << std::endl; + return false; + } + } + + if (options.verbose) { + LOG_F(INFO, "Creating directory: {}", fullPath); + } + options.onCreate(fullPath); + if (options.delay > 0) { + std::this_thread::sleep_for( + std::chrono::milliseconds(options.delay)); + } + } + + return true; +} + bool removeDirectory(const std::string &path) { ATOM_IO_CHECK_ARGUMENT(path); try { @@ -311,13 +368,22 @@ bool isFolderExists(const std::string &folderName) { return fs::exists(folderName) && fs::is_directory(folderName); } +bool isFolderExists(const fs::path &folderName) { + return isFolderExists(folderName.string()); +} + bool isFileExists(const std::string &fileName) { if (!isFileNameValid(fileName)) { + LOG_F(ERROR, "Invalid file name: {}", fileName); return false; } return fs::exists(fileName) && fs::is_regular_file(fileName); } +bool isFileExists(const fs::path &fileName) { + return isFileExists(fileName.string()); +} + bool isFolderEmpty(const std::string &folderName) { if (!isFolderExists(folderName)) { return false; @@ -460,4 +526,40 @@ std::vector checkFileTypeInFolder(const std::string &folderPath, return files; } + +bool isExecutableFile(const std::string &fileName, const std::string &fileExt) { +#ifdef _WIN32 + fs::path filePath = fileName + fileExt; +#else + fs::path filePath = fileName; +#endif + + DLOG_F(INFO, "Checking file '{}'.", filePath.string()); + + if (!fs::exists(filePath)) { + DLOG_F(WARNING, "The file '{}' does not exist.", filePath.string()); + return false; + } + +#ifdef _WIN32 + if (!fs::is_regular_file(filePath) || + !(GetFileAttributesA(filePath.generic_string().c_str()) & + FILE_ATTRIBUTE_DIRECTORY)) { + DLOG_F(WARNING, + "The file '{}' is not a regular file or is not executable.", + filePath.string()); + return false; + } +#else + if (!fs::is_regular_file(filePath) || access(filePath.c_str(), X_OK) != 0) { + DLOG_F(WARNING, + "The file '{}' is not a regular file or is not executable.", + filePath.string()); + return false; + } +#endif + + DLOG_F(INFO, "The file '{}' exists and is executable.", filePath.string()); + return true; +} } // namespace Atom::IO diff --git a/src/atom/io/io.hpp b/src/atom/io/io.hpp index ab6393dd..51005e81 100644 --- a/src/atom/io/io.hpp +++ b/src/atom/io/io.hpp @@ -17,6 +17,7 @@ Description: IO #include #include +#include #include #include namespace fs = std::filesystem; @@ -35,6 +36,39 @@ namespace Atom::IO { */ [[nodiscard]] bool createDirectory(const std::string &path); +/** + * @brief Creates a directory with the specified path. +*/ +struct CreateDirectoriesOptions { + bool verbose = true; + bool dryRun = false; + int delay = 0; + std::function filter = [](const std::string&) { + return true; + }; + std::function onCreate = [](const std::string&) { + }; +}; + +/** + * @brief Creates a directory with the specified path. + * + * @param basePath The base path of the directory to create. + * @param subdirs The subdirectories to create. + * @param options The options for creating the directory. + * @return True if the operation was successful, false otherwise. + * + * 使用指定路径创建一个目录。 + * + * @param basePath 要创建的目录的基本路径。 + * @param subdirs 要创建的子目录。 + * @param options 创建目录的选项。 + * @return 如果操作成功,则返回true,否则返回false。 + */ +bool createDirectoriesRecursive(const fs::path& basePath, + const std::vector& subdirs, + const CreateDirectoriesOptions& options); + /** * @brief Creates a directory with the specified path. * @@ -272,6 +306,7 @@ void traverseDirectories(const std::filesystem::path &directory, * @return 如果文件夹存在,则返回true,否则返回false。 */ [[nodiscard]] bool isFolderExists(const std::string &folderPath); +[[nodiscard]] bool isFolderExists(const fs::path &folderPath); /** * @brief Check if the file exists. @@ -285,6 +320,7 @@ void traverseDirectories(const std::filesystem::path &directory, * @return 如果文件存在,则返回true,否则返回false。 */ [[nodiscard]] bool isFileExists(const std::string &filePath); +[[nodiscard]] bool isFileExists(const fs::path &filePath); /** * @brief Check if the folder is empty. @@ -345,6 +381,19 @@ enum class FileOption { Path, Name }; [[nodiscard]] std::vector checkFileTypeInFolder( const std::string &folderPath, const std::string &fileType, FileOption fileOption); + +/** + * @brief Check whether the specified file exists. + * 检查指定文件是否存在 + * + * @param fileName The name of the file. 文件名称 + * @param fileExt The extension of the file. 文件扩展名 + * @return true if the file exists. + * 如果文件存在,则返回 true + * @return false if the file does not exist or an error occurred. + * 如果文件不存在或发生错误,则返回 false + */ +bool isExecutableFile(const std::string &fileName, const std::string &fileExt); } // namespace Atom::IO #endif diff --git a/src/atom/io/meson.build b/src/atom/io/meson.build new file mode 100644 index 00000000..2a98279d --- /dev/null +++ b/src/atom/io/meson.build @@ -0,0 +1,42 @@ +project('atom-io', 'cpp') + +# Sources +atom_io_sources = [ + 'compress.cpp', + 'file.cpp', + 'io.cpp', +] + +# Headers +atom_io_headers = [ + 'compress.hpp', + 'file.hpp', + 'glob.hpp', + 'io.hpp', +] + +# Build Object Library +atom_io_object = library( + 'atom-io-object', + atom_io_sources, + include_directories: include_directories('.') +) + +# Link libraries +atom_io_object_deps = [ + dependency('loguru'), + dependency('libzippp') +] + +# Build Static Library +atom_io_static = library( + 'atom-io', + atom_io_sources, + include_directories: include_directories('.'), + dependencies: atom_io_object_deps, + version: '1.0', + soversion: '1' +) + +# Install +install_targets(atom_io_static) diff --git a/src/atom/io/xmake.lua b/src/atom/io/xmake.lua new file mode 100644 index 00000000..bbc5ff37 --- /dev/null +++ b/src/atom/io/xmake.lua @@ -0,0 +1,34 @@ +-- xmake.lua for Atom-IO + +-- Project Information +set_project("Atom-IO") +set_version("1.0.0") +set_description("IO Components for Element Astro Project") +set_licenses("GPL-3.0") + +-- Specify the C++ Languages +set_languages("c++17") + +-- Add Source Files +add_files("*.cpp") +add_files("*.hpp") + +-- Add Private Header Files +add_headerfiles("*.hpp", {prefixdir = "$(projectdir)"}) + +-- Add Target +target("Atom-IO") + set_kind("static") + add_packages("loguru", "libzippp") + add_links("pthread") + add_includedirs(".") + +-- Install Rules +after_install("install_headers") +after_install("install_libraries") + +-- Install Headers +install_headers("*.hpp", "$(projectdir)/include/$(projectname)") + +-- Install Libraries +install_libraries("$(targetdir)/*.a", "$(projectdir)/lib") \ No newline at end of file diff --git a/src/atom/log/CMakeLists.txt b/src/atom/log/CMakeLists.txt index 981af7ed..7034d88a 100644 --- a/src/atom/log/CMakeLists.txt +++ b/src/atom/log/CMakeLists.txt @@ -80,7 +80,11 @@ endif() # ---------------------------------------------------------- set(LOGURU_USE_FMTLIB On) -set(LOGURU_STACKTRACES On) + +if (WIN32) + find_package(dlfcn-win32 REQUIRED) + set(CMAKE_DL_LIBS dlfcn-win32::dl) +endif () if (LOGURU_STACKTRACES AND (NOT CMAKE_DL_LIBS)) message(WARNING @@ -107,11 +111,20 @@ target_include_directories(loguru target_compile_features(loguru PUBLIC cxx_std_11) find_package(Threads REQUIRED) # defines IMPORTED target Threads::Threads +if(WIN32) +target_link_libraries(loguru + PUBLIC + Threads::Threads # pthreads (or equivalent) + dlfcn-win32::dl + dbghelp +) +else() target_link_libraries(loguru PUBLIC Threads::Threads # pthreads (or equivalent) ${_lib_dl_linkflag} # dl (or equivalent) ) +endif() set_target_properties(loguru PROPERTIES diff --git a/src/atom/log/loguru.cpp b/src/atom/log/loguru.cpp index 997d0c61..6735f0b9 100644 --- a/src/atom/log/loguru.cpp +++ b/src/atom/log/loguru.cpp @@ -17,7 +17,8 @@ #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" #elif defined(_MSC_VER) #pragma warning(push) -#pragma warning(disable:4365) // conversion from 'X' to 'Y', signed/unsigned mismatch +#pragma warning( \ + disable : 4365) // conversion from 'X' to 'Y', signed/unsigned mismatch #endif #include "loguru.hpp" @@ -25,7 +26,8 @@ #ifndef LOGURU_HAS_BEEN_IMPLEMENTED #define LOGURU_HAS_BEEN_IMPLEMENTED -#define LOGURU_PREAMBLE_WIDTH (53 + LOGURU_THREADNAME_WIDTH + LOGURU_FILENAME_WIDTH) +#define LOGURU_PREAMBLE_WIDTH \ + (53 + LOGURU_THREADNAME_WIDTH + LOGURU_FILENAME_WIDTH) #undef min #undef max @@ -51,209 +53,206 @@ #endif #ifdef _WIN32 - #include - - #define localtime_r(a, b) localtime_s(b, a) // No localtime_r with MSVC, but arguments are swapped for localtime_s +#include +#include +#define localtime_r(a, b) \ + localtime_s(b, a) // No localtime_r with MSVC, but arguments are swapped + // for localtime_s #else - #include - #include // mkdir - #include // STDERR_FILENO +#include +#include // mkdir +#include // STDERR_FILENO #endif #ifdef __linux__ - #include // PATH_MAX +#include // PATH_MAX #elif !defined(_WIN32) - #include // PATH_MAX +#include // PATH_MAX #endif #ifndef PATH_MAX - #define PATH_MAX 1024 +#define PATH_MAX 1024 #endif #ifdef __APPLE__ - #include "TargetConditionals.h" +#include "TargetConditionals.h" #endif // TODO: use defined(_POSIX_VERSION) for some of these things? #if defined(_WIN32) || defined(__CYGWIN__) - #define LOGURU_PTHREADS 0 - #define LOGURU_WINTHREADS 1 - #ifndef LOGURU_STACKTRACES - #define LOGURU_STACKTRACES 0 - #endif +#define LOGURU_PTHREADS 0 +#define LOGURU_WINTHREADS 1 +#ifndef LOGURU_STACKTRACES +#define LOGURU_STACKTRACES 0 +#endif +#else +#define LOGURU_PTHREADS 1 +#define LOGURU_WINTHREADS 0 +#ifdef __GLIBC__ +#ifndef LOGURU_STACKTRACES +#define LOGURU_STACKTRACES 1 +#endif #else - #define LOGURU_PTHREADS 1 - #define LOGURU_WINTHREADS 0 - #ifdef __GLIBC__ - #ifndef LOGURU_STACKTRACES - #define LOGURU_STACKTRACES 1 - #endif - #else - #ifndef LOGURU_STACKTRACES - #define LOGURU_STACKTRACES 0 - #endif - #endif +#ifndef LOGURU_STACKTRACES +#define LOGURU_STACKTRACES 0 +#endif +#endif #endif #if LOGURU_STACKTRACES - #include // for __cxa_demangle - #include // for dladdr - #include // for backtrace -#endif // LOGURU_STACKTRACES +#include // for __cxa_demangle +#include // for dladdr +#include // for backtrace +#endif // LOGURU_STACKTRACES #if LOGURU_PTHREADS - #include - #if defined(__FreeBSD__) - #include - #include - #elif defined(__OpenBSD__) - #include - #endif - - #ifdef __linux__ - /* On Linux, the default thread name is the same as the name of the binary. - Additionally, all new threads inherit the name of the thread it got forked from. - For this reason, Loguru use the pthread Thread Local Storage - for storing thread names on Linux. */ - #ifndef LOGURU_PTLS_NAMES - #define LOGURU_PTLS_NAMES 1 - #endif - #endif +#include +#if defined(__FreeBSD__) +#include +#include +#elif defined(__OpenBSD__) +#include +#endif + +#ifdef __linux__ +/* On Linux, the default thread name is the same as the name of the binary. + Additionally, all new threads inherit the name of the thread it got forked + from. For this reason, Loguru use the pthread Thread Local Storage for + storing thread names on Linux. */ +#ifndef LOGURU_PTLS_NAMES +#define LOGURU_PTLS_NAMES 1 +#endif +#endif #endif #if LOGURU_WINTHREADS - #ifndef _WIN32_WINNT - #define _WIN32_WINNT 0x0502 - #endif - #define WIN32_LEAN_AND_MEAN - #include - #include +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0502 +#endif +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include #endif #ifndef LOGURU_PTLS_NAMES - #define LOGURU_PTLS_NAMES 0 +#define LOGURU_PTLS_NAMES 0 #endif LOGURU_ANONYMOUS_NAMESPACE_BEGIN -namespace loguru -{ - using namespace std::chrono; +namespace loguru { +using namespace std::chrono; #if LOGURU_WITH_FILEABS - struct FileAbs - { - char path[PATH_MAX]; - char mode_str[4]; - Verbosity verbosity; - struct stat st; - FILE* fp; - bool is_reopening = false; // to prevent recursive call in file_reopen. - decltype(steady_clock::now()) last_check_time = steady_clock::now(); - }; +struct FileAbs { + char path[PATH_MAX]; + char mode_str[4]; + Verbosity verbosity; + struct stat st; + FILE* fp; + bool is_reopening = false; // to prevent recursive call in file_reopen. + decltype(steady_clock::now()) last_check_time = steady_clock::now(); +}; #else - typedef FILE* FileAbs; +typedef FILE* FileAbs; +#endif + +struct Callback { + std::string id; + log_handler_t callback; + void* user_data; + Verbosity verbosity; // Does not change! + close_handler_t close; + flush_handler_t flush; + unsigned indentation; +}; + +using CallbackVec = std::vector; + +using StringPair = std::pair; +using StringPairList = std::vector; + +const auto s_start_time = steady_clock::now(); + +Verbosity g_stderr_verbosity = Verbosity_0; +bool g_colorlogtostderr = true; +unsigned g_flush_interval_ms = 0; +bool g_preamble_header = true; +bool g_preamble = true; + +Verbosity g_internal_verbosity = Verbosity_0; + +// Preamble details +bool g_preamble_date = true; +bool g_preamble_time = true; +bool g_preamble_uptime = true; +bool g_preamble_thread = true; +bool g_preamble_file = true; +bool g_preamble_verbose = true; +bool g_preamble_pipe = true; + +static std::recursive_mutex s_mutex; +static Verbosity s_max_out_verbosity = Verbosity_OFF; +static std::string s_argv0_filename; +static std::string s_arguments; +static char s_current_dir[PATH_MAX]; +static CallbackVec s_callbacks; +static fatal_handler_t s_fatal_handler = nullptr; +static verbosity_to_name_t s_verbosity_to_name_callback = nullptr; +static name_to_verbosity_t s_name_to_verbosity_callback = nullptr; +static StringPairList s_user_stack_cleanups; +static bool s_strip_file_path = true; +static std::atomic s_stderr_indentation{0}; + +// For periodic flushing: +static std::thread* s_flush_thread = nullptr; +static bool s_needs_flushing = false; + +static SignalOptions s_signal_options = SignalOptions::none(); + +static const bool s_terminal_has_color = []() { +#ifdef _WIN32 +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 #endif + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut != INVALID_HANDLE_VALUE) { + DWORD dwMode = 0; + GetConsoleMode(hOut, &dwMode); + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + return SetConsoleMode(hOut, dwMode) != 0; + } + return false; +#else + if (!isatty(STDERR_FILENO)) { + return false; + } + if (const char* term = getenv("TERM")) { + return 0 == strcmp(term, "cygwin") || 0 == strcmp(term, "linux") || + 0 == strcmp(term, "rxvt-unicode-256color") || + 0 == strcmp(term, "screen") || + 0 == strcmp(term, "screen-256color") || + 0 == strcmp(term, "screen.xterm-256color") || + 0 == strcmp(term, "tmux-256color") || + 0 == strcmp(term, "xterm") || + 0 == strcmp(term, "xterm-256color") || + 0 == strcmp(term, "xterm-termite") || + 0 == strcmp(term, "xterm-color"); + } else { + return false; + } +#endif +}(); + +static void print_preamble_header(char* out_buff, size_t out_buff_size); + +// ------------------------------------------------------------------------------ +// Colors - struct Callback - { - std::string id; - log_handler_t callback; - void* user_data; - Verbosity verbosity; // Does not change! - close_handler_t close; - flush_handler_t flush; - unsigned indentation; - }; - - using CallbackVec = std::vector; - - using StringPair = std::pair; - using StringPairList = std::vector; - - const auto s_start_time = steady_clock::now(); - - Verbosity g_stderr_verbosity = Verbosity_0; - bool g_colorlogtostderr = true; - unsigned g_flush_interval_ms = 0; - bool g_preamble_header = true; - bool g_preamble = true; - - Verbosity g_internal_verbosity = Verbosity_0; - - // Preamble details - bool g_preamble_date = true; - bool g_preamble_time = true; - bool g_preamble_uptime = true; - bool g_preamble_thread = true; - bool g_preamble_file = true; - bool g_preamble_verbose = true; - bool g_preamble_pipe = true; - - static std::recursive_mutex s_mutex; - static Verbosity s_max_out_verbosity = Verbosity_OFF; - static std::string s_argv0_filename; - static std::string s_arguments; - static char s_current_dir[PATH_MAX]; - static CallbackVec s_callbacks; - static fatal_handler_t s_fatal_handler = nullptr; - static verbosity_to_name_t s_verbosity_to_name_callback = nullptr; - static name_to_verbosity_t s_name_to_verbosity_callback = nullptr; - static StringPairList s_user_stack_cleanups; - static bool s_strip_file_path = true; - static std::atomic s_stderr_indentation { 0 }; - - // For periodic flushing: - static std::thread* s_flush_thread = nullptr; - static bool s_needs_flushing = false; - - static SignalOptions s_signal_options = SignalOptions::none(); - - static const bool s_terminal_has_color = [](){ - #ifdef _WIN32 - #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING - #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 - #endif - - HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); - if (hOut != INVALID_HANDLE_VALUE) { - DWORD dwMode = 0; - GetConsoleMode(hOut, &dwMode); - dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - return SetConsoleMode(hOut, dwMode) != 0; - } - return false; - #else - if (!isatty(STDERR_FILENO)) { - return false; - } - if (const char* term = getenv("TERM")) { - return 0 == strcmp(term, "cygwin") - || 0 == strcmp(term, "linux") - || 0 == strcmp(term, "rxvt-unicode-256color") - || 0 == strcmp(term, "screen") - || 0 == strcmp(term, "screen-256color") - || 0 == strcmp(term, "screen.xterm-256color") - || 0 == strcmp(term, "tmux-256color") - || 0 == strcmp(term, "xterm") - || 0 == strcmp(term, "xterm-256color") - || 0 == strcmp(term, "xterm-termite") - || 0 == strcmp(term, "xterm-color"); - } else { - return false; - } - #endif - }(); - - static void print_preamble_header(char* out_buff, size_t out_buff_size); - - // ------------------------------------------------------------------------------ - // Colors - - bool terminal_has_color() { return s_terminal_has_color; } - - // Colors +bool terminal_has_color() { return s_terminal_has_color; } + +// Colors #ifdef _WIN32 #define VTSEQ(ID) ("\x1b[1;" #ID "m") @@ -261,1633 +260,1670 @@ namespace loguru #define VTSEQ(ID) ("\x1b[" #ID "m") #endif - const char* terminal_black() { return s_terminal_has_color ? VTSEQ(30) : ""; } - const char* terminal_red() { return s_terminal_has_color ? VTSEQ(31) : ""; } - const char* terminal_green() { return s_terminal_has_color ? VTSEQ(32) : ""; } - const char* terminal_yellow() { return s_terminal_has_color ? VTSEQ(33) : ""; } - const char* terminal_blue() { return s_terminal_has_color ? VTSEQ(34) : ""; } - const char* terminal_purple() { return s_terminal_has_color ? VTSEQ(35) : ""; } - const char* terminal_cyan() { return s_terminal_has_color ? VTSEQ(36) : ""; } - const char* terminal_light_gray() { return s_terminal_has_color ? VTSEQ(37) : ""; } - const char* terminal_white() { return s_terminal_has_color ? VTSEQ(37) : ""; } - const char* terminal_light_red() { return s_terminal_has_color ? VTSEQ(91) : ""; } - const char* terminal_dim() { return s_terminal_has_color ? VTSEQ(2) : ""; } - - // Formating - const char* terminal_bold() { return s_terminal_has_color ? VTSEQ(1) : ""; } - const char* terminal_underline() { return s_terminal_has_color ? VTSEQ(4) : ""; } - - // You should end each line with this! - const char* terminal_reset() { return s_terminal_has_color ? VTSEQ(0) : ""; } - - // ------------------------------------------------------------------------------ +const char* terminal_black() { return s_terminal_has_color ? VTSEQ(30) : ""; } +const char* terminal_red() { return s_terminal_has_color ? VTSEQ(31) : ""; } +const char* terminal_green() { return s_terminal_has_color ? VTSEQ(32) : ""; } +const char* terminal_yellow() { return s_terminal_has_color ? VTSEQ(33) : ""; } +const char* terminal_blue() { return s_terminal_has_color ? VTSEQ(34) : ""; } +const char* terminal_purple() { return s_terminal_has_color ? VTSEQ(35) : ""; } +const char* terminal_cyan() { return s_terminal_has_color ? VTSEQ(36) : ""; } +const char* terminal_light_gray() { + return s_terminal_has_color ? VTSEQ(37) : ""; +} +const char* terminal_white() { return s_terminal_has_color ? VTSEQ(37) : ""; } +const char* terminal_light_red() { + return s_terminal_has_color ? VTSEQ(91) : ""; +} +const char* terminal_dim() { return s_terminal_has_color ? VTSEQ(2) : ""; } + +// Formating +const char* terminal_bold() { return s_terminal_has_color ? VTSEQ(1) : ""; } +const char* terminal_underline() { + return s_terminal_has_color ? VTSEQ(4) : ""; +} + +// You should end each line with this! +const char* terminal_reset() { return s_terminal_has_color ? VTSEQ(0) : ""; } + +// ------------------------------------------------------------------------------ #if LOGURU_WITH_FILEABS - void file_reopen(void* user_data); - inline FILE* to_file(void* user_data) { return reinterpret_cast(user_data)->fp; } +void file_reopen(void* user_data); +inline FILE* to_file(void* user_data) { + return reinterpret_cast(user_data)->fp; +} #else - inline FILE* to_file(void* user_data) { return reinterpret_cast(user_data); } +inline FILE* to_file(void* user_data) { + return reinterpret_cast(user_data); +} #endif - void file_log(void* user_data, const Message& message) - { +void file_log(void* user_data, const Message& message) { #if LOGURU_WITH_FILEABS - FileAbs* file_abs = reinterpret_cast(user_data); - if (file_abs->is_reopening) { - return; - } - // It is better checking file change every minute/hour/day, - // instead of doing this every time we log. - // Here check_interval is set to zero to enable checking every time; - const auto check_interval = seconds(0); - if (duration_cast(steady_clock::now() - file_abs->last_check_time) > check_interval) { - file_abs->last_check_time = steady_clock::now(); - file_reopen(user_data); - } - FILE* file = to_file(user_data); - if (!file) { - return; - } + FileAbs* file_abs = reinterpret_cast(user_data); + if (file_abs->is_reopening) { + return; + } + // It is better checking file change every minute/hour/day, + // instead of doing this every time we log. + // Here check_interval is set to zero to enable checking every time; + const auto check_interval = seconds(0); + if (duration_cast(steady_clock::now() - + file_abs->last_check_time) > check_interval) { + file_abs->last_check_time = steady_clock::now(); + file_reopen(user_data); + } + FILE* file = to_file(user_data); + if (!file) { + return; + } #else - FILE* file = to_file(user_data); + FILE* file = to_file(user_data); #endif - fprintf(file, "%s%s%s%s\n", - message.preamble, message.indentation, message.prefix, message.message); - if (g_flush_interval_ms == 0) { - fflush(file); - } - } - - void file_close(void* user_data) - { - FILE* file = to_file(user_data); - if (file) { - fclose(file); - } + fprintf(file, "%s%s%s%s\n", message.preamble, message.indentation, + message.prefix, message.message); + if (g_flush_interval_ms == 0) { + fflush(file); + } +} + +void file_close(void* user_data) { + FILE* file = to_file(user_data); + if (file) { + fclose(file); + } #if LOGURU_WITH_FILEABS - delete reinterpret_cast(user_data); + delete reinterpret_cast(user_data); #endif - } +} - void file_flush(void* user_data) - { - FILE* file = to_file(user_data); - fflush(file); - } +void file_flush(void* user_data) { + FILE* file = to_file(user_data); + fflush(file); +} #if LOGURU_WITH_FILEABS - void file_reopen(void* user_data) - { - FileAbs * file_abs = reinterpret_cast(user_data); - struct stat st; - int ret; - if (!file_abs->fp || (ret = stat(file_abs->path, &st)) == -1 || (st.st_ino != file_abs->st.st_ino)) { - file_abs->is_reopening = true; - if (file_abs->fp) { - fclose(file_abs->fp); - } - if (!file_abs->fp) { - VLOG_F(g_internal_verbosity, "Reopening file '" LOGURU_FMT(s) "' due to previous error", file_abs->path); - } - else if (ret < 0) { - const auto why = errno_as_text(); - VLOG_F(g_internal_verbosity, "Reopening file '" LOGURU_FMT(s) "' due to '" LOGURU_FMT(s) "'", file_abs->path, why.c_str()); - } else { - VLOG_F(g_internal_verbosity, "Reopening file '" LOGURU_FMT(s) "' due to file changed", file_abs->path); - } - // try reopen current file. - if (!create_directories(file_abs->path)) { - LOG_F(ERROR, "Failed to create directories to '" LOGURU_FMT(s) "'", file_abs->path); - } - file_abs->fp = fopen(file_abs->path, file_abs->mode_str); - if (!file_abs->fp) { - LOG_F(ERROR, "Failed to open '" LOGURU_FMT(s) "'", file_abs->path); - } else { - stat(file_abs->path, &file_abs->st); - } - file_abs->is_reopening = false; - } - } +void file_reopen(void* user_data) { + FileAbs* file_abs = reinterpret_cast(user_data); + struct stat st; + int ret; + if (!file_abs->fp || (ret = stat(file_abs->path, &st)) == -1 || + (st.st_ino != file_abs->st.st_ino)) { + file_abs->is_reopening = true; + if (file_abs->fp) { + fclose(file_abs->fp); + } + if (!file_abs->fp) { + VLOG_F(g_internal_verbosity, + "Reopening file '" LOGURU_FMT(s) "' due to previous error", + file_abs->path); + } else if (ret < 0) { + const auto why = errno_as_text(); + VLOG_F( + g_internal_verbosity, + "Reopening file '" LOGURU_FMT(s) "' due to '" LOGURU_FMT(s) "'", + file_abs->path, why.c_str()); + } else { + VLOG_F(g_internal_verbosity, + "Reopening file '" LOGURU_FMT(s) "' due to file changed", + file_abs->path); + } + // try reopen current file. + if (!create_directories(file_abs->path)) { + LOG_F(ERROR, "Failed to create directories to '" LOGURU_FMT(s) "'", + file_abs->path); + } + file_abs->fp = fopen(file_abs->path, file_abs->mode_str); + if (!file_abs->fp) { + LOG_F(ERROR, "Failed to open '" LOGURU_FMT(s) "'", file_abs->path); + } else { + stat(file_abs->path, &file_abs->st); + } + file_abs->is_reopening = false; + } +} #endif - // ------------------------------------------------------------------------------ - // ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ +// ------------------------------------------------------------------------------ #if LOGURU_SYSLOG - void syslog_log(void* /*user_data*/, const Message& message) - { - /* - Level 0: Is reserved for kernel panic type situations. - Level 1: Is for Major resource failure. - Level 2->7 Application level failures - */ - int level; - if (message.verbosity < Verbosity_FATAL) { - level = 1; // System Alert - } else { - switch(message.verbosity) { - case Verbosity_FATAL: level = 2; break; // System Critical - case Verbosity_ERROR: level = 3; break; // System Error - case Verbosity_WARNING: level = 4; break; // System Warning - case Verbosity_INFO: level = 5; break; // System Notice - case Verbosity_1: level = 6; break; // System Info - default: level = 7; break; // System Debug - } - } - - // Note: We don't add the time info. - // This is done automatically by the syslog deamon. - // Otherwise log all information that the file log does. - syslog(level, "%s%s%s", message.indentation, message.prefix, message.message); - } - - void syslog_close(void* /*user_data*/) - { - closelog(); - } - - void syslog_flush(void* /*user_data*/) - {} +void syslog_log(void* /*user_data*/, const Message& message) { + /* + Level 0: Is reserved for kernel panic type situations. + Level 1: Is for Major resource failure. + Level 2->7 Application level failures + */ + int level; + if (message.verbosity < Verbosity_FATAL) { + level = 1; // System Alert + } else { + switch (message.verbosity) { + case Verbosity_FATAL: + level = 2; + break; // System Critical + case Verbosity_ERROR: + level = 3; + break; // System Error + case Verbosity_WARNING: + level = 4; + break; // System Warning + case Verbosity_INFO: + level = 5; + break; // System Notice + case Verbosity_1: + level = 6; + break; // System Info + default: + level = 7; + break; // System Debug + } + } + + // Note: We don't add the time info. + // This is done automatically by the syslog deamon. + // Otherwise log all information that the file log does. + syslog(level, "%s%s%s", message.indentation, message.prefix, + message.message); +} + +void syslog_close(void* /*user_data*/) { closelog(); } + +void syslog_flush(void* /*user_data*/) {} #endif // ------------------------------------------------------------------------------ - // Helpers: +// Helpers: - Text::~Text() { free(_str); } +Text::~Text() { free(_str); } #if LOGURU_USE_FMTLIB - Text vtextprintf(const char* format, fmt::format_args args) - { - return Text(STRDUP(fmt::vformat(format, args).c_str())); - } +Text vtextprintf(const char* format, fmt::format_args args) { + return Text(STRDUP(fmt::vformat(format, args).c_str())); +} #else - LOGURU_PRINTF_LIKE(1, 0) - static Text vtextprintf(const char* format, va_list vlist) - { +LOGURU_PRINTF_LIKE(1, 0) +static Text vtextprintf(const char* format, va_list vlist) { #ifdef _WIN32 - int bytes_needed = _vscprintf(format, vlist); - CHECK_F(bytes_needed >= 0, "Bad string format: '%s'", format); - char* buff = (char*)malloc(bytes_needed+1); - vsnprintf(buff, bytes_needed+1, format, vlist); - return Text(buff); + int bytes_needed = _vscprintf(format, vlist); + CHECK_F(bytes_needed >= 0, "Bad string format: '%s'", format); + char* buff = (char*)malloc(bytes_needed + 1); + vsnprintf(buff, bytes_needed + 1, format, vlist); + return Text(buff); +#else + char* buff = nullptr; + int result = vasprintf(&buff, format, vlist); + CHECK_F(result >= 0, "Bad string format: '" LOGURU_FMT(s) "'", format); + return Text(buff); +#endif +} + +Text textprintf(const char* format, ...) { + va_list vlist; + va_start(vlist, format); + auto result = vtextprintf(format, vlist); + va_end(vlist); + return result; +} +#endif + +// Overloaded for variadic template matching. +Text textprintf() { return Text(static_cast(calloc(1, 1))); } + +static const char* indentation(unsigned depth) { + static const char buff[] = + ". . . . . . . . . . " + ". . . . . . . . . . " + ". . . . . . . . . . " + ". . . . . . . . . . " + ". . . . . . . . . . " + ". . . . . . . . . . " + ". . . . . . . . . . " + ". . . . . . . . . . " + ". . . . . . . . . . " + ". . . . . . . . . . "; + static const size_t INDENTATION_WIDTH = 4; + static const size_t NUM_INDENTATIONS = + (sizeof(buff) - 1) / INDENTATION_WIDTH; + depth = std::min(depth, NUM_INDENTATIONS); + return buff + INDENTATION_WIDTH * (NUM_INDENTATIONS - depth); +} + +static void parse_args(int& argc, char* argv[], const char* verbosity_flag) { + int arg_dest = 1; + int out_argc = argc; + + for (int arg_it = 1; arg_it < argc; ++arg_it) { + auto cmd = argv[arg_it]; + auto arg_len = strlen(verbosity_flag); + + bool last_is_alpha = false; +#if LOGURU_USE_LOCALE + try { // locale variant of isalpha will throw on error + last_is_alpha = std::isalpha(cmd[arg_len], std::locale("")); + } catch (...) { + last_is_alpha = std::isalpha(static_cast(cmd[arg_len])); + } +#else + last_is_alpha = std::isalpha(static_cast(cmd[arg_len])); +#endif + + if (strncmp(cmd, verbosity_flag, arg_len) == 0 && !last_is_alpha) { + out_argc -= 1; + auto value_str = cmd + arg_len; + if (value_str[0] == '\0') { + // Value in separate argument + arg_it += 1; + CHECK_LT_F(arg_it, argc, + "Missing verbosiy level after " LOGURU_FMT(s) "", + verbosity_flag); + value_str = argv[arg_it]; + out_argc -= 1; + } + if (*value_str == '=') { + value_str += 1; + } + + auto req_verbosity = get_verbosity_from_name(value_str); + if (req_verbosity != Verbosity_INVALID) { + g_stderr_verbosity = req_verbosity; + } else { + char* end = 0; + g_stderr_verbosity = + static_cast(strtol(value_str, &end, 10)); + CHECK_F(end && *end == '\0', + "Invalid verbosity. Expected integer, INFO, WARNING, " + "ERROR or OFF, got '" LOGURU_FMT(s) "'", + value_str); + } + } else { + argv[arg_dest++] = argv[arg_it]; + } + } + + argc = out_argc; + argv[argc] = nullptr; +} + +static long long now_ns() { + return duration_cast( + high_resolution_clock::now().time_since_epoch()) + .count(); +} + +// Returns the part of the path after the last / or \ (if any). +const char* filename(const char* path) { + for (auto ptr = path; *ptr; ++ptr) { + if (*ptr == '/' || *ptr == '\\') { + path = ptr + 1; + } + } + return path; +} + +// ------------------------------------------------------------------------------ + +static void on_atexit() { + VLOG_F(g_internal_verbosity, "atexit"); + flush(); +} + +static void install_signal_handlers(const SignalOptions& signal_options); + +static void write_hex_digit(std::string& out, unsigned num) { + DCHECK_LT_F(num, 16u); + if (num < 10u) { + out.push_back(char('0' + num)); + } else { + out.push_back(char('A' + num - 10)); + } +} + +static void write_hex_byte(std::string& out, uint8_t n) { + write_hex_digit(out, n >> 4u); + write_hex_digit(out, n & 0x0f); +} + +static void escape(std::string& out, const std::string& str) { + for (char c : str) { + /**/ if (c == '\a') { + out += "\\a"; + } else if (c == '\b') { + out += "\\b"; + } else if (c == '\f') { + out += "\\f"; + } else if (c == '\n') { + out += "\\n"; + } else if (c == '\r') { + out += "\\r"; + } else if (c == '\t') { + out += "\\t"; + } else if (c == '\v') { + out += "\\v"; + } else if (c == '\\') { + out += "\\\\"; + } else if (c == '\'') { + out += "\\\'"; + } else if (c == '\"') { + out += "\\\""; + } else if (c == ' ') { + out += "\\ "; + } else if (0 <= c && c < 0x20) { // ASCI control character: + // else if (c < 0x20 || c != (c & 127)) { // ASCII control character + // or UTF-8: + out += "\\x"; + write_hex_byte(out, static_cast(c)); + } else { + out += c; + } + } +} + +Text errno_as_text() { + char buff[256]; +#if defined(__GLIBC__) && defined(_GNU_SOURCE) + // GNU Version + return Text(STRDUP(strerror_r(errno, buff, sizeof(buff)))); +#elif defined(__APPLE__) || _POSIX_C_SOURCE >= 200112L + // XSI Version + strerror_r(errno, buff, sizeof(buff)); + return Text(strdup(buff)); +#elif defined(_WIN32) + strerror_s(buff, sizeof(buff), errno); + return Text(STRDUP(buff)); #else - char* buff = nullptr; - int result = vasprintf(&buff, format, vlist); - CHECK_F(result >= 0, "Bad string format: '" LOGURU_FMT(s) "'", format); - return Text(buff); + // Not thread-safe. + return Text(STRDUP(strerror(errno))); +#endif +} + +void init(int& argc, char* argv[], const Options& options) { + CHECK_GT_F(argc, 0, "Expected proper argc/argv"); + CHECK_EQ_F(argv[argc], nullptr, "Expected proper argc/argv"); + + s_argv0_filename = filename(argv[0]); + +#ifdef _WIN32 +#define getcwd _getcwd +#endif + + if (!getcwd(s_current_dir, sizeof(s_current_dir))) { + const auto error_text = errno_as_text(); + LOG_F(WARNING, + "Failed to get current working directory: " LOGURU_FMT(s) "", + error_text.c_str()); + } + + s_arguments = ""; + for (int i = 0; i < argc; ++i) { + escape(s_arguments, argv[i]); + if (i + 1 < argc) { + s_arguments += " "; + } + } + + if (options.verbosity_flag) { + parse_args(argc, argv, options.verbosity_flag); + } + + if (const auto main_thread_name = options.main_thread_name) { +#if LOGURU_PTLS_NAMES || LOGURU_WINTHREADS + set_thread_name(main_thread_name); +#elif LOGURU_PTHREADS + char old_thread_name[16] = {0}; + auto this_thread = pthread_self(); +#if defined(__APPLE__) || defined(__linux__) || defined(__sun) + pthread_getname_np(this_thread, old_thread_name, + sizeof(old_thread_name)); #endif - } - - Text textprintf(const char* format, ...) - { - va_list vlist; - va_start(vlist, format); - auto result = vtextprintf(format, vlist); - va_end(vlist); - return result; - } + if (old_thread_name[0] == 0) { +#ifdef __APPLE__ + pthread_setname_np(main_thread_name); +#elif defined(__FreeBSD__) || defined(__OpenBSD__) + pthread_set_name_np(this_thread, main_thread_name); +#elif defined(__linux__) || defined(__sun) + pthread_setname_np(this_thread, main_thread_name); +#endif + } +#endif // LOGURU_PTHREADS + } + + if (g_stderr_verbosity >= Verbosity_INFO) { + if (g_preamble_header) { + char preamble_explain[LOGURU_PREAMBLE_WIDTH]; + print_preamble_header(preamble_explain, sizeof(preamble_explain)); + if (g_colorlogtostderr && s_terminal_has_color) { + fprintf(stderr, "%s%s%s\n", terminal_reset(), terminal_dim(), + preamble_explain); + } else { + fprintf(stderr, "%s\n", preamble_explain); + } + } + fflush(stderr); + } + VLOG_F(g_internal_verbosity, "arguments: " LOGURU_FMT(s) "", + s_arguments.c_str()); + if (strlen(s_current_dir) != 0) { + VLOG_F(g_internal_verbosity, "Current dir: " LOGURU_FMT(s) "", + s_current_dir); + } + VLOG_F(g_internal_verbosity, "stderr verbosity: " LOGURU_FMT(d) "", + g_stderr_verbosity); + VLOG_F(g_internal_verbosity, "-----------------------------------"); + + install_signal_handlers(options.signal_options); + + atexit(on_atexit); +} + +void shutdown() { + VLOG_F(g_internal_verbosity, "loguru::shutdown()"); + remove_all_callbacks(); + set_fatal_handler(nullptr); + set_verbosity_to_name_callback(nullptr); + set_name_to_verbosity_callback(nullptr); +} + +void write_date_time(char* buff, unsigned long long buff_size) { + auto now = system_clock::now(); + long long ms_since_epoch = + duration_cast(now.time_since_epoch()).count(); + time_t sec_since_epoch = time_t(ms_since_epoch / 1000); + tm time_info; + localtime_r(&sec_since_epoch, &time_info); + snprintf(buff, buff_size, "%04d%02d%02d_%02d%02d%02d.%03lld", + 1900 + time_info.tm_year, 1 + time_info.tm_mon, time_info.tm_mday, + time_info.tm_hour, time_info.tm_min, time_info.tm_sec, + ms_since_epoch % 1000); +} + +const char* argv0_filename() { return s_argv0_filename.c_str(); } + +const char* arguments() { return s_arguments.c_str(); } + +const char* current_dir() { return s_current_dir; } + +const char* home_dir() { +#ifdef __MINGW32__ + auto home = getenv("USERPROFILE"); + CHECK_F(home != nullptr, "Missing USERPROFILE"); + return home; +#elif defined(_WIN32) + char* user_profile; + size_t len; + errno_t err = _dupenv_s(&user_profile, &len, "USERPROFILE"); + CHECK_F(err == 0, "Missing USERPROFILE"); + return user_profile; +#else // _WIN32 + auto home = getenv("HOME"); + CHECK_F(home != nullptr, "Missing HOME"); + return home; +#endif // _WIN32 +} + +void suggest_log_path(const char* prefix, char* buff, + unsigned long long buff_size) { + if (prefix[0] == '~') { + snprintf(buff, buff_size - 1, "%s%s", home_dir(), prefix + 1); + } else { + snprintf(buff, buff_size - 1, "%s", prefix); + } + + // Check for terminating / + size_t n = strlen(buff); + if (n != 0) { + if (buff[n - 1] != '/') { + CHECK_F(n + 2 < buff_size, "Filename buffer too small"); + buff[n] = '/'; + buff[n + 1] = '\0'; + } + } + +#ifdef _WIN32 + strncat_s(buff, buff_size - strlen(buff) - 1, s_argv0_filename.c_str(), + buff_size - strlen(buff) - 1); + strncat_s(buff, buff_size - strlen(buff) - 1, "/", + buff_size - strlen(buff) - 1); + write_date_time(buff + strlen(buff), buff_size - strlen(buff)); + strncat_s(buff, buff_size - strlen(buff) - 1, ".log", + buff_size - strlen(buff) - 1); +#else + strncat(buff, s_argv0_filename.c_str(), buff_size - strlen(buff) - 1); + strncat(buff, "/", buff_size - strlen(buff) - 1); + write_date_time(buff + strlen(buff), buff_size - strlen(buff)); + strncat(buff, ".log", buff_size - strlen(buff) - 1); #endif +} + +bool create_directories(const char* file_path_const) { + CHECK_F(file_path_const && *file_path_const); + char* file_path = STRDUP(file_path_const); + for (char* p = strchr(file_path + 1, '/'); p; p = strchr(p + 1, '/')) { + *p = '\0'; - // Overloaded for variadic template matching. - Text textprintf() - { - return Text(static_cast(calloc(1, 1))); - } - - static const char* indentation(unsigned depth) - { - static const char buff[] = - ". . . . . . . . . . " ". . . . . . . . . . " - ". . . . . . . . . . " ". . . . . . . . . . " - ". . . . . . . . . . " ". . . . . . . . . . " - ". . . . . . . . . . " ". . . . . . . . . . " - ". . . . . . . . . . " ". . . . . . . . . . "; - static const size_t INDENTATION_WIDTH = 4; - static const size_t NUM_INDENTATIONS = (sizeof(buff) - 1) / INDENTATION_WIDTH; - depth = std::min(depth, NUM_INDENTATIONS); - return buff + INDENTATION_WIDTH * (NUM_INDENTATIONS - depth); - } - - static void parse_args(int& argc, char* argv[], const char* verbosity_flag) - { - int arg_dest = 1; - int out_argc = argc; - - for (int arg_it = 1; arg_it < argc; ++arg_it) { - auto cmd = argv[arg_it]; - auto arg_len = strlen(verbosity_flag); - - bool last_is_alpha = false; - #if LOGURU_USE_LOCALE - try { // locale variant of isalpha will throw on error - last_is_alpha = std::isalpha(cmd[arg_len], std::locale("")); - } - catch (...) { - last_is_alpha = std::isalpha(static_cast(cmd[arg_len])); - } - #else - last_is_alpha = std::isalpha(static_cast(cmd[arg_len])); - #endif - - if (strncmp(cmd, verbosity_flag, arg_len) == 0 && !last_is_alpha) { - out_argc -= 1; - auto value_str = cmd + arg_len; - if (value_str[0] == '\0') { - // Value in separate argument - arg_it += 1; - CHECK_LT_F(arg_it, argc, "Missing verbosiy level after " LOGURU_FMT(s) "", verbosity_flag); - value_str = argv[arg_it]; - out_argc -= 1; - } - if (*value_str == '=') { value_str += 1; } - - auto req_verbosity = get_verbosity_from_name(value_str); - if (req_verbosity != Verbosity_INVALID) { - g_stderr_verbosity = req_verbosity; - } else { - char* end = 0; - g_stderr_verbosity = static_cast(strtol(value_str, &end, 10)); - CHECK_F(end && *end == '\0', - "Invalid verbosity. Expected integer, INFO, WARNING, ERROR or OFF, got '" LOGURU_FMT(s) "'", value_str); - } - } else { - argv[arg_dest++] = argv[arg_it]; - } - } - - argc = out_argc; - argv[argc] = nullptr; - } - - static long long now_ns() - { - return duration_cast(high_resolution_clock::now().time_since_epoch()).count(); - } - - // Returns the part of the path after the last / or \ (if any). - const char* filename(const char* path) - { - for (auto ptr = path; *ptr; ++ptr) { - if (*ptr == '/' || *ptr == '\\') { - path = ptr + 1; - } - } - return path; - } - - // ------------------------------------------------------------------------------ - - static void on_atexit() - { - VLOG_F(g_internal_verbosity, "atexit"); - flush(); - } - - static void install_signal_handlers(const SignalOptions& signal_options); - - static void write_hex_digit(std::string& out, unsigned num) - { - DCHECK_LT_F(num, 16u); - if (num < 10u) { out.push_back(char('0' + num)); } - else { out.push_back(char('A' + num - 10)); } - } - - static void write_hex_byte(std::string& out, uint8_t n) - { - write_hex_digit(out, n >> 4u); - write_hex_digit(out, n & 0x0f); - } - - static void escape(std::string& out, const std::string& str) - { - for (char c : str) { - /**/ if (c == '\a') { out += "\\a"; } - else if (c == '\b') { out += "\\b"; } - else if (c == '\f') { out += "\\f"; } - else if (c == '\n') { out += "\\n"; } - else if (c == '\r') { out += "\\r"; } - else if (c == '\t') { out += "\\t"; } - else if (c == '\v') { out += "\\v"; } - else if (c == '\\') { out += "\\\\"; } - else if (c == '\'') { out += "\\\'"; } - else if (c == '\"') { out += "\\\""; } - else if (c == ' ') { out += "\\ "; } - else if (0 <= c && c < 0x20) { // ASCI control character: - // else if (c < 0x20 || c != (c & 127)) { // ASCII control character or UTF-8: - out += "\\x"; - write_hex_byte(out, static_cast(c)); - } else { out += c; } - } - } - - Text errno_as_text() - { - char buff[256]; - #if defined(__GLIBC__) && defined(_GNU_SOURCE) - // GNU Version - return Text(STRDUP(strerror_r(errno, buff, sizeof(buff)))); - #elif defined(__APPLE__) || _POSIX_C_SOURCE >= 200112L - // XSI Version - strerror_r(errno, buff, sizeof(buff)); - return Text(strdup(buff)); - #elif defined(_WIN32) - strerror_s(buff, sizeof(buff), errno); - return Text(STRDUP(buff)); - #else - // Not thread-safe. - return Text(STRDUP(strerror(errno))); - #endif - } - - void init(int& argc, char* argv[], const Options& options) - { - CHECK_GT_F(argc, 0, "Expected proper argc/argv"); - CHECK_EQ_F(argv[argc], nullptr, "Expected proper argc/argv"); - - s_argv0_filename = filename(argv[0]); - - #ifdef _WIN32 - #define getcwd _getcwd - #endif - - if (!getcwd(s_current_dir, sizeof(s_current_dir))) { - const auto error_text = errno_as_text(); - LOG_F(WARNING, "Failed to get current working directory: " LOGURU_FMT(s) "", error_text.c_str()); - } - - s_arguments = ""; - for (int i = 0; i < argc; ++i) { - escape(s_arguments, argv[i]); - if (i + 1 < argc) { - s_arguments += " "; - } - } - - if (options.verbosity_flag) { - parse_args(argc, argv, options.verbosity_flag); - } - - if (const auto main_thread_name = options.main_thread_name) { - #if LOGURU_PTLS_NAMES || LOGURU_WINTHREADS - set_thread_name(main_thread_name); - #elif LOGURU_PTHREADS - char old_thread_name[16] = {0}; - auto this_thread = pthread_self(); - #if defined(__APPLE__) || defined(__linux__) || defined(__sun) - pthread_getname_np(this_thread, old_thread_name, sizeof(old_thread_name)); - #endif - if (old_thread_name[0] == 0) { - #ifdef __APPLE__ - pthread_setname_np(main_thread_name); - #elif defined(__FreeBSD__) || defined(__OpenBSD__) - pthread_set_name_np(this_thread, main_thread_name); - #elif defined(__linux__) || defined(__sun) - pthread_setname_np(this_thread, main_thread_name); - #endif - } - #endif // LOGURU_PTHREADS - } - - if (g_stderr_verbosity >= Verbosity_INFO) { - if (g_preamble_header) { - char preamble_explain[LOGURU_PREAMBLE_WIDTH]; - print_preamble_header(preamble_explain, sizeof(preamble_explain)); - if (g_colorlogtostderr && s_terminal_has_color) { - fprintf(stderr, "%s%s%s\n", terminal_reset(), terminal_dim(), preamble_explain); - } else { - fprintf(stderr, "%s\n", preamble_explain); - } - } - fflush(stderr); - } - VLOG_F(g_internal_verbosity, "arguments: " LOGURU_FMT(s) "", s_arguments.c_str()); - if (strlen(s_current_dir) != 0) - { - VLOG_F(g_internal_verbosity, "Current dir: " LOGURU_FMT(s) "", s_current_dir); - } - VLOG_F(g_internal_verbosity, "stderr verbosity: " LOGURU_FMT(d) "", g_stderr_verbosity); - VLOG_F(g_internal_verbosity, "-----------------------------------"); - - install_signal_handlers(options.signal_options); - - atexit(on_atexit); - } - - void shutdown() - { - VLOG_F(g_internal_verbosity, "loguru::shutdown()"); - remove_all_callbacks(); - set_fatal_handler(nullptr); - set_verbosity_to_name_callback(nullptr); - set_name_to_verbosity_callback(nullptr); - } - - void write_date_time(char* buff, unsigned long long buff_size) - { - auto now = system_clock::now(); - long long ms_since_epoch = duration_cast(now.time_since_epoch()).count(); - time_t sec_since_epoch = time_t(ms_since_epoch / 1000); - tm time_info; - localtime_r(&sec_since_epoch, &time_info); - snprintf(buff, buff_size, "%04d%02d%02d_%02d%02d%02d.%03lld", - 1900 + time_info.tm_year, 1 + time_info.tm_mon, time_info.tm_mday, - time_info.tm_hour, time_info.tm_min, time_info.tm_sec, ms_since_epoch % 1000); - } - - const char* argv0_filename() - { - return s_argv0_filename.c_str(); - } - - const char* arguments() - { - return s_arguments.c_str(); - } - - const char* current_dir() - { - return s_current_dir; - } - - const char* home_dir() - { - #ifdef __MINGW32__ - auto home = getenv("USERPROFILE"); - CHECK_F(home != nullptr, "Missing USERPROFILE"); - return home; - #elif defined(_WIN32) - char* user_profile; - size_t len; - errno_t err = _dupenv_s(&user_profile, &len, "USERPROFILE"); - CHECK_F(err == 0, "Missing USERPROFILE"); - return user_profile; - #else // _WIN32 - auto home = getenv("HOME"); - CHECK_F(home != nullptr, "Missing HOME"); - return home; - #endif // _WIN32 - } - - void suggest_log_path(const char* prefix, char* buff, unsigned long long buff_size) - { - if (prefix[0] == '~') { - snprintf(buff, buff_size - 1, "%s%s", home_dir(), prefix + 1); - } else { - snprintf(buff, buff_size - 1, "%s", prefix); - } - - // Check for terminating / - size_t n = strlen(buff); - if (n != 0) { - if (buff[n - 1] != '/') { - CHECK_F(n + 2 < buff_size, "Filename buffer too small"); - buff[n] = '/'; - buff[n + 1] = '\0'; - } - } - - #ifdef _WIN32 - strncat_s(buff, buff_size - strlen(buff) - 1, s_argv0_filename.c_str(), buff_size - strlen(buff) - 1); - strncat_s(buff, buff_size - strlen(buff) - 1, "/", buff_size - strlen(buff) - 1); - write_date_time(buff + strlen(buff), buff_size - strlen(buff)); - strncat_s(buff, buff_size - strlen(buff) - 1, ".log", buff_size - strlen(buff) - 1); - #else - strncat(buff, s_argv0_filename.c_str(), buff_size - strlen(buff) - 1); - strncat(buff, "/", buff_size - strlen(buff) - 1); - write_date_time(buff + strlen(buff), buff_size - strlen(buff)); - strncat(buff, ".log", buff_size - strlen(buff) - 1); - #endif - } - - bool create_directories(const char* file_path_const) - { - CHECK_F(file_path_const && *file_path_const); - char* file_path = STRDUP(file_path_const); - for (char* p = strchr(file_path + 1, '/'); p; p = strchr(p + 1, '/')) { - *p = '\0'; - - #ifdef _WIN32 - if (_mkdir(file_path) == -1) { - #else - if (mkdir(file_path, 0755) == -1) { - #endif - if (errno != EEXIST) { - LOG_F(ERROR, "Failed to create directory '" LOGURU_FMT(s) "'", file_path); - LOG_IF_F(ERROR, errno == EACCES, "EACCES"); - LOG_IF_F(ERROR, errno == ENAMETOOLONG, "ENAMETOOLONG"); - LOG_IF_F(ERROR, errno == ENOENT, "ENOENT"); - LOG_IF_F(ERROR, errno == ENOTDIR, "ENOTDIR"); - LOG_IF_F(ERROR, errno == ELOOP, "ELOOP"); - - *p = '/'; - free(file_path); - return false; - } - } - *p = '/'; - } - free(file_path); - return true; - } - bool add_file(const char* path_in, FileMode mode, Verbosity verbosity) - { - char path[PATH_MAX]; - if (path_in[0] == '~') { - snprintf(path, sizeof(path) - 1, "%s%s", home_dir(), path_in + 1); - } else { - snprintf(path, sizeof(path) - 1, "%s", path_in); - } - - if (!create_directories(path)) { - LOG_F(ERROR, "Failed to create directories to '" LOGURU_FMT(s) "'", path); - } - - const char* mode_str = (mode == FileMode::Truncate ? "w" : "a"); - FILE* file; - #ifdef _WIN32 - file = _fsopen(path, mode_str, _SH_DENYNO); - #else - file = fopen(path, mode_str); - #endif - if (!file) { - LOG_F(ERROR, "Failed to open '" LOGURU_FMT(s) "'", path); - return false; - } +#ifdef _WIN32 + if (_mkdir(file_path) == -1) { +#else + if (mkdir(file_path, 0755) == -1) { +#endif + if (errno != EEXIST) { + LOG_F(ERROR, "Failed to create directory '" LOGURU_FMT(s) "'", + file_path); + LOG_IF_F(ERROR, errno == EACCES, "EACCES"); + LOG_IF_F(ERROR, errno == ENAMETOOLONG, "ENAMETOOLONG"); + LOG_IF_F(ERROR, errno == ENOENT, "ENOENT"); + LOG_IF_F(ERROR, errno == ENOTDIR, "ENOTDIR"); + LOG_IF_F(ERROR, errno == ELOOP, "ELOOP"); + + *p = '/'; + free(file_path); + return false; + } + } + *p = '/'; + } + free(file_path); + return true; +} +bool add_file(const char* path_in, FileMode mode, Verbosity verbosity) { + char path[PATH_MAX]; + if (path_in[0] == '~') { + snprintf(path, sizeof(path) - 1, "%s%s", home_dir(), path_in + 1); + } else { + snprintf(path, sizeof(path) - 1, "%s", path_in); + } + + if (!create_directories(path)) { + LOG_F(ERROR, "Failed to create directories to '" LOGURU_FMT(s) "'", + path); + } + + const char* mode_str = (mode == FileMode::Truncate ? "w" : "a"); + FILE* file; +#ifdef _WIN32 + file = _fsopen(path, mode_str, _SH_DENYNO); +#else + file = fopen(path, mode_str); +#endif + if (!file) { + LOG_F(ERROR, "Failed to open '" LOGURU_FMT(s) "'", path); + return false; + } #if LOGURU_WITH_FILEABS - FileAbs* file_abs = new FileAbs(); // this is deleted in file_close; - snprintf(file_abs->path, sizeof(file_abs->path) - 1, "%s", path); - snprintf(file_abs->mode_str, sizeof(file_abs->mode_str) - 1, "%s", mode_str); - stat(file_abs->path, &file_abs->st); - file_abs->fp = file; - file_abs->verbosity = verbosity; - add_callback(path_in, file_log, file_abs, verbosity, file_close, file_flush); + FileAbs* file_abs = new FileAbs(); // this is deleted in file_close; + snprintf(file_abs->path, sizeof(file_abs->path) - 1, "%s", path); + snprintf(file_abs->mode_str, sizeof(file_abs->mode_str) - 1, "%s", + mode_str); + stat(file_abs->path, &file_abs->st); + file_abs->fp = file; + file_abs->verbosity = verbosity; + add_callback(path_in, file_log, file_abs, verbosity, file_close, + file_flush); #else - add_callback(path_in, file_log, file, verbosity, file_close, file_flush); + add_callback(path_in, file_log, file, verbosity, file_close, file_flush); #endif - if (mode == FileMode::Append) { - fprintf(file, "\n\n\n\n\n"); - } - if (!s_arguments.empty()) { - fprintf(file, "arguments: %s\n", s_arguments.c_str()); - } - if (strlen(s_current_dir) != 0) { - fprintf(file, "Current dir: %s\n", s_current_dir); - } - fprintf(file, "File verbosity level: %d\n", verbosity); - if (g_preamble_header) { - char preamble_explain[LOGURU_PREAMBLE_WIDTH]; - print_preamble_header(preamble_explain, sizeof(preamble_explain)); - fprintf(file, "%s\n", preamble_explain); - } - fflush(file); - - VLOG_F(g_internal_verbosity, "Logging to '" LOGURU_FMT(s) "', mode: '" LOGURU_FMT(s) "', verbosity: " LOGURU_FMT(d) "", path, mode_str, verbosity); - return true; - } - - /* - Will add syslog as a standard sink for log messages - Any logging message with a verbosity lower or equal to - the given verbosity will be included. - - This works for Unix like systems (i.e. Linux/Mac) - There is no current implementation for Windows (as I don't know the - equivalent calls or have a way to test them). If you know please - add and send a pull request. - - The code should still compile under windows but will only generate - a warning message that syslog is unavailable. - - Search for LOGURU_SYSLOG to find and fix. - */ - bool add_syslog(const char* app_name, Verbosity verbosity) - { - return add_syslog(app_name, verbosity, LOG_USER); - } - bool add_syslog(const char* app_name, Verbosity verbosity, int facility) - { + if (mode == FileMode::Append) { + fprintf(file, "\n\n\n\n\n"); + } + if (!s_arguments.empty()) { + fprintf(file, "arguments: %s\n", s_arguments.c_str()); + } + if (strlen(s_current_dir) != 0) { + fprintf(file, "Current dir: %s\n", s_current_dir); + } + fprintf(file, "File verbosity level: %d\n", verbosity); + if (g_preamble_header) { + char preamble_explain[LOGURU_PREAMBLE_WIDTH]; + print_preamble_header(preamble_explain, sizeof(preamble_explain)); + fprintf(file, "%s\n", preamble_explain); + } + fflush(file); + + VLOG_F(g_internal_verbosity, + "Logging to '" LOGURU_FMT(s) "', mode: '" LOGURU_FMT( + s) "', verbosity: " LOGURU_FMT(d) "", + path, mode_str, verbosity); + return true; +} + +/* + Will add syslog as a standard sink for log messages + Any logging message with a verbosity lower or equal to + the given verbosity will be included. + + This works for Unix like systems (i.e. Linux/Mac) + There is no current implementation for Windows (as I don't know the + equivalent calls or have a way to test them). If you know please + add and send a pull request. + + The code should still compile under windows but will only generate + a warning message that syslog is unavailable. + + Search for LOGURU_SYSLOG to find and fix. +*/ +bool add_syslog(const char* app_name, Verbosity verbosity) { + return add_syslog(app_name, verbosity, LOG_USER); +} +bool add_syslog(const char* app_name, Verbosity verbosity, int facility) { #if LOGURU_SYSLOG - if (app_name == nullptr) { - app_name = argv0_filename(); - } - openlog(app_name, 0, facility); - add_callback("'syslog'", syslog_log, nullptr, verbosity, syslog_close, syslog_flush); - - VLOG_F(g_internal_verbosity, "Logging to 'syslog' , verbosity: " LOGURU_FMT(d) "", verbosity); - return true; + if (app_name == nullptr) { + app_name = argv0_filename(); + } + openlog(app_name, 0, facility); + add_callback("'syslog'", syslog_log, nullptr, verbosity, syslog_close, + syslog_flush); + + VLOG_F(g_internal_verbosity, + "Logging to 'syslog' , verbosity: " LOGURU_FMT(d) "", verbosity); + return true; #else - (void)app_name; - (void)verbosity; - (void)facility; - VLOG_F(g_internal_verbosity, "syslog not implemented on this system. Request to install syslog logging ignored."); - return false; + (void)app_name; + (void)verbosity; + (void)facility; + VLOG_F(g_internal_verbosity, + "syslog not implemented on this system. Request to install syslog " + "logging ignored."); + return false; #endif - } - // Will be called right before abort(). - void set_fatal_handler(fatal_handler_t handler) - { - s_fatal_handler = handler; - } - - fatal_handler_t get_fatal_handler() - { - return s_fatal_handler; - } - - void set_verbosity_to_name_callback(verbosity_to_name_t callback) - { - s_verbosity_to_name_callback = callback; - } - - void set_name_to_verbosity_callback(name_to_verbosity_t callback) - { - s_name_to_verbosity_callback = callback; - } - - void add_stack_cleanup(const char* find_this, const char* replace_with_this) - { - if (strlen(find_this) <= strlen(replace_with_this)) { - LOG_F(WARNING, "add_stack_cleanup: the replacement should be shorter than the pattern!"); - return; - } - - s_user_stack_cleanups.push_back(StringPair(find_this, replace_with_this)); - } - - static void on_callback_change() - { - s_max_out_verbosity = Verbosity_OFF; - for (const auto& callback : s_callbacks) { - s_max_out_verbosity = std::max(s_max_out_verbosity, callback.verbosity); - } - } - - void add_callback( - const char* id, - log_handler_t callback, - void* user_data, - Verbosity verbosity, - close_handler_t on_close, - flush_handler_t on_flush) - { - std::lock_guard lock(s_mutex); - s_callbacks.push_back(Callback{id, callback, user_data, verbosity, on_close, on_flush, 0}); - on_callback_change(); - } - - // Returns a custom verbosity name if one is available, or nullptr. - // See also set_verbosity_to_name_callback. - const char* get_verbosity_name(Verbosity verbosity) - { - auto name = s_verbosity_to_name_callback - ? (*s_verbosity_to_name_callback)(verbosity) - : nullptr; - - // Use standard replacements if callback fails: - if (!name) - { - if (verbosity <= Verbosity_FATAL) { - name = "FATL"; - } else if (verbosity == Verbosity_ERROR) { - name = "ERR"; - } else if (verbosity == Verbosity_WARNING) { - name = "WARN"; - } else if (verbosity == Verbosity_INFO) { - name = "INFO"; - } - } - - return name; - } - - // Returns Verbosity_INVALID if the name is not found. - // See also set_name_to_verbosity_callback. - Verbosity get_verbosity_from_name(const char* name) - { - auto verbosity = s_name_to_verbosity_callback - ? (*s_name_to_verbosity_callback)(name) - : Verbosity_INVALID; - - // Use standard replacements if callback fails: - if (verbosity == Verbosity_INVALID) { - if (strcmp(name, "OFF") == 0) { - verbosity = Verbosity_OFF; - } else if (strcmp(name, "INFO") == 0) { - verbosity = Verbosity_INFO; - } else if (strcmp(name, "WARNING") == 0) { - verbosity = Verbosity_WARNING; - } else if (strcmp(name, "ERROR") == 0) { - verbosity = Verbosity_ERROR; - } else if (strcmp(name, "FATAL") == 0) { - verbosity = Verbosity_FATAL; - } - } - - return verbosity; - } - - bool remove_callback(const char* id) - { - std::lock_guard lock(s_mutex); - auto it = std::find_if(begin(s_callbacks), end(s_callbacks), [&](const Callback& c) { return c.id == id; }); - if (it != s_callbacks.end()) { - if (it->close) { it->close(it->user_data); } - s_callbacks.erase(it); - on_callback_change(); - return true; - } else { - LOG_F(ERROR, "Failed to locate callback with id '" LOGURU_FMT(s) "'", id); - return false; - } - } - - void remove_all_callbacks() - { - std::lock_guard lock(s_mutex); - for (auto& callback : s_callbacks) { - if (callback.close) { - callback.close(callback.user_data); - } - } - s_callbacks.clear(); - on_callback_change(); - } - - // Returns the maximum of g_stderr_verbosity and all file/custom outputs. - Verbosity current_verbosity_cutoff() - { - return g_stderr_verbosity > s_max_out_verbosity ? - g_stderr_verbosity : s_max_out_verbosity; - } - - // ------------------------------------------------------------------------ - // Threads names +} +// Will be called right before abort(). +void set_fatal_handler(fatal_handler_t handler) { s_fatal_handler = handler; } + +fatal_handler_t get_fatal_handler() { return s_fatal_handler; } + +void set_verbosity_to_name_callback(verbosity_to_name_t callback) { + s_verbosity_to_name_callback = callback; +} + +void set_name_to_verbosity_callback(name_to_verbosity_t callback) { + s_name_to_verbosity_callback = callback; +} + +void add_stack_cleanup(const char* find_this, const char* replace_with_this) { + if (strlen(find_this) <= strlen(replace_with_this)) { + LOG_F(WARNING, + "add_stack_cleanup: the replacement should be shorter than the " + "pattern!"); + return; + } + + s_user_stack_cleanups.push_back(StringPair(find_this, replace_with_this)); +} + +static void on_callback_change() { + s_max_out_verbosity = Verbosity_OFF; + for (const auto& callback : s_callbacks) { + s_max_out_verbosity = std::max(s_max_out_verbosity, callback.verbosity); + } +} + +void add_callback(const char* id, log_handler_t callback, void* user_data, + Verbosity verbosity, close_handler_t on_close, + flush_handler_t on_flush) { + std::lock_guard lock(s_mutex); + s_callbacks.push_back( + Callback{id, callback, user_data, verbosity, on_close, on_flush, 0}); + on_callback_change(); +} + +// Returns a custom verbosity name if one is available, or nullptr. +// See also set_verbosity_to_name_callback. +const char* get_verbosity_name(Verbosity verbosity) { + auto name = s_verbosity_to_name_callback + ? (*s_verbosity_to_name_callback)(verbosity) + : nullptr; + + // Use standard replacements if callback fails: + if (!name) { + if (verbosity <= Verbosity_FATAL) { + name = "FATL"; + } else if (verbosity == Verbosity_ERROR) { + name = "ERR"; + } else if (verbosity == Verbosity_WARNING) { + name = "WARN"; + } else if (verbosity == Verbosity_INFO) { + name = "INFO"; + } + } + + return name; +} + +// Returns Verbosity_INVALID if the name is not found. +// See also set_name_to_verbosity_callback. +Verbosity get_verbosity_from_name(const char* name) { + auto verbosity = s_name_to_verbosity_callback + ? (*s_name_to_verbosity_callback)(name) + : Verbosity_INVALID; + + // Use standard replacements if callback fails: + if (verbosity == Verbosity_INVALID) { + if (strcmp(name, "OFF") == 0) { + verbosity = Verbosity_OFF; + } else if (strcmp(name, "INFO") == 0) { + verbosity = Verbosity_INFO; + } else if (strcmp(name, "WARNING") == 0) { + verbosity = Verbosity_WARNING; + } else if (strcmp(name, "ERROR") == 0) { + verbosity = Verbosity_ERROR; + } else if (strcmp(name, "FATAL") == 0) { + verbosity = Verbosity_FATAL; + } + } + + return verbosity; +} + +bool remove_callback(const char* id) { + std::lock_guard lock(s_mutex); + auto it = std::find_if(begin(s_callbacks), end(s_callbacks), + [&](const Callback& c) { return c.id == id; }); + if (it != s_callbacks.end()) { + if (it->close) { + it->close(it->user_data); + } + s_callbacks.erase(it); + on_callback_change(); + return true; + } else { + LOG_F(ERROR, "Failed to locate callback with id '" LOGURU_FMT(s) "'", + id); + return false; + } +} + +void remove_all_callbacks() { + std::lock_guard lock(s_mutex); + for (auto& callback : s_callbacks) { + if (callback.close) { + callback.close(callback.user_data); + } + } + s_callbacks.clear(); + on_callback_change(); +} + +// Returns the maximum of g_stderr_verbosity and all file/custom outputs. +Verbosity current_verbosity_cutoff() { + return g_stderr_verbosity > s_max_out_verbosity ? g_stderr_verbosity + : s_max_out_verbosity; +} + +// ------------------------------------------------------------------------ +// Threads names #if LOGURU_PTLS_NAMES - static pthread_once_t s_pthread_key_once = PTHREAD_ONCE_INIT; - static pthread_key_t s_pthread_key_name; +static pthread_once_t s_pthread_key_once = PTHREAD_ONCE_INIT; +static pthread_key_t s_pthread_key_name; - void make_pthread_key_name() - { - (void)pthread_key_create(&s_pthread_key_name, free); - } +void make_pthread_key_name() { + (void)pthread_key_create(&s_pthread_key_name, free); +} #endif #if LOGURU_WINTHREADS - // Where we store the custom thread name set by `set_thread_name` - char* thread_name_buffer() - { - __declspec( thread ) static char thread_name[LOGURU_THREADNAME_WIDTH + 1] = {0}; - return &thread_name[0]; - } -#endif // LOGURU_WINTHREADS - - void set_thread_name(const char* name) - { - #if LOGURU_PTLS_NAMES - // Store thread name in thread-local storage at `s_pthread_key_name` - (void)pthread_once(&s_pthread_key_once, make_pthread_key_name); - (void)pthread_setspecific(s_pthread_key_name, STRDUP(name)); - #elif LOGURU_PTHREADS - // Tell the OS the thread name - #ifdef __APPLE__ - pthread_setname_np(name); - #elif defined(__FreeBSD__) || defined(__OpenBSD__) - pthread_set_name_np(pthread_self(), name); - #elif defined(__linux__) || defined(__sun) - pthread_setname_np(pthread_self(), name); - #endif - #elif LOGURU_WINTHREADS - // Store thread name in a thread-local storage: - strncpy_s(thread_name_buffer(), LOGURU_THREADNAME_WIDTH + 1, name, _TRUNCATE); - #else // LOGURU_PTHREADS - // TODO: on these weird platforms we should also store the thread name - // in a generic thread-local storage. - (void)name; - #endif // LOGURU_PTHREADS - } - - void get_thread_name(char* buffer, unsigned long long length, bool right_align_hex_id) - { - CHECK_NE_F(length, 0u, "Zero length buffer in get_thread_name"); - CHECK_NOTNULL_F(buffer, "nullptr in get_thread_name"); - - #if LOGURU_PTLS_NAMES - (void)pthread_once(&s_pthread_key_once, make_pthread_key_name); - if (const char* name = static_cast(pthread_getspecific(s_pthread_key_name))) { - snprintf(buffer, static_cast(length), "%s", name); - } else { - buffer[0] = 0; - } - #elif LOGURU_PTHREADS - // Ask the OS about the thread name. - // This is what we *want* to do on all platforms, but - // only some platforms support it (currently). - pthread_getname_np(pthread_self(), buffer, length); - #elif LOGURU_WINTHREADS - snprintf(buffer, static_cast(length), "%s", thread_name_buffer()); - #else - // Thread names unsupported - buffer[0] = 0; - #endif - - if (buffer[0] == 0) { - // We failed to get a readable thread name. - // Write a HEX thread ID instead. - // We try to get an ID that is the same as the ID you could - // read in your debugger, system monitor etc. - - #ifdef __APPLE__ - uint64_t thread_id; - pthread_threadid_np(pthread_self(), &thread_id); - #elif defined(__FreeBSD__) - long thread_id; - (void)thr_self(&thread_id); - #elif LOGURU_PTHREADS - uint64_t thread_id = pthread_self(); - #else - // This ID does not correllate to anything we can get from the OS, - // so this is the worst way to get the ID. - const auto thread_id = std::hash{}(std::this_thread::get_id()); - #endif - - if (right_align_hex_id) { - snprintf(buffer, static_cast(length), "%*X", static_cast(length - 1), static_cast(thread_id)); - } else { - snprintf(buffer, static_cast(length), "%X", static_cast(thread_id)); - } - } - } - - // ------------------------------------------------------------------------ - // Stack traces +// Where we store the custom thread name set by `set_thread_name` +char* thread_name_buffer() { + thread_local static char thread_name[LOGURU_THREADNAME_WIDTH + 1] = {0}; + return &thread_name[0]; +} +#endif // LOGURU_WINTHREADS + +void set_thread_name(const char* name) { +#if LOGURU_PTLS_NAMES + // Store thread name in thread-local storage at `s_pthread_key_name` + (void)pthread_once(&s_pthread_key_once, make_pthread_key_name); + (void)pthread_setspecific(s_pthread_key_name, STRDUP(name)); +#elif LOGURU_PTHREADS +// Tell the OS the thread name +#ifdef __APPLE__ + pthread_setname_np(name); +#elif defined(__FreeBSD__) || defined(__OpenBSD__) + pthread_set_name_np(pthread_self(), name); +#elif defined(__linux__) || defined(__sun) + pthread_setname_np(pthread_self(), name); +#endif +#elif LOGURU_WINTHREADS + // Store thread name in a thread-local storage: + strncpy_s(thread_name_buffer(), LOGURU_THREADNAME_WIDTH + 1, name, + _TRUNCATE); +#else // LOGURU_PTHREADS + // TODO: on these weird platforms we should also store the thread name + // in a generic thread-local storage. + (void)name; +#endif // LOGURU_PTHREADS +} + +void get_thread_name(char* buffer, unsigned long long length, + bool right_align_hex_id) { + CHECK_NE_F(length, 0u, "Zero length buffer in get_thread_name"); + CHECK_NOTNULL_F(buffer, "nullptr in get_thread_name"); + +#if LOGURU_PTLS_NAMES + (void)pthread_once(&s_pthread_key_once, make_pthread_key_name); + if (const char* name = + static_cast(pthread_getspecific(s_pthread_key_name))) { + snprintf(buffer, static_cast(length), "%s", name); + } else { + buffer[0] = 0; + } +#elif LOGURU_PTHREADS + // Ask the OS about the thread name. + // This is what we *want* to do on all platforms, but + // only some platforms support it (currently). + pthread_getname_np(pthread_self(), buffer, length); +#elif LOGURU_WINTHREADS + snprintf(buffer, static_cast(length), "%s", thread_name_buffer()); +#else + // Thread names unsupported + buffer[0] = 0; +#endif + + if (buffer[0] == 0) { + // We failed to get a readable thread name. + // Write a HEX thread ID instead. + // We try to get an ID that is the same as the ID you could + // read in your debugger, system monitor etc. + +#ifdef __APPLE__ + uint64_t thread_id; + pthread_threadid_np(pthread_self(), &thread_id); +#elif defined(__FreeBSD__) + long thread_id; + (void)thr_self(&thread_id); +#elif LOGURU_PTHREADS + uint64_t thread_id = pthread_self(); +#else + // This ID does not correllate to anything we can get from the OS, + // so this is the worst way to get the ID. + const auto thread_id = + std::hash{}(std::this_thread::get_id()); +#endif + + if (right_align_hex_id) { + snprintf(buffer, static_cast(length), "%*X", + static_cast(length - 1), + static_cast(thread_id)); + } else { + snprintf(buffer, static_cast(length), "%X", + static_cast(thread_id)); + } + } +} + +// ------------------------------------------------------------------------ +// Stack traces #if LOGURU_STACKTRACES - Text demangle(const char* name) - { - int status = -1; - char* demangled = abi::__cxa_demangle(name, 0, 0, &status); - Text result{status == 0 ? demangled : STRDUP(name)}; - return result; - } - - #if LOGURU_RTTI - template - std::string type_name() - { - auto demangled = demangle(typeid(T).name()); - return demangled.c_str(); - } - #endif // LOGURU_RTTI - - static const StringPairList REPLACE_LIST = { - #if LOGURU_RTTI - { type_name(), "std::string" }, - { type_name(), "std::wstring" }, - { type_name(), "std::u16string" }, - { type_name(), "std::u32string" }, - #endif // LOGURU_RTTI - { "std::__1::", "std::" }, - { "__thiscall ", "" }, - { "__cdecl ", "" }, - }; - - void do_replacements(const StringPairList& replacements, std::string& str) - { - for (auto&& p : replacements) { - if (p.first.size() <= p.second.size()) { - // On gcc, "type_name()" is "std::string" - continue; - } - - size_t it; - while ((it=str.find(p.first)) != std::string::npos) { - str.replace(it, p.first.size(), p.second); - } - } - } - - std::string prettify_stacktrace(const std::string& input) - { - std::string output = input; - - do_replacements(s_user_stack_cleanups, output); - do_replacements(REPLACE_LIST, output); - - try { - std::regex std_allocator_re(R"(,\s*std::allocator<[^<>]+>)"); - output = std::regex_replace(output, std_allocator_re, std::string("")); - - std::regex template_spaces_re(R"(<\s*([^<> ]+)\s*>)"); - output = std::regex_replace(output, template_spaces_re, std::string("<$1>")); - } catch (std::regex_error&) { - // Probably old GCC. - } - - return output; - } - - std::string stacktrace_as_stdstring(int skip) - { - // From https://gist.github.com/fmela/591333 - void* callstack[128]; - const auto max_frames = sizeof(callstack) / sizeof(callstack[0]); - int num_frames = backtrace(callstack, max_frames); - char** symbols = backtrace_symbols(callstack, num_frames); - - std::string result; - // Print stack traces so the most relevant ones are written last - // Rationale: http://yellerapp.com/posts/2015-01-22-upside-down-stacktraces.html - for (int i = num_frames - 1; i >= skip; --i) { - char buf[1024]; - Dl_info info; - if (dladdr(callstack[i], &info) && info.dli_sname) { - char* demangled = NULL; - int status = -1; - if (info.dli_sname[0] == '_') { - demangled = abi::__cxa_demangle(info.dli_sname, 0, 0, &status); - } - snprintf(buf, sizeof(buf), "%-3d %*p %s + %zd\n", - i - skip, int(2 + sizeof(void*) * 2), callstack[i], - status == 0 ? demangled : - info.dli_sname == 0 ? symbols[i] : info.dli_sname, - static_cast(callstack[i]) - static_cast(info.dli_saddr)); - free(demangled); - } else { - snprintf(buf, sizeof(buf), "%-3d %*p %s\n", - i - skip, int(2 + sizeof(void*) * 2), callstack[i], symbols[i]); - } - result += buf; - } - free(symbols); - - if (num_frames == max_frames) { - result = "[truncated]\n" + result; - } - - if (!result.empty() && result[result.size() - 1] == '\n') { - result.resize(result.size() - 1); - } - - return prettify_stacktrace(result); - } - -#else // LOGURU_STACKTRACES - Text demangle(const char* name) - { - return Text(STRDUP(name)); - } - - std::string stacktrace_as_stdstring(int) - { - // No stacktraces available on this platform" - return ""; - } - -#endif // LOGURU_STACKTRACES - - Text stacktrace(int skip) - { - auto str = stacktrace_as_stdstring(skip + 1); - return Text(STRDUP(str.c_str())); - } - - // ------------------------------------------------------------------------ - - static void print_preamble_header(char* out_buff, size_t out_buff_size) - { - if (out_buff_size == 0) { return; } - out_buff[0] = '\0'; - size_t pos = 0; - if (g_preamble_date && pos < out_buff_size) { - int bytes = snprintf(out_buff + pos, out_buff_size - pos, "date "); - if (bytes > 0) { - pos += bytes; - } - } - if (g_preamble_time && pos < out_buff_size) { - int bytes = snprintf(out_buff + pos, out_buff_size - pos, "time "); - if (bytes > 0) { - pos += bytes; - } - } - if (g_preamble_uptime && pos < out_buff_size) { - int bytes = snprintf(out_buff + pos, out_buff_size - pos, "( uptime ) "); - if (bytes > 0) { - pos += bytes; - } - } - if (g_preamble_thread && pos < out_buff_size) { - int bytes = snprintf(out_buff + pos, out_buff_size - pos, "[%-*s]", LOGURU_THREADNAME_WIDTH, " thread name/id"); - if (bytes > 0) { - pos += bytes; - } - } - if (g_preamble_file && pos < out_buff_size) { - int bytes = snprintf(out_buff + pos, out_buff_size - pos, "%*s:line ", LOGURU_FILENAME_WIDTH, "file"); - if (bytes > 0) { - pos += bytes; - } - } - if (g_preamble_verbose && pos < out_buff_size) { - int bytes = snprintf(out_buff + pos, out_buff_size - pos, " v"); - if (bytes > 0) { - pos += bytes; - } - } - if (g_preamble_pipe && pos < out_buff_size) { - int bytes = snprintf(out_buff + pos, out_buff_size - pos, "| "); - if (bytes > 0) { - pos += bytes; - } - } - } - - static void print_preamble(char* out_buff, size_t out_buff_size, Verbosity verbosity, const char* file, unsigned line) - { - if (out_buff_size == 0) { return; } - out_buff[0] = '\0'; - if (!g_preamble) { return; } - long long ms_since_epoch = duration_cast(system_clock::now().time_since_epoch()).count(); - time_t sec_since_epoch = time_t(ms_since_epoch / 1000); - tm time_info; - localtime_r(&sec_since_epoch, &time_info); - - auto uptime_ms = duration_cast(steady_clock::now() - s_start_time).count(); - auto uptime_sec = static_cast (uptime_ms) / 1000.0; - - char thread_name[LOGURU_THREADNAME_WIDTH + 1] = {0}; - get_thread_name(thread_name, LOGURU_THREADNAME_WIDTH + 1, true); - - if (s_strip_file_path) { - file = filename(file); - } - - char level_buff[6]; - const char* custom_level_name = get_verbosity_name(verbosity); - if (custom_level_name) { - snprintf(level_buff, sizeof(level_buff) - 1, "%s", custom_level_name); - } else { - snprintf(level_buff, sizeof(level_buff) - 1, "% 4d", static_cast(verbosity)); - } - - size_t pos = 0; - - if (g_preamble_date && pos < out_buff_size) { - int bytes = snprintf(out_buff + pos, out_buff_size - pos, "%04d-%02d-%02d ", - 1900 + time_info.tm_year, 1 + time_info.tm_mon, time_info.tm_mday); - if (bytes > 0) { - pos += bytes; - } - } - if (g_preamble_time && pos < out_buff_size) { - int bytes = snprintf(out_buff + pos, out_buff_size - pos, "%02d:%02d:%02d.%03lld ", - time_info.tm_hour, time_info.tm_min, time_info.tm_sec, ms_since_epoch % 1000); - if (bytes > 0) { - pos += bytes; - } - } - if (g_preamble_uptime && pos < out_buff_size) { - int bytes = snprintf(out_buff + pos, out_buff_size - pos, "(%8.3fs) ", - uptime_sec); - if (bytes > 0) { - pos += bytes; - } - } - if (g_preamble_thread && pos < out_buff_size) { - int bytes = snprintf(out_buff + pos, out_buff_size - pos, "[%-*s]", - LOGURU_THREADNAME_WIDTH, thread_name); - if (bytes > 0) { - pos += bytes; - } - } - if (g_preamble_file && pos < out_buff_size) { - char shortened_filename[LOGURU_FILENAME_WIDTH + 1]; - snprintf(shortened_filename, LOGURU_FILENAME_WIDTH + 1, "%s", file); - int bytes = snprintf(out_buff + pos, out_buff_size - pos, "%*s:%-5u ", - LOGURU_FILENAME_WIDTH, shortened_filename, line); - if (bytes > 0) { - pos += bytes; - } - } - if (g_preamble_verbose && pos < out_buff_size) { - int bytes = snprintf(out_buff + pos, out_buff_size - pos, "%4s", - level_buff); - if (bytes > 0) { - pos += bytes; - } - } - if (g_preamble_pipe && pos < out_buff_size) { - int bytes = snprintf(out_buff + pos, out_buff_size - pos, "| "); - if (bytes > 0) { - pos += bytes; - } - } - } - - // stack_trace_skip is just if verbosity == FATAL. - static void log_message(int stack_trace_skip, Message& message, bool with_indentation, bool abort_if_fatal) - { - const auto verbosity = message.verbosity; - std::lock_guard lock(s_mutex); - - if (message.verbosity == Verbosity_FATAL) { - auto st = loguru::stacktrace(stack_trace_skip + 2); - if (!st.empty()) { - RAW_LOG_F(ERROR, "Stack trace:\n" LOGURU_FMT(s) "", st.c_str()); - } - - auto ec = loguru::get_error_context(); - if (!ec.empty()) { - RAW_LOG_F(ERROR, "" LOGURU_FMT(s) "", ec.c_str()); - } - } - - if (with_indentation) { - message.indentation = indentation(s_stderr_indentation); - } - - if (verbosity <= g_stderr_verbosity) { - if (g_colorlogtostderr && s_terminal_has_color) { - if (verbosity > Verbosity_WARNING) { - fprintf(stderr, "%s%s%s%s%s%s%s%s\n", - terminal_reset(), - terminal_dim(), - message.preamble, - message.indentation, - verbosity == Verbosity_INFO ? terminal_reset() : "", // un-dim for info - message.prefix, - message.message, - terminal_reset()); - } else { - fprintf(stderr, "%s%s%s%s%s%s%s\n", - terminal_reset(), - verbosity == Verbosity_WARNING ? terminal_yellow() : terminal_red(), - message.preamble, - message.indentation, - message.prefix, - message.message, - terminal_reset()); - } - } else { - fprintf(stderr, "%s%s%s%s\n", - message.preamble, message.indentation, message.prefix, message.message); - } - - if (g_flush_interval_ms == 0) { - fflush(stderr); - } else { - s_needs_flushing = true; - } - } - - for (auto& p : s_callbacks) { - if (verbosity <= p.verbosity) { - if (with_indentation) { - message.indentation = indentation(p.indentation); - } - p.callback(p.user_data, message); - if (g_flush_interval_ms == 0) { - if (p.flush) { p.flush(p.user_data); } - } else { - s_needs_flushing = true; - } - } - } - - if (g_flush_interval_ms > 0 && !s_flush_thread) { - s_flush_thread = new std::thread([](){ - for (;;) { - if (s_needs_flushing) { - flush(); - } - std::this_thread::sleep_for(std::chrono::milliseconds(g_flush_interval_ms)); - } - }); - } - - if (message.verbosity == Verbosity_FATAL) { - flush(); - - if (s_fatal_handler) { - s_fatal_handler(message); - flush(); - } - - if (abort_if_fatal) { +Text demangle(const char* name) { + int status = -1; + char* demangled = abi::__cxa_demangle(name, 0, 0, &status); + Text result{status == 0 ? demangled : STRDUP(name)}; + return result; +} + +#if LOGURU_RTTI +template +std::string type_name() { + auto demangled = demangle(typeid(T).name()); + return demangled.c_str(); +} +#endif // LOGURU_RTTI + +static const StringPairList REPLACE_LIST = { +#if LOGURU_RTTI + {type_name(), "std::string"}, + {type_name(), "std::wstring"}, + {type_name(), "std::u16string"}, + {type_name(), "std::u32string"}, +#endif // LOGURU_RTTI + {"std::__1::", "std::"}, + {"__thiscall ", ""}, + {"__cdecl ", ""}, +}; + +void do_replacements(const StringPairList& replacements, std::string& str) { + for (auto&& p : replacements) { + if (p.first.size() <= p.second.size()) { + // On gcc, "type_name()" is "std::string" + continue; + } + + size_t it; + while ((it = str.find(p.first)) != std::string::npos) { + str.replace(it, p.first.size(), p.second); + } + } +} + +std::string prettify_stacktrace(const std::string& input) { + std::string output = input; + + do_replacements(s_user_stack_cleanups, output); + do_replacements(REPLACE_LIST, output); + + try { + std::regex std_allocator_re(R"(,\s*std::allocator<[^<>]+>)"); + output = std::regex_replace(output, std_allocator_re, std::string("")); + + std::regex template_spaces_re(R"(<\s*([^<> ]+)\s*>)"); + output = + std::regex_replace(output, template_spaces_re, std::string("<$1>")); + } catch (std::regex_error&) { + // Probably old GCC. + } + + return output; +} + +std::string stacktrace_as_stdstring(int skip) { + // From https://gist.github.com/fmela/591333 + void* callstack[128]; + const auto max_frames = sizeof(callstack) / sizeof(callstack[0]); + int num_frames = backtrace(callstack, max_frames); + char** symbols = backtrace_symbols(callstack, num_frames); + + std::string result; + // Print stack traces so the most relevant ones are written last + // Rationale: + // http://yellerapp.com/posts/2015-01-22-upside-down-stacktraces.html + for (int i = num_frames - 1; i >= skip; --i) { + char buf[1024]; + Dl_info info; + if (dladdr(callstack[i], &info) && info.dli_sname) { + char* demangled = NULL; + int status = -1; + if (info.dli_sname[0] == '_') { + demangled = abi::__cxa_demangle(info.dli_sname, 0, 0, &status); + } + snprintf(buf, sizeof(buf), "%-3d %*p %s + %zd\n", i - skip, + int(2 + sizeof(void*) * 2), callstack[i], + status == 0 ? demangled + : info.dli_sname == 0 ? symbols[i] + : info.dli_sname, + static_cast(callstack[i]) - + static_cast(info.dli_saddr)); + free(demangled); + } else { + snprintf(buf, sizeof(buf), "%-3d %*p %s\n", i - skip, + int(2 + sizeof(void*) * 2), callstack[i], symbols[i]); + } + result += buf; + } + free(symbols); + + if (num_frames == max_frames) { + result = "[truncated]\n" + result; + } + + if (!result.empty() && result[result.size() - 1] == '\n') { + result.resize(result.size() - 1); + } + + return prettify_stacktrace(result); +} + +#else // LOGURU_STACKTRACES +Text demangle(const char* name) { return Text(STRDUP(name)); } + +std::string stacktrace_as_stdstring(int) { + // No stacktraces available on this platform" + return ""; +} + +#endif // LOGURU_STACKTRACES + +Text stacktrace(int skip) { + auto str = stacktrace_as_stdstring(skip + 1); + return Text(STRDUP(str.c_str())); +} + +// ------------------------------------------------------------------------ + +static void print_preamble_header(char* out_buff, size_t out_buff_size) { + if (out_buff_size == 0) { + return; + } + out_buff[0] = '\0'; + size_t pos = 0; + if (g_preamble_date && pos < out_buff_size) { + int bytes = + snprintf(out_buff + pos, out_buff_size - pos, "date "); + if (bytes > 0) { + pos += bytes; + } + } + if (g_preamble_time && pos < out_buff_size) { + int bytes = + snprintf(out_buff + pos, out_buff_size - pos, "time "); + if (bytes > 0) { + pos += bytes; + } + } + if (g_preamble_uptime && pos < out_buff_size) { + int bytes = + snprintf(out_buff + pos, out_buff_size - pos, "( uptime ) "); + if (bytes > 0) { + pos += bytes; + } + } + if (g_preamble_thread && pos < out_buff_size) { + int bytes = snprintf(out_buff + pos, out_buff_size - pos, "[%-*s]", + LOGURU_THREADNAME_WIDTH, " thread name/id"); + if (bytes > 0) { + pos += bytes; + } + } + if (g_preamble_file && pos < out_buff_size) { + int bytes = snprintf(out_buff + pos, out_buff_size - pos, "%*s:line ", + LOGURU_FILENAME_WIDTH, "file"); + if (bytes > 0) { + pos += bytes; + } + } + if (g_preamble_verbose && pos < out_buff_size) { + int bytes = snprintf(out_buff + pos, out_buff_size - pos, " v"); + if (bytes > 0) { + pos += bytes; + } + } + if (g_preamble_pipe && pos < out_buff_size) { + int bytes = snprintf(out_buff + pos, out_buff_size - pos, "| "); + if (bytes > 0) { + pos += bytes; + } + } +} + +static void print_preamble(char* out_buff, size_t out_buff_size, + Verbosity verbosity, const char* file, + unsigned line) { + if (out_buff_size == 0) { + return; + } + out_buff[0] = '\0'; + if (!g_preamble) { + return; + } + long long ms_since_epoch = + duration_cast(system_clock::now().time_since_epoch()) + .count(); + time_t sec_since_epoch = time_t(ms_since_epoch / 1000); + tm time_info; + localtime_r(&sec_since_epoch, &time_info); + + auto uptime_ms = + duration_cast(steady_clock::now() - s_start_time).count(); + auto uptime_sec = static_cast(uptime_ms) / 1000.0; + + char thread_name[LOGURU_THREADNAME_WIDTH + 1] = {0}; + get_thread_name(thread_name, LOGURU_THREADNAME_WIDTH + 1, true); + + if (s_strip_file_path) { + file = filename(file); + } + + char level_buff[6]; + const char* custom_level_name = get_verbosity_name(verbosity); + if (custom_level_name) { + snprintf(level_buff, sizeof(level_buff) - 1, "%s", custom_level_name); + } else { + snprintf(level_buff, sizeof(level_buff) - 1, "% 4d", + static_cast(verbosity)); + } + + size_t pos = 0; + + if (g_preamble_date && pos < out_buff_size) { + int bytes = snprintf(out_buff + pos, out_buff_size - pos, + "%04d-%02d-%02d ", 1900 + time_info.tm_year, + 1 + time_info.tm_mon, time_info.tm_mday); + if (bytes > 0) { + pos += bytes; + } + } + if (g_preamble_time && pos < out_buff_size) { + int bytes = + snprintf(out_buff + pos, out_buff_size - pos, + "%02d:%02d:%02d.%03lld ", time_info.tm_hour, + time_info.tm_min, time_info.tm_sec, ms_since_epoch % 1000); + if (bytes > 0) { + pos += bytes; + } + } + if (g_preamble_uptime && pos < out_buff_size) { + int bytes = snprintf(out_buff + pos, out_buff_size - pos, "(%8.3fs) ", + uptime_sec); + if (bytes > 0) { + pos += bytes; + } + } + if (g_preamble_thread && pos < out_buff_size) { + int bytes = snprintf(out_buff + pos, out_buff_size - pos, "[%-*s]", + LOGURU_THREADNAME_WIDTH, thread_name); + if (bytes > 0) { + pos += bytes; + } + } + if (g_preamble_file && pos < out_buff_size) { + char shortened_filename[LOGURU_FILENAME_WIDTH + 1]; + snprintf(shortened_filename, LOGURU_FILENAME_WIDTH + 1, "%s", file); + int bytes = snprintf(out_buff + pos, out_buff_size - pos, "%*s:%-5u ", + LOGURU_FILENAME_WIDTH, shortened_filename, line); + if (bytes > 0) { + pos += bytes; + } + } + if (g_preamble_verbose && pos < out_buff_size) { + int bytes = + snprintf(out_buff + pos, out_buff_size - pos, "%4s", level_buff); + if (bytes > 0) { + pos += bytes; + } + } + if (g_preamble_pipe && pos < out_buff_size) { + int bytes = snprintf(out_buff + pos, out_buff_size - pos, "| "); + if (bytes > 0) { + pos += bytes; + } + } +} + +// stack_trace_skip is just if verbosity == FATAL. +static void log_message(int stack_trace_skip, Message& message, + bool with_indentation, bool abort_if_fatal) { + const auto verbosity = message.verbosity; + std::lock_guard lock(s_mutex); + + if (message.verbosity == Verbosity_FATAL) { + auto st = loguru::stacktrace(stack_trace_skip + 2); + if (!st.empty()) { + RAW_LOG_F(ERROR, "Stack trace:\n" LOGURU_FMT(s) "", st.c_str()); + } + + auto ec = loguru::get_error_context(); + if (!ec.empty()) { + RAW_LOG_F(ERROR, "" LOGURU_FMT(s) "", ec.c_str()); + } + } + + if (with_indentation) { + message.indentation = indentation(s_stderr_indentation); + } + + if (verbosity <= g_stderr_verbosity) { + if (g_colorlogtostderr && s_terminal_has_color) { + if (verbosity > Verbosity_WARNING) { + fprintf(stderr, "%s%s%s%s%s%s%s%s\n", terminal_reset(), + terminal_dim(), message.preamble, message.indentation, + verbosity == Verbosity_INFO ? terminal_reset() + : "", // un-dim for info + message.prefix, message.message, terminal_reset()); + } else { + fprintf(stderr, "%s%s%s%s%s%s%s\n", terminal_reset(), + verbosity == Verbosity_WARNING ? terminal_yellow() + : terminal_red(), + message.preamble, message.indentation, message.prefix, + message.message, terminal_reset()); + } + } else { + fprintf(stderr, "%s%s%s%s\n", message.preamble, message.indentation, + message.prefix, message.message); + } + + if (g_flush_interval_ms == 0) { + fflush(stderr); + } else { + s_needs_flushing = true; + } + } + + for (auto& p : s_callbacks) { + if (verbosity <= p.verbosity) { + if (with_indentation) { + message.indentation = indentation(p.indentation); + } + p.callback(p.user_data, message); + if (g_flush_interval_ms == 0) { + if (p.flush) { + p.flush(p.user_data); + } + } else { + s_needs_flushing = true; + } + } + } + + if (g_flush_interval_ms > 0 && !s_flush_thread) { + s_flush_thread = new std::thread([]() { + for (;;) { + if (s_needs_flushing) { + flush(); + } + std::this_thread::sleep_for( + std::chrono::milliseconds(g_flush_interval_ms)); + } + }); + } + + if (message.verbosity == Verbosity_FATAL) { + flush(); + + if (s_fatal_handler) { + s_fatal_handler(message); + flush(); + } + + if (abort_if_fatal) { #if !defined(_WIN32) - if (s_signal_options.sigabrt) { - // Make sure we don't catch our own abort: - signal(SIGABRT, SIG_DFL); - } + if (s_signal_options.sigabrt) { + // Make sure we don't catch our own abort: + signal(SIGABRT, SIG_DFL); + } #endif - abort(); - } - } - } - - // stack_trace_skip is just if verbosity == FATAL. - void log_to_everywhere(int stack_trace_skip, Verbosity verbosity, - const char* file, unsigned line, - const char* prefix, const char* buff) - { - char preamble_buff[LOGURU_PREAMBLE_WIDTH]; - print_preamble(preamble_buff, sizeof(preamble_buff), verbosity, file, line); - auto message = Message{verbosity, file, line, preamble_buff, "", prefix, buff}; - log_message(stack_trace_skip + 1, message, true, true); - } + abort(); + } + } +} + +// stack_trace_skip is just if verbosity == FATAL. +void log_to_everywhere(int stack_trace_skip, Verbosity verbosity, + const char* file, unsigned line, const char* prefix, + const char* buff) { + char preamble_buff[LOGURU_PREAMBLE_WIDTH]; + print_preamble(preamble_buff, sizeof(preamble_buff), verbosity, file, line); + auto message = + Message{verbosity, file, line, preamble_buff, "", prefix, buff}; + log_message(stack_trace_skip + 1, message, true, true); +} #if LOGURU_USE_FMTLIB - void vlog(Verbosity verbosity, const char* file, unsigned line, const char* format, fmt::format_args args) - { - auto formatted = fmt::vformat(format, args); - log_to_everywhere(1, verbosity, file, line, "", formatted.c_str()); - } - - void raw_vlog(Verbosity verbosity, const char* file, unsigned line, const char* format, fmt::format_args args) - { - auto formatted = fmt::vformat(format, args); - auto message = Message{verbosity, file, line, "", "", "", formatted.c_str()}; - log_message(1, message, false, true); - } +void vlog(Verbosity verbosity, const char* file, unsigned line, + const char* format, fmt::format_args args) { + auto formatted = fmt::vformat(format, args); + log_to_everywhere(1, verbosity, file, line, "", formatted.c_str()); +} + +void raw_vlog(Verbosity verbosity, const char* file, unsigned line, + const char* format, fmt::format_args args) { + auto formatted = fmt::vformat(format, args); + auto message = + Message{verbosity, file, line, "", "", "", formatted.c_str()}; + log_message(1, message, false, true); +} #else - void log(Verbosity verbosity, const char* file, unsigned line, const char* format, ...) - { - va_list vlist; - va_start(vlist, format); - vlog(verbosity, file, line, format, vlist); - va_end(vlist); - } - - void vlog(Verbosity verbosity, const char* file, unsigned line, const char* format, va_list vlist) - { - auto buff = vtextprintf(format, vlist); - log_to_everywhere(1, verbosity, file, line, "", buff.c_str()); - } - - void raw_log(Verbosity verbosity, const char* file, unsigned line, const char* format, ...) - { - va_list vlist; - va_start(vlist, format); - auto buff = vtextprintf(format, vlist); - auto message = Message{verbosity, file, line, "", "", "", buff.c_str()}; - log_message(1, message, false, true); - va_end(vlist); - } +void log(Verbosity verbosity, const char* file, unsigned line, + const char* format, ...) { + va_list vlist; + va_start(vlist, format); + vlog(verbosity, file, line, format, vlist); + va_end(vlist); +} + +void vlog(Verbosity verbosity, const char* file, unsigned line, + const char* format, va_list vlist) { + auto buff = vtextprintf(format, vlist); + log_to_everywhere(1, verbosity, file, line, "", buff.c_str()); +} + +void raw_log(Verbosity verbosity, const char* file, unsigned line, + const char* format, ...) { + va_list vlist; + va_start(vlist, format); + auto buff = vtextprintf(format, vlist); + auto message = Message{verbosity, file, line, "", "", "", buff.c_str()}; + log_message(1, message, false, true); + va_end(vlist); +} #endif - void flush() - { - std::lock_guard lock(s_mutex); - fflush(stderr); - for (const auto& callback : s_callbacks) - { - if (callback.flush) { - callback.flush(callback.user_data); - } - } - s_needs_flushing = false; - } - - LogScopeRAII::LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, const char* format, va_list vlist) : - _verbosity(verbosity), _file(file), _line(line) - { - this->Init(format, vlist); - } - - LogScopeRAII::LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, const char* format, ...) : - _verbosity(verbosity), _file(file), _line(line) - { - va_list vlist; - va_start(vlist, format); - this->Init(format, vlist); - va_end(vlist); - } - - LogScopeRAII::~LogScopeRAII() - { - if (_file) { - std::lock_guard lock(s_mutex); - if (_indent_stderr && s_stderr_indentation > 0) { - --s_stderr_indentation; - } - for (auto& p : s_callbacks) { - // Note: Callback indentation cannot change! - if (_verbosity <= p.verbosity) { - // in unlikely case this callback is new - if (p.indentation > 0) { - --p.indentation; - } - } - } +void flush() { + std::lock_guard lock(s_mutex); + fflush(stderr); + for (const auto& callback : s_callbacks) { + if (callback.flush) { + callback.flush(callback.user_data); + } + } + s_needs_flushing = false; +} + +LogScopeRAII::LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, + const char* format, va_list vlist) + : _verbosity(verbosity), _file(file), _line(line) { + this->Init(format, vlist); +} + +LogScopeRAII::LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, + const char* format, ...) + : _verbosity(verbosity), _file(file), _line(line) { + va_list vlist; + va_start(vlist, format); + this->Init(format, vlist); + va_end(vlist); +} + +LogScopeRAII::~LogScopeRAII() { + if (_file) { + std::lock_guard lock(s_mutex); + if (_indent_stderr && s_stderr_indentation > 0) { + --s_stderr_indentation; + } + for (auto& p : s_callbacks) { + // Note: Callback indentation cannot change! + if (_verbosity <= p.verbosity) { + // in unlikely case this callback is new + if (p.indentation > 0) { + --p.indentation; + } + } + } #if LOGURU_VERBOSE_SCOPE_ENDINGS - auto duration_sec = static_cast(now_ns() - _start_time_ns) / 1e9; + auto duration_sec = + static_cast(now_ns() - _start_time_ns) / 1e9; #if LOGURU_USE_FMTLIB - auto buff = textprintf("{:.{}f} s: {:s}", duration_sec, LOGURU_SCOPE_TIME_PRECISION, _name); + auto buff = textprintf("{:.{}f} s: {:s}", duration_sec, + LOGURU_SCOPE_TIME_PRECISION, _name); #else - auto buff = textprintf("%.*f s: %s", LOGURU_SCOPE_TIME_PRECISION, duration_sec, _name); + auto buff = textprintf("%.*f s: %s", LOGURU_SCOPE_TIME_PRECISION, + duration_sec, _name); #endif - log_to_everywhere(1, _verbosity, _file, _line, "} ", buff.c_str()); + log_to_everywhere(1, _verbosity, _file, _line, "} ", buff.c_str()); #else - log_to_everywhere(1, _verbosity, _file, _line, "}", ""); + log_to_everywhere(1, _verbosity, _file, _line, "}", ""); #endif - } - } - - void LogScopeRAII::Init(const char* format, va_list vlist) - { - if (_verbosity <= current_verbosity_cutoff()) { - std::lock_guard lock(s_mutex); - _indent_stderr = (_verbosity <= g_stderr_verbosity); - _start_time_ns = now_ns(); - vsnprintf(_name, sizeof(_name), format, vlist); - log_to_everywhere(1, _verbosity, _file, _line, "{ ", _name); - - if (_indent_stderr) { - ++s_stderr_indentation; - } - - for (auto& p : s_callbacks) { - if (_verbosity <= p.verbosity) { - ++p.indentation; - } - } - } else { - _file = nullptr; - } - } + } +} + +void LogScopeRAII::Init(const char* format, va_list vlist) { + if (_verbosity <= current_verbosity_cutoff()) { + std::lock_guard lock(s_mutex); + _indent_stderr = (_verbosity <= g_stderr_verbosity); + _start_time_ns = now_ns(); + vsnprintf(_name, sizeof(_name), format, vlist); + log_to_everywhere(1, _verbosity, _file, _line, "{ ", _name); + + if (_indent_stderr) { + ++s_stderr_indentation; + } + + for (auto& p : s_callbacks) { + if (_verbosity <= p.verbosity) { + ++p.indentation; + } + } + } else { + _file = nullptr; + } +} #if LOGURU_USE_FMTLIB - void vlog_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, const char* format, fmt::format_args args) - { - auto formatted = fmt::vformat(format, args); - log_to_everywhere(stack_trace_skip + 1, Verbosity_FATAL, file, line, expr, formatted.c_str()); - abort(); // log_to_everywhere already does this, but this makes the analyzer happy. - } +void vlog_and_abort(int stack_trace_skip, const char* expr, const char* file, + unsigned line, const char* format, fmt::format_args args) { + auto formatted = fmt::vformat(format, args); + log_to_everywhere(stack_trace_skip + 1, Verbosity_FATAL, file, line, expr, + formatted.c_str()); + abort(); // log_to_everywhere already does this, but this makes the + // analyzer happy. +} #else - void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, const char* format, ...) - { - va_list vlist; - va_start(vlist, format); - auto buff = vtextprintf(format, vlist); - log_to_everywhere(stack_trace_skip + 1, Verbosity_FATAL, file, line, expr, buff.c_str()); - va_end(vlist); - abort(); // log_to_everywhere already does this, but this makes the analyzer happy. - } +void log_and_abort(int stack_trace_skip, const char* expr, const char* file, + unsigned line, const char* format, ...) { + va_list vlist; + va_start(vlist, format); + auto buff = vtextprintf(format, vlist); + log_to_everywhere(stack_trace_skip + 1, Verbosity_FATAL, file, line, expr, + buff.c_str()); + va_end(vlist); + abort(); // log_to_everywhere already does this, but this makes the + // analyzer happy. +} #endif - void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line) - { - log_and_abort(stack_trace_skip + 1, expr, file, line, " "); - } +void log_and_abort(int stack_trace_skip, const char* expr, const char* file, + unsigned line) { + log_and_abort(stack_trace_skip + 1, expr, file, line, " "); +} - // ---------------------------------------------------------------------------- - // Streams: +// ---------------------------------------------------------------------------- +// Streams: #if LOGURU_USE_FMTLIB - template - std::string vstrprintf(const char* format, const Args&... args) - { - auto text = textprintf(format, args...); - std::string result = text.c_str(); - return result; - } - - template - std::string strprintf(const char* format, const Args&... args) - { - return vstrprintf(format, args...); - } +template +std::string vstrprintf(const char* format, const Args&... args) { + auto text = textprintf(format, args...); + std::string result = text.c_str(); + return result; +} + +template +std::string strprintf(const char* format, const Args&... args) { + return vstrprintf(format, args...); +} #else - std::string vstrprintf(const char* format, va_list vlist) - { - auto text = vtextprintf(format, vlist); - std::string result = text.c_str(); - return result; - } - - std::string strprintf(const char* format, ...) - { - va_list vlist; - va_start(vlist, format); - auto result = vstrprintf(format, vlist); - va_end(vlist); - return result; - } +std::string vstrprintf(const char* format, va_list vlist) { + auto text = vtextprintf(format, vlist); + std::string result = text.c_str(); + return result; +} + +std::string strprintf(const char* format, ...) { + va_list vlist; + va_start(vlist, format); + auto result = vstrprintf(format, vlist); + va_end(vlist); + return result; +} #endif - #if LOGURU_WITH_STREAMS +#if LOGURU_WITH_STREAMS - StreamLogger::~StreamLogger() noexcept(false) - { - auto message = _ss.str(); - log(_verbosity, _file, _line, LOGURU_FMT(s), message.c_str()); - } +StreamLogger::~StreamLogger() noexcept(false) { + auto message = _ss.str(); + log(_verbosity, _file, _line, LOGURU_FMT(s), message.c_str()); +} - AbortLogger::~AbortLogger() noexcept(false) - { - auto message = _ss.str(); - loguru::log_and_abort(1, _expr, _file, _line, LOGURU_FMT(s), message.c_str()); - } +AbortLogger::~AbortLogger() noexcept(false) { + auto message = _ss.str(); + loguru::log_and_abort(1, _expr, _file, _line, LOGURU_FMT(s), + message.c_str()); +} - #endif // LOGURU_WITH_STREAMS +#endif // LOGURU_WITH_STREAMS - // ---------------------------------------------------------------------------- - // 888888 88""Yb 88""Yb dP"Yb 88""Yb dP""b8 dP"Yb 88b 88 888888 888888 Yb dP 888888 - // 88__ 88__dP 88__dP dP Yb 88__dP dP `" dP Yb 88Yb88 88 88__ YbdP 88 - // 88"" 88"Yb 88"Yb Yb dP 88"Yb Yb Yb dP 88 Y88 88 88"" dPYb 88 - // 888888 88 Yb 88 Yb YbodP 88 Yb YboodP YbodP 88 Y8 88 888888 dP Yb 88 - // ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// 888888 88""Yb 88""Yb dP"Yb 88""Yb dP""b8 dP"Yb 88b 88 888888 888888 +// Yb dP 888888 88__ 88__dP 88__dP dP Yb 88__dP dP `" dP Yb 88Yb88 +// 88 88__ YbdP 88 88"" 88"Yb 88"Yb Yb dP 88"Yb Yb Yb dP +// 88 Y88 88 88"" dPYb 88 888888 88 Yb 88 Yb YbodP 88 Yb YboodP +// YbodP 88 Y8 88 888888 dP Yb 88 +// ---------------------------------------------------------------------------- - struct StringStream - { - std::string str; - }; +struct StringStream { + std::string str; +}; - // Use this in your EcPrinter implementations. - void stream_print(StringStream& out_string_stream, const char* text) - { - out_string_stream.str += text; - } +// Use this in your EcPrinter implementations. +void stream_print(StringStream& out_string_stream, const char* text) { + out_string_stream.str += text; +} - // ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- - using ECPtr = EcEntryBase*; +using ECPtr = EcEntryBase*; #if defined(_WIN32) || (defined(__APPLE__) && !TARGET_OS_IPHONE) - #ifdef __APPLE__ - #define LOGURU_THREAD_LOCAL __thread - #else - #define LOGURU_THREAD_LOCAL thread_local - #endif - static LOGURU_THREAD_LOCAL ECPtr thread_ec_ptr = nullptr; - - ECPtr& get_thread_ec_head_ref() - { - return thread_ec_ptr; - } -#else // !thread_local - static pthread_once_t s_ec_pthread_once = PTHREAD_ONCE_INIT; - static pthread_key_t s_ec_pthread_key; - - void free_ec_head_ref(void* io_error_context) - { - delete reinterpret_cast(io_error_context); - } - - void ec_make_pthread_key() - { - (void)pthread_key_create(&s_ec_pthread_key, free_ec_head_ref); - } - - ECPtr& get_thread_ec_head_ref() - { - (void)pthread_once(&s_ec_pthread_once, ec_make_pthread_key); - auto ec = reinterpret_cast(pthread_getspecific(s_ec_pthread_key)); - if (ec == nullptr) { - ec = new ECPtr(nullptr); - (void)pthread_setspecific(s_ec_pthread_key, ec); - } - return *ec; - } -#endif // !thread_local - - // ---------------------------------------------------------------------------- - - EcHandle get_thread_ec_handle() - { - return get_thread_ec_head_ref(); - } - - Text get_error_context() - { - return get_error_context_for(get_thread_ec_head_ref()); - } - - Text get_error_context_for(const EcEntryBase* ec_head) - { - std::vector stack; - while (ec_head) { - stack.push_back(ec_head); - ec_head = ec_head->_previous; - } - std::reverse(stack.begin(), stack.end()); - - StringStream result; - if (!stack.empty()) { - result.str += "------------------------------------------------\n"; - for (auto entry : stack) { - const auto description = std::string(entry->_descr) + ":"; +#ifdef __APPLE__ +#define LOGURU_THREAD_LOCAL __thread +#else +#define LOGURU_THREAD_LOCAL thread_local +#endif +static LOGURU_THREAD_LOCAL ECPtr thread_ec_ptr = nullptr; + +ECPtr& get_thread_ec_head_ref() { return thread_ec_ptr; } +#else // !thread_local +static pthread_once_t s_ec_pthread_once = PTHREAD_ONCE_INIT; +static pthread_key_t s_ec_pthread_key; + +void free_ec_head_ref(void* io_error_context) { + delete reinterpret_cast(io_error_context); +} + +void ec_make_pthread_key() { + (void)pthread_key_create(&s_ec_pthread_key, free_ec_head_ref); +} + +ECPtr& get_thread_ec_head_ref() { + (void)pthread_once(&s_ec_pthread_once, ec_make_pthread_key); + auto ec = reinterpret_cast(pthread_getspecific(s_ec_pthread_key)); + if (ec == nullptr) { + ec = new ECPtr(nullptr); + (void)pthread_setspecific(s_ec_pthread_key, ec); + } + return *ec; +} +#endif // !thread_local + +// ---------------------------------------------------------------------------- + +EcHandle get_thread_ec_handle() { return get_thread_ec_head_ref(); } + +Text get_error_context() { + return get_error_context_for(get_thread_ec_head_ref()); +} + +Text get_error_context_for(const EcEntryBase* ec_head) { + std::vector stack; + while (ec_head) { + stack.push_back(ec_head); + ec_head = ec_head->_previous; + } + std::reverse(stack.begin(), stack.end()); + + StringStream result; + if (!stack.empty()) { + result.str += "------------------------------------------------\n"; + for (auto entry : stack) { + const auto description = std::string(entry->_descr) + ":"; #if LOGURU_USE_FMTLIB - auto prefix = textprintf("[ErrorContext] {.{}s}:{:-5u} {:-20s} ", - filename(entry->_file), LOGURU_FILENAME_WIDTH, entry->_line, description.c_str()); + auto prefix = textprintf( + "[ErrorContext] {.{}s}:{:-5u} {:-20s} ", filename(entry->_file), + LOGURU_FILENAME_WIDTH, entry->_line, description.c_str()); +#else + auto prefix = textprintf( + "[ErrorContext] %*s:%-5u %-20s ", LOGURU_FILENAME_WIDTH, + filename(entry->_file), entry->_line, description.c_str()); +#endif + result.str += prefix.c_str(); + entry->print_value(result); + result.str += "\n"; + } + result.str += "------------------------------------------------"; + } + return Text(STRDUP(result.str.c_str())); +} + +EcEntryBase::EcEntryBase(const char* file, unsigned line, const char* descr) + : _file(file), _line(line), _descr(descr) { + EcEntryBase*& ec_head = get_thread_ec_head_ref(); + _previous = ec_head; + ec_head = this; +} + +EcEntryBase::~EcEntryBase() { get_thread_ec_head_ref() = _previous; } + +// ------------------------------------------------------------------------ + +Text ec_to_text(const char* value) { + // Add quotes around the string to make it obvious where it begin and ends. + // This is great for detecting erroneous leading or trailing spaces in e.g. + // an identifier. + auto str = "\"" + std::string(value) + "\""; + return Text{STRDUP(str.c_str())}; +} + +Text ec_to_text(char c) { + // Add quotes around the character to make it obvious where it begin and + // ends. + std::string str = "'"; + + auto write_hex_digit = [&](unsigned num) { + if (num < 10u) { + str += char('0' + num); + } else { + str += char('a' + num - 10); + } + }; + + auto write_hex_16 = [&](uint16_t n) { + write_hex_digit((n >> 12u) & 0x0f); + write_hex_digit((n >> 8u) & 0x0f); + write_hex_digit((n >> 4u) & 0x0f); + write_hex_digit((n >> 0u) & 0x0f); + }; + + if (c == '\\') { + str += "\\\\"; + } else if (c == '\"') { + str += "\\\""; + } else if (c == '\'') { + str += "\\\'"; + } else if (c == '\0') { + str += "\\0"; + } else if (c == '\b') { + str += "\\b"; + } else if (c == '\f') { + str += "\\f"; + } else if (c == '\n') { + str += "\\n"; + } else if (c == '\r') { + str += "\\r"; + } else if (c == '\t') { + str += "\\t"; + } else if (0 <= c && c < 0x20) { + str += "\\u"; + write_hex_16(static_cast(c)); + } else { + str += c; + } + + str += "'"; + + return Text{STRDUP(str.c_str())}; +} + +#define DEFINE_EC(Type) \ + Text ec_to_text(Type value) { \ + auto str = std::to_string(value); \ + return Text{STRDUP(str.c_str())}; \ + } + +DEFINE_EC(int) +DEFINE_EC(unsigned int) +DEFINE_EC(long) +DEFINE_EC(unsigned long) +DEFINE_EC(long long) +DEFINE_EC(unsigned long long) +DEFINE_EC(float) +DEFINE_EC(double) +DEFINE_EC(long double) + +#undef DEFINE_EC + +Text ec_to_text(EcHandle ec_handle) { + Text parent_ec = get_error_context_for(ec_handle); + size_t buffer_size = strlen(parent_ec.c_str()) + 2; + char* with_newline = reinterpret_cast(malloc(buffer_size)); + with_newline[0] = '\n'; +#ifdef _WIN32 + strncpy_s(with_newline + 1, buffer_size, parent_ec.c_str(), + buffer_size - 2); #else - auto prefix = textprintf("[ErrorContext] %*s:%-5u %-20s ", - LOGURU_FILENAME_WIDTH, filename(entry->_file), entry->_line, description.c_str()); + strcpy(with_newline + 1, parent_ec.c_str()); #endif - result.str += prefix.c_str(); - entry->print_value(result); - result.str += "\n"; - } - result.str += "------------------------------------------------"; - } - return Text(STRDUP(result.str.c_str())); - } - - EcEntryBase::EcEntryBase(const char* file, unsigned line, const char* descr) - : _file(file), _line(line), _descr(descr) - { - EcEntryBase*& ec_head = get_thread_ec_head_ref(); - _previous = ec_head; - ec_head = this; - } - - EcEntryBase::~EcEntryBase() - { - get_thread_ec_head_ref() = _previous; - } - - // ------------------------------------------------------------------------ - - Text ec_to_text(const char* value) - { - // Add quotes around the string to make it obvious where it begin and ends. - // This is great for detecting erroneous leading or trailing spaces in e.g. an identifier. - auto str = "\"" + std::string(value) + "\""; - return Text{STRDUP(str.c_str())}; - } - - Text ec_to_text(char c) - { - // Add quotes around the character to make it obvious where it begin and ends. - std::string str = "'"; - - auto write_hex_digit = [&](unsigned num) - { - if (num < 10u) { str += char('0' + num); } - else { str += char('a' + num - 10); } - }; - - auto write_hex_16 = [&](uint16_t n) - { - write_hex_digit((n >> 12u) & 0x0f); - write_hex_digit((n >> 8u) & 0x0f); - write_hex_digit((n >> 4u) & 0x0f); - write_hex_digit((n >> 0u) & 0x0f); - }; - - if (c == '\\') { str += "\\\\"; } - else if (c == '\"') { str += "\\\""; } - else if (c == '\'') { str += "\\\'"; } - else if (c == '\0') { str += "\\0"; } - else if (c == '\b') { str += "\\b"; } - else if (c == '\f') { str += "\\f"; } - else if (c == '\n') { str += "\\n"; } - else if (c == '\r') { str += "\\r"; } - else if (c == '\t') { str += "\\t"; } - else if (0 <= c && c < 0x20) { - str += "\\u"; - write_hex_16(static_cast(c)); - } else { str += c; } - - str += "'"; - - return Text{STRDUP(str.c_str())}; - } - - #define DEFINE_EC(Type) \ - Text ec_to_text(Type value) \ - { \ - auto str = std::to_string(value); \ - return Text{STRDUP(str.c_str())}; \ - } - - DEFINE_EC(int) - DEFINE_EC(unsigned int) - DEFINE_EC(long) - DEFINE_EC(unsigned long) - DEFINE_EC(long long) - DEFINE_EC(unsigned long long) - DEFINE_EC(float) - DEFINE_EC(double) - DEFINE_EC(long double) - - #undef DEFINE_EC - - Text ec_to_text(EcHandle ec_handle) - { - Text parent_ec = get_error_context_for(ec_handle); - size_t buffer_size = strlen(parent_ec.c_str()) + 2; - char* with_newline = reinterpret_cast(malloc(buffer_size)); - with_newline[0] = '\n'; - #ifdef _WIN32 - strncpy_s(with_newline + 1, buffer_size, parent_ec.c_str(), buffer_size - 2); - #else - strcpy(with_newline + 1, parent_ec.c_str()); - #endif - return Text(with_newline); - } - - // ---------------------------------------------------------------------------- - -} // namespace loguru + return Text(with_newline); +} + +// ---------------------------------------------------------------------------- + +} // namespace loguru // ---------------------------------------------------------------------------- // .dP"Y8 88 dP""b8 88b 88 db 88 .dP"Y8 @@ -1898,133 +1934,147 @@ namespace loguru #ifdef _WIN32 namespace loguru { - void install_signal_handlers(const SignalOptions& signal_options) - { - (void)signal_options; - // TODO: implement signal handlers on windows - } -} // namespace loguru - -#else // _WIN32 - -namespace loguru -{ - void write_to_stderr(const char* data, size_t size) - { - auto result = write(STDERR_FILENO, data, size); - (void)result; // Ignore errors. - } - - void write_to_stderr(const char* data) - { - write_to_stderr(data, strlen(data)); - } - - void call_default_signal_handler(int signal_number) - { - struct sigaction sig_action; - memset(&sig_action, 0, sizeof(sig_action)); - sigemptyset(&sig_action.sa_mask); - sig_action.sa_handler = SIG_DFL; - sigaction(signal_number, &sig_action, NULL); - kill(getpid(), signal_number); - } - - void signal_handler(int signal_number, siginfo_t*, void*) - { - const char* signal_name = "UNKNOWN SIGNAL"; - - if (signal_number == SIGABRT) { signal_name = "SIGABRT"; } - if (signal_number == SIGBUS) { signal_name = "SIGBUS"; } - if (signal_number == SIGFPE) { signal_name = "SIGFPE"; } - if (signal_number == SIGILL) { signal_name = "SIGILL"; } - if (signal_number == SIGINT) { signal_name = "SIGINT"; } - if (signal_number == SIGSEGV) { signal_name = "SIGSEGV"; } - if (signal_number == SIGTERM) { signal_name = "SIGTERM"; } - - // -------------------------------------------------------------------- - /* There are few things that are safe to do in a signal handler, - but writing to stderr is one of them. - So we first print out what happened to stderr so we're sure that gets out, - then we do the unsafe things, like logging the stack trace. - */ - - if (g_colorlogtostderr && s_terminal_has_color) { - write_to_stderr(terminal_reset()); - write_to_stderr(terminal_bold()); - write_to_stderr(terminal_light_red()); - } - write_to_stderr("\n"); - write_to_stderr("Loguru caught a signal: "); - write_to_stderr(signal_name); - write_to_stderr("\n"); - if (g_colorlogtostderr && s_terminal_has_color) { - write_to_stderr(terminal_reset()); - } - - // -------------------------------------------------------------------- - - if (s_signal_options.unsafe_signal_handler) { - // -------------------------------------------------------------------- - /* Now we do unsafe things. This can for example lead to deadlocks if - the signal was triggered from the system's memory management functions - and the code below tries to do allocations. - */ - - flush(); - char preamble_buff[LOGURU_PREAMBLE_WIDTH]; - print_preamble(preamble_buff, sizeof(preamble_buff), Verbosity_FATAL, "", 0); - auto message = Message{Verbosity_FATAL, "", 0, preamble_buff, "", "Signal: ", signal_name}; - try { - log_message(1, message, false, false); - } catch (...) { - // This can happed due to s_fatal_handler. - write_to_stderr("Exception caught and ignored by Loguru signal handler.\n"); - } - flush(); - - // -------------------------------------------------------------------- - } - - call_default_signal_handler(signal_number); - } - - void install_signal_handlers(const SignalOptions& signal_options) - { - s_signal_options = signal_options; - - struct sigaction sig_action; - memset(&sig_action, 0, sizeof(sig_action)); - sigemptyset(&sig_action.sa_mask); - sig_action.sa_flags |= SA_SIGINFO; - sig_action.sa_sigaction = &signal_handler; - - if (signal_options.sigabrt) { - CHECK_F(sigaction(SIGABRT, &sig_action, NULL) != -1, "Failed to install handler for SIGABRT"); - } - if (signal_options.sigbus) { - CHECK_F(sigaction(SIGBUS, &sig_action, NULL) != -1, "Failed to install handler for SIGBUS"); - } - if (signal_options.sigfpe) { - CHECK_F(sigaction(SIGFPE, &sig_action, NULL) != -1, "Failed to install handler for SIGFPE"); - } - if (signal_options.sigill) { - CHECK_F(sigaction(SIGILL, &sig_action, NULL) != -1, "Failed to install handler for SIGILL"); - } - if (signal_options.sigint) { - CHECK_F(sigaction(SIGINT, &sig_action, NULL) != -1, "Failed to install handler for SIGINT"); - } - if (signal_options.sigsegv) { - CHECK_F(sigaction(SIGSEGV, &sig_action, NULL) != -1, "Failed to install handler for SIGSEGV"); - } - if (signal_options.sigterm) { - CHECK_F(sigaction(SIGTERM, &sig_action, NULL) != -1, "Failed to install handler for SIGTERM"); - } - } -} // namespace loguru - -#endif // _WIN32 +void install_signal_handlers(const SignalOptions& signal_options) { + (void)signal_options; + // TODO: implement signal handlers on windows +} +} // namespace loguru +#else // _WIN32 + +namespace loguru { +void write_to_stderr(const char* data, size_t size) { + auto result = write(STDERR_FILENO, data, size); + (void)result; // Ignore errors. +} + +void write_to_stderr(const char* data) { write_to_stderr(data, strlen(data)); } + +void call_default_signal_handler(int signal_number) { + struct sigaction sig_action; + memset(&sig_action, 0, sizeof(sig_action)); + sigemptyset(&sig_action.sa_mask); + sig_action.sa_handler = SIG_DFL; + sigaction(signal_number, &sig_action, NULL); + kill(getpid(), signal_number); +} + +void signal_handler(int signal_number, siginfo_t*, void*) { + const char* signal_name = "UNKNOWN SIGNAL"; + + if (signal_number == SIGABRT) { + signal_name = "SIGABRT"; + } + if (signal_number == SIGBUS) { + signal_name = "SIGBUS"; + } + if (signal_number == SIGFPE) { + signal_name = "SIGFPE"; + } + if (signal_number == SIGILL) { + signal_name = "SIGILL"; + } + if (signal_number == SIGINT) { + signal_name = "SIGINT"; + } + if (signal_number == SIGSEGV) { + signal_name = "SIGSEGV"; + } + if (signal_number == SIGTERM) { + signal_name = "SIGTERM"; + } + + // -------------------------------------------------------------------- + /* There are few things that are safe to do in a signal handler, + but writing to stderr is one of them. + So we first print out what happened to stderr so we're sure that gets + out, then we do the unsafe things, like logging the stack trace. + */ + + if (g_colorlogtostderr && s_terminal_has_color) { + write_to_stderr(terminal_reset()); + write_to_stderr(terminal_bold()); + write_to_stderr(terminal_light_red()); + } + write_to_stderr("\n"); + write_to_stderr("Loguru caught a signal: "); + write_to_stderr(signal_name); + write_to_stderr("\n"); + if (g_colorlogtostderr && s_terminal_has_color) { + write_to_stderr(terminal_reset()); + } + + // -------------------------------------------------------------------- + + if (s_signal_options.unsafe_signal_handler) { + // -------------------------------------------------------------------- + /* Now we do unsafe things. This can for example lead to deadlocks if + the signal was triggered from the system's memory management + functions and the code below tries to do allocations. + */ + + flush(); + char preamble_buff[LOGURU_PREAMBLE_WIDTH]; + print_preamble(preamble_buff, sizeof(preamble_buff), Verbosity_FATAL, + "", 0); + auto message = Message{ + Verbosity_FATAL, "", 0, preamble_buff, "", "Signal: ", signal_name}; + try { + log_message(1, message, false, false); + } catch (...) { + // This can happed due to s_fatal_handler. + write_to_stderr( + "Exception caught and ignored by Loguru signal handler.\n"); + } + flush(); + + // -------------------------------------------------------------------- + } + + call_default_signal_handler(signal_number); +} + +void install_signal_handlers(const SignalOptions& signal_options) { + s_signal_options = signal_options; + + struct sigaction sig_action; + memset(&sig_action, 0, sizeof(sig_action)); + sigemptyset(&sig_action.sa_mask); + sig_action.sa_flags |= SA_SIGINFO; + sig_action.sa_sigaction = &signal_handler; + + if (signal_options.sigabrt) { + CHECK_F(sigaction(SIGABRT, &sig_action, NULL) != -1, + "Failed to install handler for SIGABRT"); + } + if (signal_options.sigbus) { + CHECK_F(sigaction(SIGBUS, &sig_action, NULL) != -1, + "Failed to install handler for SIGBUS"); + } + if (signal_options.sigfpe) { + CHECK_F(sigaction(SIGFPE, &sig_action, NULL) != -1, + "Failed to install handler for SIGFPE"); + } + if (signal_options.sigill) { + CHECK_F(sigaction(SIGILL, &sig_action, NULL) != -1, + "Failed to install handler for SIGILL"); + } + if (signal_options.sigint) { + CHECK_F(sigaction(SIGINT, &sig_action, NULL) != -1, + "Failed to install handler for SIGINT"); + } + if (signal_options.sigsegv) { + CHECK_F(sigaction(SIGSEGV, &sig_action, NULL) != -1, + "Failed to install handler for SIGSEGV"); + } + if (signal_options.sigterm) { + CHECK_F(sigaction(SIGTERM, &sig_action, NULL) != -1, + "Failed to install handler for SIGTERM"); + } +} +} // namespace loguru + +#endif // _WIN32 #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop @@ -2034,4 +2084,4 @@ namespace loguru LOGURU_ANONYMOUS_NAMESPACE_END -#endif // LOGURU_IMPLEMENTATION +#endif // LOGURU_IMPLEMENTATION diff --git a/src/atom/log/loguru.hpp b/src/atom/log/loguru.hpp index 4742b272..c23e6adf 100644 --- a/src/atom/log/loguru.hpp +++ b/src/atom/log/loguru.hpp @@ -7,99 +7,124 @@ Mail: emil.ernerfeldt@gmail.com Website: www.ilikebigbits.com # License - This software is in the public domain. Where that dedication is not - recognized, you are granted a perpetual, irrevocable license to - copy, modify and distribute it as you see fit. + This software is in the public domain. Where that dedication is not + recognized, you are granted a perpetual, irrevocable license to + copy, modify and distribute it as you see fit. # Inspiration - Much of Loguru was inspired by GLOG, https://code.google.com/p/google-glog/. - The choice of public domain is fully due Sean T. Barrett - and his wonderful stb libraries at https://github.com/nothings/stb. + Much of Loguru was inspired by GLOG, +https://code.google.com/p/google-glog/. The choice of public domain is fully due +Sean T. Barrett and his wonderful stb libraries at +https://github.com/nothings/stb. # Version history - * Version 0.1.0 - 2015-03-22 - Works great on Mac. - * Version 0.2.0 - 2015-09-17 - Removed the only dependency. - * Version 0.3.0 - 2015-10-02 - Drop-in replacement for most of GLOG - * Version 0.4.0 - 2015-10-07 - Single-file! - * Version 0.5.0 - 2015-10-17 - Improved file logging - * Version 0.6.0 - 2015-10-24 - Add stack traces - * Version 0.7.0 - 2015-10-27 - Signals - * Version 0.8.0 - 2015-10-30 - Color logging. - * Version 0.9.0 - 2015-11-26 - ABORT_S and proper handling of FATAL - * Version 1.0.0 - 2016-02-14 - ERROR_CONTEXT - * Version 1.1.0 - 2016-02-19 - -v OFF, -v INFO etc - * Version 1.1.1 - 2016-02-20 - textprintf vs strprintf - * Version 1.1.2 - 2016-02-22 - Remove g_alsologtostderr - * Version 1.1.3 - 2016-02-29 - ERROR_CONTEXT as linked list - * Version 1.2.0 - 2016-03-19 - Add get_thread_name() - * Version 1.2.1 - 2016-03-20 - Minor fixes - * Version 1.2.2 - 2016-03-29 - Fix issues with set_fatal_handler throwing an exception - * Version 1.2.3 - 2016-05-16 - Log current working directory in loguru::init(). - * Version 1.2.4 - 2016-05-18 - Custom replacement for -v in loguru::init() by bjoernpollex - * Version 1.2.5 - 2016-05-18 - Add ability to print ERROR_CONTEXT of parent thread. - * Version 1.2.6 - 2016-05-19 - Bug fix regarding VLOG verbosity argument lacking (). - * Version 1.2.7 - 2016-05-23 - Fix PATH_MAX problem. - * Version 1.2.8 - 2016-05-26 - Add shutdown() and remove_all_callbacks() - * Version 1.2.9 - 2016-06-09 - Use a monotonic clock for uptime. - * Version 1.3.0 - 2016-07-20 - Fix issues with callback flush/close not being called. - * Version 1.3.1 - 2016-07-20 - Add LOGURU_UNSAFE_SIGNAL_HANDLER to toggle stacktrace on signals. - * Version 1.3.2 - 2016-07-20 - Add loguru::arguments() - * Version 1.4.0 - 2016-09-15 - Semantic versioning + add loguru::create_directories - * Version 1.4.1 - 2016-09-29 - Customize formating with LOGURU_FILENAME_WIDTH - * Version 1.5.0 - 2016-12-22 - LOGURU_USE_FMTLIB by kolis and LOGURU_WITH_FILEABS by scinart - * Version 1.5.1 - 2017-08-08 - Terminal colors on Windows 10 thanks to looki - * Version 1.6.0 - 2018-01-03 - Add LOGURU_RTTI and LOGURU_STACKTRACES settings - * Version 1.7.0 - 2018-01-03 - Add ability to turn off the preamble with loguru::g_preamble - * Version 1.7.1 - 2018-04-05 - Add function get_fatal_handler - * Version 1.7.2 - 2018-04-22 - Fix a bug where large file names could cause stack corruption (thanks @ccamporesi) - * Version 1.8.0 - 2018-04-23 - Shorten long file names to keep preamble fixed width - * Version 1.9.0 - 2018-09-22 - Adjust terminal colors, add LOGURU_VERBOSE_SCOPE_ENDINGS, add LOGURU_SCOPE_TIME_PRECISION, add named log levels - * Version 2.0.0 - 2018-09-22 - Split loguru.hpp into loguru.hpp and loguru.cpp - * Version 2.1.0 - 2019-09-23 - Update fmtlib + add option to loguru::init to NOT set main thread name. - * Version 2.2.0 - 2020-07-31 - Replace LOGURU_CATCH_SIGABRT with struct SignalOptions + * Version 0.1.0 - 2015-03-22 - Works great on Mac. + * Version 0.2.0 - 2015-09-17 - Removed the only dependency. + * Version 0.3.0 - 2015-10-02 - Drop-in replacement for most of GLOG + * Version 0.4.0 - 2015-10-07 - Single-file! + * Version 0.5.0 - 2015-10-17 - Improved file logging + * Version 0.6.0 - 2015-10-24 - Add stack traces + * Version 0.7.0 - 2015-10-27 - Signals + * Version 0.8.0 - 2015-10-30 - Color logging. + * Version 0.9.0 - 2015-11-26 - ABORT_S and proper handling of FATAL + * Version 1.0.0 - 2016-02-14 - ERROR_CONTEXT + * Version 1.1.0 - 2016-02-19 - -v OFF, -v INFO etc + * Version 1.1.1 - 2016-02-20 - textprintf vs strprintf + * Version 1.1.2 - 2016-02-22 - Remove g_alsologtostderr + * Version 1.1.3 - 2016-02-29 - ERROR_CONTEXT as linked list + * Version 1.2.0 - 2016-03-19 - Add get_thread_name() + * Version 1.2.1 - 2016-03-20 - Minor fixes + * Version 1.2.2 - 2016-03-29 - Fix issues with set_fatal_handler +throwing an exception + * Version 1.2.3 - 2016-05-16 - Log current working directory in +loguru::init(). + * Version 1.2.4 - 2016-05-18 - Custom replacement for -v in +loguru::init() by bjoernpollex + * Version 1.2.5 - 2016-05-18 - Add ability to print ERROR_CONTEXT of +parent thread. + * Version 1.2.6 - 2016-05-19 - Bug fix regarding VLOG verbosity argument +lacking (). + * Version 1.2.7 - 2016-05-23 - Fix PATH_MAX problem. + * Version 1.2.8 - 2016-05-26 - Add shutdown() and remove_all_callbacks() + * Version 1.2.9 - 2016-06-09 - Use a monotonic clock for uptime. + * Version 1.3.0 - 2016-07-20 - Fix issues with callback flush/close not +being called. + * Version 1.3.1 - 2016-07-20 - Add LOGURU_UNSAFE_SIGNAL_HANDLER to +toggle stacktrace on signals. + * Version 1.3.2 - 2016-07-20 - Add loguru::arguments() + * Version 1.4.0 - 2016-09-15 - Semantic versioning + add +loguru::create_directories + * Version 1.4.1 - 2016-09-29 - Customize formating with +LOGURU_FILENAME_WIDTH + * Version 1.5.0 - 2016-12-22 - LOGURU_USE_FMTLIB by kolis and +LOGURU_WITH_FILEABS by scinart + * Version 1.5.1 - 2017-08-08 - Terminal colors on Windows 10 thanks to +looki + * Version 1.6.0 - 2018-01-03 - Add LOGURU_RTTI and LOGURU_STACKTRACES +settings + * Version 1.7.0 - 2018-01-03 - Add ability to turn off the preamble with +loguru::g_preamble + * Version 1.7.1 - 2018-04-05 - Add function get_fatal_handler + * Version 1.7.2 - 2018-04-22 - Fix a bug where large file names could +cause stack corruption (thanks @ccamporesi) + * Version 1.8.0 - 2018-04-23 - Shorten long file names to keep preamble +fixed width + * Version 1.9.0 - 2018-09-22 - Adjust terminal colors, add +LOGURU_VERBOSE_SCOPE_ENDINGS, add LOGURU_SCOPE_TIME_PRECISION, add named log +levels + * Version 2.0.0 - 2018-09-22 - Split loguru.hpp into loguru.hpp and +loguru.cpp + * Version 2.1.0 - 2019-09-23 - Update fmtlib + add option to +loguru::init to NOT set main thread name. + * Version 2.2.0 - 2020-07-31 - Replace LOGURU_CATCH_SIGABRT with struct +SignalOptions # Compiling - Just include where you want to use Loguru. - Then, in one .cpp file #include - Make sure you compile with -std=c++11 -lstdc++ -lpthread -ldl + Just include where you want to use Loguru. + Then, in one .cpp file #include + Make sure you compile with -std=c++11 -lstdc++ -lpthread -ldl # Usage - For details, please see the official documentation at emilk.github.io/loguru + For details, please see the official documentation at +emilk.github.io/loguru - #include + #include - int main(int argc, char* argv[]) { - loguru::init(argc, argv); + int main(int argc, char* argv[]) { + loguru::init(argc, argv); - // Put every log message in "everything.log": - loguru::add_file("everything.log", loguru::Append, loguru::Verbosity_MAX); + // Put every log message in "everything.log": + loguru::add_file("everything.log", loguru::Append, +loguru::Verbosity_MAX); - LOG_F(INFO, "The magic number is %d", 42); - } + LOG_F(INFO, "The magic number is %d", 42); + } */ #if defined(LOGURU_IMPLEMENTATION) - #error "You are defining LOGURU_IMPLEMENTATION. This is for older versions of Loguru. You should now instead include loguru.cpp (or build it and link with it)" +#error \ + "You are defining LOGURU_IMPLEMENTATION. This is for older versions of Loguru. You should now instead include loguru.cpp (or build it and link with it)" #endif // Disable all warnings from gcc/clang: #if defined(__clang__) - #pragma clang system_header +#pragma clang system_header #elif defined(__GNUC__) - #pragma GCC system_header +#pragma GCC system_header #endif #ifndef LOGURU_HAS_DECLARED_FORMAT_HEADER #define LOGURU_HAS_DECLARED_FORMAT_HEADER -// Semantic versioning. Loguru version can be printed with printf("%d.%d.%d", LOGURU_VERSION_MAJOR, LOGURU_VERSION_MINOR, LOGURU_VERSION_PATCH); +// Semantic versioning. Loguru version can be printed with printf("%d.%d.%d", +// LOGURU_VERSION_MAJOR, LOGURU_VERSION_MINOR, LOGURU_VERSION_PATCH); #define LOGURU_VERSION_MAJOR 2 #define LOGURU_VERSION_MINOR 1 #define LOGURU_VERSION_PATCH 0 #if defined(_MSC_VER) -#include // Needed for _In_z_ etc annotations +#include // Needed for _In_z_ etc annotations #endif #if defined(__linux__) || defined(__APPLE__) @@ -111,128 +136,134 @@ Website: www.ilikebigbits.com // ---------------------------------------------------------------------------- #ifndef LOGURU_EXPORT - // Define to your project's export declaration if needed for use in a shared library. - #define LOGURU_EXPORT +// Define to your project's export declaration if needed for use in a shared +// library. +#define LOGURU_EXPORT #endif #ifndef LOGURU_SCOPE_TEXT_SIZE - // Maximum length of text that can be printed by a LOG_SCOPE. - // This should be long enough to get most things, but short enough not to clutter the stack. - #define LOGURU_SCOPE_TEXT_SIZE 196 +// Maximum length of text that can be printed by a LOG_SCOPE. +// This should be long enough to get most things, but short enough not to +// clutter the stack. +#define LOGURU_SCOPE_TEXT_SIZE 196 #endif #ifndef LOGURU_FILENAME_WIDTH - // Width of the column containing the file name - #define LOGURU_FILENAME_WIDTH 23 +// Width of the column containing the file name +#define LOGURU_FILENAME_WIDTH 23 #endif #ifndef LOGURU_THREADNAME_WIDTH - // Width of the column containing the thread name - #define LOGURU_THREADNAME_WIDTH 16 +// Width of the column containing the thread name +#define LOGURU_THREADNAME_WIDTH 16 #endif #ifndef LOGURU_SCOPE_TIME_PRECISION - // Resolution of scope timers. 3=ms, 6=us, 9=ns - #define LOGURU_SCOPE_TIME_PRECISION 3 +// Resolution of scope timers. 3=ms, 6=us, 9=ns +#define LOGURU_SCOPE_TIME_PRECISION 3 #endif #ifdef LOGURU_CATCH_SIGABRT - #error "You are defining LOGURU_CATCH_SIGABRT. This is for older versions of Loguru. You should now instead set the options passed to loguru::init" +#error \ + "You are defining LOGURU_CATCH_SIGABRT. This is for older versions of Loguru. You should now instead set the options passed to loguru::init" #endif #ifndef LOGURU_VERBOSE_SCOPE_ENDINGS - // Show milliseconds and scope name at end of scope. - #define LOGURU_VERBOSE_SCOPE_ENDINGS 1 +// Show milliseconds and scope name at end of scope. +#define LOGURU_VERBOSE_SCOPE_ENDINGS 1 #endif #ifndef LOGURU_REDEFINE_ASSERT - #define LOGURU_REDEFINE_ASSERT 0 +#define LOGURU_REDEFINE_ASSERT 0 #endif #ifndef LOGURU_WITH_STREAMS - #define LOGURU_WITH_STREAMS 0 +#define LOGURU_WITH_STREAMS 0 #endif #ifndef LOGURU_REPLACE_GLOG - #define LOGURU_REPLACE_GLOG 0 +#define LOGURU_REPLACE_GLOG 0 #endif #if LOGURU_REPLACE_GLOG - #undef LOGURU_WITH_STREAMS - #define LOGURU_WITH_STREAMS 1 +#undef LOGURU_WITH_STREAMS +#define LOGURU_WITH_STREAMS 1 #endif #if defined(LOGURU_UNSAFE_SIGNAL_HANDLER) - #error "You are defining LOGURU_UNSAFE_SIGNAL_HANDLER. This is for older versions of Loguru. You should now instead set the unsafe_signal_handler option when you call loguru::init." +#error \ + "You are defining LOGURU_UNSAFE_SIGNAL_HANDLER. This is for older versions of Loguru. You should now instead set the unsafe_signal_handler option when you call loguru::init." #endif #if LOGURU_IMPLEMENTATION - #undef LOGURU_WITH_STREAMS - #define LOGURU_WITH_STREAMS 1 +#undef LOGURU_WITH_STREAMS +#define LOGURU_WITH_STREAMS 1 #endif #ifndef LOGURU_USE_FMTLIB - #define LOGURU_USE_FMTLIB 1 +#define LOGURU_USE_FMTLIB 0 #endif #ifndef LOGURU_USE_LOCALE - #define LOGURU_USE_LOCALE 0 +#define LOGURU_USE_LOCALE 0 #endif #ifndef LOGURU_WITH_FILEABS - #define LOGURU_WITH_FILEABS 0 +#define LOGURU_WITH_FILEABS 0 #endif #ifndef LOGURU_RTTI #if defined(__clang__) - #if __has_feature(cxx_rtti) - #define LOGURU_RTTI 1 - #endif +#if __has_feature(cxx_rtti) +#define LOGURU_RTTI 1 +#endif #elif defined(__GNUG__) - #if defined(__GXX_RTTI) - #define LOGURU_RTTI 1 - #endif +#if defined(__GXX_RTTI) +#define LOGURU_RTTI 1 +#endif #elif defined(_MSC_VER) - #if defined(_CPPRTTI) - #define LOGURU_RTTI 1 - #endif +#if defined(_CPPRTTI) +#define LOGURU_RTTI 1 +#endif #endif #endif #ifdef LOGURU_USE_ANONYMOUS_NAMESPACE - #define LOGURU_ANONYMOUS_NAMESPACE_BEGIN namespace { - #define LOGURU_ANONYMOUS_NAMESPACE_END } +#define LOGURU_ANONYMOUS_NAMESPACE_BEGIN namespace { +#define LOGURU_ANONYMOUS_NAMESPACE_END } #else - #define LOGURU_ANONYMOUS_NAMESPACE_BEGIN - #define LOGURU_ANONYMOUS_NAMESPACE_END +#define LOGURU_ANONYMOUS_NAMESPACE_BEGIN +#define LOGURU_ANONYMOUS_NAMESPACE_END #endif // -------------------------------------------------------------------- // Utility macros -#define LOGURU_CONCATENATE_IMPL(s1, s2) s1 ## s2 +#define LOGURU_CONCATENATE_IMPL(s1, s2) s1##s2 #define LOGURU_CONCATENATE(s1, s2) LOGURU_CONCATENATE_IMPL(s1, s2) #ifdef __COUNTER__ -# define LOGURU_ANONYMOUS_VARIABLE(str) LOGURU_CONCATENATE(str, __COUNTER__) +#define LOGURU_ANONYMOUS_VARIABLE(str) LOGURU_CONCATENATE(str, __COUNTER__) #else -# define LOGURU_ANONYMOUS_VARIABLE(str) LOGURU_CONCATENATE(str, __LINE__) +#define LOGURU_ANONYMOUS_VARIABLE(str) LOGURU_CONCATENATE(str, __LINE__) #endif #if defined(__clang__) || defined(__GNUC__) - // Helper macro for declaring functions as having similar signature to printf. - // This allows the compiler to catch format errors at compile-time. - #define LOGURU_PRINTF_LIKE(fmtarg, firstvararg) __attribute__((__format__ (__printf__, fmtarg, firstvararg))) - #define LOGURU_FORMAT_STRING_TYPE const char* +// Helper macro for declaring functions as having similar signature to printf. +// This allows the compiler to catch format errors at compile-time. +#define LOGURU_PRINTF_LIKE(fmtarg, firstvararg) \ + __attribute__((__format__(__printf__, fmtarg, firstvararg))) +#define LOGURU_FORMAT_STRING_TYPE const char* #elif defined(_MSC_VER) - #define LOGURU_PRINTF_LIKE(fmtarg, firstvararg) - #define LOGURU_FORMAT_STRING_TYPE _In_z_ _Printf_format_string_ const char* +#define LOGURU_PRINTF_LIKE(fmtarg, firstvararg) +#define LOGURU_FORMAT_STRING_TYPE _In_z_ _Printf_format_string_ const char* #else - #define LOGURU_PRINTF_LIKE(fmtarg, firstvararg) - #define LOGURU_FORMAT_STRING_TYPE const char* +#define LOGURU_PRINTF_LIKE(fmtarg, firstvararg) +#define LOGURU_FORMAT_STRING_TYPE const char* #endif -// Used to mark log_and_abort for the benefit of the static analyzer and optimizer. +// Used to mark log_and_abort for the benefit of the static analyzer and +// optimizer. #if defined(_MSC_VER) #define LOGURU_NORETURN __declspec(noreturn) #else @@ -241,23 +272,23 @@ Website: www.ilikebigbits.com #if defined(_MSC_VER) #define LOGURU_PREDICT_FALSE(x) (x) -#define LOGURU_PREDICT_TRUE(x) (x) +#define LOGURU_PREDICT_TRUE(x) (x) #else -#define LOGURU_PREDICT_FALSE(x) (__builtin_expect(x, 0)) -#define LOGURU_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) +#define LOGURU_PREDICT_FALSE(x) (__builtin_expect(x, 0)) +#define LOGURU_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) #endif #if LOGURU_USE_FMTLIB - #include - #define LOGURU_FMT(x) "{:" #x "}" +#include +#define LOGURU_FMT(x) "{:" #x "}" #else - #define LOGURU_FMT(x) "%" #x +#define LOGURU_FMT(x) "%" #x #endif #ifdef _WIN32 - #define STRDUP(str) _strdup(str) +#define STRDUP(str) _strdup(str) #else - #define STRDUP(str) strdup(str) +#define STRDUP(str) strdup(str) #endif #include @@ -265,57 +296,53 @@ Website: www.ilikebigbits.com // -------------------------------------------------------------------- LOGURU_ANONYMOUS_NAMESPACE_BEGIN -namespace loguru -{ - // Simple RAII ownership of a char*. - class LOGURU_EXPORT Text - { - public: - explicit Text(char* owned_str) : _str(owned_str) {} - ~Text(); - Text(Text&& t) - { - _str = t._str; - t._str = nullptr; - } - Text(Text& t) = delete; - Text& operator=(Text& t) = delete; - void operator=(Text&& t) = delete; - - const char* c_str() const { return _str; } - bool empty() const { return _str == nullptr || *_str == '\0'; } - - char* release() - { - auto result = _str; - _str = nullptr; - return result; - } - - private: - char* _str; - }; - - // Like printf, but returns the formated text. +namespace loguru { +// Simple RAII ownership of a char*. +class LOGURU_EXPORT Text { +public: + explicit Text(char* owned_str) : _str(owned_str) {} + ~Text(); + Text(Text&& t) { + _str = t._str; + t._str = nullptr; + } + Text(Text& t) = delete; + Text& operator=(Text& t) = delete; + void operator=(Text&& t) = delete; + + const char* c_str() const { return _str; } + bool empty() const { return _str == nullptr || *_str == '\0'; } + + char* release() { + auto result = _str; + _str = nullptr; + return result; + } + +private: + char* _str; +}; + +// Like printf, but returns the formated text. #if LOGURU_USE_FMTLIB - LOGURU_EXPORT - Text vtextprintf(const char* format, fmt::format_args args); - - template - LOGURU_EXPORT - Text textprintf(LOGURU_FORMAT_STRING_TYPE format, const Args&... args) { - return vtextprintf(format, fmt::make_format_args(args...)); - } +LOGURU_EXPORT +Text vtextprintf(const char* format, fmt::format_args args); + +template +LOGURU_EXPORT Text textprintf(LOGURU_FORMAT_STRING_TYPE format, + const Args&... args) { + return vtextprintf(format, fmt::make_format_args(args...)); +} #else - LOGURU_EXPORT - Text textprintf(LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(1, 2); +LOGURU_EXPORT +Text textprintf(LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(1, 2); #endif - // Overloaded for variadic template matching. - LOGURU_EXPORT - Text textprintf(); +// Overloaded for variadic template matching. +LOGURU_EXPORT +Text textprintf(); - using Verbosity = int; +using Verbosity = int; #undef FATAL #undef ERROR @@ -323,733 +350,824 @@ namespace loguru #undef INFO #undef MAX - enum NamedVerbosity : Verbosity - { - // Used to mark an invalid verbosity. Do not log to this level. - Verbosity_INVALID = -10, // Never do LOG_F(INVALID) - - // You may use Verbosity_OFF on g_stderr_verbosity, but for nothing else! - Verbosity_OFF = -9, // Never do LOG_F(OFF) - - // Prefer to use ABORT_F or ABORT_S over LOG_F(FATAL) or LOG_S(FATAL). - Verbosity_FATAL = -3, - Verbosity_ERROR = -2, - Verbosity_WARNING = -1, - - // Normal messages. By default written to stderr. - Verbosity_INFO = 0, - - // Same as Verbosity_INFO in every way. - Verbosity_0 = 0, - - // Verbosity levels 1-9 are generally not written to stderr, but are written to file. - Verbosity_1 = +1, - Verbosity_2 = +2, - Verbosity_3 = +3, - Verbosity_4 = +4, - Verbosity_5 = +5, - Verbosity_6 = +6, - Verbosity_7 = +7, - Verbosity_8 = +8, - Verbosity_9 = +9, - - // Do not use higher verbosity levels, as that will make grepping log files harder. - Verbosity_MAX = +9, - }; - - struct Message - { - // You would generally print a Message by just concatenating the buffers without spacing. - // Optionally, ignore preamble and indentation. - Verbosity verbosity; // Already part of preamble - const char* filename; // Already part of preamble - unsigned line; // Already part of preamble - const char* preamble; // Date, time, uptime, thread, file:line, verbosity. - const char* indentation; // Just a bunch of spacing. - const char* prefix; // Assertion failure info goes here (or ""). - const char* message; // User message goes here. - }; - - /* Everything with a verbosity equal or greater than g_stderr_verbosity will be - written to stderr. You can set this in code or via the -v argument. - Set to loguru::Verbosity_OFF to write nothing to stderr. - Default is 0, i.e. only log ERROR, WARNING and INFO are written to stderr. - */ - LOGURU_EXPORT extern Verbosity g_stderr_verbosity; - LOGURU_EXPORT extern bool g_colorlogtostderr; // True by default. - LOGURU_EXPORT extern unsigned g_flush_interval_ms; // 0 (unbuffered) by default. - LOGURU_EXPORT extern bool g_preamble_header; // Prepend each log start by a descriptions line with all columns name? True by default. - LOGURU_EXPORT extern bool g_preamble; // Prefix each log line with date, time etc? True by default. - - /* Specify the verbosity used by loguru to log its info messages including the header - logged when logged::init() is called or on exit. Default is 0 (INFO). - */ - LOGURU_EXPORT extern Verbosity g_internal_verbosity; - - // Turn off individual parts of the preamble - LOGURU_EXPORT extern bool g_preamble_date; // The date field - LOGURU_EXPORT extern bool g_preamble_time; // The time of the current day - LOGURU_EXPORT extern bool g_preamble_uptime; // The time since init call - LOGURU_EXPORT extern bool g_preamble_thread; // The logging thread - LOGURU_EXPORT extern bool g_preamble_file; // The file from which the log originates from - LOGURU_EXPORT extern bool g_preamble_verbose; // The verbosity field - LOGURU_EXPORT extern bool g_preamble_pipe; // The pipe symbol right before the message - - // May not throw! - typedef void (*log_handler_t)(void* user_data, const Message& message); - typedef void (*close_handler_t)(void* user_data); - typedef void (*flush_handler_t)(void* user_data); - - // May throw if that's how you'd like to handle your errors. - typedef void (*fatal_handler_t)(const Message& message); - - // Given a verbosity level, return the level's name or nullptr. - typedef const char* (*verbosity_to_name_t)(Verbosity verbosity); - - // Given a verbosity level name, return the verbosity level or - // Verbosity_INVALID if name is not recognized. - typedef Verbosity (*name_to_verbosity_t)(const char* name); - - struct SignalOptions - { - /// Make Loguru try to do unsafe but useful things, - /// like printing a stack trace, when catching signals. - /// This may lead to bad things like deadlocks in certain situations. - bool unsafe_signal_handler = true; - - /// Should Loguru catch SIGABRT ? - bool sigabrt = true; - - /// Should Loguru catch SIGBUS ? - bool sigbus = true; - - /// Should Loguru catch SIGFPE ? - bool sigfpe = true; - - /// Should Loguru catch SIGILL ? - bool sigill = true; - - /// Should Loguru catch SIGINT ? - bool sigint = true; - - /// Should Loguru catch SIGSEGV ? - bool sigsegv = true; - - /// Should Loguru catch SIGTERM ? - bool sigterm = true; - - static SignalOptions none() - { - SignalOptions options; - options.unsafe_signal_handler = false; - options.sigabrt = false; - options.sigbus = false; - options.sigfpe = false; - options.sigill = false; - options.sigint = false; - options.sigsegv = false; - options.sigterm = false; - return options; - } - }; - - // Runtime options passed to loguru::init - struct Options - { - // This allows you to use something else instead of "-v" via verbosity_flag. - // Set to nullptr if you don't want Loguru to parse verbosity from the args. - const char* verbosity_flag = "-v"; - - // loguru::init will set the name of the calling thread to this. - // If you don't want Loguru to set the name of the main thread, - // set this to nullptr. - // NOTE: on SOME platforms loguru::init will only overwrite the thread name - // if a thread name has not already been set. - // To always set a thread name, use loguru::set_thread_name instead. - const char* main_thread_name = "main thread"; - - SignalOptions signal_options; - }; - - /* Should be called from the main thread. - You don't *need* to call this, but if you do you get: - * Signal handlers installed - * Program arguments logged - * Working dir logged - * Optional -v verbosity flag parsed - * Main thread name set to "main thread" - * Explanation of the preamble (date, thread name, etc) logged - - loguru::init() will look for arguments meant for loguru and remove them. - Arguments meant for loguru are: - -v n Set loguru::g_stderr_verbosity level. Examples: - -v 3 Show verbosity level 3 and lower. - -v 0 Only show INFO, WARNING, ERROR, FATAL (default). - -v INFO Only show INFO, WARNING, ERROR, FATAL (default). - -v WARNING Only show WARNING, ERROR, FATAL. - -v ERROR Only show ERROR, FATAL. - -v FATAL Only show FATAL. - -v OFF Turn off logging to stderr. - - Tip: You can set g_stderr_verbosity before calling loguru::init. - That way you can set the default but have the user override it with the -v flag. - Note that -v does not affect file logging (see loguru::add_file). - - You can you something other than the -v flag by setting the verbosity_flag option. - */ - LOGURU_EXPORT - void init(int& argc, char* argv[], const Options& options = {}); - - // Will call remove_all_callbacks(). After calling this, logging will still go to stderr. - // You generally don't need to call this. - LOGURU_EXPORT - void shutdown(); - - // What ~ will be replaced with, e.g. "/home/your_user_name/" - LOGURU_EXPORT - const char* home_dir(); - - /* Returns the name of the app as given in argv[0] but without leading path. - That is, if argv[0] is "../foo/app" this will return "app". - */ - LOGURU_EXPORT - const char* argv0_filename(); - - // Returns all arguments given to loguru::init(), but escaped with a single space as separator. - LOGURU_EXPORT - const char* arguments(); - - // Returns the path to the current working dir when loguru::init() was called. - LOGURU_EXPORT - const char* current_dir(); - - // Returns the part of the path after the last / or \ (if any). - LOGURU_EXPORT - const char* filename(const char* path); - - // e.g. "foo/bar/baz.ext" will create the directories "foo/" and "foo/bar/" - LOGURU_EXPORT - bool create_directories(const char* file_path_const); - - // Writes date and time with millisecond precision, e.g. "20151017_161503.123" - LOGURU_EXPORT - void write_date_time(char* buff, unsigned long long buff_size); - - // Helper: thread-safe version strerror - LOGURU_EXPORT - Text errno_as_text(); - - /* Given a prefix of e.g. "~/loguru/" this might return - "/home/your_username/loguru/app_name/20151017_161503.123.log" - - where "app_name" is a sanitized version of argv[0]. - */ - LOGURU_EXPORT - void suggest_log_path(const char* prefix, char* buff, unsigned long long buff_size); - - enum FileMode { Truncate, Append }; - - /* Will log to a file at the given path. - Any logging message with a verbosity lower or equal to - the given verbosity will be included. - The function will create all directories in 'path' if needed. - If path starts with a ~, it will be replaced with loguru::home_dir() - To stop the file logging, just call loguru::remove_callback(path) with the same path. - */ - LOGURU_EXPORT - bool add_file(const char* path, FileMode mode, Verbosity verbosity); - - LOGURU_EXPORT - // Send logs to syslog with LOG_USER facility (see next call) - bool add_syslog(const char* app_name, Verbosity verbosity); - LOGURU_EXPORT - // Send logs to syslog with your own choice of facility (LOG_USER, LOG_AUTH, ...) - // see loguru.cpp: syslog_log() for more details. - bool add_syslog(const char* app_name, Verbosity verbosity, int facility); - - /* Will be called right before abort(). - You can for instance use this to print custom error messages, or throw an exception. - Feel free to call LOG:ing function from this, but not FATAL ones! */ - LOGURU_EXPORT - void set_fatal_handler(fatal_handler_t handler); - - // Get the current fatal handler, if any. Default value is nullptr. - LOGURU_EXPORT - fatal_handler_t get_fatal_handler(); - - /* Will be called on each log messages with a verbosity less or equal to the given one. - Useful for displaying messages on-screen in a game, for example. - The given on_close is also expected to flush (if desired). - */ - LOGURU_EXPORT - void add_callback( - const char* id, - log_handler_t callback, - void* user_data, - Verbosity verbosity, - close_handler_t on_close = nullptr, - flush_handler_t on_flush = nullptr); - - /* Set a callback that returns custom verbosity level names. If callback - is nullptr or returns nullptr, default log names will be used. - */ - LOGURU_EXPORT - void set_verbosity_to_name_callback(verbosity_to_name_t callback); - - /* Set a callback that returns the verbosity level matching a name. The - callback should return Verbosity_INVALID if the name is not - recognized. - */ - LOGURU_EXPORT - void set_name_to_verbosity_callback(name_to_verbosity_t callback); - - /* Get a custom name for a specific verbosity, if one exists, or nullptr. */ - LOGURU_EXPORT - const char* get_verbosity_name(Verbosity verbosity); - - /* Get the verbosity enum value from a custom 4-character level name, if one exists. - If the name does not match a custom level name, Verbosity_INVALID is returned. - */ - LOGURU_EXPORT - Verbosity get_verbosity_from_name(const char* name); - - // Returns true iff the callback was found (and removed). - LOGURU_EXPORT - bool remove_callback(const char* id); - - // Shut down all file logging and any other callback hooks installed. - LOGURU_EXPORT - void remove_all_callbacks(); - - // Returns the maximum of g_stderr_verbosity and all file/custom outputs. - LOGURU_EXPORT - Verbosity current_verbosity_cutoff(); +enum NamedVerbosity : Verbosity { + // Used to mark an invalid verbosity. Do not log to this level. + Verbosity_INVALID = -10, // Never do LOG_F(INVALID) + + // You may use Verbosity_OFF on g_stderr_verbosity, but for nothing else! + Verbosity_OFF = -9, // Never do LOG_F(OFF) + + // Prefer to use ABORT_F or ABORT_S over LOG_F(FATAL) or LOG_S(FATAL). + Verbosity_FATAL = -3, + Verbosity_ERROR = -2, + Verbosity_WARNING = -1, + + // Normal messages. By default written to stderr. + Verbosity_INFO = 0, + + // Same as Verbosity_INFO in every way. + Verbosity_0 = 0, + + // Verbosity levels 1-9 are generally not written to stderr, but are written + // to file. + Verbosity_1 = +1, + Verbosity_2 = +2, + Verbosity_3 = +3, + Verbosity_4 = +4, + Verbosity_5 = +5, + Verbosity_6 = +6, + Verbosity_7 = +7, + Verbosity_8 = +8, + Verbosity_9 = +9, + + // Do not use higher verbosity levels, as that will make grepping log files + // harder. + Verbosity_MAX = +9, +}; + +struct Message { + // You would generally print a Message by just concatenating the buffers + // without spacing. Optionally, ignore preamble and indentation. + Verbosity verbosity; // Already part of preamble + const char* filename; // Already part of preamble + unsigned line; // Already part of preamble + const char* preamble; // Date, time, uptime, thread, file:line, verbosity. + const char* indentation; // Just a bunch of spacing. + const char* prefix; // Assertion failure info goes here (or ""). + const char* message; // User message goes here. +}; + +/* Everything with a verbosity equal or greater than g_stderr_verbosity will be +written to stderr. You can set this in code or via the -v argument. +Set to loguru::Verbosity_OFF to write nothing to stderr. +Default is 0, i.e. only log ERROR, WARNING and INFO are written to stderr. +*/ +LOGURU_EXPORT extern Verbosity g_stderr_verbosity; +LOGURU_EXPORT extern bool g_colorlogtostderr; // True by default. +LOGURU_EXPORT extern unsigned + g_flush_interval_ms; // 0 (unbuffered) by default. +LOGURU_EXPORT extern bool + g_preamble_header; // Prepend each log start by a descriptions line with + // all columns name? True by default. +LOGURU_EXPORT extern bool + g_preamble; // Prefix each log line with date, time etc? True by default. + +/* Specify the verbosity used by loguru to log its info messages including the +header logged when logged::init() is called or on exit. Default is 0 (INFO). +*/ +LOGURU_EXPORT extern Verbosity g_internal_verbosity; + +// Turn off individual parts of the preamble +LOGURU_EXPORT extern bool g_preamble_date; // The date field +LOGURU_EXPORT extern bool g_preamble_time; // The time of the current day +LOGURU_EXPORT extern bool g_preamble_uptime; // The time since init call +LOGURU_EXPORT extern bool g_preamble_thread; // The logging thread +LOGURU_EXPORT extern bool + g_preamble_file; // The file from which the log originates from +LOGURU_EXPORT extern bool g_preamble_verbose; // The verbosity field +LOGURU_EXPORT extern bool + g_preamble_pipe; // The pipe symbol right before the message + +// May not throw! +typedef void (*log_handler_t)(void* user_data, const Message& message); +typedef void (*close_handler_t)(void* user_data); +typedef void (*flush_handler_t)(void* user_data); + +// May throw if that's how you'd like to handle your errors. +typedef void (*fatal_handler_t)(const Message& message); + +// Given a verbosity level, return the level's name or nullptr. +typedef const char* (*verbosity_to_name_t)(Verbosity verbosity); + +// Given a verbosity level name, return the verbosity level or +// Verbosity_INVALID if name is not recognized. +typedef Verbosity (*name_to_verbosity_t)(const char* name); + +struct SignalOptions { + /// Make Loguru try to do unsafe but useful things, + /// like printing a stack trace, when catching signals. + /// This may lead to bad things like deadlocks in certain situations. + bool unsafe_signal_handler = true; + + /// Should Loguru catch SIGABRT ? + bool sigabrt = true; + + /// Should Loguru catch SIGBUS ? + bool sigbus = true; + + /// Should Loguru catch SIGFPE ? + bool sigfpe = true; + + /// Should Loguru catch SIGILL ? + bool sigill = true; + + /// Should Loguru catch SIGINT ? + bool sigint = true; + + /// Should Loguru catch SIGSEGV ? + bool sigsegv = true; + + /// Should Loguru catch SIGTERM ? + bool sigterm = true; + + static SignalOptions none() { + SignalOptions options; + options.unsafe_signal_handler = false; + options.sigabrt = false; + options.sigbus = false; + options.sigfpe = false; + options.sigill = false; + options.sigint = false; + options.sigsegv = false; + options.sigterm = false; + return options; + } +}; + +// Runtime options passed to loguru::init +struct Options { + // This allows you to use something else instead of "-v" via verbosity_flag. + // Set to nullptr if you don't want Loguru to parse verbosity from the args. + const char* verbosity_flag = "-v"; + + // loguru::init will set the name of the calling thread to this. + // If you don't want Loguru to set the name of the main thread, + // set this to nullptr. + // NOTE: on SOME platforms loguru::init will only overwrite the thread name + // if a thread name has not already been set. + // To always set a thread name, use loguru::set_thread_name instead. + const char* main_thread_name = "main thread"; + + SignalOptions signal_options; +}; + +/* Should be called from the main thread. + You don't *need* to call this, but if you do you get: + * Signal handlers installed + * Program arguments logged + * Working dir logged + * Optional -v verbosity flag parsed + * Main thread name set to "main thread" + * Explanation of the preamble (date, thread name, etc) logged + + loguru::init() will look for arguments meant for loguru and remove them. + Arguments meant for loguru are: + -v n Set loguru::g_stderr_verbosity level. Examples: + -v 3 Show verbosity level 3 and lower. + -v 0 Only show INFO, WARNING, ERROR, FATAL + (default). -v INFO Only show INFO, WARNING, ERROR, FATAL (default). -v + WARNING Only show WARNING, ERROR, FATAL. -v ERROR Only show ERROR, FATAL. + -v FATAL Only show FATAL. + -v OFF Turn off logging to stderr. + + Tip: You can set g_stderr_verbosity before calling loguru::init. + That way you can set the default but have the user override it with the + -v flag. Note that -v does not affect file logging (see loguru::add_file). + + You can you something other than the -v flag by setting the + verbosity_flag option. +*/ +LOGURU_EXPORT +void init(int& argc, char* argv[], const Options& options = {}); + +// Will call remove_all_callbacks(). After calling this, logging will still go +// to stderr. You generally don't need to call this. +LOGURU_EXPORT +void shutdown(); + +// What ~ will be replaced with, e.g. "/home/your_user_name/" +LOGURU_EXPORT +const char* home_dir(); + +/* Returns the name of the app as given in argv[0] but without leading path. + That is, if argv[0] is "../foo/app" this will return "app". +*/ +LOGURU_EXPORT +const char* argv0_filename(); + +// Returns all arguments given to loguru::init(), but escaped with a single +// space as separator. +LOGURU_EXPORT +const char* arguments(); + +// Returns the path to the current working dir when loguru::init() was called. +LOGURU_EXPORT +const char* current_dir(); + +// Returns the part of the path after the last / or \ (if any). +LOGURU_EXPORT +const char* filename(const char* path); + +// e.g. "foo/bar/baz.ext" will create the directories "foo/" and "foo/bar/" +LOGURU_EXPORT +bool create_directories(const char* file_path_const); + +// Writes date and time with millisecond precision, e.g. "20151017_161503.123" +LOGURU_EXPORT +void write_date_time(char* buff, unsigned long long buff_size); + +// Helper: thread-safe version strerror +LOGURU_EXPORT +Text errno_as_text(); + +/* Given a prefix of e.g. "~/loguru/" this might return + "/home/your_username/loguru/app_name/20151017_161503.123.log" + + where "app_name" is a sanitized version of argv[0]. +*/ +LOGURU_EXPORT +void suggest_log_path(const char* prefix, char* buff, + unsigned long long buff_size); + +enum FileMode { Truncate, Append }; + +/* Will log to a file at the given path. + Any logging message with a verbosity lower or equal to + the given verbosity will be included. + The function will create all directories in 'path' if needed. + If path starts with a ~, it will be replaced with loguru::home_dir() + To stop the file logging, just call loguru::remove_callback(path) with + the same path. +*/ +LOGURU_EXPORT +bool add_file(const char* path, FileMode mode, Verbosity verbosity); + +LOGURU_EXPORT +// Send logs to syslog with LOG_USER facility (see next call) +bool add_syslog(const char* app_name, Verbosity verbosity); +LOGURU_EXPORT +// Send logs to syslog with your own choice of facility (LOG_USER, LOG_AUTH, +// ...) see loguru.cpp: syslog_log() for more details. +bool add_syslog(const char* app_name, Verbosity verbosity, int facility); + +/* Will be called right before abort(). + You can for instance use this to print custom error messages, or throw + an exception. Feel free to call LOG:ing function from this, but not FATAL + ones! */ +LOGURU_EXPORT +void set_fatal_handler(fatal_handler_t handler); + +// Get the current fatal handler, if any. Default value is nullptr. +LOGURU_EXPORT +fatal_handler_t get_fatal_handler(); + +/* Will be called on each log messages with a verbosity less or equal to the + given one. Useful for displaying messages on-screen in a game, for example. + The given on_close is also expected to flush (if desired). +*/ +LOGURU_EXPORT +void add_callback(const char* id, log_handler_t callback, void* user_data, + Verbosity verbosity, close_handler_t on_close = nullptr, + flush_handler_t on_flush = nullptr); + +/* Set a callback that returns custom verbosity level names. If callback + is nullptr or returns nullptr, default log names will be used. +*/ +LOGURU_EXPORT +void set_verbosity_to_name_callback(verbosity_to_name_t callback); + +/* Set a callback that returns the verbosity level matching a name. The + callback should return Verbosity_INVALID if the name is not + recognized. +*/ +LOGURU_EXPORT +void set_name_to_verbosity_callback(name_to_verbosity_t callback); + +/* Get a custom name for a specific verbosity, if one exists, or nullptr. */ +LOGURU_EXPORT +const char* get_verbosity_name(Verbosity verbosity); + +/* Get the verbosity enum value from a custom 4-character level name, if one + exists. If the name does not match a custom level name, Verbosity_INVALID is + returned. +*/ +LOGURU_EXPORT +Verbosity get_verbosity_from_name(const char* name); + +// Returns true iff the callback was found (and removed). +LOGURU_EXPORT +bool remove_callback(const char* id); + +// Shut down all file logging and any other callback hooks installed. +LOGURU_EXPORT +void remove_all_callbacks(); + +// Returns the maximum of g_stderr_verbosity and all file/custom outputs. +LOGURU_EXPORT +Verbosity current_verbosity_cutoff(); #if LOGURU_USE_FMTLIB - // Internal functions - LOGURU_EXPORT - void vlog(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, fmt::format_args args); - LOGURU_EXPORT - void raw_vlog(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, fmt::format_args args); - - // Actual logging function. Use the LOG macro instead of calling this directly. - template - LOGURU_EXPORT - void log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, const Args &... args) { - vlog(verbosity, file, line, format, fmt::make_format_args(args...)); - } - - // Log without any preamble or indentation. - template - LOGURU_EXPORT - void raw_log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, const Args &... args) { - raw_vlog(verbosity, file, line, format, fmt::make_format_args(args...)); - } -#else // LOGURU_USE_FMTLIB? - // Actual logging function. Use the LOG macro instead of calling this directly. - LOGURU_EXPORT - void log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(4, 5); - - // Actual logging function. - LOGURU_EXPORT - void vlog(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, va_list) LOGURU_PRINTF_LIKE(4, 0); - - // Log without any preamble or indentation. - LOGURU_EXPORT - void raw_log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(4, 5); -#endif // !LOGURU_USE_FMTLIB - - // Helper class for LOG_SCOPE_F - class LOGURU_EXPORT LogScopeRAII - { - public: - LogScopeRAII() : _file(nullptr) {} // No logging - LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, va_list vlist) LOGURU_PRINTF_LIKE(5, 0); - LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(5, 6); - ~LogScopeRAII(); - - void Init(LOGURU_FORMAT_STRING_TYPE format, va_list vlist) LOGURU_PRINTF_LIKE(2, 0); +// Internal functions +LOGURU_EXPORT +void vlog(Verbosity verbosity, const char* file, unsigned line, + LOGURU_FORMAT_STRING_TYPE format, fmt::format_args args); +LOGURU_EXPORT +void raw_vlog(Verbosity verbosity, const char* file, unsigned line, + LOGURU_FORMAT_STRING_TYPE format, fmt::format_args args); + +// Actual logging function. Use the LOG macro instead of calling this directly. +template +LOGURU_EXPORT void log(Verbosity verbosity, const char* file, unsigned line, + LOGURU_FORMAT_STRING_TYPE format, const Args&... args) { + vlog(verbosity, file, line, format, fmt::make_format_args(args...)); +} + +// Log without any preamble or indentation. +template +LOGURU_EXPORT void raw_log(Verbosity verbosity, const char* file, unsigned line, + LOGURU_FORMAT_STRING_TYPE format, + const Args&... args) { + raw_vlog(verbosity, file, line, format, fmt::make_format_args(args...)); +} +#else // LOGURU_USE_FMTLIB? + // Actual logging function. Use the LOG macro instead of calling this + // directly. +LOGURU_EXPORT +void log(Verbosity verbosity, const char* file, unsigned line, + LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(4, 5); + +// Actual logging function. +LOGURU_EXPORT +void vlog(Verbosity verbosity, const char* file, unsigned line, + LOGURU_FORMAT_STRING_TYPE format, va_list) LOGURU_PRINTF_LIKE(4, 0); + +// Log without any preamble or indentation. +LOGURU_EXPORT +void raw_log(Verbosity verbosity, const char* file, unsigned line, + LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(4, 5); +#endif // !LOGURU_USE_FMTLIB + +// Helper class for LOG_SCOPE_F +class LOGURU_EXPORT LogScopeRAII { +public: + LogScopeRAII() : _file(nullptr) {} // No logging + LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, + LOGURU_FORMAT_STRING_TYPE format, va_list vlist) + LOGURU_PRINTF_LIKE(5, 0); + LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, + LOGURU_FORMAT_STRING_TYPE format, ...) + LOGURU_PRINTF_LIKE(5, 6); + ~LogScopeRAII(); + + void Init(LOGURU_FORMAT_STRING_TYPE format, va_list vlist) + LOGURU_PRINTF_LIKE(2, 0); #if defined(_MSC_VER) && _MSC_VER > 1800 - // older MSVC default move ctors close the scope on move. See - // issue #43 - LogScopeRAII(LogScopeRAII&& other) - : _verbosity(other._verbosity) - , _file(other._file) - , _line(other._line) - , _indent_stderr(other._indent_stderr) - , _start_time_ns(other._start_time_ns) - { - // Make sure the tmp object's destruction doesn't close the scope: - other._file = nullptr; - - for (unsigned int i = 0; i < LOGURU_SCOPE_TEXT_SIZE; ++i) { - _name[i] = other._name[i]; - } - } + // older MSVC default move ctors close the scope on move. See + // issue #43 + LogScopeRAII(LogScopeRAII&& other) + : _verbosity(other._verbosity), + _file(other._file), + _line(other._line), + _indent_stderr(other._indent_stderr), + _start_time_ns(other._start_time_ns) { + // Make sure the tmp object's destruction doesn't close the scope: + other._file = nullptr; + + for (unsigned int i = 0; i < LOGURU_SCOPE_TEXT_SIZE; ++i) { + _name[i] = other._name[i]; + } + } #else - LogScopeRAII(LogScopeRAII&&) = default; + LogScopeRAII(LogScopeRAII&&) = default; #endif - private: - LogScopeRAII(const LogScopeRAII&) = delete; - LogScopeRAII& operator=(const LogScopeRAII&) = delete; - void operator=(LogScopeRAII&&) = delete; - - Verbosity _verbosity; - const char* _file; // Set to null if we are disabled due to verbosity - unsigned _line; - bool _indent_stderr; // Did we? - long long _start_time_ns; - char _name[LOGURU_SCOPE_TEXT_SIZE]; - }; - - // Marked as 'noreturn' for the benefit of the static analyzer and optimizer. - // stack_trace_skip is the number of extrace stack frames to skip above log_and_abort. +private: + LogScopeRAII(const LogScopeRAII&) = delete; + LogScopeRAII& operator=(const LogScopeRAII&) = delete; + void operator=(LogScopeRAII&&) = delete; + + Verbosity _verbosity; + const char* _file; // Set to null if we are disabled due to verbosity + unsigned _line; + bool _indent_stderr; // Did we? + long long _start_time_ns; + char _name[LOGURU_SCOPE_TEXT_SIZE]; +}; + +// Marked as 'noreturn' for the benefit of the static analyzer and optimizer. +// stack_trace_skip is the number of extrace stack frames to skip above +// log_and_abort. #if LOGURU_USE_FMTLIB - LOGURU_EXPORT - LOGURU_NORETURN void vlog_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, fmt::format_args); - template - LOGURU_EXPORT - LOGURU_NORETURN void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, const Args&... args) { - vlog_and_abort(stack_trace_skip, expr, file, line, format, fmt::make_format_args(args...)); - } +LOGURU_EXPORT +LOGURU_NORETURN void vlog_and_abort(int stack_trace_skip, const char* expr, + const char* file, unsigned line, + LOGURU_FORMAT_STRING_TYPE format, + fmt::format_args); +template +LOGURU_EXPORT LOGURU_NORETURN void log_and_abort( + int stack_trace_skip, const char* expr, const char* file, unsigned line, + LOGURU_FORMAT_STRING_TYPE format, const Args&... args) { + vlog_and_abort(stack_trace_skip, expr, file, line, format, + fmt::make_format_args(args...)); +} #else - LOGURU_EXPORT - LOGURU_NORETURN void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(5, 6); +LOGURU_EXPORT +LOGURU_NORETURN void log_and_abort(int stack_trace_skip, const char* expr, + const char* file, unsigned line, + LOGURU_FORMAT_STRING_TYPE format, ...) + LOGURU_PRINTF_LIKE(5, 6); #endif - LOGURU_EXPORT - LOGURU_NORETURN void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line); - - // Flush output to stderr and files. - // If g_flush_interval_ms is set to non-zero, this will be called automatically this often. - // If not set, you do not need to call this at all. - LOGURU_EXPORT - void flush(); - - template inline Text format_value(const T&) { return textprintf("N/A"); } - template<> inline Text format_value(const char& v) { return textprintf(LOGURU_FMT(c), v); } - template<> inline Text format_value(const int& v) { return textprintf(LOGURU_FMT(d), v); } - template<> inline Text format_value(const float& v) { return textprintf(LOGURU_FMT(f), v); } - template<> inline Text format_value(const double& v) { return textprintf(LOGURU_FMT(f), v); } +LOGURU_EXPORT +LOGURU_NORETURN void log_and_abort(int stack_trace_skip, const char* expr, + const char* file, unsigned line); + +// Flush output to stderr and files. +// If g_flush_interval_ms is set to non-zero, this will be called automatically +// this often. If not set, you do not need to call this at all. +LOGURU_EXPORT +void flush(); + +template +inline Text format_value(const T&) { + return textprintf("N/A"); +} +template <> +inline Text format_value(const char& v) { + return textprintf(LOGURU_FMT(c), v); +} +template <> +inline Text format_value(const int& v) { + return textprintf(LOGURU_FMT(d), v); +} +template <> +inline Text format_value(const float& v) { + return textprintf(LOGURU_FMT(f), v); +} +template <> +inline Text format_value(const double& v) { + return textprintf(LOGURU_FMT(f), v); +} #if LOGURU_USE_FMTLIB - template<> inline Text format_value(const unsigned int& v) { return textprintf(LOGURU_FMT(d), v); } - template<> inline Text format_value(const long& v) { return textprintf(LOGURU_FMT(d), v); } - template<> inline Text format_value(const unsigned long& v) { return textprintf(LOGURU_FMT(d), v); } - template<> inline Text format_value(const long long& v) { return textprintf(LOGURU_FMT(d), v); } - template<> inline Text format_value(const unsigned long long& v) { return textprintf(LOGURU_FMT(d), v); } +template <> +inline Text format_value(const unsigned int& v) { + return textprintf(LOGURU_FMT(d), v); +} +template <> +inline Text format_value(const long& v) { + return textprintf(LOGURU_FMT(d), v); +} +template <> +inline Text format_value(const unsigned long& v) { + return textprintf(LOGURU_FMT(d), v); +} +template <> +inline Text format_value(const long long& v) { + return textprintf(LOGURU_FMT(d), v); +} +template <> +inline Text format_value(const unsigned long long& v) { + return textprintf(LOGURU_FMT(d), v); +} #else - template<> inline Text format_value(const unsigned int& v) { return textprintf(LOGURU_FMT(u), v); } - template<> inline Text format_value(const long& v) { return textprintf(LOGURU_FMT(lu), v); } - template<> inline Text format_value(const unsigned long& v) { return textprintf(LOGURU_FMT(ld), v); } - template<> inline Text format_value(const long long& v) { return textprintf(LOGURU_FMT(llu), v); } - template<> inline Text format_value(const unsigned long long& v) { return textprintf(LOGURU_FMT(lld), v); } +template <> +inline Text format_value(const unsigned int& v) { + return textprintf(LOGURU_FMT(u), v); +} +template <> +inline Text format_value(const long& v) { + return textprintf(LOGURU_FMT(lu), v); +} +template <> +inline Text format_value(const unsigned long& v) { + return textprintf(LOGURU_FMT(ld), v); +} +template <> +inline Text format_value(const long long& v) { + return textprintf(LOGURU_FMT(llu), v); +} +template <> +inline Text format_value(const unsigned long long& v) { + return textprintf(LOGURU_FMT(lld), v); +} #endif - /* Thread names can be set for the benefit of readable logs. - If you do not set the thread name, a hex id will be shown instead. - These thread names may or may not be the same as the system thread names, - depending on the system. - Try to limit the thread name to 15 characters or less. */ - LOGURU_EXPORT - void set_thread_name(const char* name); - - /* Returns the thread name for this thread. - On most *nix systems this will return the system thread name (settable from both within and without Loguru). - On other systems it will return whatever you set in `set_thread_name()`; - If no thread name is set, this will return a hexadecimal thread id. - `length` should be the number of bytes available in the buffer. - 17 is a good number for length. - `right_align_hex_id` means any hexadecimal thread id will be written to the end of buffer. - */ - LOGURU_EXPORT - void get_thread_name(char* buffer, unsigned long long length, bool right_align_hex_id); - - /* Generates a readable stacktrace as a string. - 'skip' specifies how many stack frames to skip. - For instance, the default skip (1) means: - don't include the call to loguru::stacktrace in the stack trace. */ - LOGURU_EXPORT - Text stacktrace(int skip = 1); - - /* Add a string to be replaced with something else in the stack output. - - For instance, instead of having a stack trace look like this: - 0x41f541 some_function(std::basic_ofstream >&) - You can clean it up with: - auto verbose_type_name = loguru::demangle(typeid(std::ofstream).name()); - loguru::add_stack_cleanup(verbose_type_name.c_str(); "std::ofstream"); - So the next time you will instead see: - 0x41f541 some_function(std::ofstream&) - - `replace_with_this` must be shorter than `find_this`. - */ - LOGURU_EXPORT - void add_stack_cleanup(const char* find_this, const char* replace_with_this); - - // Example: demangle(typeid(std::ofstream).name()) -> "std::basic_ofstream >" - LOGURU_EXPORT - Text demangle(const char* name); - - // ------------------------------------------------------------------------ - /* - Not all terminals support colors, but if they do, and g_colorlogtostderr - is set, Loguru will write them to stderr to make errors in red, etc. - - You also have the option to manually use them, via the function below. - - Note, however, that if you do, the color codes could end up in your logfile! - - This means if you intend to use them functions you should either: - * Use them on the stderr/stdout directly (bypass Loguru). - * Don't add file outputs to Loguru. - * Expect some \e[1m things in your logfile. - - Usage: - printf("%sRed%sGreen%sBold green%sClear again\n", - loguru::terminal_red(), loguru::terminal_green(), - loguru::terminal_bold(), loguru::terminal_reset()); - - If the terminal at hand does not support colors the above output - will just not have funky \e[1m things showing. - */ - - // Do the output terminal support colors? - LOGURU_EXPORT - bool terminal_has_color(); - - // Colors - LOGURU_EXPORT const char* terminal_black(); - LOGURU_EXPORT const char* terminal_red(); - LOGURU_EXPORT const char* terminal_green(); - LOGURU_EXPORT const char* terminal_yellow(); - LOGURU_EXPORT const char* terminal_blue(); - LOGURU_EXPORT const char* terminal_purple(); - LOGURU_EXPORT const char* terminal_cyan(); - LOGURU_EXPORT const char* terminal_light_gray(); - LOGURU_EXPORT const char* terminal_light_red(); - LOGURU_EXPORT const char* terminal_white(); - - // Formating - LOGURU_EXPORT const char* terminal_bold(); - LOGURU_EXPORT const char* terminal_underline(); - - // You should end each line with this! - LOGURU_EXPORT const char* terminal_reset(); - - // -------------------------------------------------------------------- - // Error context related: - - struct StringStream; - - // Use this in your EcEntryBase::print_value overload. - LOGURU_EXPORT - void stream_print(StringStream& out_string_stream, const char* text); - - class LOGURU_EXPORT EcEntryBase - { - public: - EcEntryBase(const char* file, unsigned line, const char* descr); - ~EcEntryBase(); - EcEntryBase(const EcEntryBase&) = delete; - EcEntryBase(EcEntryBase&&) = delete; - EcEntryBase& operator=(const EcEntryBase&) = delete; - EcEntryBase& operator=(EcEntryBase&&) = delete; - - virtual void print_value(StringStream& out_string_stream) const = 0; - - EcEntryBase* previous() const { return _previous; } - - // private: - const char* _file; - unsigned _line; - const char* _descr; - EcEntryBase* _previous; - }; - - template - class EcEntryData : public EcEntryBase - { - public: - using Printer = Text(*)(T data); - - EcEntryData(const char* file, unsigned line, const char* descr, T data, Printer&& printer) - : EcEntryBase(file, line, descr), _data(data), _printer(printer) {} - - virtual void print_value(StringStream& out_string_stream) const override - { - const auto str = _printer(_data); - stream_print(out_string_stream, str.c_str()); - } - - private: - T _data; - Printer _printer; - }; - - // template - // class EcEntryLambda : public EcEntryBase - // { - // public: - // EcEntryLambda(const char* file, unsigned line, const char* descr, Printer&& printer) - // : EcEntryBase(file, line, descr), _printer(std::move(printer)) {} - - // virtual void print_value(StringStream& out_string_stream) const override - // { - // const auto str = _printer(); - // stream_print(out_string_stream, str.c_str()); - // } - - // private: - // Printer _printer; - // }; - - // template - // EcEntryLambda make_ec_entry_lambda(const char* file, unsigned line, const char* descr, Printer&& printer) - // { - // return {file, line, descr, std::move(printer)}; - // } - - template - struct decay_char_array { using type = T; }; - - template - struct decay_char_array { using type = const char*; }; - - template - struct make_const_ptr { using type = T; }; - - template - struct make_const_ptr { using type = const T*; }; - - template - struct make_ec_type { using type = typename make_const_ptr::type>::type; }; - - /* A stack trace gives you the names of the function at the point of a crash. - With ERROR_CONTEXT, you can also get the values of select local variables. - Usage: - - void process_customers(const std::string& filename) - { - ERROR_CONTEXT("Processing file", filename.c_str()); - for (int customer_index : ...) - { - ERROR_CONTEXT("Customer index", customer_index); - ... - } - } - - The context is in effect during the scope of the ERROR_CONTEXT. - Use loguru::get_error_context() to get the contents of the active error contexts. - - Example result: - - ------------------------------------------------ - [ErrorContext] main.cpp:416 Processing file: "customers.json" - [ErrorContext] main.cpp:417 Customer index: 42 - ------------------------------------------------ - - Error contexts are printed automatically on crashes, and only on crashes. - This makes them much faster than logging the value of a variable. - */ - #define ERROR_CONTEXT(descr, data) \ - const loguru::EcEntryData::type> \ - LOGURU_ANONYMOUS_VARIABLE(error_context_scope_)( \ - __FILE__, __LINE__, descr, data, \ - static_cast::type>::Printer>(loguru::ec_to_text) ) // For better error messages +/* Thread names can be set for the benefit of readable logs. + If you do not set the thread name, a hex id will be shown instead. + These thread names may or may not be the same as the system thread names, + depending on the system. + Try to limit the thread name to 15 characters or less. */ +LOGURU_EXPORT +void set_thread_name(const char* name); + +/* Returns the thread name for this thread. + On most *nix systems this will return the system thread name (settable from + both within and without Loguru). On other systems it will return whatever you + set in `set_thread_name()`; If no thread name is set, this will return a + hexadecimal thread id. `length` should be the number of bytes available in + the buffer. 17 is a good number for length. `right_align_hex_id` means any + hexadecimal thread id will be written to the end of buffer. +*/ +LOGURU_EXPORT +void get_thread_name(char* buffer, unsigned long long length, + bool right_align_hex_id); + +/* Generates a readable stacktrace as a string. + 'skip' specifies how many stack frames to skip. + For instance, the default skip (1) means: + don't include the call to loguru::stacktrace in the stack trace. */ +LOGURU_EXPORT +Text stacktrace(int skip = 1); + +/* Add a string to be replaced with something else in the stack output. + + For instance, instead of having a stack trace look like this: + 0x41f541 some_function(std::basic_ofstream >&) You can clean it up with: auto verbose_type_name = + loguru::demangle(typeid(std::ofstream).name()); + loguru::add_stack_cleanup(verbose_type_name.c_str(); + "std::ofstream"); So the next time you will instead see: 0x41f541 + some_function(std::ofstream&) + + `replace_with_this` must be shorter than `find_this`. +*/ +LOGURU_EXPORT +void add_stack_cleanup(const char* find_this, const char* replace_with_this); + +// Example: demangle(typeid(std::ofstream).name()) -> "std::basic_ofstream >" +LOGURU_EXPORT +Text demangle(const char* name); + +// ------------------------------------------------------------------------ +/* +Not all terminals support colors, but if they do, and g_colorlogtostderr +is set, Loguru will write them to stderr to make errors in red, etc. + +You also have the option to manually use them, via the function below. + +Note, however, that if you do, the color codes could end up in your logfile! + +This means if you intend to use them functions you should either: + * Use them on the stderr/stdout directly (bypass Loguru). + * Don't add file outputs to Loguru. + * Expect some \e[1m things in your logfile. + +Usage: + printf("%sRed%sGreen%sBold green%sClear again\n", + loguru::terminal_red(), loguru::terminal_green(), + loguru::terminal_bold(), loguru::terminal_reset()); + +If the terminal at hand does not support colors the above output +will just not have funky \e[1m things showing. +*/ + +// Do the output terminal support colors? +LOGURU_EXPORT +bool terminal_has_color(); + +// Colors +LOGURU_EXPORT const char* terminal_black(); +LOGURU_EXPORT const char* terminal_red(); +LOGURU_EXPORT const char* terminal_green(); +LOGURU_EXPORT const char* terminal_yellow(); +LOGURU_EXPORT const char* terminal_blue(); +LOGURU_EXPORT const char* terminal_purple(); +LOGURU_EXPORT const char* terminal_cyan(); +LOGURU_EXPORT const char* terminal_light_gray(); +LOGURU_EXPORT const char* terminal_light_red(); +LOGURU_EXPORT const char* terminal_white(); + +// Formating +LOGURU_EXPORT const char* terminal_bold(); +LOGURU_EXPORT const char* terminal_underline(); + +// You should end each line with this! +LOGURU_EXPORT const char* terminal_reset(); + +// -------------------------------------------------------------------- +// Error context related: + +struct StringStream; + +// Use this in your EcEntryBase::print_value overload. +LOGURU_EXPORT +void stream_print(StringStream& out_string_stream, const char* text); + +class LOGURU_EXPORT EcEntryBase { +public: + EcEntryBase(const char* file, unsigned line, const char* descr); + ~EcEntryBase(); + EcEntryBase(const EcEntryBase&) = delete; + EcEntryBase(EcEntryBase&&) = delete; + EcEntryBase& operator=(const EcEntryBase&) = delete; + EcEntryBase& operator=(EcEntryBase&&) = delete; + + virtual void print_value(StringStream& out_string_stream) const = 0; + + EcEntryBase* previous() const { return _previous; } + + // private: + const char* _file; + unsigned _line; + const char* _descr; + EcEntryBase* _previous; +}; + +template +class EcEntryData : public EcEntryBase { +public: + using Printer = Text (*)(T data); + + EcEntryData(const char* file, unsigned line, const char* descr, T data, + Printer&& printer) + : EcEntryBase(file, line, descr), _data(data), _printer(printer) {} + + virtual void print_value(StringStream& out_string_stream) const override { + const auto str = _printer(_data); + stream_print(out_string_stream, str.c_str()); + } + +private: + T _data; + Printer _printer; +}; + +// template +// class EcEntryLambda : public EcEntryBase +// { +// public: +// EcEntryLambda(const char* file, unsigned line, const char* descr, +// Printer&& printer) : EcEntryBase(file, line, descr), +// _printer(std::move(printer)) {} + +// virtual void print_value(StringStream& out_string_stream) const override +// { +// const auto str = _printer(); +// stream_print(out_string_stream, str.c_str()); +// } + +// private: +// Printer _printer; +// }; + +// template +// EcEntryLambda make_ec_entry_lambda(const char* file, unsigned line, +// const char* descr, Printer&& printer) +// { +// return {file, line, descr, std::move(printer)}; +// } + +template +struct decay_char_array { + using type = T; +}; + +template +struct decay_char_array { + using type = const char*; +}; + +template +struct make_const_ptr { + using type = T; +}; + +template +struct make_const_ptr { + using type = const T*; +}; + +template +struct make_ec_type { + using type = + typename make_const_ptr::type>::type; +}; + +/* A stack trace gives you the names of the function at the point of a + crash. With ERROR_CONTEXT, you can also get the values of select local + variables. Usage: + + void process_customers(const std::string& filename) + { + ERROR_CONTEXT("Processing file", filename.c_str()); + for (int customer_index : ...) + { + ERROR_CONTEXT("Customer index", customer_index); + ... + } + } + + The context is in effect during the scope of the ERROR_CONTEXT. + Use loguru::get_error_context() to get the contents of the active error + contexts. + + Example result: + + ------------------------------------------------ + [ErrorContext] main.cpp:416 Processing file: + "customers.json" [ErrorContext] main.cpp:417 Customer index: + 42 + ------------------------------------------------ + + Error contexts are printed automatically on crashes, and only on + crashes. This makes them much faster than logging the value of a variable. +*/ +#define ERROR_CONTEXT(descr, data) \ + const loguru::EcEntryData::type> \ + LOGURU_ANONYMOUS_VARIABLE(error_context_scope_)( \ + __FILE__, __LINE__, descr, data, \ + static_cast::type>::Printer>( \ + loguru::ec_to_text)) // For better error messages /* - #define ERROR_CONTEXT(descr, data) \ - const auto LOGURU_ANONYMOUS_VARIABLE(error_context_scope_)( \ - loguru::make_ec_entry_lambda(__FILE__, __LINE__, descr, \ - [=](){ return loguru::ec_to_text(data); })) + #define ERROR_CONTEXT(descr, data) \ + const auto LOGURU_ANONYMOUS_VARIABLE(error_context_scope_)( \ + loguru::make_ec_entry_lambda(__FILE__, __LINE__, descr, + \ + [=](){ return loguru::ec_to_text(data); })) */ - using EcHandle = const EcEntryBase*; - - /* - Get a light-weight handle to the error context stack on this thread. - The handle is valid as long as the current thread has no changes to its error context stack. - You can pass the handle to loguru::get_error_context on another thread. - This can be very useful for when you have a parent thread spawning several working threads, - and you want the error context of the parent thread to get printed (too) when there is an - error on the child thread. You can accomplish this thusly: - - void foo(const char* parameter) - { - ERROR_CONTEXT("parameter", parameter) - const auto parent_ec_handle = loguru::get_thread_ec_handle(); - - std::thread([=]{ - loguru::set_thread_name("child thread"); - ERROR_CONTEXT("parent context", parent_ec_handle); - dangerous_code(); - }.join(); - } - - */ - LOGURU_EXPORT - EcHandle get_thread_ec_handle(); - - // Get a string describing the current stack of error context. Empty string if there is none. - LOGURU_EXPORT - Text get_error_context(); - - // Get a string describing the error context of the given thread handle. - LOGURU_EXPORT - Text get_error_context_for(EcHandle ec_handle); - - // ------------------------------------------------------------------------ - - LOGURU_EXPORT Text ec_to_text(const char* data); - LOGURU_EXPORT Text ec_to_text(char data); - LOGURU_EXPORT Text ec_to_text(int data); - LOGURU_EXPORT Text ec_to_text(unsigned int data); - LOGURU_EXPORT Text ec_to_text(long data); - LOGURU_EXPORT Text ec_to_text(unsigned long data); - LOGURU_EXPORT Text ec_to_text(long long data); - LOGURU_EXPORT Text ec_to_text(unsigned long long data); - LOGURU_EXPORT Text ec_to_text(float data); - LOGURU_EXPORT Text ec_to_text(double data); - LOGURU_EXPORT Text ec_to_text(long double data); - LOGURU_EXPORT Text ec_to_text(EcHandle); - - /* - You can add ERROR_CONTEXT support for your own types by overloading ec_to_text. Here's how: - - some.hpp: - namespace loguru { - Text ec_to_text(MySmallType data) - Text ec_to_text(const MyBigType* data) - } // namespace loguru - - some.cpp: - namespace loguru { - Text ec_to_text(MySmallType small_value) - { - // Called only when needed, i.e. on a crash. - std::string str = small_value.as_string(); // Format 'small_value' here somehow. - return Text{STRDUP(str.c_str())}; - } - - Text ec_to_text(const MyBigType* big_value) - { - // Called only when needed, i.e. on a crash. - std::string str = big_value->as_string(); // Format 'big_value' here somehow. - return Text{STRDUP(str.c_str())}; - } - } // namespace loguru - - Any file that include some.hpp: - void foo(MySmallType small, const MyBigType& big) - { - ERROR_CONTEXT("Small", small); // Copy ´small` by value. - ERROR_CONTEXT("Big", &big); // `big` should not change during this scope! - .... - } - */ -} // namespace loguru +using EcHandle = const EcEntryBase*; + +/* + Get a light-weight handle to the error context stack on this thread. + The handle is valid as long as the current thread has no changes to its + error context stack. You can pass the handle to loguru::get_error_context on + another thread. This can be very useful for when you have a parent thread + spawning several working threads, and you want the error context of the + parent thread to get printed (too) when there is an error on the child + thread. You can accomplish this thusly: + + void foo(const char* parameter) + { + ERROR_CONTEXT("parameter", parameter) + const auto parent_ec_handle = loguru::get_thread_ec_handle(); + + std::thread([=]{ + loguru::set_thread_name("child thread"); + ERROR_CONTEXT("parent context", parent_ec_handle); + dangerous_code(); + }.join(); + } + +*/ +LOGURU_EXPORT +EcHandle get_thread_ec_handle(); + +// Get a string describing the current stack of error context. Empty string if +// there is none. +LOGURU_EXPORT +Text get_error_context(); + +// Get a string describing the error context of the given thread handle. +LOGURU_EXPORT +Text get_error_context_for(EcHandle ec_handle); + +// ------------------------------------------------------------------------ + +LOGURU_EXPORT Text ec_to_text(const char* data); +LOGURU_EXPORT Text ec_to_text(char data); +LOGURU_EXPORT Text ec_to_text(int data); +LOGURU_EXPORT Text ec_to_text(unsigned int data); +LOGURU_EXPORT Text ec_to_text(long data); +LOGURU_EXPORT Text ec_to_text(unsigned long data); +LOGURU_EXPORT Text ec_to_text(long long data); +LOGURU_EXPORT Text ec_to_text(unsigned long long data); +LOGURU_EXPORT Text ec_to_text(float data); +LOGURU_EXPORT Text ec_to_text(double data); +LOGURU_EXPORT Text ec_to_text(long double data); +LOGURU_EXPORT Text ec_to_text(EcHandle); + +/* +You can add ERROR_CONTEXT support for your own types by overloading ec_to_text. +Here's how: + +some.hpp: + namespace loguru { + Text ec_to_text(MySmallType data) + Text ec_to_text(const MyBigType* data) + } // namespace loguru + +some.cpp: + namespace loguru { + Text ec_to_text(MySmallType small_value) + { + // Called only when needed, i.e. on a crash. + std::string str = small_value.as_string(); // Format +'small_value' here somehow. return Text{STRDUP(str.c_str())}; + } + + Text ec_to_text(const MyBigType* big_value) + { + // Called only when needed, i.e. on a crash. + std::string str = big_value->as_string(); // Format +'big_value' here somehow. return Text{STRDUP(str.c_str())}; + } + } // namespace loguru + +Any file that include some.hpp: + void foo(MySmallType small, const MyBigType& big) + { + ERROR_CONTEXT("Small", small); // Copy ´small` by value. + ERROR_CONTEXT("Big", &big); // `big` should not change during +this scope! + .... + } +*/ +} // namespace loguru LOGURU_ANONYMOUS_NAMESPACE_END @@ -1057,36 +1175,41 @@ LOGURU_ANONYMOUS_NAMESPACE_END // Logging macros // LOG_F(2, "Only logged if verbosity is 2 or higher: %d", some_number); -#define VLOG_F(verbosity, ...) \ - ((verbosity) > loguru::current_verbosity_cutoff()) ? (void)0 \ - : loguru::log(verbosity, __FILE__, __LINE__, __VA_ARGS__) +#define VLOG_F(verbosity, ...) \ + ((verbosity) > loguru::current_verbosity_cutoff()) \ + ? (void)0 \ + : loguru::log(verbosity, __FILE__, __LINE__, __VA_ARGS__) // LOG_F(INFO, "Foo: %d", some_number); -#define LOG_F(verbosity_name, ...) VLOG_F(loguru::Verbosity_ ## verbosity_name, __VA_ARGS__) +#define LOG_F(verbosity_name, ...) \ + VLOG_F(loguru::Verbosity_##verbosity_name, __VA_ARGS__) -#define VLOG_IF_F(verbosity, cond, ...) \ - ((verbosity) > loguru::current_verbosity_cutoff() || (cond) == false) \ - ? (void)0 \ - : loguru::log(verbosity, __FILE__, __LINE__, __VA_ARGS__) +#define VLOG_IF_F(verbosity, cond, ...) \ + ((verbosity) > loguru::current_verbosity_cutoff() || (cond) == false) \ + ? (void)0 \ + : loguru::log(verbosity, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_IF_F(verbosity_name, cond, ...) \ - VLOG_IF_F(loguru::Verbosity_ ## verbosity_name, cond, __VA_ARGS__) +#define LOG_IF_F(verbosity_name, cond, ...) \ + VLOG_IF_F(loguru::Verbosity_##verbosity_name, cond, __VA_ARGS__) -#define VLOG_SCOPE_F(verbosity, ...) \ - loguru::LogScopeRAII LOGURU_ANONYMOUS_VARIABLE(error_context_RAII_) = \ - ((verbosity) > loguru::current_verbosity_cutoff()) ? loguru::LogScopeRAII() : \ - loguru::LogScopeRAII(verbosity, __FILE__, __LINE__, __VA_ARGS__) +#define VLOG_SCOPE_F(verbosity, ...) \ + loguru::LogScopeRAII LOGURU_ANONYMOUS_VARIABLE(error_context_RAII_) = \ + ((verbosity) > loguru::current_verbosity_cutoff()) \ + ? loguru::LogScopeRAII() \ + : loguru::LogScopeRAII(verbosity, __FILE__, __LINE__, __VA_ARGS__) // Raw logging - no preamble, no indentation. Slightly faster than full logging. -#define RAW_VLOG_F(verbosity, ...) \ - ((verbosity) > loguru::current_verbosity_cutoff()) ? (void)0 \ - : loguru::raw_log(verbosity, __FILE__, __LINE__, __VA_ARGS__) +#define RAW_VLOG_F(verbosity, ...) \ + ((verbosity) > loguru::current_verbosity_cutoff()) \ + ? (void)0 \ + : loguru::raw_log(verbosity, __FILE__, __LINE__, __VA_ARGS__) -#define RAW_LOG_F(verbosity_name, ...) RAW_VLOG_F(loguru::Verbosity_ ## verbosity_name, __VA_ARGS__) +#define RAW_LOG_F(verbosity_name, ...) \ + RAW_VLOG_F(loguru::Verbosity_##verbosity_name, __VA_ARGS__) // Use to book-end a scope. Affects logging on all threads. -#define LOG_SCOPE_F(verbosity_name, ...) \ - VLOG_SCOPE_F(loguru::Verbosity_ ## verbosity_name, __VA_ARGS__) +#define LOG_SCOPE_F(verbosity_name, ...) \ + VLOG_SCOPE_F(loguru::Verbosity_##verbosity_name, __VA_ARGS__) #define LOG_SCOPE_FUNCTION(verbosity_name) LOG_SCOPE_F(verbosity_name, __func__) @@ -1094,118 +1217,115 @@ LOGURU_ANONYMOUS_NAMESPACE_END // ABORT_F macro. Usage: ABORT_F("Cause of error: %s", error_str); // Message is optional -#define ABORT_F(...) loguru::log_and_abort(0, "ABORT: ", __FILE__, __LINE__, __VA_ARGS__) +#define ABORT_F(...) \ + loguru::log_and_abort(0, "ABORT: ", __FILE__, __LINE__, __VA_ARGS__) // -------------------------------------------------------------------- // CHECK_F macros: -#define CHECK_WITH_INFO_F(test, info, ...) \ - LOGURU_PREDICT_TRUE((test) == true) ? (void)0 : loguru::log_and_abort(0, "CHECK FAILED: " info " ", __FILE__, \ - __LINE__, ##__VA_ARGS__) +#define CHECK_WITH_INFO_F(test, info, ...) \ + LOGURU_PREDICT_TRUE((test) == true) \ + ? (void)0 \ + : loguru::log_and_abort(0, "CHECK FAILED: " info " ", __FILE__, \ + __LINE__, ##__VA_ARGS__) -/* Checked at runtime too. Will print error, then call fatal_handler (if any), then 'abort'. - Note that the test must be boolean. - CHECK_F(ptr); will not compile, but CHECK_F(ptr != nullptr); will. */ +/* Checked at runtime too. Will print error, then call fatal_handler (if any), + then 'abort'. Note that the test must be boolean. CHECK_F(ptr); will not + compile, but CHECK_F(ptr != nullptr); will. */ #define CHECK_F(test, ...) CHECK_WITH_INFO_F(test, #test, ##__VA_ARGS__) -#define CHECK_NOTNULL_F(x, ...) CHECK_WITH_INFO_F((x) != nullptr, #x " != nullptr", ##__VA_ARGS__) - -#define CHECK_OP_F(expr_left, expr_right, op, ...) \ - do \ - { \ - auto val_left = expr_left; \ - auto val_right = expr_right; \ - if (! LOGURU_PREDICT_TRUE(val_left op val_right)) \ - { \ - auto str_left = loguru::format_value(val_left); \ - auto str_right = loguru::format_value(val_right); \ - auto fail_info = loguru::textprintf("CHECK FAILED: " LOGURU_FMT(s) " " LOGURU_FMT(s) " " LOGURU_FMT(s) " (" LOGURU_FMT(s) " " LOGURU_FMT(s) " " LOGURU_FMT(s) ") ", \ - #expr_left, #op, #expr_right, str_left.c_str(), #op, str_right.c_str()); \ - auto user_msg = loguru::textprintf(__VA_ARGS__); \ - loguru::log_and_abort(0, fail_info.c_str(), __FILE__, __LINE__, \ - LOGURU_FMT(s), user_msg.c_str()); \ - } \ - } while (false) +#define CHECK_NOTNULL_F(x, ...) \ + CHECK_WITH_INFO_F((x) != nullptr, #x " != nullptr", ##__VA_ARGS__) + +#define CHECK_OP_F(expr_left, expr_right, op, ...) \ + do { \ + auto val_left = expr_left; \ + auto val_right = expr_right; \ + if (!LOGURU_PREDICT_TRUE(val_left op val_right)) { \ + auto str_left = loguru::format_value(val_left); \ + auto str_right = loguru::format_value(val_right); \ + auto fail_info = loguru::textprintf("CHECK FAILED: " LOGURU_FMT(s) " " LOGURU_FMT(s) " " LOGURU_FMT(s) " (" LOGURU_FMT(s) " " LOGURU_FMT(s) " " LOGURU_FMT(s) ") ", \ + #expr_left, #op, #expr_right, str_left.c_str(), #op, str_right.c_str()); \ + auto user_msg = loguru::textprintf(__VA_ARGS__); \ + loguru::log_and_abort(0, fail_info.c_str(), __FILE__, __LINE__, \ + LOGURU_FMT(s), user_msg.c_str()); \ + } \ + } while (false) #ifndef LOGURU_DEBUG_LOGGING - #ifndef NDEBUG - #define LOGURU_DEBUG_LOGGING 1 - #else - #define LOGURU_DEBUG_LOGGING 0 - #endif +#ifndef NDEBUG +#define LOGURU_DEBUG_LOGGING 1 +#else +#define LOGURU_DEBUG_LOGGING 0 +#endif #endif #if LOGURU_DEBUG_LOGGING - // Debug logging enabled: - #define DLOG_F(verbosity_name, ...) LOG_F(verbosity_name, __VA_ARGS__) - #define DVLOG_F(verbosity, ...) VLOG_F(verbosity, __VA_ARGS__) - #define DLOG_IF_F(verbosity_name, ...) LOG_IF_F(verbosity_name, __VA_ARGS__) - #define DVLOG_IF_F(verbosity, ...) VLOG_IF_F(verbosity, __VA_ARGS__) - #define DRAW_LOG_F(verbosity_name, ...) RAW_LOG_F(verbosity_name, __VA_ARGS__) - #define DRAW_VLOG_F(verbosity, ...) RAW_VLOG_F(verbosity, __VA_ARGS__) - #define DLOG_SCOPE_F(verbosity_name,...) LOG_SCOPE_F(verbosity_name, __VA_ARGS__) - #define DLOG_SCOPE_FUNCTION(...) LOG_SCOPE_FUNCTION(__VA_ARGS__) +// Debug logging enabled: +#define DLOG_F(verbosity_name, ...) LOG_F(verbosity_name, __VA_ARGS__) +#define DVLOG_F(verbosity, ...) VLOG_F(verbosity, __VA_ARGS__) +#define DLOG_IF_F(verbosity_name, ...) LOG_IF_F(verbosity_name, __VA_ARGS__) +#define DVLOG_IF_F(verbosity, ...) VLOG_IF_F(verbosity, __VA_ARGS__) +#define DRAW_LOG_F(verbosity_name, ...) RAW_LOG_F(verbosity_name, __VA_ARGS__) +#define DRAW_VLOG_F(verbosity, ...) RAW_VLOG_F(verbosity, __VA_ARGS__) #else - // Debug logging disabled: - #define DLOG_F(verbosity_name, ...) - #define DVLOG_F(verbosity, ...) - #define DLOG_IF_F(verbosity_name, ...) - #define DVLOG_IF_F(verbosity, ...) - #define DRAW_LOG_F(verbosity_name, ...) - #define DRAW_VLOG_F(verbosity, ...) - #define DLOG_SCOPE_F(verbosity_name,...) - #define DLOG_SCOPE_FUNCTION(...) +// Debug logging disabled: +#define DLOG_F(verbosity_name, ...) +#define DVLOG_F(verbosity, ...) +#define DLOG_IF_F(verbosity_name, ...) +#define DVLOG_IF_F(verbosity, ...) +#define DRAW_LOG_F(verbosity_name, ...) +#define DRAW_VLOG_F(verbosity, ...) #endif #define CHECK_EQ_F(a, b, ...) CHECK_OP_F(a, b, ==, ##__VA_ARGS__) #define CHECK_NE_F(a, b, ...) CHECK_OP_F(a, b, !=, ##__VA_ARGS__) -#define CHECK_LT_F(a, b, ...) CHECK_OP_F(a, b, < , ##__VA_ARGS__) -#define CHECK_GT_F(a, b, ...) CHECK_OP_F(a, b, > , ##__VA_ARGS__) +#define CHECK_LT_F(a, b, ...) CHECK_OP_F(a, b, <, ##__VA_ARGS__) +#define CHECK_GT_F(a, b, ...) CHECK_OP_F(a, b, >, ##__VA_ARGS__) #define CHECK_LE_F(a, b, ...) CHECK_OP_F(a, b, <=, ##__VA_ARGS__) #define CHECK_GE_F(a, b, ...) CHECK_OP_F(a, b, >=, ##__VA_ARGS__) #ifndef LOGURU_DEBUG_CHECKS - #ifndef NDEBUG - #define LOGURU_DEBUG_CHECKS 1 - #else - #define LOGURU_DEBUG_CHECKS 0 - #endif +#ifndef NDEBUG +#define LOGURU_DEBUG_CHECKS 1 +#else +#define LOGURU_DEBUG_CHECKS 0 +#endif #endif #if LOGURU_DEBUG_CHECKS - // Debug checks enabled: - #define DCHECK_F(test, ...) CHECK_F(test, ##__VA_ARGS__) - #define DCHECK_NOTNULL_F(x, ...) CHECK_NOTNULL_F(x, ##__VA_ARGS__) - #define DCHECK_EQ_F(a, b, ...) CHECK_EQ_F(a, b, ##__VA_ARGS__) - #define DCHECK_NE_F(a, b, ...) CHECK_NE_F(a, b, ##__VA_ARGS__) - #define DCHECK_LT_F(a, b, ...) CHECK_LT_F(a, b, ##__VA_ARGS__) - #define DCHECK_LE_F(a, b, ...) CHECK_LE_F(a, b, ##__VA_ARGS__) - #define DCHECK_GT_F(a, b, ...) CHECK_GT_F(a, b, ##__VA_ARGS__) - #define DCHECK_GE_F(a, b, ...) CHECK_GE_F(a, b, ##__VA_ARGS__) +// Debug checks enabled: +#define DCHECK_F(test, ...) CHECK_F(test, ##__VA_ARGS__) +#define DCHECK_NOTNULL_F(x, ...) CHECK_NOTNULL_F(x, ##__VA_ARGS__) +#define DCHECK_EQ_F(a, b, ...) CHECK_EQ_F(a, b, ##__VA_ARGS__) +#define DCHECK_NE_F(a, b, ...) CHECK_NE_F(a, b, ##__VA_ARGS__) +#define DCHECK_LT_F(a, b, ...) CHECK_LT_F(a, b, ##__VA_ARGS__) +#define DCHECK_LE_F(a, b, ...) CHECK_LE_F(a, b, ##__VA_ARGS__) +#define DCHECK_GT_F(a, b, ...) CHECK_GT_F(a, b, ##__VA_ARGS__) +#define DCHECK_GE_F(a, b, ...) CHECK_GE_F(a, b, ##__VA_ARGS__) #else - // Debug checks disabled: - #define DCHECK_F(test, ...) - #define DCHECK_NOTNULL_F(x, ...) - #define DCHECK_EQ_F(a, b, ...) - #define DCHECK_NE_F(a, b, ...) - #define DCHECK_LT_F(a, b, ...) - #define DCHECK_LE_F(a, b, ...) - #define DCHECK_GT_F(a, b, ...) - #define DCHECK_GE_F(a, b, ...) -#endif // NDEBUG - +// Debug checks disabled: +#define DCHECK_F(test, ...) +#define DCHECK_NOTNULL_F(x, ...) +#define DCHECK_EQ_F(a, b, ...) +#define DCHECK_NE_F(a, b, ...) +#define DCHECK_LT_F(a, b, ...) +#define DCHECK_LE_F(a, b, ...) +#define DCHECK_GT_F(a, b, ...) +#define DCHECK_GE_F(a, b, ...) +#endif // NDEBUG #if LOGURU_REDEFINE_ASSERT - #undef assert - #ifndef NDEBUG - // Debug: - #define assert(test) CHECK_WITH_INFO_F(!!(test), #test) // HACK - #else - #define assert(test) - #endif -#endif // LOGURU_REDEFINE_ASSERT +#undef assert +#ifndef NDEBUG +// Debug: +#define assert(test) CHECK_WITH_INFO_F(!!(test), #test) // HACK +#else +#define assert(test) +#endif +#endif // LOGURU_REDEFINE_ASSERT -#endif // LOGURU_HAS_DECLARED_FORMAT_HEADER +#endif // LOGURU_HAS_DECLARED_FORMAT_HEADER // ---------------------------------------------------------------------------- // .dP"Y8 888888 88""Yb 888888 db 8b d8 .dP"Y8 @@ -1223,125 +1343,131 @@ LOGURU_ANONYMOUS_NAMESPACE_END */ #include -#include // Adds about 38 kLoC on clang. +#include // Adds about 38 kLoC on clang. #include LOGURU_ANONYMOUS_NAMESPACE_BEGIN -namespace loguru -{ - // Like sprintf, but returns the formated text. - LOGURU_EXPORT - std::string strprintf(LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(1, 2); - - // Like vsprintf, but returns the formated text. - LOGURU_EXPORT - std::string vstrprintf(LOGURU_FORMAT_STRING_TYPE format, va_list) LOGURU_PRINTF_LIKE(1, 0); - - class LOGURU_EXPORT StreamLogger - { - public: - StreamLogger(Verbosity verbosity, const char* file, unsigned line) : _verbosity(verbosity), _file(file), _line(line) {} - ~StreamLogger() noexcept(false); - - template - StreamLogger& operator<<(const T& t) - { - _ss << t; - return *this; - } - - // std::endl and other iomanip:s. - StreamLogger& operator<<(std::ostream&(*f)(std::ostream&)) - { - f(_ss); - return *this; - } - - private: - Verbosity _verbosity; - const char* _file; - unsigned _line; - std::ostringstream _ss; - }; - - class LOGURU_EXPORT AbortLogger - { - public: - AbortLogger(const char* expr, const char* file, unsigned line) : _expr(expr), _file(file), _line(line) { } - LOGURU_NORETURN ~AbortLogger() noexcept(false); - - template - AbortLogger& operator<<(const T& t) - { - _ss << t; - return *this; - } - - // std::endl and other iomanip:s. - AbortLogger& operator<<(std::ostream&(*f)(std::ostream&)) - { - f(_ss); - return *this; - } - - private: - const char* _expr; - const char* _file; - unsigned _line; - std::ostringstream _ss; - }; - - class LOGURU_EXPORT Voidify - { - public: - Voidify() {} - // This has to be an operator with a precedence lower than << but higher than ?: - void operator&(const StreamLogger&) { } - void operator&(const AbortLogger&) { } - }; - - /* Helper functions for CHECK_OP_S macro. - GLOG trick: The (int, int) specialization works around the issue that the compiler - will not instantiate the template version of the function on values of unnamed enum type. */ - #define DEFINE_CHECK_OP_IMPL(name, op) \ - template \ - inline std::string* name(const char* expr, const T1& v1, const char* op_str, const T2& v2) \ - { \ - if (LOGURU_PREDICT_TRUE(v1 op v2)) { return NULL; } \ - std::ostringstream ss; \ - ss << "CHECK FAILED: " << expr << " (" << v1 << " " << op_str << " " << v2 << ") "; \ - return new std::string(ss.str()); \ - } \ - inline std::string* name(const char* expr, int v1, const char* op_str, int v2) \ - { \ - return name(expr, v1, op_str, v2); \ - } - - DEFINE_CHECK_OP_IMPL(check_EQ_impl, ==) - DEFINE_CHECK_OP_IMPL(check_NE_impl, !=) - DEFINE_CHECK_OP_IMPL(check_LE_impl, <=) - DEFINE_CHECK_OP_IMPL(check_LT_impl, < ) - DEFINE_CHECK_OP_IMPL(check_GE_impl, >=) - DEFINE_CHECK_OP_IMPL(check_GT_impl, > ) - #undef DEFINE_CHECK_OP_IMPL - - /* GLOG trick: Function is overloaded for integral types to allow static const integrals - declared in classes and not defined to be used as arguments to CHECK* macros. */ - template - inline const T& referenceable_value(const T& t) { return t; } - inline char referenceable_value(char t) { return t; } - inline unsigned char referenceable_value(unsigned char t) { return t; } - inline signed char referenceable_value(signed char t) { return t; } - inline short referenceable_value(short t) { return t; } - inline unsigned short referenceable_value(unsigned short t) { return t; } - inline int referenceable_value(int t) { return t; } - inline unsigned int referenceable_value(unsigned int t) { return t; } - inline long referenceable_value(long t) { return t; } - inline unsigned long referenceable_value(unsigned long t) { return t; } - inline long long referenceable_value(long long t) { return t; } - inline unsigned long long referenceable_value(unsigned long long t) { return t; } -} // namespace loguru +namespace loguru { +// Like sprintf, but returns the formated text. +LOGURU_EXPORT +std::string strprintf(LOGURU_FORMAT_STRING_TYPE format, ...) + LOGURU_PRINTF_LIKE(1, 2); + +// Like vsprintf, but returns the formated text. +LOGURU_EXPORT +std::string vstrprintf(LOGURU_FORMAT_STRING_TYPE format, va_list) + LOGURU_PRINTF_LIKE(1, 0); + +class LOGURU_EXPORT StreamLogger { +public: + StreamLogger(Verbosity verbosity, const char* file, unsigned line) + : _verbosity(verbosity), _file(file), _line(line) {} + ~StreamLogger() noexcept(false); + + template + StreamLogger& operator<<(const T& t) { + _ss << t; + return *this; + } + + // std::endl and other iomanip:s. + StreamLogger& operator<<(std::ostream& (*f)(std::ostream&)) { + f(_ss); + return *this; + } + +private: + Verbosity _verbosity; + const char* _file; + unsigned _line; + std::ostringstream _ss; +}; + +class LOGURU_EXPORT AbortLogger { +public: + AbortLogger(const char* expr, const char* file, unsigned line) + : _expr(expr), _file(file), _line(line) {} + LOGURU_NORETURN ~AbortLogger() noexcept(false); + + template + AbortLogger& operator<<(const T& t) { + _ss << t; + return *this; + } + + // std::endl and other iomanip:s. + AbortLogger& operator<<(std::ostream& (*f)(std::ostream&)) { + f(_ss); + return *this; + } + +private: + const char* _expr; + const char* _file; + unsigned _line; + std::ostringstream _ss; +}; + +class LOGURU_EXPORT Voidify { +public: + Voidify() {} + // This has to be an operator with a precedence lower than << but higher + // than ?: + void operator&(const StreamLogger&) {} + void operator&(const AbortLogger&) {} +}; + +/* Helper functions for CHECK_OP_S macro. + GLOG trick: The (int, int) specialization works around the issue that + the compiler will not instantiate the template version of the function on + values of unnamed enum type. */ +#define DEFINE_CHECK_OP_IMPL(name, op) \ + template \ + inline std::string* name(const char* expr, const T1& v1, \ + const char* op_str, const T2& v2) { \ + if (LOGURU_PREDICT_TRUE(v1 op v2)) { \ + return NULL; \ + } \ + std::ostringstream ss; \ + ss << "CHECK FAILED: " << expr << " (" << v1 << " " << op_str << " " \ + << v2 << ") "; \ + return new std::string(ss.str()); \ + } \ + inline std::string* name(const char* expr, int v1, const char* op_str, \ + int v2) { \ + return name(expr, v1, op_str, v2); \ + } + +DEFINE_CHECK_OP_IMPL(check_EQ_impl, ==) +DEFINE_CHECK_OP_IMPL(check_NE_impl, !=) +DEFINE_CHECK_OP_IMPL(check_LE_impl, <=) +DEFINE_CHECK_OP_IMPL(check_LT_impl, <) +DEFINE_CHECK_OP_IMPL(check_GE_impl, >=) +DEFINE_CHECK_OP_IMPL(check_GT_impl, >) +#undef DEFINE_CHECK_OP_IMPL + +/* GLOG trick: Function is overloaded for integral types to allow static const + integrals declared in classes and not defined to be used as arguments to + CHECK* macros. */ +template +inline const T& referenceable_value(const T& t) { + return t; +} +inline char referenceable_value(char t) { return t; } +inline unsigned char referenceable_value(unsigned char t) { return t; } +inline signed char referenceable_value(signed char t) { return t; } +inline short referenceable_value(short t) { return t; } +inline unsigned short referenceable_value(unsigned short t) { return t; } +inline int referenceable_value(int t) { return t; } +inline unsigned int referenceable_value(unsigned int t) { return t; } +inline long referenceable_value(long t) { return t; } +inline unsigned long referenceable_value(unsigned long t) { return t; } +inline long long referenceable_value(long long t) { return t; } +inline unsigned long long referenceable_value(unsigned long long t) { + return t; +} +} // namespace loguru LOGURU_ANONYMOUS_NAMESPACE_END @@ -1349,138 +1475,146 @@ LOGURU_ANONYMOUS_NAMESPACE_END // Logging macros: // usage: LOG_STREAM(INFO) << "Foo " << std::setprecision(10) << some_value; -#define VLOG_IF_S(verbosity, cond) \ - ((verbosity) > loguru::current_verbosity_cutoff() || (cond) == false) \ - ? (void)0 \ - : loguru::Voidify() & loguru::StreamLogger(verbosity, __FILE__, __LINE__) -#define LOG_IF_S(verbosity_name, cond) VLOG_IF_S(loguru::Verbosity_ ## verbosity_name, cond) -#define VLOG_S(verbosity) VLOG_IF_S(verbosity, true) -#define LOG_S(verbosity_name) VLOG_S(loguru::Verbosity_ ## verbosity_name) +#define VLOG_IF_S(verbosity, cond) \ + ((verbosity) > loguru::current_verbosity_cutoff() || (cond) == false) \ + ? (void)0 \ + : loguru::Voidify() & \ + loguru::StreamLogger(verbosity, __FILE__, __LINE__) +#define LOG_IF_S(verbosity_name, cond) \ + VLOG_IF_S(loguru::Verbosity_##verbosity_name, cond) +#define VLOG_S(verbosity) VLOG_IF_S(verbosity, true) +#define LOG_S(verbosity_name) VLOG_S(loguru::Verbosity_##verbosity_name) // ----------------------------------------------- // ABORT_S macro. Usage: ABORT_S() << "Causo of error: " << details; -#define ABORT_S() loguru::Voidify() & loguru::AbortLogger("ABORT: ", __FILE__, __LINE__) +#define ABORT_S() \ + loguru::Voidify() & loguru::AbortLogger("ABORT: ", __FILE__, __LINE__) // ----------------------------------------------- // CHECK_S macros: -#define CHECK_WITH_INFO_S(cond, info) \ - LOGURU_PREDICT_TRUE((cond) == true) \ - ? (void)0 \ - : loguru::Voidify() & loguru::AbortLogger("CHECK FAILED: " info " ", __FILE__, __LINE__) +#define CHECK_WITH_INFO_S(cond, info) \ + LOGURU_PREDICT_TRUE((cond) == true) \ + ? (void)0 \ + : loguru::Voidify() & loguru::AbortLogger("CHECK FAILED: " info " ", \ + __FILE__, __LINE__) #define CHECK_S(cond) CHECK_WITH_INFO_S(cond, #cond) #define CHECK_NOTNULL_S(x) CHECK_WITH_INFO_S((x) != nullptr, #x " != nullptr") -#define CHECK_OP_S(function_name, expr1, op, expr2) \ - while (auto error_string = loguru::function_name(#expr1 " " #op " " #expr2, \ - loguru::referenceable_value(expr1), #op, \ - loguru::referenceable_value(expr2))) \ - loguru::AbortLogger(error_string->c_str(), __FILE__, __LINE__) +#define CHECK_OP_S(function_name, expr1, op, expr2) \ + while (auto error_string = loguru::function_name( \ + #expr1 " " #op " " #expr2, loguru::referenceable_value(expr1), \ + #op, loguru::referenceable_value(expr2))) \ + loguru::AbortLogger(error_string->c_str(), __FILE__, __LINE__) #define CHECK_EQ_S(expr1, expr2) CHECK_OP_S(check_EQ_impl, expr1, ==, expr2) #define CHECK_NE_S(expr1, expr2) CHECK_OP_S(check_NE_impl, expr1, !=, expr2) #define CHECK_LE_S(expr1, expr2) CHECK_OP_S(check_LE_impl, expr1, <=, expr2) -#define CHECK_LT_S(expr1, expr2) CHECK_OP_S(check_LT_impl, expr1, < , expr2) +#define CHECK_LT_S(expr1, expr2) CHECK_OP_S(check_LT_impl, expr1, <, expr2) #define CHECK_GE_S(expr1, expr2) CHECK_OP_S(check_GE_impl, expr1, >=, expr2) -#define CHECK_GT_S(expr1, expr2) CHECK_OP_S(check_GT_impl, expr1, > , expr2) +#define CHECK_GT_S(expr1, expr2) CHECK_OP_S(check_GT_impl, expr1, >, expr2) #if LOGURU_DEBUG_LOGGING - // Debug logging enabled: - #define DVLOG_IF_S(verbosity, cond) VLOG_IF_S(verbosity, cond) - #define DLOG_IF_S(verbosity_name, cond) LOG_IF_S(verbosity_name, cond) - #define DVLOG_S(verbosity) VLOG_S(verbosity) - #define DLOG_S(verbosity_name) LOG_S(verbosity_name) +// Debug logging enabled: +#define DVLOG_IF_S(verbosity, cond) VLOG_IF_S(verbosity, cond) +#define DLOG_IF_S(verbosity_name, cond) LOG_IF_S(verbosity_name, cond) +#define DVLOG_S(verbosity) VLOG_S(verbosity) +#define DLOG_S(verbosity_name) LOG_S(verbosity_name) #else - // Debug logging disabled: - #define DVLOG_IF_S(verbosity, cond) \ - (true || (verbosity) > loguru::current_verbosity_cutoff() || (cond) == false) \ - ? (void)0 \ - : loguru::Voidify() & loguru::StreamLogger(verbosity, __FILE__, __LINE__) - - #define DLOG_IF_S(verbosity_name, cond) DVLOG_IF_S(loguru::Verbosity_ ## verbosity_name, cond) - #define DVLOG_S(verbosity) DVLOG_IF_S(verbosity, true) - #define DLOG_S(verbosity_name) DVLOG_S(loguru::Verbosity_ ## verbosity_name) +// Debug logging disabled: +#define DVLOG_IF_S(verbosity, cond) \ + (true || (verbosity) > loguru::current_verbosity_cutoff() || \ + (cond) == false) \ + ? (void)0 \ + : loguru::Voidify() & \ + loguru::StreamLogger(verbosity, __FILE__, __LINE__) + +#define DLOG_IF_S(verbosity_name, cond) \ + DVLOG_IF_S(loguru::Verbosity_##verbosity_name, cond) +#define DVLOG_S(verbosity) DVLOG_IF_S(verbosity, true) +#define DLOG_S(verbosity_name) DVLOG_S(loguru::Verbosity_##verbosity_name) #endif #if LOGURU_DEBUG_CHECKS - // Debug checks enabled: - #define DCHECK_S(cond) CHECK_S(cond) - #define DCHECK_NOTNULL_S(x) CHECK_NOTNULL_S(x) - #define DCHECK_EQ_S(a, b) CHECK_EQ_S(a, b) - #define DCHECK_NE_S(a, b) CHECK_NE_S(a, b) - #define DCHECK_LT_S(a, b) CHECK_LT_S(a, b) - #define DCHECK_LE_S(a, b) CHECK_LE_S(a, b) - #define DCHECK_GT_S(a, b) CHECK_GT_S(a, b) - #define DCHECK_GE_S(a, b) CHECK_GE_S(a, b) +// Debug checks enabled: +#define DCHECK_S(cond) CHECK_S(cond) +#define DCHECK_NOTNULL_S(x) CHECK_NOTNULL_S(x) +#define DCHECK_EQ_S(a, b) CHECK_EQ_S(a, b) +#define DCHECK_NE_S(a, b) CHECK_NE_S(a, b) +#define DCHECK_LT_S(a, b) CHECK_LT_S(a, b) +#define DCHECK_LE_S(a, b) CHECK_LE_S(a, b) +#define DCHECK_GT_S(a, b) CHECK_GT_S(a, b) +#define DCHECK_GE_S(a, b) CHECK_GE_S(a, b) #else // Debug checks disabled: - #define DCHECK_S(cond) CHECK_S(true || (cond)) - #define DCHECK_NOTNULL_S(x) CHECK_S(true || (x) != nullptr) - #define DCHECK_EQ_S(a, b) CHECK_S(true || (a) == (b)) - #define DCHECK_NE_S(a, b) CHECK_S(true || (a) != (b)) - #define DCHECK_LT_S(a, b) CHECK_S(true || (a) < (b)) - #define DCHECK_LE_S(a, b) CHECK_S(true || (a) <= (b)) - #define DCHECK_GT_S(a, b) CHECK_S(true || (a) > (b)) - #define DCHECK_GE_S(a, b) CHECK_S(true || (a) >= (b)) +#define DCHECK_S(cond) CHECK_S(true || (cond)) +#define DCHECK_NOTNULL_S(x) CHECK_S(true || (x) != nullptr) +#define DCHECK_EQ_S(a, b) CHECK_S(true || (a) == (b)) +#define DCHECK_NE_S(a, b) CHECK_S(true || (a) != (b)) +#define DCHECK_LT_S(a, b) CHECK_S(true || (a) < (b)) +#define DCHECK_LE_S(a, b) CHECK_S(true || (a) <= (b)) +#define DCHECK_GT_S(a, b) CHECK_S(true || (a) > (b)) +#define DCHECK_GE_S(a, b) CHECK_S(true || (a) >= (b)) #endif #if LOGURU_REPLACE_GLOG - #undef LOG - #undef VLOG - #undef LOG_IF - #undef VLOG_IF - #undef CHECK - #undef CHECK_NOTNULL - #undef CHECK_EQ - #undef CHECK_NE - #undef CHECK_LT - #undef CHECK_LE - #undef CHECK_GT - #undef CHECK_GE - #undef DLOG - #undef DVLOG - #undef DLOG_IF - #undef DVLOG_IF - #undef DCHECK - #undef DCHECK_NOTNULL - #undef DCHECK_EQ - #undef DCHECK_NE - #undef DCHECK_LT - #undef DCHECK_LE - #undef DCHECK_GT - #undef DCHECK_GE - #undef VLOG_IS_ON - - #define LOG LOG_S - #define VLOG VLOG_S - #define LOG_IF LOG_IF_S - #define VLOG_IF VLOG_IF_S - #define CHECK(cond) CHECK_S(!!(cond)) - #define CHECK_NOTNULL CHECK_NOTNULL_S - #define CHECK_EQ CHECK_EQ_S - #define CHECK_NE CHECK_NE_S - #define CHECK_LT CHECK_LT_S - #define CHECK_LE CHECK_LE_S - #define CHECK_GT CHECK_GT_S - #define CHECK_GE CHECK_GE_S - #define DLOG DLOG_S - #define DVLOG DVLOG_S - #define DLOG_IF DLOG_IF_S - #define DVLOG_IF DVLOG_IF_S - #define DCHECK DCHECK_S - #define DCHECK_NOTNULL DCHECK_NOTNULL_S - #define DCHECK_EQ DCHECK_EQ_S - #define DCHECK_NE DCHECK_NE_S - #define DCHECK_LT DCHECK_LT_S - #define DCHECK_LE DCHECK_LE_S - #define DCHECK_GT DCHECK_GT_S - #define DCHECK_GE DCHECK_GE_S - #define VLOG_IS_ON(verbosity) ((verbosity) <= loguru::current_verbosity_cutoff()) - -#endif // LOGURU_REPLACE_GLOG - -#endif // LOGURU_WITH_STREAMS - -#endif // LOGURU_HAS_DECLARED_STREAMS_HEADER +#undef LOG +#undef VLOG +#undef LOG_IF +#undef VLOG_IF +#undef CHECK +#undef CHECK_NOTNULL +#undef CHECK_EQ +#undef CHECK_NE +#undef CHECK_LT +#undef CHECK_LE +#undef CHECK_GT +#undef CHECK_GE +#undef DLOG +#undef DVLOG +#undef DLOG_IF +#undef DVLOG_IF +#undef DCHECK +#undef DCHECK_NOTNULL +#undef DCHECK_EQ +#undef DCHECK_NE +#undef DCHECK_LT +#undef DCHECK_LE +#undef DCHECK_GT +#undef DCHECK_GE +#undef VLOG_IS_ON + +#define LOG LOG_S +#define VLOG VLOG_S +#define LOG_IF LOG_IF_S +#define VLOG_IF VLOG_IF_S +#define CHECK(cond) CHECK_S(!!(cond)) +#define CHECK_NOTNULL CHECK_NOTNULL_S +#define CHECK_EQ CHECK_EQ_S +#define CHECK_NE CHECK_NE_S +#define CHECK_LT CHECK_LT_S +#define CHECK_LE CHECK_LE_S +#define CHECK_GT CHECK_GT_S +#define CHECK_GE CHECK_GE_S +#define DLOG DLOG_S +#define DVLOG DVLOG_S +#define DLOG_IF DLOG_IF_S +#define DVLOG_IF DVLOG_IF_S +#define DCHECK DCHECK_S +#define DCHECK_NOTNULL DCHECK_NOTNULL_S +#define DCHECK_EQ DCHECK_EQ_S +#define DCHECK_NE DCHECK_NE_S +#define DCHECK_LT DCHECK_LT_S +#define DCHECK_LE DCHECK_LE_S +#define DCHECK_GT DCHECK_GT_S +#define DCHECK_GE DCHECK_GE_S +#define VLOG_IS_ON(verbosity) \ + ((verbosity) <= loguru::current_verbosity_cutoff()) + +#endif // LOGURU_REPLACE_GLOG + +#endif // LOGURU_WITH_STREAMS + +#endif // LOGURU_HAS_DECLARED_STREAMS_HEADER diff --git a/src/atom/log/xmake.lua b/src/atom/log/xmake.lua new file mode 100644 index 00000000..3085d628 --- /dev/null +++ b/src/atom/log/xmake.lua @@ -0,0 +1,86 @@ +-- xmake.lua for Loguru +-- This project is licensed under the terms of the MIT license. + +add_rules("mode.debug", "mode.release") + +-- Define project and policies +set_project("loguru") +set_version("2.1.0") +set_languages("cxx11") + +-- Expose xmake-specific user options +option("build_examples", {showmenu = true, default = false, description = "Build the project examples"}) +option("build_tests", {showmenu = true, default = false, description = "Build the tests"}) + +-- Set global compile flags +if is_mode("debug") then + set_symbols("debug") + set_optimize("none") +else + set_symbols("hidden") + set_optimize("fastest") + set_strip("all") +end + +-- Add loguru target +target("loguru") + set_kind("$(kind)") + add_files("loguru.cpp") + add_headerfiles("loguru.hpp") + add_includedirs(".", {public = true}) + set_configvar("LOGURU_STACKTRACES", 1) + set_configvar("LOGURU_USE_FMTLIB", 1) + + -- Determine if linking 'dl' is required + if is_plat("windows") then + add_packages("dlfcn-win32") + add_links("dlfcn-win32", "dbghelp") + else + add_links("dl") + end + + -- Link fmt (if needed) + if has_config("LOGURU_USE_FMTLIB") then + add_packages("fmt") + if has_config("LOGURU_FMT_HEADER_ONLY") then + add_packages("fmt", {configs = {header_only = true}}) + end + end + + -- Set target properties + set_configvar("LOGURU_DEBUG_LOGGING", 0) + set_configvar("LOGURU_DEBUG_CHECKS", 0) + set_configvar("LOGURU_REDEFINE_ASSERT", 0) + set_configvar("LOGURU_WITH_STREAMS", 1) + set_configvar("LOGURU_REPLACE_GLOG", 0) + set_configvar("LOGURU_FMT_HEADER_ONLY", 0) + set_configvar("LOGURU_WITH_FILEABS", 0) + set_configvar("LOGURU_RTTI", 1) + set_configvar("LOGURU_FILENAME_WIDTH", 23) + set_configvar("LOGURU_THREADNAME_WIDTH", 16) + set_configvar("LOGURU_SCOPE_TIME_PRECISION", 3) + set_configvar("LOGURU_VERBOSE_SCOPE_ENDINGS", 0) + +-- Setup examples +if has_config("build_examples") then + -- TODO: Add example targets +end + +-- Setup tests +if has_config("build_tests") then + -- TODO: Add test targets +end + +-- Setup install rules +target("install") + set_kind("phony") + on_install(function (target) + -- Install the main library + install_targets("loguru", {libdir = "lib"}) + + -- Install the header file + install_headers("loguru.hpp", {prefixdir = "include/loguru"}) + + -- Install pkgconfig file + -- TODO: Generate and install pkgconfig file + end) \ No newline at end of file diff --git a/src/atom/meson.build b/src/atom/meson.build new file mode 100644 index 00000000..d28569a7 --- /dev/null +++ b/src/atom/meson.build @@ -0,0 +1,73 @@ +project('Atom', 'cpp') + +# Define sources and headers +atom_sources = [ + 'error/error_stack.cpp', + 'log/logger.cpp', + 'log/global_logger.cpp', + 'log/syslog.cpp' +] +atom_headers = [ + 'error/error_code.hpp', + 'error/error_stack.hpp', + 'log/logger.hpp', + 'log/global_logger.hpp', + 'log/syslog.hpp' +] + +# Private headers +atom_private_headers = [] + +# Define libraries +atom_libs = [ + 'loguru', + 'cpp_httplib', + 'libzippp', + 'atom-async', + 'atom-task', + 'atom-io', + 'atom-driver', + 'atom-event', + 'atom-experiment', + 'atom-component', + 'atom-type', + 'atom-utils', + 'atom-search', + 'atom-web', + 'atom-system', + 'atom-server' +] + +# Check if fmt library is available +has_fmt = find_library('fmt') + +if not has_fmt.found() + atom_libs += 'fmt' +endif + +# Create object library +atom_obj = library('atom_obj', + atom_sources + atom_headers + atom_private_headers, + cpp_std: c_std +) + +# Link with necessary libraries +foreach lib: atom_libs + atom_obj_link_libs += dependency(lib) +endforeach + +atom_obj.link_with(atom_obj_link_libs) + +# Create static library +atom_static = static_library('atom_static', atom_obj) + +# Set version properties +version = '1.0.0' # You should define your version here +soversion = '1' + +atom_static.version = version +atom_static.soversion = soversion +atom_static.basename = 'Atom' + +# Install target +install_targets(atom_static) diff --git a/src/atom/script/_json.hpp b/src/atom/script/_json.hpp new file mode 100644 index 00000000..7ffdfc76 --- /dev/null +++ b/src/atom/script/_json.hpp @@ -0,0 +1,191 @@ +/* + * _json.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-4 + +Description: Json <-> Python converter from(https://github.com/pybind/pybind11_json) + +**************************************************/ + +#ifndef ATOM_SCRIPT_JSON_HPP +#define ATOM_SCRIPT_JSON_HPP + +#include +#include + +#include "atom/type/json.hpp" + +#include "pybind11/pybind11.h" + +namespace py = pybind11; +namespace nl = nlohmann; + +namespace pyjson { +inline py::object from_json(const nl::json& j) { + if (j.is_null()) { + return py::none(); + } else if (j.is_boolean()) { + return py::bool_(j.get()); + } else if (j.is_number_unsigned()) { + return py::int_(j.get()); + } else if (j.is_number_integer()) { + return py::int_(j.get()); + } else if (j.is_number_float()) { + return py::float_(j.get()); + } else if (j.is_string()) { + return py::str(j.get()); + } else if (j.is_array()) { + py::list obj(j.size()); + for (std::size_t i = 0; i < j.size(); i++) { + obj[i] = from_json(j[i]); + } + return std::move(obj); + } else // Object + { + py::dict obj; + for (nl::json::const_iterator it = j.cbegin(); it != j.cend(); ++it) { + obj[py::str(it.key())] = from_json(it.value()); + } + return std::move(obj); + } +} + +inline nl::json to_json(const py::handle& obj) { + if (obj.ptr() == nullptr || obj.is_none()) { + return nullptr; + } + if (py::isinstance(obj)) { + return obj.cast(); + } + if (py::isinstance(obj)) { + try { + nl::json::number_integer_t s = + obj.cast(); + if (py::int_(s).equal(obj)) { + return s; + } + } catch (...) { + } + try { + nl::json::number_unsigned_t u = + obj.cast(); + if (py::int_(u).equal(obj)) { + return u; + } + } catch (...) { + } + throw std::runtime_error( + "to_json received an integer out of range for both " + "nl::json::number_integer_t and nl::json::number_unsigned_t " + "type: " + + py::repr(obj).cast()); + } + if (py::isinstance(obj)) { + return obj.cast(); + } + if (py::isinstance(obj)) { + py::module base64 = py::module::import("base64"); + return base64.attr("b64encode")(obj) + .attr("decode")("utf-8") + .cast(); + } + if (py::isinstance(obj)) { + return obj.cast(); + } + if (py::isinstance(obj) || py::isinstance(obj)) { + auto out = nl::json::array(); + for (const py::handle value : obj) { + out.push_back(to_json(value)); + } + return out; + } + if (py::isinstance(obj)) { + auto out = nl::json::object(); + for (const py::handle key : obj) { + out[py::str(key).cast()] = to_json(obj[key]); + } + return out; + } + throw std::runtime_error( + "to_json not implemented for this type of object: " + + py::repr(obj).cast()); +} +} // namespace pyjson + +// nlohmann_json serializers +namespace nlohmann { +#define MAKE_NLJSON_SERIALIZER_DESERIALIZER(T) \ + template <> \ + struct adl_serializer { \ + inline static void to_json(json& j, const T& obj) { \ + j = pyjson::to_json(obj); \ + } \ + \ + inline static T from_json(const json& j) { \ + return pyjson::from_json(j); \ + } \ + } + +#define MAKE_NLJSON_SERIALIZER_ONLY(T) \ + template <> \ + struct adl_serializer { \ + inline static void to_json(json& j, const T& obj) { \ + j = pyjson::to_json(obj); \ + } \ + } + +MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::object); + +MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::bool_); +MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::int_); +MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::float_); +MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::str); + +MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::list); +MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::tuple); +MAKE_NLJSON_SERIALIZER_DESERIALIZER(py::dict); + +MAKE_NLJSON_SERIALIZER_ONLY(py::handle); +MAKE_NLJSON_SERIALIZER_ONLY(py::detail::item_accessor); +MAKE_NLJSON_SERIALIZER_ONLY(py::detail::list_accessor); +MAKE_NLJSON_SERIALIZER_ONLY(py::detail::tuple_accessor); +MAKE_NLJSON_SERIALIZER_ONLY(py::detail::sequence_accessor); +MAKE_NLJSON_SERIALIZER_ONLY(py::detail::str_attr_accessor); +MAKE_NLJSON_SERIALIZER_ONLY(py::detail::obj_attr_accessor); + +#undef MAKE_NLJSON_SERIALIZER +#undef MAKE_NLJSON_SERIALIZER_ONLY +} // namespace nlohmann + +// pybind11 caster +namespace pybind11 { +namespace detail { +template <> +struct type_caster { +public: + PYBIND11_TYPE_CASTER(nl::json, _("json")); + + bool load(handle src, bool) { + try { + value = pyjson::to_json(src); + return true; + } catch (...) { + return false; + } + } + + static handle cast(nl::json src, return_value_policy /* policy */, + handle /* parent */) { + object obj = pyjson::from_json(src); + return obj.release(); + } +}; +} // namespace detail +} // namespace pybind11 + +#endif diff --git a/src/atom/search/CMakeLists.txt b/src/atom/search/CMakeLists.txt index 67533262..3fe931aa 100644 --- a/src/atom/search/CMakeLists.txt +++ b/src/atom/search/CMakeLists.txt @@ -1,7 +1,7 @@ # CMakeLists.txt for Atom-Search # This project is licensed under the terms of the GPL3 license. # -# Project Name: Atom-Utils +# Project Name: Atom-Search # Description: Search Engine for Element Astro Project # Author: Max Qian # License: GPL3 diff --git a/src/atom/search/meson.build b/src/atom/search/meson.build new file mode 100644 index 00000000..c99b30f8 --- /dev/null +++ b/src/atom/search/meson.build @@ -0,0 +1,38 @@ +project('atom-search', 'cpp') + +# Sources +atom_search_sources = [ + 'search.cpp', + 'sqlite.cpp', +] + +# Headers +atom_search_headers = [ + 'cache_impl.hpp', + 'cache.hpp', + 'search.hpp', + 'sqlite.hpp', +] + +# Build Object Library +atom_search_object = library( + 'atom-search-object', + atom_search_sources, + include_directories: include_directories('.') +) + +# Link libraries +atom_search_object_deps = dependency('loguru') + +# Build Static Library +atom_search_static = library( + 'atom-search', + atom_search_sources, + include_directories: include_directories('.'), + dependencies: [atom_search_object_deps], + version: '1.0', + soversion: '1' +) + +# Install +install_targets(atom_search_static) diff --git a/src/atom/search/xmake.lua b/src/atom/search/xmake.lua new file mode 100644 index 00000000..b894c7bd --- /dev/null +++ b/src/atom/search/xmake.lua @@ -0,0 +1,34 @@ +-- xmake.lua for Atom-Search + +-- Project Information +set_project("Atom-Search") +set_version("1.0.0") +set_description("Search Engine for Element Astro Project") +set_licenses("GPL-3.0") + +-- Specify the C++ Languages +set_languages("c++17") + +-- Add Source Files +add_files("*.cpp") +add_files("*.hpp") + +-- Add Private Header Files +add_headerfiles("*.hpp", {prefixdir = "$(projectdir)"}) + +-- Add Target +target("Atom-Search") + set_kind("static") + add_packages("loguru") + add_links("pthread") + add_includedirs(".") + +-- Install Rules +after_install("install_headers") +after_install("install_libraries") + +-- Install Headers +install_headers("*.hpp", "$(projectdir)/include/$(projectname)") + +-- Install Libraries +install_libraries("$(targetdir)/*.a", "$(projectdir)/lib") \ No newline at end of file diff --git a/src/atom/server/CMakeLists.txt b/src/atom/server/CMakeLists.txt index 594b29e9..1e7ca566 100644 --- a/src/atom/server/CMakeLists.txt +++ b/src/atom/server/CMakeLists.txt @@ -21,10 +21,12 @@ set(${PROJECT_NAME}_SOURCES # Headers set(${PROJECT_NAME}_HEADERS - commander_impl.hpp + commander.inl commander.hpp daemon.hpp deserialize.hpp + eventstack.hpp + eventstack.inl global_ptr.hpp json_checker.hpp message_bus.hpp diff --git a/src/atom/server/commander.hpp b/src/atom/server/commander.hpp index 9eb97fba..b8d7c3b2 100644 --- a/src/atom/server/commander.hpp +++ b/src/atom/server/commander.hpp @@ -244,6 +244,6 @@ class CommandDispatcher : public NonCopyable { size_t m_maxHistorySize = 100; }; -#include "commander_impl.hpp" +#include "commander.inl" #endif diff --git a/src/atom/server/commander_impl.hpp b/src/atom/server/commander.inl similarity index 98% rename from src/atom/server/commander_impl.hpp rename to src/atom/server/commander.inl index 8a6955e6..86834e65 100644 --- a/src/atom/server/commander_impl.hpp +++ b/src/atom/server/commander.inl @@ -15,6 +15,8 @@ Description: Commander #ifndef ATOM_SERVER_COMMANDER_IMPL_HPP #define ATOM_SERVER_COMMANDER_IMPL_HPP +#include "commander.hpp" + template CommandDispatcher::~CommandDispatcher() { m_handlers.clear(); @@ -97,8 +99,8 @@ Result CommandDispatcher::dispatch(const std::string &name, const Argument &data) { std::shared_lock lock(m_sharedMutex); - // Max: Here we check if a decorator is registered for the command and run it in advance - // Check if a decorator is registered for the command + // Max: Here we check if a decorator is registered for the command and run + // it in advance Check if a decorator is registered for the command auto it = m_decorators.find(name); if (it != m_decorators.end()) { auto decorator = it->second; diff --git a/src/atom/server/daemon.cpp b/src/atom/server/daemon.cpp index 61c50190..650fa1d9 100644 --- a/src/atom/server/daemon.cpp +++ b/src/atom/server/daemon.cpp @@ -9,7 +9,7 @@ Date: 2023-11-11 Description: Daemon process implementation for Linux and Windows. But there is -still some problems on Windows, espacially the console. +still some problems on Windows, especially the console. **************************************************/ @@ -42,7 +42,7 @@ std::string DaemonGuard::ToString() const { ss << "[DaemonGuard parentId=" << m_parentId << " mainId=" << m_mainId << " parentStartTime=" << Utils::timeStampToString(m_parentStartTime) << " mainStartTime=" << Utils::timeStampToString(m_mainStartTime) - << " restartCount=" << m_restartCount << "]"; + << " restartCount=" << m_restartCount.load() << "]"; return ss.str(); } @@ -82,7 +82,7 @@ int DaemonGuard::RealDaemon(int argc, char **argv, CloseHandle(DaemonGuard.hThread); // 等待一段时间后重新启动子进程 - m_restartCount += 1; + m_restartCount++; Sleep(g_daemonRestartInterval * 1000); } #else @@ -123,7 +123,7 @@ int DaemonGuard::RealDaemon(int argc, char **argv, } // 等待一段时间后重新启动子进程 - m_restartCount += 1; + m_restartCount++; sleep(g_daemonRestartInterval); } } @@ -205,55 +205,4 @@ bool CheckPidFile() { return true; #endif } -} // namespace Atom::Async - -/* - -// 定义 MainCb 函数 -int MainCb(int argc, char **argv) -{ - // 实际任务的代码逻辑 - for (int i = 0; i < 10; ++i) - { - std::cout << "hello world" << std::endl; - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - - return 0; -} - -// 主函数 -int main(int argc, char **argv) -{ - // 初始化日志库、配置文件等 - // ... - - // 检查 PID 文件是否存在,并检查文件中的 PID 是否有效 - if (CheckPidFile()) - { - // LOG_F(ERROR, "process already running with pid file {}", -g_pidFilePath); exit(-1); - } - - // 写入 PID 文件 - WritePidFile(); - - // 注册退出信号处理函数 - signal(SIGTERM, SignalHandler); - signal(SIGINT, SignalHandler); - - // 创建 DaemonGuard 对象并启动进程,如果需要创建守护进程,则先创建守护进程 - DaemonGuard DaemonGuard; - DaemonGuard.StartDaemon(argc, argv, std::bind(MainCb, argc, argv), -g_isDaemon); DaemonGuard.RealDaemon(argc, argv, std::bind(MainCb, argc, argv)); - if (g_isDaemon) - { - // LOG_F(INFO, "daemon process start pid={} argv={}", getpid(), argv) - } - - std::cout << DaemonGuard.ToString() << std::endl; - // 删除 PID 文件并退出程序 - remove(g_pidFilePath.c_str()); - return 0; -} -*/ +} // namespace Atom::Async \ No newline at end of file diff --git a/src/atom/server/daemon.hpp b/src/atom/server/daemon.hpp index 0a7c167f..5cd2d3eb 100644 --- a/src/atom/server/daemon.hpp +++ b/src/atom/server/daemon.hpp @@ -15,10 +15,13 @@ Description: Daemon process implementation #ifndef ATOM_SERVER_DAEMON_HPP #define ATOM_SERVER_DAEMON_HPP +#include #include #include #include +#include #include +#include #ifdef _WIN32 #include @@ -93,7 +96,7 @@ class DaemonGuard { #endif time_t m_parentStartTime = 0; /**< The start time of the parent process. */ time_t m_mainStartTime = 0; /**< The start time of the child process. */ - int m_restartCount = 0; /**< The number of restarts. */ + std::atomic m_restartCount{0}; /**< The number of restarts. */ }; /** diff --git a/src/atom/server/eventstack.hpp b/src/atom/server/eventstack.hpp index b788f6f7..65d75b7a 100644 --- a/src/atom/server/eventstack.hpp +++ b/src/atom/server/eventstack.hpp @@ -15,15 +15,19 @@ Description: A thread-safe stack data structure for managing events. #ifndef ATOM_SERVER_EVENTSTACK_HPP #define ATOM_SERVER_EVENTSTACK_HPP -#include -#include -#include #include +#include +#include #include +#include +#include +#include +#include +#include /** * @brief A thread-safe stack data structure for managing events. - * + * * @tparam T The type of events to store. */ template @@ -31,21 +35,20 @@ class EventStack { public: EventStack(); /**< Constructor. */ ~EventStack(); /**< Destructor. */ - + /** * @brief Pushes an event onto the stack. - * + * * @param event The event to push. */ - void pushEvent(const T& event); + void pushEvent(T event); /** * @brief Pops an event from the stack. - * - * @return The popped event. - * @throw std::out_of_range if the stack is empty. + * + * @return The popped event, or std::nullopt if the stack is empty. */ - T popEvent(); + std::optional popEvent(); /** * @brief Prints all events in the stack. @@ -54,14 +57,14 @@ class EventStack { /** * @brief Checks if the stack is empty. - * + * * @return true if the stack is empty, false otherwise. */ bool isEmpty() const; /** * @brief Returns the number of events in the stack. - * + * * @return The number of events. */ size_t size() const; @@ -73,46 +76,96 @@ class EventStack { /** * @brief Returns the top event in the stack without removing it. - * - * @return The top event. - * @throw std::out_of_range if the stack is empty. + * + * @return The top event, or std::nullopt if the stack is empty. */ - T peekTopEvent() const; + std::optional peekTopEvent() const; /** * @brief Copies the current stack. - * + * * @return A copy of the stack. */ EventStack copyStack() const; /** * @brief Filters events based on a custom filter function. - * + * * @param filterFunc The filter function. */ - void filterEvents(bool (*filterFunc)(const T&)); + void filterEvents(std::function filterFunc); /** * @brief Serializes the stack into a string. - * + * * @return The serialized stack. */ std::string serializeStack() const; /** * @brief Deserializes a string into the stack. - * + * * @param serializedData The serialized stack data. */ - void deserializeStack(const std::string& serializedData); + void deserializeStack(std::string_view serializedData); + + /** + * @brief Removes duplicate events from the stack. + */ + void removeDuplicates(); + + /** + * @brief Sorts the events in the stack based on a custom comparison + * function. + * + * @param compareFunc The comparison function. + */ + void sortEvents(std::function compareFunc); + + /** + * @brief Reverses the order of events in the stack. + */ + void reverseEvents(); + + /** + * @brief Counts the number of events that satisfy a predicate. + * + * @param predicate The predicate function. + * @return The count of events satisfying the predicate. + */ + size_t countEvents(std::function predicate) const; + + /** + * @brief Finds the first event that satisfies a predicate. + * * + * @param predicate The predicate function. + * @return The first event satisfying the predicate, or std::nullopt if not + * found. + */ + std::optional findEvent(std::function predicate) const; + + /** + * @brief Checks if any event in the stack satisfies a predicate. + * + * @param predicate The predicate function. + * @return true if any event satisfies the predicate, false otherwise. + */ + bool anyEvent(std::function predicate) const; + + /** + * @brief Checks if all events in the stack satisfy a predicate. + * + * @param predicate The predicate function. + * @return true if all events satisfy the predicate, false otherwise. + */ + bool allEvents(std::function predicate) const; private: - class EventStackImpl; /**< Forward declaration of the implementation class. */ - std::unique_ptr impl; /**< Pointer to the implementation. */ - mutable std::mutex mtx; /**< Mutex for thread safety. */ + std::vector events; /**< Vector to store events. */ + mutable std::shared_mutex mtx; /**< Mutex for thread safety. */ + std::atomic eventCount{0}; /**< Atomic counter for event count. */ }; -#include "eventstack_impl.hpp" +#include "eventstack.inl" -#endif // ATOM_SERVER_EVENTSTACK_HPP +#endif // ATOM_SERVER_EVENTSTACK_HPP \ No newline at end of file diff --git a/src/atom/server/eventstack.inl b/src/atom/server/eventstack.inl new file mode 100644 index 00000000..5485c469 --- /dev/null +++ b/src/atom/server/eventstack.inl @@ -0,0 +1,176 @@ +/* + * eventstack_impl.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-3-26 + +Description: A thread-safe stack data structure for managing events. + +**************************************************/ + +#ifndef ATOM_SERVER_EVENTSTACK_INL +#define ATOM_SERVER_EVENTSTACK_INL + +#include "eventstack.hpp" + +template +EventStack::EventStack() {} + +template +EventStack::~EventStack() {} + +template +void EventStack::pushEvent(T event) { + std::unique_lock lock(mtx); + events.push_back(std::move(event)); + ++eventCount; +} + +template +std::optional EventStack::popEvent() { + std::unique_lock lock(mtx); + if (!events.empty()) { + T event = std::move(events.back()); + events.pop_back(); + --eventCount; + return event; + } + return std::nullopt; +} + +template +void EventStack::printEvents() const { + std::shared_lock lock(mtx); + std::cout << "Events in stack:" << std::endl; + for (const T& event : events) { + std::cout << event << std::endl; + } +} + +template +bool EventStack::isEmpty() const { + std::shared_lock lock(mtx); + return events.empty(); +} + +template +size_t EventStack::size() const { + return eventCount.load(); +} + +template +void EventStack::clearEvents() { + std::unique_lock lock(mtx); + events.clear(); + eventCount.store(0); +} + +template +std::optional EventStack::peekTopEvent() const { + std::shared_lock lock(mtx); + if (!events.empty()) { + return events.back(); + } + return std::nullopt; +} + +template +EventStack EventStack::copyStack() const { + std::shared_lock lock(mtx); + EventStack newStack; + newStack.events = events; + newStack.eventCount.store(eventCount.load()); + return newStack; +} + +template +void EventStack::filterEvents(std::function filterFunc) { + std::unique_lock lock(mtx); + events.erase( + std::remove_if(events.begin(), events.end(), + [&](const T& event) { return !filterFunc(event); }), + events.end()); + eventCount.store(events.size()); +} + +template +std::string EventStack::serializeStack() const { + std::shared_lock lock(mtx); + std::string serializedStack; + for (const T& event : events) { + serializedStack += event + ";"; + } + return serializedStack; +} + +template +void EventStack::deserializeStack(std::string_view serializedData) { + std::unique_lock lock(mtx); + events.clear(); + size_t pos = 0; + size_t nextPos = 0; + while ((nextPos = serializedData.find(";", pos)) != + std::string_view::npos) { + T event = serializedData.substr(pos, nextPos - pos); + events.push_back(std::move(event)); + pos = nextPos + 1; + } + eventCount.store(events.size()); +} + +template +void EventStack::removeDuplicates() { + std::unique_lock lock(mtx); + std::sort(events.begin(), events.end()); + events.erase(std::unique(events.begin(), events.end()), events.end()); + eventCount.store(events.size()); +} + +template +void EventStack::sortEvents( + std::function compareFunc) { + std::unique_lock lock(mtx); + std::sort(events.begin(), events.end(), compareFunc); +} + +template +void EventStack::reverseEvents() { + std::unique_lock lock(mtx); + std::reverse(events.begin(), events.end()); +} + +template +size_t EventStack::countEvents( + std::function predicate) const { + std::shared_lock lock(mtx); + return std::count_if(events.begin(), events.end(), predicate); +} + +template +std::optional EventStack::findEvent( + std::function predicate) const { + std::shared_lock lock(mtx); + auto it = std::find_if(events.begin(), events.end(), predicate); + if (it != events.end()) { + return *it; + } + return std::nullopt; +} + +template +bool EventStack::anyEvent(std::function predicate) const { + std::shared_lock lock(mtx); + return std::any_of(events.begin(), events.end(), predicate); +} + +template +bool EventStack::allEvents(std::function predicate) const { + std::shared_lock lock(mtx); + return std::all_of(events.begin(), events.end(), predicate); +} + +#endif diff --git a/src/atom/server/eventstack_impl.hpp b/src/atom/server/eventstack_impl.hpp deleted file mode 100644 index 27f74907..00000000 --- a/src/atom/server/eventstack_impl.hpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * eventstack_impl.hpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2024-3-26 - -Description: A thread-safe stack data structure for managing events. - -**************************************************/ - -template -class EventStack::EventStackImpl { -public: - std::vector events; /**< Vector to store events. */ -}; - -template -EventStack::EventStack() : impl(std::make_unique()) {} - -template -EventStack::~EventStack() { -} - -template -void EventStack::pushEvent(const T& event) { - std::lock_guard lock(mtx); - impl->events.push_back(event); -} - -template -T EventStack::popEvent() { - std::lock_guard lock(mtx); - if (!impl->events.empty()) { - T event = impl->events.back(); - impl->events.pop_back(); - return event; - } else { - throw std::out_of_range("Event stack is empty."); - } -} - -template -void EventStack::printEvents() const { - std::lock_guard lock(mtx); - std::cout << "Events in stack:" << std::endl; - for (const T& event : impl->events) { - std::cout << event << std::endl; - } -} - -template -bool EventStack::isEmpty() const { - std::lock_guard lock(mtx); - return impl->events.empty(); -} - -template -size_t EventStack::size() const { - std::lock_guard lock(mtx); - return impl->events.size(); -} - -template -void EventStack::clearEvents() { - std::lock_guard lock(mtx); - impl->events.clear(); -} - -template -T EventStack::peekTopEvent() const { - std::lock_guard lock(mtx); - if (!impl->events.empty()) { - return impl->events.back(); - } else { - throw std::out_of_range("Event stack is empty."); - } -} - -template -EventStack EventStack::copyStack() const { - std::lock_guard lock(mtx); - EventStack newStack; - newStack.impl->events = impl->events; - return newStack; -} - -template -void EventStack::filterEvents(bool (*filterFunc)(const T&)) { - std::lock_guard lock(mtx); - impl->events.erase(std::remove_if(impl->events.begin(), impl->events.end(), [&](const T& event) { - return !filterFunc(event); - }), impl->events.end()); -} - -template -std::string EventStack::serializeStack() const { - std::lock_guard lock(mtx); - std::string serializedStack; - for (const T& event : impl->events) { - serializedStack += event + ";"; - } - return serializedStack; -} - -template -void EventStack::deserializeStack(const std::string& serializedData) { - std::lock_guard lock(mtx); - impl->events.clear(); - size_t pos = 0; - size_t nextPos = 0; - while ((nextPos = serializedData.find(";", pos)) != std::string::npos) { - T event = serializedData.substr(pos, nextPos - pos); - impl->events.push_back(event); - pos = nextPos + 1; - } -} diff --git a/src/atom/server/global_ptr.cpp b/src/atom/server/global_ptr.cpp index e6c610c4..66986469 100644 --- a/src/atom/server/global_ptr.cpp +++ b/src/atom/server/global_ptr.cpp @@ -23,17 +23,40 @@ GlobalSharedPtrManager &GlobalSharedPtrManager::getInstance() { } void GlobalSharedPtrManager::removeSharedPtr(const std::string &key) { - std::unique_lock lock(mtx); + std::unique_lock lock(mtx); sharedPtrMap.erase(key); } +void GlobalSharedPtrManager::removeExpiredWeakPtrs() { + std::unique_lock lock(mtx); + auto it = sharedPtrMap.begin(); + while (it != sharedPtrMap.end()) { + try { + if (std::any_cast>(it->second).expired()) { + it = sharedPtrMap.erase(it); + } else { + ++it; + } + } catch (const std::bad_any_cast &) { + ++it; + } + } +} + +void GlobalSharedPtrManager::clearAll() { + std::unique_lock lock(mtx); + sharedPtrMap.clear(); +} + +size_t GlobalSharedPtrManager::size() const { + std::shared_lock lock(mtx); + return sharedPtrMap.size(); +} + void GlobalSharedPtrManager::printSharedPtrMap() const { - std::shared_lock lock(mtx); - std::cout << "Shared pointer map:" << std::endl; - for (const auto &it : sharedPtrMap) { - std::ostringstream oss; - oss << it.second.type().name(); - std::cout << "- Key: " << it.first << ", Type: " << oss.str() - << std::endl; + std::shared_lock lock(mtx); + std::cout << "GlobalSharedPtrManager:\n"; + for (const auto &pair : sharedPtrMap) { + std::cout << " " << pair.first << "\n"; } } \ No newline at end of file diff --git a/src/atom/server/global_ptr.hpp b/src/atom/server/global_ptr.hpp index d95fc6fa..15dad4e6 100644 --- a/src/atom/server/global_ptr.hpp +++ b/src/atom/server/global_ptr.hpp @@ -19,8 +19,10 @@ Description: Global shared pointer manager #include #include #include +#include #include #include +#include #if ENABLE_FASTHASH #include "emhash/hash_table8.hpp" #else @@ -30,9 +32,11 @@ Description: Global shared pointer manager #include "atom/experiment/noncopyable.hpp" #define GetPtr GlobalSharedPtrManager::getInstance().getSharedPtr -#define GetWeakPtr GlobalSharedPtrManager::getInstance().getWeakPtr +#define GetWeakPtr GlobalSharedPtrManager::getInstance().getWeakPtrFromSharedPtr #define AddPtr GlobalSharedPtrManager::getInstance().addSharedPtr #define RemovePtr GlobalSharedPtrManager::getInstance().removeSharedPtr +#define GetPtrOrCreate \ + GlobalSharedPtrManager::getInstance().getOrCreateSharedPtr /** * @brief The GlobalSharedPtrManager class manages a collection of shared @@ -55,10 +59,26 @@ class GlobalSharedPtrManager : public NonCopyable { * * @tparam T the type of the shared pointer. * @param key the key associated with the shared pointer. - * @return the shared pointer if found, nullptr otherwise. + * @return the shared pointer if found, std::nullopt otherwise. */ template - std::shared_ptr getSharedPtr(const std::string &key); + [[nodiscard]] std::optional> getSharedPtr( + const std::string &key); + + /** + * @brief getOrCreateSharedPtr retrieves a shared pointer from the shared + * pointer map with the specified key. If the shared pointer does not exist, + * it creates a new one using the provided creation function. + * + * @tparam T the type of the shared pointer. + * @param key the key associated with the shared pointer. + * @param creator a function that creates a new shared pointer if it doesn't + * exist. + * @return the shared pointer. + */ + template + std::shared_ptr getOrCreateSharedPtr(const std::string &key, + CreatorFunc creator); /** * @brief getWeakPtr retrieves a weak pointer from the shared pointer map @@ -66,11 +86,11 @@ class GlobalSharedPtrManager : public NonCopyable { * * @tparam T the type of the weak pointer. * @param key the key associated with the weak pointer. - * @return the weak pointer if found, nullptr otherwise. + * @return the weak pointer if found, an empty weak pointer otherwise. * @note The weak pointer is not guaranteed to be valid after the shared */ template - std::weak_ptr getWeakPtr(const std::string &key); + [[nodiscard]] std::weak_ptr getWeakPtr(const std::string &key); /** * @brief addSharedPtr adds a shared pointer to the shared pointer map with @@ -109,17 +129,30 @@ class GlobalSharedPtrManager : public NonCopyable { * @tparam T the type of the shared pointer. * @param key the key associated with the weak pointer. * @return the shared pointer if the weak pointer is valid and the shared - * object still exists, nullptr otherwise. + * object still exists, an empty shared pointer otherwise. + */ + template + [[nodiscard]] std::shared_ptr getSharedPtrFromWeakPtr( + const std::string &key); + + /** + * @brief getWeakPtrFromSharedPtr retrieves a weak pointer from a shared + * pointer in the shared pointer map with the specified key. + * + * @tparam T the type of the shared pointer. + * @param key the key associated with the shared pointer. + * @return the weak pointer if the shared pointer is valid, an empty weak + * pointer otherwise. */ template - std::shared_ptr getSharedPtrFromWeakPtr(const std::string &key); + [[nodiscard]] std::weak_ptr getWeakPtrFromSharedPtr( + const std::string &key); /** * @brief removeExpiredWeakPtrs removes all expired weak pointers from the * shared pointer map. Expired weak pointers are weak pointers whose shared * objects have been deleted. */ - template void removeExpiredWeakPtrs(); /** @@ -137,7 +170,7 @@ class GlobalSharedPtrManager : public NonCopyable { /** * @brief deleteObject deletes the shared object associated with the - * specified key. + * specified key using the custom deleter if available. * * @tparam T the type of the shared object. * @param key the key associated with the shared object to delete. @@ -146,6 +179,19 @@ class GlobalSharedPtrManager : public NonCopyable { template void deleteObject(const std::string &key, T *ptr); + /** + * @brief clearAll removes all shared pointers and weak pointers from the + * shared pointer map. + */ + void clearAll(); + + /** + * @brief size returns the number of elements in the shared pointer map. + * + * @return the number of elements in the shared pointer map. + */ + size_t size() const; + /** * @brief printSharedPtrMap prints the contents of the shared pointer map. */ @@ -164,30 +210,50 @@ class GlobalSharedPtrManager : public NonCopyable { }; template -std::shared_ptr GlobalSharedPtrManager::getSharedPtr( +std::optional> GlobalSharedPtrManager::getSharedPtr( const std::string &key) { - std::shared_lock lock(mtx); + std::shared_lock lock(mtx); auto it = sharedPtrMap.find(key); if (it != sharedPtrMap.end()) { try { return std::any_cast>(it->second); } catch (const std::bad_any_cast &) { - return nullptr; // 类型转换失败,返回空指针 + return std::nullopt; } } + return std::nullopt; +} - return nullptr; +template +std::shared_ptr GlobalSharedPtrManager::getOrCreateSharedPtr( + const std::string &key, CreatorFunc creator) { + std::unique_lock lock(mtx); + auto it = sharedPtrMap.find(key); + if (it != sharedPtrMap.end()) { + try { + return std::any_cast>(it->second); + } catch (const std::bad_any_cast &) { + // Key exists but the stored type does not match, replace it + auto ptr = creator(); + sharedPtrMap[key] = ptr; + return ptr; + } + } else { + auto ptr = creator(); + sharedPtrMap[key] = ptr; + return ptr; + } } template std::weak_ptr GlobalSharedPtrManager::getWeakPtr(const std::string &key) { - std::shared_lock lock(mtx); + std::shared_lock lock(mtx); auto it = sharedPtrMap.find(key); if (it != sharedPtrMap.end()) { try { - return std::weak_ptr( - std::any_cast>(it->second)); + return std::any_cast>(it->second); } catch (const std::bad_any_cast &) { + return std::weak_ptr(); } } return std::weak_ptr(); @@ -196,73 +262,76 @@ std::weak_ptr GlobalSharedPtrManager::getWeakPtr(const std::string &key) { template void GlobalSharedPtrManager::addSharedPtr(const std::string &key, std::shared_ptr sharedPtr) { - std::unique_lock lock(mtx); - sharedPtrMap[key] = sharedPtr; + std::unique_lock lock(mtx); + sharedPtrMap[key] = std::move(sharedPtr); } template void GlobalSharedPtrManager::addWeakPtr(const std::string &key, const std::weak_ptr &weakPtr) { - std::unique_lock lock(mtx); + std::unique_lock lock(mtx); sharedPtrMap[key] = weakPtr; } template std::shared_ptr GlobalSharedPtrManager::getSharedPtrFromWeakPtr( const std::string &key) { - std::shared_lock lock(mtx); + std::shared_lock lock(mtx); auto it = sharedPtrMap.find(key); if (it != sharedPtrMap.end()) { try { return std::any_cast>(it->second).lock(); } catch (const std::bad_any_cast &) { - return nullptr; // 类型转换失败,返回空指针 + return std::shared_ptr(); } } - - return nullptr; + return std::shared_ptr(); } template -void GlobalSharedPtrManager::removeExpiredWeakPtrs() { - std::vector keysToRemove; - { - std::shared_lock lock(mtx); - for (auto &it : sharedPtrMap) { - if (auto weakPtr = std::any_cast>(it.second); - weakPtr.expired()) { - keysToRemove.push_back(it.first); - } - } - } - - if (!keysToRemove.empty()) { - std::unique_lock lock(mtx); - for (auto &key : keysToRemove) { - sharedPtrMap.erase(key); +std::weak_ptr GlobalSharedPtrManager::getWeakPtrFromSharedPtr( + const std::string &key) { + std::shared_lock lock(mtx); + auto it = sharedPtrMap.find(key); + if (it != sharedPtrMap.end()) { + try { + return std::weak_ptr(std::any_cast>(it->second)); + } catch (const std::bad_any_cast &) { + return std::weak_ptr(); } } + return std::weak_ptr(); } template void GlobalSharedPtrManager::addDeleter( const std::string &key, const std::function &deleter) { - std::unique_lock lock(mtx); - sharedPtrMap[key] = deleter; + std::unique_lock lock(mtx); + auto it = sharedPtrMap.find(key); + if (it != sharedPtrMap.end()) { + try { + auto ptr = std::any_cast>(it->second); + ptr.reset(ptr.get(), deleter); + sharedPtrMap[key] = ptr; + } catch (const std::bad_any_cast &) { + // Ignore if the stored type does not match + } + } } template void GlobalSharedPtrManager::deleteObject(const std::string &key, T *ptr) { - std::unique_lock lock(mtx); + std::unique_lock lock(mtx); auto it = sharedPtrMap.find(key); if (it != sharedPtrMap.end()) { try { - std::function deleter = - std::any_cast>(it->second); + auto deleter = std::any_cast>(it->second); deleter(ptr); } catch (const std::bad_any_cast &) { + delete ptr; // Use default delete if no custom deleter is found } + sharedPtrMap.erase(it); } } -#endif +#endif // ATOM_SERVER_GLOBAL_PTR_HPP diff --git a/src/atom/server/message_bus.hpp b/src/atom/server/message_bus.hpp index bf5547f0..4b985221 100644 --- a/src/atom/server/message_bus.hpp +++ b/src/atom/server/message_bus.hpp @@ -24,6 +24,7 @@ Description: Main Message Bus #include #include #include +#include #include #include #include @@ -46,22 +47,8 @@ class MessageBus { maxMessageBusSize_.store(max_queue_size); } - ~MessageBus() { - if (!subscribers_.empty()) { - subscribers_.clear(); - } - if (!processingThreads_.empty()) { - for (auto &thread : processingThreads_) { - if (thread.second.joinable()) { -#if __cplusplus >= 202002L - thread.second.request_stop(); -#endif - thread.second.join(); - } - } - processingThreads_.clear(); - } - } + ~MessageBus() { StopAllProcessingThreads(); } + // ------------------------------------------------------------------- // Common methods // ------------------------------------------------------------------- @@ -86,12 +73,11 @@ class MessageBus { std::string fullTopic = namespace_.empty() ? topic : (namespace_ + "::" + topic); - subscribersLock_.lock(); - subscribers_[fullTopic].push_back({priority, std::any(callback)}); + std::scoped_lock lock(subscribersLock_); + subscribers_[fullTopic].emplace_back(priority, std::move(callback)); std::sort( subscribers_[fullTopic].begin(), subscribers_[fullTopic].end(), [](const auto &a, const auto &b) { return a.first > b.first; }); - subscribersLock_.unlock(); DLOG_F(INFO, "Subscribed to topic: {}", fullTopic); } @@ -111,7 +97,7 @@ class MessageBus { std::string fullTopic = namespace_.empty() ? topic : (namespace_ + "::" + topic); - subscribersLock_.lock(); + std::scoped_lock lock(subscribersLock_); auto it = subscribers_.find(fullTopic); if (it != subscribers_.end()) { auto &topicSubscribers = it->second; @@ -125,7 +111,6 @@ class MessageBus { DLOG_F(INFO, "Unsubscribed from topic: {}", fullTopic); } - subscribersLock_.unlock(); } template @@ -138,9 +123,8 @@ class MessageBus { void UnsubscribeAll(const std::string &namespace_ = "") { std::string fullTopic = namespace_.empty() ? "*" : (namespace_ + "::*"); - subscribersLock_.lock(); + std::scoped_lock lock(subscribersLock_); subscribers_.erase(fullTopic); - subscribersLock_.unlock(); DLOG_F(INFO, "Unsubscribed from all topics"); } @@ -151,9 +135,15 @@ class MessageBus { std::string fullTopic = namespace_.empty() ? topic : (namespace_ + "::" + topic); - messageQueueLock_.lock(); - messageQueue_.push({fullTopic, std::any(message)}); - messageQueueLock_.unlock(); + { + std::scoped_lock lock(messageQueueLock_); + if (messageQueue_.size() >= maxMessageBusSize_) { + LOG_F(WARNING, + "Message queue is full. Discarding oldest message."); + messageQueue_.pop(); + } + messageQueue_.emplace(fullTopic, message); + } messageAvailableFlag_.notify_one(); DLOG_F(INFO, "Published message to topic: {}", fullTopic); @@ -167,28 +157,31 @@ class MessageBus { std::string fullTopic = namespace_.empty() ? topic : (namespace_ + "::" + topic); - if (messageQueueLock_.try_lock_until(std::chrono::steady_clock::now() + - timeout)) { - messageQueue_.push({fullTopic, std::any(message)}); - messageQueueLock_.unlock(); - messageAvailableFlag_.notify_one(); - - DLOG_F(INFO, "Published message to topic: {}", fullTopic); - return true; - } else { - LOG_F(WARNING, - "Failed to publish message to topic: {} due to timeout", - fullTopic); - return false; + { + std::unique_lock lock(messageQueueLock_); + if (messageQueueCondition_.wait_for(lock, timeout, [this] { + return messageQueue_.size() < maxMessageBusSize_; + })) { + messageQueue_.emplace(fullTopic, message); + messageAvailableFlag_.notify_one(); + DLOG_F(INFO, "Published message to topic: {}", fullTopic); + return true; + } else { + LOG_F(WARNING, + "Failed to publish message to topic: {} due to timeout", + fullTopic); + return false; + } } } template bool TryReceive(T &outMessage, std::chrono::milliseconds timeout = std::chrono::milliseconds(100)) { - std::unique_lock lock(waitingMutex_); + std::unique_lock lock(waitingMutex_); if (messageAvailableFlag_.wait_for( lock, timeout, [this] { return !messageQueue_.empty(); })) { + std::scoped_lock messageLock(messageQueueLock_); auto message = std::move(messageQueue_.front()); messageQueue_.pop(); outMessage = std::any_cast(message.second); @@ -201,64 +194,53 @@ class MessageBus { template void GlobalSubscribe(std::function callback) { - globalSubscribersLock_.lock(); - globalSubscribers_.push_back({std::any(callback)}); - globalSubscribersLock_.unlock(); + std::scoped_lock lock(globalSubscribersLock_); + globalSubscribers_.emplace_back(std::move(callback)); } template void GlobalUnsubscribe(std::function callback) { - globalSubscribersLock_.lock(); + std::scoped_lock lock(globalSubscribersLock_); globalSubscribers_.erase( std::remove_if(globalSubscribers_.begin(), globalSubscribers_.end(), [&](const auto &subscriber) { return subscriber.type() == typeid(callback); }), globalSubscribers_.end()); - globalSubscribersLock_.unlock(); } template void StartProcessingThread() { std::type_index typeIndex = typeid(T); -#if __cplusplus >= 202002L - processingThreads_.emplace( - typeIndex, - std::jthread( - [&]() -#else - processingThreads_.emplace( - typeIndex, - std::thread( - [&]() -#endif - { - while (isRunning_.load()) { + if (processingThreads_.find(typeIndex) == processingThreads_.end()) { + processingThreads_.emplace( + typeIndex, + std::jthread([this, typeIndex](std::stop_token stopToken) { + while (!stopToken.stop_requested()) { std::pair message; bool hasMessage = false; - while (isRunning_.load()) { - messageQueueLock_.lock(); + { + std::unique_lock lock(waitingMutex_); + messageAvailableFlag_.wait(lock, [this] { + return !messageQueue_.empty(); + }); + if (stopToken.stop_requested()) { + break; + } + std::scoped_lock messageLock(messageQueueLock_); if (!messageQueue_.empty()) { message = std::move(messageQueue_.front()); messageQueue_.pop(); hasMessage = true; } - messageQueueLock_.unlock(); - - if (hasMessage) { - break; - } - - std::unique_lock lock(waitingMutex_); - messageAvailableFlag_.wait(lock); } if (hasMessage) { const std::string &topic = message.first; const std::any &data = message.second; - subscribersLock_.lock(); + std::shared_lock subscribersLock(subscribersLock_); auto it = subscribers_.find(topic); if (it != subscribers_.end()) { try { @@ -281,9 +263,8 @@ class MessageBus { "message processing"); } } - subscribersLock_.unlock(); - globalSubscribersLock_.lock(); + std::shared_lock globalLock(globalSubscribersLock_); try { for (const auto &subscriber : globalSubscribers_) { @@ -304,13 +285,13 @@ class MessageBus { "Unknown error occurred during global " "message processing"); } - globalSubscribersLock_.unlock(); DLOG_F(INFO, "Processed message on topic: {}", topic); } } })); + } } template @@ -318,9 +299,7 @@ class MessageBus { std::type_index typeIndex = typeid(T); auto it = processingThreads_.find(typeIndex); if (it != processingThreads_.end()) { -#if __cplusplus >= 202002L it->second.request_stop(); -#endif it->second.join(); processingThreads_.erase(it); DLOG_F(INFO, "Processing thread for type {} stopped", @@ -329,12 +308,10 @@ class MessageBus { } void StopAllProcessingThreads() { - isRunning_.store(false); - messageAvailableFlag_.notify_one(); for (auto &thread : processingThreads_) { -#if __cplusplus >= 202002L thread.second.request_stop(); -#endif + } + for (auto &thread : processingThreads_) { thread.second.join(); } processingThreads_.clear(); @@ -342,32 +319,32 @@ class MessageBus { } private: - std::unordered_map>> - subscribers_; - std::mutex subscribersLock_; - std::queue> messageQueue_; - std::timed_mutex messageQueueLock_; + // using SubscriberCallback = std::function; + using SubscriberCallback = std::any; + using SubscriberList = std::vector>; + using SubscriberMap = std::unordered_map; + + SubscriberMap subscribers_; + std::shared_mutex subscribersLock_; + + using MessageQueue = std::queue>; + MessageQueue messageQueue_; + std::mutex messageQueueLock_; + std::condition_variable messageQueueCondition_; std::condition_variable messageAvailableFlag_; std::mutex waitingMutex_; -#if __cplusplus >= 202002L -#if ENABLE_FASTHASH - emhash8::HashMap processingThreads_; -#else - std::unordered_map processingThreads_; -#endif -#else -#if ENABLE_FASTHASH - emhash8::HashMap processingThreads_; -#else - std::unordered_map processingThreads_; -#endif -#endif - std::atomic isRunning_{true}; + + using ProcessingThread = std::jthread; + using ProcessingThreadMap = + std::unordered_map; + ProcessingThreadMap processingThreads_; + std::atomic_int maxMessageBusSize_{1000}; - std::vector globalSubscribers_; - std::mutex globalSubscribersLock_; + using GlobalSubscriberList = std::vector; + GlobalSubscriberList globalSubscribers_; + std::shared_mutex globalSubscribersLock_; }; } // namespace Atom::Server -#endif +#endif \ No newline at end of file diff --git a/src/atom/server/message_queue.hpp b/src/atom/server/message_queue.hpp index 58069ece..dfcbd68d 100644 --- a/src/atom/server/message_queue.hpp +++ b/src/atom/server/message_queue.hpp @@ -24,10 +24,15 @@ Description: A simple message queue (just learn something) #include #include #include -#include #include -namespace Atom::Async { +#if ENABLE_FASTHASH +#include "emhash/hash_table8.hpp" +#else +#include +#endif + +namespace Atom::Server { /** * @brief A message queue that allows subscribers to receive messages of type T. * @@ -48,8 +53,11 @@ class MessageQueue { * @param callback The callback function to be called when a new message is * received. * @param subscriberName The name of the subscriber to be added. + * @param priority The priority of the subscriber. Higher priority + * subscribers will receive messages before lower priority subscribers. */ - void subscribe(CallbackType callback, const std::string &subscriberName); + void subscribe(CallbackType callback, const std::string &subscriberName, + int priority = 0); /** * @brief Unsubscribe a callback function from receiving messages. @@ -67,8 +75,12 @@ class MessageQueue { /** * @brief Start the processing thread(s) to receive and handle messages. + * + * @param numThreads The number of processing threads to spawn. If not + * specified, the number of hardware threads will be used. */ - void startProcessingThread(); + void startProcessingThread( + int numThreads = std::thread::hardware_concurrency()); /** * @brief Stop the processing thread(s) from receiving and handling @@ -76,6 +88,20 @@ class MessageQueue { */ void stopProcessingThread(); + /** + * @brief Get the number of messages currently in the queue. + * + * @return The number of messages in the queue. + */ + size_t getMessageCount() const; + + /** + * @brief Get the number of subscribers currently registered. + * + * @return The number of subscribers. + */ + size_t getSubscriberCount() const; + private: /** * @brief The Subscriber struct contains information about a subscribed @@ -85,6 +111,7 @@ class MessageQueue { std::string name; /**< The name of the subscriber. */ CallbackType callback; /**< The callback function to be called when a new message is received. */ + int priority; /**< The priority of the subscriber. */ /** * @brief Construct a new Subscriber object. @@ -92,19 +119,30 @@ class MessageQueue { * @param name The name of the subscriber. * @param callback The callback function to be called when a new message * is received. + * @param priority The priority of the subscriber. */ - Subscriber(const std::string &name, const CallbackType &callback) - : name(name), callback(callback) {} + Subscriber(const std::string &name, const CallbackType &callback, + int priority) + : name(name), callback(callback), priority(priority) {} + + /** + * @brief Compare two Subscriber objects based on their priority + * * + * @param other The other Subscriber object to compare with. + * @return True if this Subscriber has a higher priority than the other + * Subscriber, false otherwise. + */ + bool operator<(const Subscriber &other) const { + return priority > other.priority; + } }; std::deque m_messages; /**< The queue containing all published messages. */ std::vector m_subscribers; /**< The vector containing all subscribed callback functions. */ - std::mutex - m_mutex; /**< The mutex used to protect access to the message queue. */ - std::mutex m_subscriberMutex; /**< The mutex used to protect access to the - subscriber vector. */ + mutable std::mutex m_mutex; /**< The mutex used to protect access to the + message queue and subscriber vector. */ std::condition_variable m_condition; /**< The condition variable used to notify processing threads of new messages. */ @@ -113,21 +151,20 @@ class MessageQueue { should continue running. */ std::vector m_processingThreads; /**< The vector containing all processing threads. */ - int m_numThreads = - std::thread::hardware_concurrency(); /**< The number of processing - threads to spawn. */ }; template void MessageQueue::subscribe(CallbackType callback, - const std::string &subscriberName) { - std::lock_guard lock(m_subscriberMutex); - m_subscribers.emplace_back(subscriberName, callback); + const std::string &subscriberName, + int priority) { + std::lock_guard lock(m_mutex); + m_subscribers.emplace_back(subscriberName, callback, priority); + std::sort(m_subscribers.begin(), m_subscribers.end()); } template void MessageQueue::unsubscribe(CallbackType callback) { - std::lock_guard lock(m_subscriberMutex); + std::lock_guard lock(m_mutex); auto it = std::remove_if(m_subscribers.begin(), m_subscribers.end(), [&callback](const auto &subscriber) { return subscriber.callback.target_type() == @@ -139,21 +176,21 @@ void MessageQueue::unsubscribe(CallbackType callback) { template void MessageQueue::publish(const T &message) { { - std::lock_guard lock(m_mutex); + std::lock_guard lock(m_mutex); m_messages.emplace_back(message); } m_condition.notify_one(); } template -void MessageQueue::startProcessingThread() { - for (int i = 0; i < m_numThreads; ++i) { +void MessageQueue::startProcessingThread(int numThreads) { + for (int i = 0; i < numThreads; ++i) { m_processingThreads.emplace_back([this]() { while (m_isRunning.load()) { std::optional message; { - std::unique_lock lock(m_mutex); + std::unique_lock lock(m_mutex); m_condition.wait(lock, [this]() { return !m_messages.empty() || !m_isRunning.load(); }); @@ -171,7 +208,7 @@ void MessageQueue::startProcessingThread() { std::vector subscribersCopy; { - std::lock_guard lock(m_subscriberMutex); + std::lock_guard lock(m_mutex); subscribersCopy.reserve(m_subscribers.size()); for (const auto &subscriber : m_subscribers) { subscribersCopy.emplace_back(subscriber.callback); @@ -198,6 +235,19 @@ void MessageQueue::stopProcessingThread() { } m_processingThreads.clear(); } -} // namespace Atom::Async -#endif +template +size_t MessageQueue::getMessageCount() const { + std::lock_guard lock(m_mutex); + return m_messages.size(); +} + +template +size_t MessageQueue::getSubscriberCount() const { + std::lock_guard lock(m_mutex); + return m_subscribers.size(); +} + +} // namespace Atom::Server + +#endif \ No newline at end of file diff --git a/src/atom/system/CMakeLists.txt b/src/atom/system/CMakeLists.txt index 28d62761..b9bac6c4 100644 --- a/src/atom/system/CMakeLists.txt +++ b/src/atom/system/CMakeLists.txt @@ -22,6 +22,7 @@ set(${PROJECT_NAME}_SOURCES command.cpp crash_quotes.cpp crash.cpp + lregistry.cpp pidwatcher.cpp process.cpp register.cpp diff --git a/src/atom/system/_component.cpp b/src/atom/system/_component.cpp index e69de29b..4d30faa3 100644 --- a/src/atom/system/_component.cpp +++ b/src/atom/system/_component.cpp @@ -0,0 +1,242 @@ +/* + * _component.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-13 + +Description: Component of Atom-System + +**************************************************/ + +#include "_component.hpp" + +#include "atom/log/loguru.hpp" +#include "atom/type/json.hpp" +using json = nlohmann::json; + +#include "module/battery.hpp" +#include "module/cpu.hpp" +#include "module/disk.hpp" +#include "module/gpu.hpp" +#include "module/memory.hpp" +#include "module/os.hpp" +#include "module/wifi.hpp" + +#include "_constant.hpp" + +#define ATOM_SYSTEM_NO_ARGS \ + if (!m_params.is_null()) { \ + LOG_F(ERROR, "SystemComponent::{}: Invalid params", __func__); \ + return createErrorResponse(__func__, \ + {"error", Constants::INVALID_PARAMETER}, \ + "no argument should be found"); \ + } + +#define GET_CPU_INFO(name, func) \ + auto name = func(); \ + if (name < 0 || name > 100) { \ + LOG_F(ERROR, "SystemComponent::getCPUInfo: Failed to get {}", #name); \ + return createErrorResponse(__func__, \ + {"error", "failed to get " #name}, \ + Constants::SYSTEM_ERROR); \ + } + +#define GET_CPU_INFO_S(name, func) \ + auto name = func(); \ + if (name.empty()) { \ + LOG_F(ERROR, "SystemComponent::getCPUInfo: Failed to get {}", #name); \ + return createErrorResponse(__func__, \ + {"error", "failed to get " #name}, \ + Constants::SYSTEM_ERROR); \ + } + +#define GET_MEMORY_INFO(name, func) \ + auto name = func(); \ + if (name < 0) { \ + LOG_F(ERROR, "SystemComponent::getMemoryInfo: Failed to get {}", \ + #name); \ + return createErrorResponse(__func__, \ + {"error", "failed to get " #name}, \ + Constants::SYSTEM_ERROR); \ + } + +SystemComponent::SystemComponent(const std::string &name) + : SharedComponent(name) { + DLOG_F(INFO, "SystemComponent::SystemComponent"); + + registerFunc("getCPUInfo", &SystemComponent::getCPUInfo, this); + registerFunc("getMemoryInfo", &SystemComponent::getMemoryInfo, this); + registerFunc("getDiskInfo", &SystemComponent::getDiskInfo, this); + registerFunc("getNetworkInfo", &SystemComponent::getNetworkInfo, this); + registerFunc("getBatteryInfo", &SystemComponent::getBatteryInfo, this); + registerFunc("getGPUInfo", &SystemComponent::getGPUInfo, this); + registerFunc("getOSInfo", &SystemComponent::getOSInfo, this); + + registerVariable("cpuUsage", "", "The CPU usage"); + registerVariable("cpuTemperature", "", "The CPU temperature"); + registerVariable("cpuModel", "", "The CPU model"); + registerVariable("cpuFrequency", "", "The CPU frequency"); + registerVariable("numberOfPhysicalPackages", "", + "The number of physical " + "packages"); + registerVariable("numberOfPhysicalCPUs", "", "The number of physical CPUs"); + registerVariable("processorIdentifier", "", "The processor identifier"); + registerVariable("processorFrequency", "", "The processor frequency"); + + registerVariable("memoryUsage", "", "The memory usage"); + registerVariable("memoryTotal", "", "The total memory"); + registerVariable("memoryMax", "", "The maximum memory"); + registerVariable("memoryUsed", "", "The used memory"); + registerVariable("memoryAvailable", "", "The available memory"); + registerVariable("memorySwapTotal", "", "The total swap memory"); + + registerVariable("diskUsage", "", "The disk usage"); + registerVariable("diskTotal", "", "The total disk"); + registerVariable("diskAvailable", "", "The available disk"); + registerVariable("diskUsed", "", "The used disk"); + + registerVariable("networkWifi", "", "The wifi network"); + registerVariable("networkWired", "", "The wired network"); + registerVariable("networkHotspot", "", "The hotspot network"); +} + +SystemComponent::~SystemComponent() { + DLOG_F(INFO, "SystemComponent::~SystemComponent"); +} + +bool SystemComponent::initialize() { return true; } + +bool SystemComponent::destroy() { return true; } + +json SystemComponent::getCPUInfo(const json &m_params) { + DLOG_F(INFO, "SystemComponent::getCPUInfo"); + + ATOM_SYSTEM_NO_ARGS; + + GET_CPU_INFO(current_cpu_usage, Atom::System::getCurrentCpuUsage); + GET_CPU_INFO(current_cpu_temperature, + Atom::System::getCurrentCpuTemperature); + GET_CPU_INFO_S(cpu_model, Atom::System::getCPUModel); + GET_CPU_INFO_S(processor_identifier, Atom::System::getProcessorIdentifier); + GET_CPU_INFO(processor_frequency, Atom::System::getProcessorFrequency); + GET_CPU_INFO(number_of_physical_packages, + Atom::System::getNumberOfPhysicalPackages); + GET_CPU_INFO(number_of_physical_cpus, + Atom::System::getNumberOfPhysicalCPUs); + + return createSuccessResponse( + __func__, { + {"currentCpuUsage", current_cpu_usage}, + {"currentCpuTemperature", current_cpu_temperature}, + {"cpuModel", cpu_model}, + {"processorIdentifier", processor_identifier}, + {"processorFrequency", processor_frequency}, + {"numberOfPhysicalPackages", number_of_physical_packages}, + {"numberOfPhysicalCPUs", number_of_physical_cpus}, + }); +} + +json SystemComponent::getMemoryInfo(const json &m_params) { + DLOG_F(INFO, "SystemComponent::getMemoryInfo"); + ATOM_SYSTEM_NO_ARGS; + + GET_MEMORY_INFO(current_memory_usage, Atom::System::getMemoryUsage); + GET_MEMORY_INFO(total_memory_size, Atom::System::getTotalMemorySize); + GET_MEMORY_INFO(available_memory_size, + Atom::System::getAvailableMemorySize); + GET_MEMORY_INFO(virtual_memory_max, Atom::System::getVirtualMemoryMax); + GET_MEMORY_INFO(virtual_memory_used, Atom::System::getVirtualMemoryUsed); + GET_MEMORY_INFO(swap_memory_used, Atom::System::getSwapMemoryUsed); + GET_MEMORY_INFO(swap_memory_total, Atom::System::getSwapMemoryTotal); + + return createSuccessResponse( + __func__, { + {"currentMemoryUsage", current_memory_usage}, + {"totalMemorySize", total_memory_size}, + {"availableMemorySize", available_memory_size}, + {"virtualMemoryMax", virtual_memory_max}, + {"virtualMemoryUsed", virtual_memory_used}, + {"swapMemoryUsed", swap_memory_used}, + {"swapMemoryTotal", swap_memory_total}, + }); +} + +json SystemComponent::getDiskInfo(const json &m_params) { + DLOG_F(INFO, "SystemComponent::getDiskInfo"); + ATOM_SYSTEM_NO_ARGS; + auto disks_usage = Atom::System::getDiskUsage(); + if (disks_usage.size() == 0) { + LOG_F(ERROR, "SystemComponent::getDiskInfo: Failed to get disk info"); + return createErrorResponse(__func__, + {"error", "failed to get disk info"}, + Constants::SYSTEM_ERROR); + } + json res; + for (auto &disk : disks_usage) { + res.push_back({{"drive", disk.first}, {"usage", disk.second}}); + } + return createSuccessResponse(__func__, res); +} + +json SystemComponent::getBatteryInfo(const json &m_params) { + DLOG_F(INFO, "SystemComponent::getBatteryInfo"); + ATOM_SYSTEM_NO_ARGS; + auto battery_info = Atom::System::getBatteryInfo(); + if (battery_info.isBatteryPresent == false) { + LOG_F(ERROR, + "SystemComponent::getBatteryInfo: Failed to get battery info"); + return createErrorResponse(__func__, + {"error", "failed to get battery info"}, + Constants::SYSTEM_ERROR); + } + return createSuccessResponse( + __func__, {{"energyNow", battery_info.energyNow}, + {"energyDesign", battery_info.energyDesign}, + {"currentNow", battery_info.currentNow}, + {"batteryLifeTime", battery_info.batteryLifeTime}, + {"batteryFullLifeTime", battery_info.batteryFullLifeTime}, + {"batteryLifePercent", battery_info.batteryLifePercent}, + {"energyFull", battery_info.energyFull}, + {"voltageNow", battery_info.voltageNow}, + {"isBatteryPresent", battery_info.isBatteryPresent}, + {"isCharging", battery_info.isCharging}}); +} + +json SystemComponent::getNetworkInfo(const json &m_params) { + DLOG_F(INFO, "SystemComponent::getNetworkInfo"); + ATOM_SYSTEM_NO_ARGS; + auto wifi = Atom::System::getCurrentWifi(); + auto wired = Atom::System::getCurrentWiredNetwork(); + auto hotspot = Atom::System::isHotspotConnected(); + return createSuccessResponse( + __func__, {{"wifi", wifi}, {"wired", wired}, {"hotspot", hotspot}}); +} + +json SystemComponent::getGPUInfo(const json &m_params) { + DLOG_F(INFO, "SystemComponent::getGPUInfo"); + ATOM_SYSTEM_NO_ARGS; + auto gpu_info = Atom::System::getGPUInfo(); + if (gpu_info.empty()) { + LOG_F(ERROR, "SystemComponent::getGPUInfo: Failed to get GPU info"); + return createErrorResponse(__func__, + {"error", "failed to get GPU info"}, + Constants::SYSTEM_ERROR); + } + return createSuccessResponse(__func__, {"gpu1", gpu_info}); +} + +json SystemComponent::getOSInfo(const json &m_params) { + DLOG_F(INFO, "SystemComponent::getOSInfo"); + ATOM_SYSTEM_NO_ARGS; + auto os_info = Atom::System::getOperatingSystemInfo(); + return createSuccessResponse(__func__, + {{"osName", os_info.osName}, + {"osVersion", os_info.osVersion}, + {"kernelVersion", os_info.kernelVersion}, + {"architecture", os_info.architecture}, + {"compiler", os_info.compiler}}); +} \ No newline at end of file diff --git a/src/atom/system/_component.hpp b/src/atom/system/_component.hpp new file mode 100644 index 00000000..85471ef6 --- /dev/null +++ b/src/atom/system/_component.hpp @@ -0,0 +1,47 @@ +/* + * _component.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-13 + +Description: Component of Atom-System + +**************************************************/ + +#ifndef ATOM_SYSTEM_COMPONENT_HPP +#define ATOM_SYSTEM_COMPONENT_HPP + +#include "atom/components/templates/shared_component.hpp" + +class SystemComponent : public SharedComponent +{ +public: + explicit SystemComponent(const std::string &name); + ~SystemComponent(); + + // ------------------------------------------------------------------- + // Common methods + // ------------------------------------------------------------------- + + bool initialize() override; + bool destroy() override; + + // ------------------------------------------------------------------- + // System methods + // ------------------------------------------------------------------- + + json getCPUInfo(const json &m_params); + json getMemoryInfo(const json &m_params); + json getDiskInfo(const json &m_params); + json getNetworkInfo(const json &m_params); + json getGPUInfo(const json &m_params); + json getBatteryInfo(const json &m_params); + json getOSInfo(const json &m_params); + +}; + +#endif diff --git a/src/atom/system/_constant.hpp b/src/atom/system/_constant.hpp new file mode 100644 index 00000000..454a6140 --- /dev/null +++ b/src/atom/system/_constant.hpp @@ -0,0 +1,25 @@ +/* + * _constants.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-3-16 + +Description: Constants for Lithium + +**************************************************/ + +#ifndef ATOM_SYSTEM_CONSTANTS_HPP +#define ATOM_SYSTEM_CONSTANTS_HPP + +class Constants { +public: + static constexpr const char* SYSTEM_ERROR = "system error"; + static constexpr const char* SYSTEM_WARNING = "system warning"; + static constexpr const char* INVALID_PARAMETER = "invalid parameter"; +}; + +#endif // ATOM_SYSTEM_CONSTANTS_HPP diff --git a/src/atom/system/_script.cpp b/src/atom/system/_pybind.cpp similarity index 78% rename from src/atom/system/_script.cpp rename to src/atom/system/_pybind.cpp index 7dcd3a22..ed16e1e3 100644 --- a/src/atom/system/_script.cpp +++ b/src/atom/system/_pybind.cpp @@ -1,3 +1,17 @@ +/* + * _pybind.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-13 + +Description: Python Binding of Atom-System Module + +**************************************************/ + #include #include "module/battery.hpp" @@ -19,7 +33,6 @@ namespace py = pybind11; using namespace Atom::System; PYBIND11_MODULE(atom_system, m) { - pybind11::class_(m, "BatteryInfo") .def_readwrite("isBatteryPresent", &BatteryInfo::isBatteryPresent) .def_readwrite("isCharging", &BatteryInfo::isCharging) @@ -34,13 +47,19 @@ PYBIND11_MODULE(atom_system, m) { m.def("getBatteryInfo", &getBatteryInfo); - m.def("get_current_cpu_usage", &getCurrentCpuUsage, "Get the current CPU usage as a percentage."); - m.def("get_current_cpu_temperature", &getCurrentCpuTemperature, "Get the current CPU temperature in degrees Celsius."); + m.def("get_current_cpu_usage", &getCurrentCpuUsage, + "Get the current CPU usage as a percentage."); + m.def("get_current_cpu_temperature", &getCurrentCpuTemperature, + "Get the current CPU temperature in degrees Celsius."); m.def("get_cpu_model", &getCPUModel, "Get the model of the CPU."); - m.def("get_processor_identifier", &getProcessorIdentifier, "Get the identifier of the CPU processor."); - m.def("get_processor_frequency", &getProcessorFrequency, "Get the frequency of the CPU in GHz."); - m.def("get_number_of_physical_packages", &getNumberOfPhysicalPackages, "Get the number of physical CPU packages."); - m.def("get_number_of_physical_cpus", &getNumberOfPhysicalCPUs, "Get the number of physical CPUs."); + m.def("get_processor_identifier", &getProcessorIdentifier, + "Get the identifier of the CPU processor."); + m.def("get_processor_frequency", &getProcessorFrequency, + "Get the frequency of the CPU in GHz."); + m.def("get_number_of_physical_packages", &getNumberOfPhysicalPackages, + "Get the number of physical CPU packages."); + m.def("get_number_of_physical_cpus", &getNumberOfPhysicalCPUs, + "Get the number of physical CPUs."); m.def("getDiskUsage", &getDiskUsage); m.def("getDriveModel", &getDriveModel); @@ -64,15 +83,22 @@ PYBIND11_MODULE(atom_system, m) { m.def("getOperatingSystemInfo", &getOperatingSystemInfo); m.def("getCurrentWifi", &getCurrentWifi, "Get the current WiFi name"); - m.def("getCurrentWiredNetwork", &getCurrentWiredNetwork, "Get the current wired network name"); - m.def("isHotspotConnected", &isHotspotConnected, "Check if hotspot is connected"); + m.def("getCurrentWiredNetwork", &getCurrentWiredNetwork, + "Get the current wired network name"); + m.def("isHotspotConnected", &isHotspotConnected, + "Check if hotspot is connected"); - //m.def("executeCommand", &executeCommand, "Execute a command and return the output as a string"); + // m.def("executeCommand", &executeCommand, "Execute a command and return + // the output as a string"); m.def("executeCommands", &executeCommands, "Execute a list of commands"); - //m.def("executeCommand", &executeCommand, "Execute a command and return the process handle"); + // m.def("executeCommand", &executeCommand, "Execute a command and return + // the process handle"); m.def("killProcess", &killProcess, "Kill a process"); - m.def("executeCommandWithEnv", &executeCommandWithEnv, "Execute a command with environment variables and return the output as a string"); - m.def("executeCommandWithStatus", &executeCommandWithStatus, "Execute a command and return the output along with the exit status"); + m.def("executeCommandWithEnv", &executeCommandWithEnv, + "Execute a command with environment variables and return the output " + "as a string"); + m.def("executeCommandWithStatus", &executeCommandWithStatus, + "Execute a command and return the output along with the exit status"); py::class_(m, "Quote") .def(py::init()) @@ -82,9 +108,9 @@ PYBIND11_MODULE(atom_system, m) { py::class_(m, "QuoteManager") .def("addQuote", &QuoteManager::addQuote) .def("removeQuote", &QuoteManager::removeQuote) - #ifdef DEBUG +#ifdef DEBUG .def("displayQuotes", &QuoteManager::displayQuotes) - #endif +#endif .def("shuffleQuotes", &QuoteManager::shuffleQuotes) .def("clearQuotes", &QuoteManager::clearQuotes) .def("loadQuotesFromFile", &QuoteManager::loadQuotesFromFile) @@ -92,7 +118,7 @@ PYBIND11_MODULE(atom_system, m) { .def("searchQuotes", &QuoteManager::searchQuotes) .def("filterQuotesByAuthor", &QuoteManager::filterQuotesByAuthor) .def("getRandomQuote", &QuoteManager::getRandomQuote); - + py::class_(m, "Registry") .def(py::init<>()) .def("loadRegistryFromFile", &Registry::loadRegistryFromFile) diff --git a/src/atom/system/_script.hpp b/src/atom/system/_script.hpp new file mode 100644 index 00000000..3e1180b5 --- /dev/null +++ b/src/atom/system/_script.hpp @@ -0,0 +1,280 @@ +/* + * _script.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-23 + +Description: Carbon binding for Atom-System + +**************************************************/ + +#ifndef ATOM_SYSTEM_SCRIPT_HPP +#define ATOM_SYSTEM_SCRIPT_HPP + +#include "carbon/carbon.hpp" + +#include "module/battery.hpp" +#include "module/cpu.hpp" +#include "module/disk.hpp" +#include "module/gpu.hpp" +#include "module/memory.hpp" +#include "module/os.hpp" +#include "module/wifi.hpp" + +#include "command.hpp" +#include "crash.hpp" +#include "crash_quotes.hpp" +#include "lregistry.hpp" +#include "os.hpp" +#include "pidwatcher.hpp" +#include "process.hpp" +#include "register.hpp" +#include "storage.hpp" +#include "system.hpp" +#include "user.hpp" + +using namespace Atom::System; + +namespace Atom::_Script::System { +/** + * Adds the String Methods to the given Carbon m. + */ +Carbon::ModulePtr bootstrap( + Carbon::ModulePtr m = std::make_shared()) { + + m->add(user_type(), "BatteryInfo"); + //m->add(Carbon::constructor(), "BatteryInfo"); + m->add(Carbon::fun(&BatteryInfo::isBatteryPresent), "is_battery_present"); + m->add(Carbon::fun(&BatteryInfo::batteryLifeTime), "battery_life_time"); + m->add(Carbon::fun(&BatteryInfo::batteryLifePercent), + "battery_life_percent"); + m->add(Carbon::fun(&BatteryInfo::batteryFullLifeTime), + "battery_full_life_time"); + m->add(Carbon::fun(&BatteryInfo::energyNow), "energy_now"); + m->add(Carbon::fun(&BatteryInfo::energyFull), "energy_full"); + m->add(Carbon::fun(&BatteryInfo::energyDesign), "energy_design"); + m->add(Carbon::fun(&BatteryInfo::currentNow), "current_now"); + m->add(Carbon::fun(&BatteryInfo::voltageNow), "voltage_now"); + m->add(Carbon::fun(&BatteryInfo::isCharging), "is_charging"); + + m->add(Carbon::fun(&getBatteryInfo), "get_battery_info"); + + m->add(Carbon::fun(&getCurrentCpuUsage), "get_current_cpu_usage"); + m->add(Carbon::fun(&getCurrentCpuTemperature), + "get_current_cpu_temperature"); + m->add(Carbon::fun(&getCPUModel), "get_cpu_model"); + m->add(Carbon::fun(getProcessorIdentifier), "get_processor_identifier"); + m->add(Carbon::fun(&getProcessorFrequency), "get_processor_frequency"); + m->add(Carbon::fun(&getNumberOfPhysicalCPUs), + "get_number_of_physical_cpus"); + m->add(Carbon::fun(&getNumberOfPhysicalPackages), + "get_number_of_physical_packages"); + + m->add(Carbon::fun(&getDiskUsage), "get_disk_usage"); + m->add(Carbon::fun(&getDriveModel), "get_drive_model"); + m->add(Carbon::fun(&getAvailableDrives), "get_available_drives"); + m->add(Carbon::fun(&getStorageDeviceModels), "get_storage_device_models"); + m->add(Carbon::fun(&calculateDiskUsagePercentage), + "calculate_disk_usage_percentage"); + + m->add(Carbon::fun(&getGPUInfo), "get_gpu_info"); + + m->add(user_type(), "MemorySlot"); + //m->add(Carbon::constructor(), "MemorySlot"); + m->add( + Carbon::constructor(), + "size"); + m->add(Carbon::fun(&MemoryInfo::MemorySlot::capacity), "capacity"); + m->add(Carbon::fun(&MemoryInfo::MemorySlot::type), "type"); + m->add(Carbon::fun(&MemoryInfo::MemorySlot::clockSpeed), "speed"); + + m->add(user_type(), "MemoryInfo"); + //m->add(Carbon::constructor(), "MemoryInfo"); + m->add(Carbon::fun(&MemoryInfo::slots), "slots"); + m->add(Carbon::fun(&MemoryInfo::swapMemoryTotal), "swap_memory_total"); + m->add(Carbon::fun(&MemoryInfo::swapMemoryUsed), "swap_memory_used"); + m->add(Carbon::fun(&MemoryInfo::virtualMemoryMax), "virtual_memory_max"); + m->add(Carbon::fun(&MemoryInfo::virtualMemoryUsed), "virtual_memory_used"); + + m->add(Carbon::fun(&getMemoryUsage), "get_memory_usage"); + m->add(Carbon::fun(&getPhysicalMemoryInfo), "get_physical_memory_info"); + m->add(Carbon::fun(&getVirtualMemoryMax), "get_virtual_memory_max"); + m->add(Carbon::fun(&getVirtualMemoryUsed), "get_virtual_memory_used"); + m->add(Carbon::fun(&getSwapMemoryTotal), "get_swap_memory_total"); + m->add(Carbon::fun(&getSwapMemoryUsed), "get_swap_memory_used"); + m->add(Carbon::fun(&getTotalMemorySize), "get_total_memory_size"); + m->add(Carbon::fun(&getAvailableMemorySize), "get_available_memory_size"); + + m->add(user_type(), "OperatingSystemInfo"); + //m->add(Carbon::constructor(), "OperatingSystemInfo"); + m->add(Carbon::fun(&OperatingSystemInfo::osName), "os_name"); + m->add(Carbon::fun(&OperatingSystemInfo::osVersion), "os_version"); + m->add(Carbon::fun(&OperatingSystemInfo::kernelVersion), "kernel_version"); + m->add(Carbon::fun(&OperatingSystemInfo::architecture), "architecture"); + m->add(Carbon::fun(&OperatingSystemInfo::compiler), "compiler"); + m->add(Carbon::fun(&OperatingSystemInfo::toJson), "to_json"); + + m->add(Carbon::fun(&getOperatingSystemInfo), "get_operating_system_info"); + + m->add(Carbon::fun(&getCurrentWifi), "get_current_wifi"); + m->add(Carbon::fun(&getCurrentWiredNetwork), "get_current_wired_network"); + m->add(Carbon::fun(&isHotspotConnected), "is_hotspot_connected"); + m->add(Carbon::fun(&getHostIPs), "get_host_ips"); + + m->add(user_type(), "ProcessHandle"); + //m->add(Carbon::constructor(), "ProcessHandle"); + + m->add( + Carbon::fun)>( + &executeCommand), + "execute_command"); + m->add(Carbon::fun(&executeCommand), + "execute_command"); + m->add(Carbon::fun(&executeCommands), "execute_commands"); + m->add(Carbon::fun(&killProcess), "kill_process"); + m->add(Carbon::fun(&executeCommandWithEnv), "execute_command_with_env"); + m->add(Carbon::fun(&executeCommandWithStatus), + "execute_command_with_status"); + + m->add(user_type(), "Quote"); + m->add( + Carbon::constructor(), + "Quote"); + m->add(Carbon::fun(&Quote::getAuthor), "author"); + m->add(Carbon::fun(&Quote::getText), "text"); + + m->add(user_type(), "QuoteManager"); + //m->add(Carbon::constructor(), "QuoteManager"); +#if ENABLE_DEBUG + m->add(Carbon::fun(&QuoteManager::displayQuotes), "display_quotes"); +#endif + m->add(Carbon::fun(&QuoteManager::clearQuotes), "clear_quotes"); + m->add(Carbon::fun(&QuoteManager::saveQuotesToFile), "save_quotes_to_file"); + m->add(Carbon::fun(&QuoteManager::loadQuotesFromFile), + "load_quotes_from_file"); + m->add(Carbon::fun(&QuoteManager::shuffleQuotes), "shuffle_quotes"); + m->add(Carbon::fun(&QuoteManager::addQuote), "add_quote"); + m->add(Carbon::fun(&QuoteManager::removeQuote), "remove_quote"); + m->add(Carbon::fun(&QuoteManager::searchQuotes), "search_quotes"); + m->add(Carbon::fun(&QuoteManager::getRandomQuote), "get_random_quote"); + m->add(Carbon::fun(&QuoteManager::filterQuotesByAuthor), + "filter_quotes_by_author"); + + m->add(Carbon::fun(&saveCrashLog), "save_crash_log"); + + m->add(user_type(), "Registry"); + + //m->add(Carbon::constructor(), "Registry"); + + m->add(Carbon::fun(&Registry::loadRegistryFromFile), + "loadRegistryFromFile"); + m->add(Carbon::fun(&Registry::createKey), "createKey"); + m->add(Carbon::fun(&Registry::deleteKey), "deleteKey"); + m->add(Carbon::fun(&Registry::setValue), "setValue"); + m->add(Carbon::fun(&Registry::getValue), "getValue"); + m->add(Carbon::fun(&Registry::deleteValue), "deleteValue"); + m->add(Carbon::fun(&Registry::backupRegistryData), "backupRegistryData"); + m->add(Carbon::fun(&Registry::restoreRegistryData), "restoreRegistryData"); + m->add(Carbon::fun(&Registry::keyExists), "keyExists"); + m->add(Carbon::fun(&Registry::valueExists), "valueExists"); + m->add(Carbon::fun(&Registry::getValueNames), "getValueNames"); + + m->add(user_type(), "Utsname"); + //m->add(Carbon::constructor(), "Utsname"); + m->add(Carbon::fun(&Utsname::nodename), "nodename"); + m->add(Carbon::fun(&Utsname::sysname), "sysname"); + m->add(Carbon::fun(&Utsname::release), "release"); + m->add(Carbon::fun(&Utsname::version), "version"); + m->add(Carbon::fun(&Utsname::machine), "machine"); + + m->add(Carbon::fun(&walk), "walk"); + m->add(Carbon::fun(&jwalk), "jwalk"); + m->add(Carbon::fun(&fwalk), "fwalk"); + m->add(Carbon::fun(&Environ), "Environ"); + m->add(Carbon::fun(&ctermid), "ctermid"); + m->add(Carbon::fun(&getpriority), "getpriority"); + m->add(Carbon::fun(&getlogin), "getlogin"); + m->add(Carbon::fun(&uname), "uname"); + + m->add(user_type(), "PidWatcher"); + //m->add(Carbon::constructor(), "PidWatcher"); + m->add(Carbon::fun(&PidWatcher::SetExitCallback), "set_exit_callback"); + m->add(Carbon::fun(&PidWatcher::SetMonitorFunction), + "set_monitor_function"); + m->add(Carbon::fun(&PidWatcher::Start), "start"); + m->add(Carbon::fun(&PidWatcher::Stop), "stop"); + m->add(Carbon::fun(&PidWatcher::Switch), "switch"); + m->add(Carbon::fun(&PidWatcher::GetPidByName), "get_pid_by_name"); + + m->add(user_type(), "ProcessManager"); + //m->add(Carbon::constructor(), "ProcessManager"); + m->add(Carbon::fun(&ProcessManager::createProcess), "create_process"); + m->add(Carbon::fun(&ProcessManager::terminateProcess), "terminate_process"); + m->add(Carbon::fun(&ProcessManager::terminateProcessByName), + "terminate_process_by_name"); + m->add(Carbon::fun(&ProcessManager::hasProcess), "has_process"); + m->add(Carbon::fun(&ProcessManager::getProcessOutput), + "get_process_output"); + m->add(Carbon::fun(&ProcessManager::runScript), "run_script"); + m->add(Carbon::fun(&ProcessManager::waitForCompletion), + "wait_for_completion"); + m->add(Carbon::fun(&ProcessManager::getRunningProcesses), + "get_running_processes"); + +#ifdef _WIN32 + + m->add(Carbon::fun(&getRegistrySubKeys), "get_registry_sub_keys"); + m->add(Carbon::fun(&getRegistryValues), "get_registry_values"); + m->add(Carbon::fun(&findRegistryKey), "find_registry_key"); + m->add(Carbon::fun(&findRegistryValue), "find_registry_value"); + m->add(Carbon::fun(&recursivelyEnumerateRegistrySubKeys), + "recursively_enumerate_registry_sub_keys"); + m->add(Carbon::fun(&exportRegistry), "export_registry"); + m->add(Carbon::fun(&backupRegistry), "backup_registry"); + m->add(Carbon::fun(&modifyRegistryValue), "modify_registry_value"); + m->add(Carbon::fun(&deleteRegistrySubKey), "delete_registry_sub_key"); + m->add(Carbon::fun(&deleteRegistryValue), "delete_registry_value"); + +#endif + + m->add(user_type(), "StorageMonitor"); + //m->add(Carbon::constructor(), "StorageMonitor"); + m->add(Carbon::fun(&StorageMonitor::registerCallback), "register_callback"); + m->add(Carbon::fun(&StorageMonitor::startMonitoring), "start_monitoring"); + m->add(Carbon::fun(&StorageMonitor::stopMonitoring), "stop_monitoring"); + m->add(Carbon::fun(&StorageMonitor::triggerCallbacks), "trigger_callbacks"); + + m->add(Carbon::fun(&checkSoftwareInstalled), "check_software_installed"); + m->add(Carbon::fun(&checkDuplicateProcess), "check_duplicate_process"); + m->add(Carbon::fun(&getProcessInfoByName), "get_process_info_by_name"); + m->add(Carbon::fun(&getProcessInfoByID), "get_process_info_by_id"); + m->add(Carbon::fun(&GetProcessDetails), "get_process_details"); + m->add(Carbon::fun(&getProcessInfo), "get_process_info"); + m->add(Carbon::fun(&GetAllProcesses), "get_all_processes"); + m->add(Carbon::fun(&GetSelfProcessInfo), "get_self_process_info"); + m->add(Carbon::fun(&isProcessRunning), "is_process_running"); + m->add(Carbon::fun(&isRoot), "is_root"); + m->add(Carbon::fun(&reboot), "reboot"); + m->add(Carbon::fun(&shutdown), "shutdown"); + + m->add(Carbon::fun(&getUsername), "get_username"); + m->add(Carbon::fun(&getUserId), "get_user_id"); + m->add(Carbon::fun(&getGroupId), "get_group_id"); + m->add(Carbon::fun(&getHomeDirectory), "get_home_directory"); + m->add(Carbon::fun(&getLoginShell), "get_login_shell"); + m->add(Carbon::fun(&getUserGroups), "get_user_groups"); + m->add(Carbon::fun(&getHostname), "get_hostname"); + + return m; +} +} // namespace Atom::_Script::System + + +#endif + diff --git a/src/atom/system/command.cpp b/src/atom/system/command.cpp index 1f58b28f..e527149b 100644 --- a/src/atom/system/command.cpp +++ b/src/atom/system/command.cpp @@ -38,8 +38,8 @@ Description: Simple wrapper for executing commands. namespace Atom::System { std::string executeCommand( - const std::string &command, bool openTerminal = false, - std::function processLine = nullptr) { + const std::string &command, bool openTerminal, + std::function processLine) { if (command.empty()) { return ""; } diff --git a/src/atom/system/command.hpp b/src/atom/system/command.hpp index 2ce53eb7..4953e51c 100644 --- a/src/atom/system/command.hpp +++ b/src/atom/system/command.hpp @@ -48,8 +48,9 @@ struct ProcessHandle { * execute. */ [[nodiscard]] std::string executeCommand( - const std::string &command, bool openTerminal, - std::function processLine); + const std::string &command, bool openTerminal = false, + std::function processLine = + [](const std::string &) {}); /** * @brief Execute a list of commands. diff --git a/src/atom/system/lregistry.cpp b/src/atom/system/lregistry.cpp index 864b02c1..f8fc41d5 100644 --- a/src/atom/system/lregistry.cpp +++ b/src/atom/system/lregistry.cpp @@ -16,6 +16,7 @@ Description: A self-contained registry manager. #include #include +#include #include "atom/log/loguru.hpp" @@ -23,10 +24,12 @@ namespace Atom::System { class Registry::RegistryImpl { public: - std::map> registryData; + std::unordered_map> + registryData; void saveRegistryToFile(); - void notifyEvent(std::string eventType, std::string keyName); + void notifyEvent(const std::string& eventType, const std::string& keyName); }; Registry::Registry() : pImpl(std::make_unique()) {} @@ -36,25 +39,26 @@ void Registry::loadRegistryFromFile() { pImpl->saveRegistryToFile(); // Delegate to implementation } -void Registry::createKey(std::string keyName) { +void Registry::createKey(const std::string& keyName) { pImpl->saveRegistryToFile(); // Delegate to implementation pImpl->notifyEvent("KeyCreated", keyName); // Delegate to implementation } -void Registry::deleteKey(std::string keyName) { +void Registry::deleteKey(const std::string& keyName) { pImpl->saveRegistryToFile(); // Delegate to implementation pImpl->notifyEvent("KeyDeleted", keyName); // Delegate to implementation } -void Registry::setValue(std::string keyName, std::string valueName, - std::string data) { +void Registry::setValue(const std::string& keyName, + const std::string& valueName, const std::string& data) { pImpl->registryData[keyName][valueName] = data; // Access implementation directly pImpl->saveRegistryToFile(); // Delegate to implementation pImpl->notifyEvent("ValueSet", keyName); // Delegate to implementation } -std::string Registry::getValue(std::string keyName, std::string valueName) { +std::string Registry::getValue(const std::string& keyName, + const std::string& valueName) { if (pImpl->registryData.find(keyName) != pImpl->registryData.end() && pImpl->registryData[keyName].find(valueName) != pImpl->registryData[keyName].end()) { @@ -66,7 +70,8 @@ std::string Registry::getValue(std::string keyName, std::string valueName) { } } -void Registry::deleteValue(std::string keyName, std::string valueName) { +void Registry::deleteValue(const std::string& keyName, + const std::string& valueName) { if (pImpl->registryData.find(keyName) != pImpl->registryData.end()) { pImpl->registryData[keyName].erase( valueName); // Access implementation directly @@ -78,14 +83,14 @@ void Registry::deleteValue(std::string keyName, std::string valueName) { void Registry::backupRegistryData() { std::time_t currentTime = std::time(nullptr); - std::string backupFileName = + const std::string& backupFileName = "registry_backup_" + std::to_string(currentTime) + ".txt"; std::ofstream backupFile(backupFileName); if (backupFile.is_open()) { - for (const auto &key : pImpl->registryData) { + for (const auto& key : pImpl->registryData) { backupFile << key.first << std::endl; - for (const auto &value : key.second) { + for (const auto& value : key.second) { backupFile << value.first << "=" << value.second << std::endl; } backupFile << std::endl; @@ -98,7 +103,7 @@ void Registry::backupRegistryData() { } } -void Registry::restoreRegistryData(std::string backupFile) { +void Registry::restoreRegistryData(const std::string& backupFile) { std::ifstream backup(backupFile); if (backup.is_open()) { pImpl->registryData.clear(); // Clear existing data before restoring @@ -109,7 +114,7 @@ void Registry::restoreRegistryData(std::string backupFile) { if (!line.empty() && line.find('=') == std::string::npos) { currentKey = line; pImpl->registryData[currentKey] = - std::map(); + std::unordered_map(); } else if (line.find('=') != std::string::npos) { size_t splitPos = line.find('='); std::string valueName = line.substr(0, splitPos); @@ -126,20 +131,22 @@ void Registry::restoreRegistryData(std::string backupFile) { } } -bool Registry::keyExists(std::string keyName) const { +bool Registry::keyExists(const std::string& keyName) const { return pImpl->registryData.find(keyName) != pImpl->registryData.end(); } -bool Registry::valueExists(std::string keyName, std::string valueName) const { +bool Registry::valueExists(const std::string& keyName, + const std::string& valueName) const { auto keyIter = pImpl->registryData.find(keyName); return keyIter != pImpl->registryData.end() && keyIter->second.find(valueName) != keyIter->second.end(); } -std::vector Registry::getValueNames(std::string keyName) const { +std::vector Registry::getValueNames( + const std::string& keyName) const { std::vector valueNames; if (pImpl->registryData.find(keyName) != pImpl->registryData.end()) { - for (const auto &pair : pImpl->registryData.at(keyName)) { + for (const auto& pair : pImpl->registryData.at(keyName)) { valueNames.push_back(pair.first); } } @@ -148,9 +155,9 @@ std::vector Registry::getValueNames(std::string keyName) const { void Registry::RegistryImpl::saveRegistryToFile() { std::ofstream file("registry_data.txt"); if (file.is_open()) { - for (auto const &key : registryData) { + for (auto const& key : registryData) { file << key.first << std::endl; - for (auto const &value : key.second) { + for (auto const& value : key.second) { file << value.first << "=" << value.second << std::endl; } file << std::endl; @@ -161,8 +168,8 @@ void Registry::RegistryImpl::saveRegistryToFile() { } } -void Registry::RegistryImpl::notifyEvent(std::string eventType, - std::string keyName) { +void Registry::RegistryImpl::notifyEvent(const std::string& eventType, + const std::string& keyName) { DLOG_F(INFO, "Event: {} occurred for key: {}", eventType, keyName); } diff --git a/src/atom/system/lregistry.hpp b/src/atom/system/lregistry.hpp index c05b41df..e65ea895 100644 --- a/src/atom/system/lregistry.hpp +++ b/src/atom/system/lregistry.hpp @@ -36,14 +36,14 @@ class Registry { * * @param keyName The name of the key to create. */ - void createKey(std::string keyName); + void createKey(const std::string &keyName); /** * @brief Deletes a key from the registry. * * @param keyName The name of the key to delete. */ - void deleteKey(std::string keyName); + void deleteKey(const std::string &keyName); /** * @brief Sets a value for a key in the registry. @@ -52,7 +52,8 @@ class Registry { * @param valueName The name of the value to set. * @param data The data to set for the value. */ - void setValue(std::string keyName, std::string valueName, std::string data); + void setValue(const std::string &keyName, const std::string &valueName, + const std::string &data); /** * @brief Gets the value associated with a key and value name from the @@ -62,7 +63,8 @@ class Registry { * @param valueName The name of the value to retrieve. * @return The value associated with the key and value name. */ - std::string getValue(std::string keyName, std::string valueName); + std::string getValue(const std::string &keyName, + const std::string &valueName); /** * @brief Deletes a value from a key in the registry. @@ -70,7 +72,7 @@ class Registry { * @param keyName The name of the key. * @param valueName The name of the value to delete. */ - void deleteValue(std::string keyName, std::string valueName); + void deleteValue(const std::string &keyName, const std::string &valueName); /** * @brief Backs up the registry data. @@ -82,7 +84,7 @@ class Registry { * * @param backupFile The backup file to restore data from. */ - void restoreRegistryData(std::string backupFile); + void restoreRegistryData(const std::string &backupFile); /** * @brief Checks if a key exists in the registry. @@ -90,7 +92,7 @@ class Registry { * @param keyName The name of the key to check for existence. * @return true if the key exists, false otherwise. */ - bool keyExists(std::string keyName) const; + bool keyExists(const std::string &keyName) const; /** * @brief Checks if a value exists for a key in the registry. @@ -99,7 +101,8 @@ class Registry { * @param valueName The name of the value to check for existence. * @return true if the value exists, false otherwise. */ - bool valueExists(std::string keyName, std::string valueName) const; + bool valueExists(const std::string &keyName, + const std::string &valueName) const; /** * @brief Retrieves all value names for a given key from the registry. @@ -107,7 +110,7 @@ class Registry { * @param keyName The name of the key. * @return A vector of value names associated with the given key. */ - std::vector getValueNames(std::string keyName) const; + std::vector getValueNames(const std::string &keyName) const; private: class RegistryImpl; // Forward declaration of the implementation class diff --git a/src/atom/system/meson.build b/src/atom/system/meson.build new file mode 100644 index 00000000..346ff75a --- /dev/null +++ b/src/atom/system/meson.build @@ -0,0 +1,64 @@ +project('atom-system', 'cpp') + +# Sources +atom_system_sources = [ + 'command.cpp', + 'crash_quotes.cpp', + 'crash.cpp', + 'pidwatcher.cpp', + 'process.cpp', + 'register.cpp', + 'storage.cpp', + 'system.cpp', + 'user.cpp', + + 'module/cpu.cpp', + 'module/disk.cpp', + 'module/memory.cpp', + 'module/battery.cpp', + 'module/wifi.cpp', + 'module/os.cpp', +] + +# Headers +atom_system_headers = [ + 'command.hpp', + 'crash_quotes.hpp', + 'crash.hpp', + 'pidwatcher.hpp', + 'process.hpp', + 'register.hpp', + 'storage.hpp', + 'system.hpp', + 'user.hpp', + + 'module/cpu.hpp', + 'module/disk.hpp', + 'module/memory.hpp', + 'module/battery.hpp', +] + +# Build Object Library +atom_system_object = library( + 'atom-system-object', + atom_system_sources, + include_directories: include_directories('.') +) + +# Link libraries +atom_system_object_deps = [ + dependency('loguru') +] + +# Build Static Library +atom_system_static = library( + 'atom-system', + atom_system_sources, + include_directories: include_directories('.'), + dependencies: atom_system_object_deps, + version: '1.0', + soversion: '1' +) + +# Install +install_targets(atom_system_static) diff --git a/src/atom/system/module/gpu.cpp b/src/atom/system/module/gpu.cpp new file mode 100644 index 00000000..56130c26 --- /dev/null +++ b/src/atom/system/module/gpu.cpp @@ -0,0 +1,79 @@ +/* + * gpu.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-2-21 + +Description: System Information Module - GPU + +**************************************************/ + +#include "gpu.hpp" + +#ifdef _WIN32 +#include +#include +#include + +#elif defined(__linux__) +#include +#endif + +namespace Atom::System { +std::string getGPUInfo() { + std::string gpuInfo; + +#ifdef _WIN32 + if (IsWindows10OrGreater()) { + // Windows 10 或更高版本 + // 使用 Windows API 获取 GPU 信息 + HDEVINFO deviceInfoSet = + SetupDiGetClassDevsA(nullptr, "DISPLAY", nullptr, DIGCF_PRESENT); + if (deviceInfoSet == INVALID_HANDLE_VALUE) { + return "Failed to get GPU information."; + } + + SP_DEVINFO_DATA deviceInfoData; + deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + for (DWORD i = 0; + SetupDiEnumDeviceInfo(deviceInfoSet, i, &deviceInfoData); ++i) { + CHAR buffer[4096]; + DWORD dataSize = sizeof(buffer); + if (SetupDiGetDeviceRegistryPropertyA( + deviceInfoSet, &deviceInfoData, SPDRP_DEVICEDESC, nullptr, + (PBYTE)buffer, dataSize, nullptr)) { + gpuInfo += buffer; + gpuInfo += "\n"; + } + } + SetupDiDestroyDeviceInfoList(deviceInfoSet); + } else { + gpuInfo = + "Windows version is not supported for GPU information retrieval."; + } +#elif defined(__linux__) + // Linux 平台 + // 读取 GPU 相关文件获取信息 + std::ifstream file("/proc/driver/nvidia/gpus/0/information"); + if (file) { + std::string line; + while (std::getline(file, line)) { + gpuInfo += line; + gpuInfo += "\n"; + } + file.close(); + } else { + gpuInfo = "Failed to open GPU information file."; + } +#else + // 其他操作系统 + gpuInfo = "GPU information retrieval is not supported on this platform."; +#endif + + return gpuInfo; +} +} // namespace Atom::System diff --git a/src/atom/system/module/gpu.hpp b/src/atom/system/module/gpu.hpp new file mode 100644 index 00000000..dc83a0e0 --- /dev/null +++ b/src/atom/system/module/gpu.hpp @@ -0,0 +1,28 @@ +/* + * gpu.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-2-21 + +Description: System Information Module - GPU + +**************************************************/ + +#ifndef ATOM_SYSTEM_MODULE_GPU_HPP +#define ATOM_SYSTEM_MODULE_GPU_HPP + +#include + +namespace Atom::System { +/** + * @brief Get GPU information + * @return std::string GPU information + */ +[[nodiscard]] std::string getGPUInfo(); +} // namespace Atom::System + +#endif diff --git a/src/atom/system/module/wifi.cpp b/src/atom/system/module/wifi.cpp index 5d1ec9fe..f91645eb 100644 --- a/src/atom/system/module/wifi.cpp +++ b/src/atom/system/module/wifi.cpp @@ -1,14 +1,32 @@ +/* + * wifi.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-2-21 + +Description: System Information Module - Wifi Information + +**************************************************/ + #include "wifi.hpp" #include #include #ifdef _WIN32 +#include #include #include #include #include +#include +#if !defined(__MINGW32__) && !defined(__MINGW64__) #pragma comment(lib, "wlanapi.lib") +#endif #elif defined(__linux__) #include #include @@ -220,4 +238,84 @@ bool isHotspotConnected() { return isConnected; } + +std::vector getHostIPs() { + std::vector hostIPs; + +#ifdef _WIN32 + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + LOG_F(ERROR, "Error: WSAStartup failed"); + return hostIPs; + } + + char hostname[256]; + if (gethostname(hostname, sizeof(hostname)) == SOCKET_ERROR) { + LOG_F(ERROR, "Error: gethostname failed"); + WSACleanup(); + return hostIPs; + } + + addrinfo hints, *res; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + if (getaddrinfo(hostname, NULL, &hints, &res) != 0) { + LOG_F(ERROR, "Error: getaddrinfo failed"); + WSACleanup(); + return hostIPs; + } + + for (addrinfo* p = res; p != NULL; p = p->ai_next) { + void* addr; + char ipstr[INET6_ADDRSTRLEN]; + if (p->ai_family == AF_INET) { + sockaddr_in* ipv4 = reinterpret_cast(p->ai_addr); + addr = &(ipv4->sin_addr); + } else { + sockaddr_in6* ipv6 = reinterpret_cast(p->ai_addr); + addr = &(ipv6->sin6_addr); + } + inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr)); + hostIPs.push_back(std::string(ipstr)); + } + + freeaddrinfo(res); + WSACleanup(); +#else + ifaddrs* ifaddr; + if (getifaddrs(&ifaddr) == -1) { + LOG_F(ERROR, "Error: getifaddrs failed"); + return hostIPs; + } + + for (ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + + if (ifa->ifa_addr->sa_family == AF_INET || + ifa->ifa_addr->sa_family == AF_INET6) { + char ipstr[INET6_ADDRSTRLEN]; + void* addr; + if (ifa->ifa_addr->sa_family == AF_INET) { + sockaddr_in* ipv4 = + reinterpret_cast(ifa->ifa_addr); + addr = &(ipv4->sin_addr); + } else { + sockaddr_in6* ipv6 = + reinterpret_cast(ifa->ifa_addr); + addr = &(ipv6->sin6_addr); + } + inet_ntop(ifa->ifa_addr->sa_family, addr, ipstr, sizeof(ipstr)); + hostIPs.push_back(std::string(ipstr)); + } + } + + freeifaddrs(ifaddr); +#endif + + return hostIPs; +} } diff --git a/src/atom/system/module/wifi.hpp b/src/atom/system/module/wifi.hpp index 9cc83761..df8e88e0 100644 --- a/src/atom/system/module/wifi.hpp +++ b/src/atom/system/module/wifi.hpp @@ -1,7 +1,22 @@ +/* + * wifi.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-2-21 + +Description: System Information Module - Wifi Information + +**************************************************/ + #ifndef ATOM_SYSTEM_MODULE_WIFI_HPP #define ATOM_SYSTEM_MODULE_WIFI_HPP #include +#include namespace Atom::System { /** @@ -21,6 +36,12 @@ namespace Atom::System { * @return True if hotspot is connected */ [[nodiscard]] bool isHotspotConnected(); + +/* + * @brief Get host IP addresses + * @return Vector of host IP addresses + */ +std::vector getHostIPs(); } // namespace Atom::System #endif \ No newline at end of file diff --git a/src/atom/system/pidwatcher.hpp b/src/atom/system/pidwatcher.hpp index df4d274c..ef3e31f1 100644 --- a/src/atom/system/pidwatcher.hpp +++ b/src/atom/system/pidwatcher.hpp @@ -24,59 +24,108 @@ Description: PID Watcher namespace Atom::System { +/** + * @brief A class for monitoring processes by their PID. + * + * This class allows monitoring of processes by their PID. It provides + * functionality to set callbacks on process exit, set a monitor function to + * run at intervals, get PID by process name, start monitoring a specific + * process, stop monitoring, and switch the target process. + */ class PidWatcher { public: using Callback = std::function; + /** + * @brief Constructs a PidWatcher object. + */ PidWatcher(); + /** + * @brief Destroys the PidWatcher object. + */ ~PidWatcher(); - // 设置进程退出后的回调函数 + /** + * @brief Sets the callback function to be executed on process exit. + * + * @param callback The callback function to set. + */ void SetExitCallback(Callback callback); - // 设置监视函数,每隔一段时间运行一次 + /** + * @brief Sets the monitor function to be executed at specified intervals. + * + * @param callback The monitor function to set. + * @param interval The interval at which the monitor function should run. + */ void SetMonitorFunction(Callback callback, std::chrono::milliseconds interval); - // 根据进程名称获取PID + /** + * @brief Retrieves the PID of a process by its name. + * + * @param name The name of the process. + * @return The PID of the process. + */ pid_t GetPidByName(const std::string &name) const; - // 开始监视指定进程 + /** + * @brief Starts monitoring the specified process by name. + * + * @param name The name of the process to monitor. + * @return True if monitoring started successfully, false otherwise. + */ bool Start(const std::string &name); - // 停止监视进程 + /** + * @brief Stops monitoring the currently monitored process. + */ void Stop(); - // 切换目标进程 + /** + * @brief Switches the target process to monitor. + * + * @param name The name of the process to switch to. + * @return True if the process was successfully switched, false otherwise. + */ bool Switch(const std::string &name); private: + /** + * @brief The thread function for monitoring the process. + */ void MonitorThread(); + /** + * @brief The thread function for handling process exit. + */ void ExitThread(); private: - pid_t pid_; - bool running_; - bool monitoring_; + pid_t pid_; ///< The PID of the currently monitored process. + bool running_; ///< Flag indicating if the monitoring is running. + bool monitoring_; ///< Flag indicating if a process is being monitored. - Callback exit_callback_; - Callback monitor_callback_; - std::chrono::milliseconds monitor_interval_; + Callback exit_callback_; ///< Callback function to execute on process exit. + Callback monitor_callback_; ///< Monitor function to execute at intervals. + std::chrono::milliseconds + monitor_interval_; ///< Interval for monitor function execution. #if __cplusplus >= 202002L - std::jthread monitor_thread_; - std::jthread exit_thread_; + std::jthread monitor_thread_; ///< Thread for monitoring the process. + std::jthread exit_thread_; ///< Thread for handling process exit. #else - std::thread monitor_thread_; - std::thread exit_thread_; + std::thread monitor_thread_; ///< Thread for monitoring the process. + std::thread exit_thread_; ///< Thread for handling process exit. #endif - std::mutex mutex_; - std::condition_variable monitor_cv_; - std::condition_variable exit_cv_; + std::mutex mutex_; ///< Mutex for thread synchronization. + std::condition_variable + monitor_cv_; ///< Condition variable for monitoring. + std::condition_variable exit_cv_; ///< Condition variable for process exit. }; + } // namespace Atom::System #endif \ No newline at end of file diff --git a/src/atom/system/system.cpp b/src/atom/system/system.cpp index 84c9f44f..3f0d3c86 100644 --- a/src/atom/system/system.cpp +++ b/src/atom/system/system.cpp @@ -54,7 +54,7 @@ namespace fs = std::filesystem; #include "atom/log/loguru.hpp" namespace Atom::System { -bool CheckSoftwareInstalled(const std::string &software_name) { +bool checkSoftwareInstalled(const std::string &software_name) { bool is_installed = false; #ifdef _WIN32 HKEY hKey; @@ -116,46 +116,7 @@ bool CheckSoftwareInstalled(const std::string &software_name) { return is_installed; } -bool checkExecutableFile(const std::string &fileName, - const std::string &fileExt) { -#ifdef _WIN32 - fs::path filePath = fileName + fileExt; -#else - fs::path filePath = fileName; -#endif - - DLOG_F(INFO, "Checking file '%s'.", filePath.string().c_str()); - - if (!fs::exists(filePath)) { - DLOG_F(WARNING, "The file '%s' does not exist.", - filePath.string().c_str()); - return false; - } - -#ifdef _WIN32 - if (!fs::is_regular_file(filePath) || - !(GetFileAttributesA(filePath.generic_string().c_str()) & - FILE_ATTRIBUTE_DIRECTORY)) { - DLOG_F(WARNING, - "The file '%s' is not a regular file or is not executable.", - filePath.string().c_str()); - return false; - } -#else - if (!fs::is_regular_file(filePath) || access(filePath.c_str(), X_OK) != 0) { - DLOG_F(WARNING, - "The file '%s' is not a regular file or is not executable.", - filePath.string().c_str()); - return false; - } -#endif - - DLOG_F(INFO, "The file '%s' exists and is executable.", - filePath.string().c_str()); - return true; -} - -bool Shutdown() { +bool shutdown() { #ifdef _WIN32 ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE, 0); #else @@ -168,7 +129,7 @@ bool Shutdown() { } // 重启函数 -bool Reboot() { +bool reboot() { #ifdef _WIN32 ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0); #else @@ -180,20 +141,20 @@ bool Reboot() { return true; } -bool IsRoot() { +bool isRoot() { #ifdef _WIN32 HANDLE hToken; TOKEN_ELEVATION elevation; DWORD dwSize; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { - LOG_F(ERROR, "IsRoot error: OpenProcessToken error"); + LOG_F(ERROR, "isRoot error: OpenProcessToken error"); return false; } if (!GetTokenInformation(hToken, TokenElevation, &elevation, sizeof(elevation), &dwSize)) { - LOG_F(ERROR, "IsRoot error: GetTokenInformation error"); + LOG_F(ERROR, "isRoot error: GetTokenInformation error"); CloseHandle(hToken); return false; } @@ -206,27 +167,10 @@ bool IsRoot() { #endif } -std::string GetCurrentUsername() { -#ifdef _WIN32 - char username[UNLEN + 1]; - DWORD usernameLen = UNLEN + 1; - if (GetUserNameA(username, &usernameLen)) { - return std::string(username); - } -#else - char username[256]; - if (getlogin_r(username, sizeof(username)) == 0) { - return std::string(username); - } -#endif - return ""; -} - -std::vector> GetProcessInfo() { +std::vector> getProcessInfo() { std::vector> processInfo; #ifdef _WIN32 - // 使用 Windows API 获取进程信息和文件地址 DWORD processes[1024]; DWORD cbNeeded; if (EnumProcesses(processes, sizeof(processes), &cbNeeded)) { @@ -330,7 +274,7 @@ std::vector> GetProcessInfo() { return processInfo; } -bool CheckDuplicateProcess(const std::string &program_name) { +bool checkDuplicateProcess(const std::string &program_name) { #ifdef _WIN32 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot == INVALID_HANDLE_VALUE) { @@ -450,22 +394,6 @@ bool isProcessRunning(const std::string &processName) { closedir(dir); return true; - // An alternative way to check - /* - std::string command = "pgrep -c " + processName; - std::string output; - std::ifstream pipe(command.c_str()); - if (pipe) - { - if (getline(pipe, output)) - { - int count = std::stoi(output); - return (count > 0); - } - } - - return false; - */ #endif } @@ -550,7 +478,7 @@ std::vector GetProcessDetails() { } #ifdef _WIN32 -DWORD GetParentProcessId(DWORD processId) { +DWORD getParentProcessId(DWORD processId) { DWORD parentProcessId = 0; HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapshot != INVALID_HANDLE_VALUE) { @@ -571,7 +499,7 @@ DWORD GetParentProcessId(DWORD processId) { return parentProcessId; } -bool GetProcessInfoByID(DWORD processID, ProcessInfo &processInfo) { +bool getProcessInfoByID(DWORD processID, ProcessInfo &processInfo) { HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID); if (hProcess != NULL) { @@ -582,7 +510,7 @@ bool GetProcessInfoByID(DWORD processID, ProcessInfo &processInfo) { processInfo.processID = processID; processInfo.executableFile = std::string(exeName); // if - // (!GetProcessId(reinterpret_cast(GetParentProcessId(reinterpret_cast(hProcess))), + // (!GetProcessId(reinterpret_cast(getParentProcessId(reinterpret_cast(hProcess))), // &processInfo.parentProcessID)) processInfo.parentProcessID = 0; processInfo.basePriority = GetPriorityClass(hProcess); @@ -597,12 +525,12 @@ bool GetProcessInfoByID(DWORD processID, ProcessInfo &processInfo) { return false; } -bool GetProcessInfoByName(const std::string &processName, +bool getProcessInfoByName(const std::string &processName, ProcessInfo &processInfo) { std::vector processes = GetProcessDetails(); for (const auto &process : processes) { if (process.executableFile == processName) { - if (GetProcessInfoByID(process.processID, processInfo)) + if (getProcessInfoByID(process.processID, processInfo)) return true; } } @@ -610,7 +538,7 @@ bool GetProcessInfoByName(const std::string &processName, return false; } #else -bool GetProcessInfoByID(int processID, ProcessInfo &processInfo) { +bool getProcessInfoByID(int processID, ProcessInfo &processInfo) { std::string statPath = "/proc/" + std::to_string(processID) + "/stat"; std::ifstream statFile(statPath); @@ -644,7 +572,7 @@ bool GetProcessInfoByID(int processID, ProcessInfo &processInfo) { return false; } -bool GetProcessInfoByName(const std::string &processName, +bool getProcessInfoByName(const std::string &processName, ProcessInfo &processInfo) { std::vector processes = GetProcessDetails(); for (const auto &process : processes) { @@ -652,7 +580,7 @@ bool GetProcessInfoByName(const std::string &processName, if (pos != std::string::npos) { std::string fileName = process.executableFile.substr(pos + 1); if (fileName == processName) { - if (GetProcessInfoByID(process.processID, processInfo)) + if (getProcessInfoByID(process.processID, processInfo)) return true; } } @@ -662,60 +590,3 @@ bool GetProcessInfoByName(const std::string &processName, } #endif } // namespace Atom::System - -/* -int main() -{ - float cpu_usage = cpustat(); - float mem_usage = memstat(); - float gpu_usage = gpustat(); - - std::cout << "CPU Usage: " << cpu_usage << "%" << std::endl; - std::cout << "Memory Usage: " << mem_usage << "%" << std::endl; - std::cout << "GPU Usage: " << gpu_usage << "%" << std::endl; - - std::vector> disk_usage = diskstat(); - for (const auto &disk : disk_usage) - { - std::cout << "Disk " << disk.first << " Usage: " << disk.second << "%" -<< std::endl; - } - - std::vector net_connections = netstat(); - for (const auto &conn : net_connections) - { - std::cout << "Network Connection: " << conn << std::endl; - } - - float cpuTemperature = GetCpuTemperature(); - - std::cout << "CPU Temperature: " << cpuTemperature << "°C" << std::endl; - - bool isConnected = IsConnectedToInternet(); - if (isConnected) { - std::cout << "Connected to the Internet." << std::endl; - } else { - std::cout << "Not connected to the Internet." << std::endl; - } - - bool elevated = IsRoot(); - if (elevated) { - std::cout << "Current process has elevated privileges." << std::endl; - } else { - std::cout << "Current process does not have elevated privileges." << -std::endl; - } - - std::vector> processInfo = -GetProcessInfo(); - - for (const auto &info : processInfo) - { - std::cout << "Process Name: " << info.first << std::endl; - std::cout << "File Address: " << info.second << std::endl; - std::cout << std::endl; - } - - return 0; -} -*/ \ No newline at end of file diff --git a/src/atom/system/system.hpp b/src/atom/system/system.hpp index 025936fa..5ff6046e 100644 --- a/src/atom/system/system.hpp +++ b/src/atom/system/system.hpp @@ -44,21 +44,7 @@ struct ProcessInfo { * @return false if the software is not installed or an error occurred. * 如果软件未安装或发生错误,则返回 false */ -bool CheckSoftwareInstalled(const std::string &software_name); - -/** - * @brief Check whether the specified file exists. - * 检查指定文件是否存在 - * - * @param fileName The name of the file. 文件名称 - * @param fileExt The extension of the file. 文件扩展名 - * @return true if the file exists. - * 如果文件存在,则返回 true - * @return false if the file does not exist or an error occurred. - * 如果文件不存在或发生错误,则返回 false - */ -bool checkExecutableFile(const std::string &fileName, - const std::string &fileExt); +bool checkSoftwareInstalled(const std::string &software_name); /** * @brief Check whether the current user has root/administrator privileges. @@ -69,16 +55,7 @@ bool checkExecutableFile(const std::string &fileName, * @return false if the current user does not have root/administrator * privileges. 如果当前用户没有根/管理员权限,则返回 false */ -bool IsRoot(); - -/** - * @brief Get the current user name. - * 获取当前用户名 - * - * @return The current user name. - * 当前用户名 - */ -std::string GetCurrentUsername(); +bool isRoot(); // Max: Surely, we will not recieve any signal after we call this function. @@ -91,7 +68,7 @@ std::string GetCurrentUsername(); * @return false if an error occurred. * 如果发生错误,则返回 false */ -bool Shutdown(); +bool shutdown(); /** * @brief Reboot the system. @@ -102,7 +79,7 @@ bool Shutdown(); * @return false if an error occurred. * 如果发生错误,则返回 false */ -bool Reboot(); +bool reboot(); /** * @brief Get the process information and file address. @@ -112,7 +89,7 @@ bool Reboot(); * process ID) and its file address. * 包含进程信息(名称和进程ID)以及对应文件地址的一对对的向量 */ -std::vector> GetProcessInfo(); +std::vector> getProcessInfo(); /** * @brief Check if there are duplicate processes with the same program name. @@ -125,7 +102,7 @@ std::vector> GetProcessInfo(); * @param program_name The name of the program to check for duplicates. * 要检查重复项的程序名称。 */ -bool CheckDuplicateProcess(const std::string &program_name); +bool checkDuplicateProcess(const std::string &program_name); /** * @brief Check if the specified process is running. @@ -160,9 +137,9 @@ std::vector GetProcessDetails(); * 如果发生错误,则返回 false */ #ifdef _WIN32 -bool GetProcessInfoByID(DWORD processID, ProcessInfo &processInfo); +bool getProcessInfoByID(DWORD processID, ProcessInfo &processInfo); #else -bool GetProcessInfoByID(int processID, ProcessInfo &processInfo); +bool getProcessInfoByID(int processID, ProcessInfo &processInfo); #endif /** @@ -176,7 +153,7 @@ bool GetProcessInfoByID(int processID, ProcessInfo &processInfo); * @return false if an error occurred. * 如果发生错误,则返回 false */ -bool GetProcessInfoByName(const std::string &processName, +bool getProcessInfoByName(const std::string &processName, ProcessInfo &processInfo); } // namespace Atom::System diff --git a/src/atom/system/xmake.lua b/src/atom/system/xmake.lua new file mode 100644 index 00000000..589bfbf4 --- /dev/null +++ b/src/atom/system/xmake.lua @@ -0,0 +1,41 @@ +-- xmake.lua for Atom-System + +-- Project Information +set_project("Atom-System") +set_version("1.0.0") +set_description("A collection of useful system functions") +set_licenses("GPL-3.0") + +-- Specify the C++ Languages +set_languages("c++17") + +-- Add Source Files +add_files("*.cpp") +add_files("module/*.cpp") +add_files("*.hpp") +add_files("module/*.hpp") + +-- Add Private Header Files +add_headerfiles("*.hpp", {prefixdir = "$(projectdir)"}) +add_headerfiles("module/*.hpp", {prefixdir = "$(projectdir)/module"}) + +-- Add Target +target("Atom-System") + set_kind("static") + add_packages("loguru") + add_links("pthread") + add_includedirs(".") + if is_plat("windows") then + add_links("pdh", "wlanapi") + end + +-- Install Rules +after_install("install_headers") +after_install("install_libraries") + +-- Install Headers +install_headers("*.hpp", "$(projectdir)/include/$(projectname)") +install_headers("module/*.hpp", "$(projectdir)/include/$(projectname)/module") + +-- Install Libraries +install_libraries("$(targetdir)/*.a", "$(projectdir)/lib") \ No newline at end of file diff --git a/src/atom/task/meson.build b/src/atom/task/meson.build new file mode 100644 index 00000000..ffc7f3bd --- /dev/null +++ b/src/atom/task/meson.build @@ -0,0 +1,35 @@ +project('atom-task', 'cpp') + +# Sources +atom_task_sources = [ + 'loop_task.cpp', + 'conditional_task.cpp', + 'task.cpp', +] + +# Headers +atom_task_headers = [ + 'loop_task.hpp', + 'conditional_task.hpp', + 'task.hpp', +] + +# Build Object Library +atom_task_object = library( + 'atom-task-object', + atom_task_sources, + include_directories: include_directories('.') +) + +# Build Static Library +atom_task_static = library( + 'atom-task', + atom_task_sources, + include_directories: include_directories('.'), + dependencies: atom_task_object, + version: '1.0', + soversion: '1' +) + +# Install +install_targets(atom_task_static) diff --git a/src/atom/task/xmake.lua b/src/atom/task/xmake.lua new file mode 100644 index 00000000..c6376c12 --- /dev/null +++ b/src/atom/task/xmake.lua @@ -0,0 +1,30 @@ +-- xmake.lua for Atom-Task + +-- Project Information +set_project("Atom-Task") +set_version("1.0.0") +set_description("Core Task Definitions") +set_licenses("GPL-3.0") + +-- Specify the C++ Languages +set_languages("c++17") + +-- Add Source Files +add_files("*.cpp") +add_files("*.hpp") + +-- Add Target +target("Atom-Task") + set_kind("static") + add_links("pthread") + add_includedirs(".") + +-- Install Rules +after_install("install_headers") +after_install("install_libraries") + +-- Install Headers +install_headers("*.hpp", "$(projectdir)/include/$(projectname)") + +-- Install Libraries +install_libraries("$(targetdir)/*.a", "$(projectdir)/lib") \ No newline at end of file diff --git a/src/atom/type/CMakeLists.txt b/src/atom/type/CMakeLists.txt index 04bf09e7..1eb8126a 100644 --- a/src/atom/type/CMakeLists.txt +++ b/src/atom/type/CMakeLists.txt @@ -11,19 +11,22 @@ project(atom-type C CXX) # Sources set(${PROJECT_NAME}_SOURCES - args.cpp ini.cpp message.cpp ) # Headers set(${PROJECT_NAME}_HEADERS + abi.hpp args.hpp - autotype.hpp + enum_flag.hpp + enum_flag.inl + flatset.hpp ini_impl.hpp ini.hpp json.hpp message.hpp + pointer.hpp small_vector.hpp ) diff --git a/src/atom/type/_script.cpp b/src/atom/type/_script.cpp new file mode 100644 index 00000000..af32cc25 --- /dev/null +++ b/src/atom/type/_script.cpp @@ -0,0 +1,143 @@ +#ifndef CARBON_NLOHMANN_JSON_WRAP_HPP +#define CARBON_NLOHMANN_JSON_WRAP_HPP + +#include "carbon/carbon.hpp" + +#include "json.hpp" + +namespace Carbon { +class json_wrap { +public: + static Module &library(Module &m) { + m.add(Carbon::fun( + [](const std::string &t_str) { return from_json(t_str); }), + "from_json"); + m.add(Carbon::fun(&json_wrap::to_json), "to_json"); + + return m; + } + +private: + static Boxed_Value from_json(const nlohmann::json &t_json) { + switch (t_json.type()) { + case nlohmann::json::value_t::null: + return Boxed_Value(); + case nlohmann::json::value_t::object: { + std::map m; + + for (const auto &p : + t_json.get>()) { + m.insert(std::make_pair(p.first, from_json(p.second))); + } + + return Boxed_Value(m); + } + case nlohmann::json::value_t::array: { + std::vector vec; + + for (const auto &p : + t_json.get>()) { + vec.emplace_back(from_json(p)); + } + + return Boxed_Value(vec); + } + case nlohmann::json::value_t::string: + return Boxed_Value(t_json.get()); + case nlohmann::json::value_t::number_float: + return Boxed_Value(t_json.get()); + case nlohmann::json::value_t::number_integer: + return Boxed_Value(t_json.get()); + case nlohmann::json::value_t::boolean: + return Boxed_Value(t_json.get()); + } + + throw std::runtime_error("Unknown JSON type"); + } + + static Boxed_Value from_json(const std::string &t_json) { + try { + return from_json(nlohmann::json::parse(t_json)); + } catch (const std::out_of_range &) { + throw std::runtime_error("Unparsed JSON input"); + } + } + + static std::string to_json(const Boxed_Value &t_bv) { + return to_json_object(t_bv).dump(); + } + + static nlohmann::json to_json_object(const Boxed_Value &t_bv) { + try { + const std::map m = + Carbon::boxed_cast &>( + t_bv); + + nlohmann::json obj(nlohmann::json::value_t::object); + for (const auto &o : m) { + obj[o.first] = to_json_object(o.second); + } + return obj; + } catch (const Carbon::exception::bad_boxed_cast &) { + // not a map + } + + try { + const std::vector v = + Carbon::boxed_cast &>(t_bv); + + nlohmann::json obj(nlohmann::json::value_t::array); + for (size_t i = 0; i < v.size(); ++i) { + obj[i] = to_json_object(v[i]); + } + return obj; + } catch (const Carbon::exception::bad_boxed_cast &) { + // not a vector + } + + try { + Boxed_Number bn(t_bv); + if (Boxed_Number::is_floating_point(t_bv)) { + return nlohmann::json(bn.get_as()); + } else { + return nlohmann::json(bn.get_as()); + } + } catch (const Carbon::detail::exception::bad_any_cast &) { + // not a number + } + + try { + return nlohmann::json(boxed_cast(t_bv)); + } catch (const Carbon::exception::bad_boxed_cast &) { + // not a bool + } + + try { + return nlohmann::json(boxed_cast(t_bv)); + } catch (const Carbon::exception::bad_boxed_cast &) { + // not a string + } + + try { + const Carbon::dispatch::Dynamic_Object &o = + boxed_cast(t_bv); + + nlohmann::json obj(nlohmann::json::value_t::object); + for (const auto &attr : o.get_attrs()) { + obj[attr.first] = to_json_object(attr.second); + } + return obj; + } catch (const Carbon::exception::bad_boxed_cast &) { + // not a dynamic object + } + + if (t_bv.is_null()) + return nlohmann::json(); // a null value + + throw std::runtime_error("Unknown object type to convert to JSON"); + } +}; + +} // namespace Carbon + +#endif diff --git a/src/atom/type/_script.hpp b/src/atom/type/_script.hpp new file mode 100644 index 00000000..e9f01210 --- /dev/null +++ b/src/atom/type/_script.hpp @@ -0,0 +1,72 @@ +/* + * _script.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-23 + +Description: Carbon binding for Atom-Type + +**************************************************/ + +#ifndef ATOM_TYPE_SCRIPT_HPP +#define ATOM_TYPE_SCRIPT_HPP + +#include "carbon/carbon.hpp" + +#include "args.hpp" +#include "flatset.hpp" +#include "ini.hpp" +#include "small_vector.hpp" + +namespace Atom::_Script::Type { +/** + * Adds the String Methods to the given Carbon module. + */ +Carbon::ModulePtr bootstrap( + Carbon::ModulePtr m = std::make_shared()) { + m->add(user_type(), "Args"); + m->add(Carbon::fun(&Args::set), "set"); + m->add(Carbon::fun(&Args::get), "get"); + m->add(Carbon::fun(&Args::get_or), "get_or"); + m->add(Carbon::fun(&Args::contains), "contains"); + m->add(Carbon::fun(&Args::get_optional), + "get_optional"); + m->add(Carbon::fun(&Args::remove), "remove"); + m->add(Carbon::fun(&Args::empty), "empty"); + m->add(Carbon::fun(&Args::size), "size"); + m->add(Carbon::fun(&Args::clear), "clear"); + m->add(Carbon::fun(&Args::data), "data"); + + //Carbon::bootstrap::standard_library::vector_type< + // SmallVector>("SmallVector", *m); + //Carbon::bootstrap::standard_library::set_type>( + // "FlatSet", *m); + //Carbon::bootstrap::standard_library::set_type< + // std::set>("Set", *m); + + m->add(user_type(), "INIFile"); + // m->add(Carbon::constructor(), "INIFile"); + m->add(Carbon::fun(&INIFile::load), "load"); + m->add(Carbon::fun(&INIFile::save), "save"); + m->add(Carbon::fun(&INIFile::set), "set_int"); + m->add(Carbon::fun(&INIFile::get), "get_int"); + m->add(Carbon::fun(&INIFile::set), "set_double"); + m->add(Carbon::fun(&INIFile::get), "get_double"); + m->add(Carbon::fun(&INIFile::set), "set_string"); + m->add(Carbon::fun(&INIFile::get), "get_string"); + m->add(Carbon::fun(&INIFile::set), "get_bool"); + m->add(Carbon::fun(&INIFile::get), "get_bool"); + m->add(Carbon::fun(&INIFile::has), "has"); + m->add(Carbon::fun(&INIFile::hasSection), "has_section"); + m->add(Carbon::fun(&INIFile::toJson), "to_json"); + m->add(Carbon::fun(&INIFile::toXml), "to_xml"); + + return m; +} +} // namespace Atom::_Script::Type + +#endif \ No newline at end of file diff --git a/src/atom/type/abi.hpp b/src/atom/type/abi.hpp new file mode 100644 index 00000000..ed46c27d --- /dev/null +++ b/src/atom/type/abi.hpp @@ -0,0 +1,74 @@ +/* + * abi.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-12-28 + +Description: A simple C++ ABI wrapper + +**************************************************/ + +#ifndef ATOM_TYPE_ABI_HPP +#define ATOM_TYPE_ABI_HPP + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#if !defined(__MINGW32__) && !defined(__MINGW64__) +#pragma comment(lib, "dbghelp.lib") +#endif +#else +#include +#endif + +class DemangleHelper { +public: + template + static std::string DemangleType() { + return Demangle(typeid(T).name()); + } + + template + static std::string DemangleType(const T& instance) { + return Demangle(typeid(instance).name()); + } + +private: + static std::string Demangle(std::string_view mangled_name) { +#ifdef _WIN32 + char buffer[1024]; + DWORD length = UnDecorateSymbolName(mangled_name.data(), buffer, + sizeof(buffer), UNDNAME_COMPLETE); + + if (length > 0) { + return std::string(buffer, length); + } else { + return std::string(mangled_name); + } +#else + int status = -1; + std::size_t length = 0; + std::unique_ptr demangled_name( + abi::__cxa_demangle(mangled_name.data(), nullptr, &length, &status), + std::free); + + if (status == 0) { + return std::string(demangled_name.get(), length); + } else { + return std::string(mangled_name); + } +#endif + } +}; + +#endif diff --git a/src/atom/type/args.cpp b/src/atom/type/args.cpp deleted file mode 100644 index 24cac700..00000000 --- a/src/atom/type/args.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * args.cpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-12-28 - -Description: Argument Container Library for C++ - -**************************************************/ - -#include "args.hpp" - -bool ArgumentContainer::remove(const std::string &name) { - if (m_arguments.find(name) == m_arguments.end()) { - return false; - } - return m_arguments.erase(name) != 0; -} - -bool ArgumentContainer::contains(const std::string &name) const { - return m_arguments.count(name) != 0; -} - -std::size_t ArgumentContainer::size() const { return m_arguments.size(); } - -std::vector ArgumentContainer::getNames() const { - std::vector names; - names.reserve(m_arguments.size()); - for (const auto &pair : m_arguments) { - names.push_back(pair.first); - } - return names; -} - -std::string ArgumentContainer::toJson() const { - std::string json; - json += "{"; - for (const auto &pair : m_arguments) { - json += "\"" + pair.first + "\":"; - if (pair.second.type() == typeid(std::string)) { - json += "\"" + std::any_cast(pair.second) + "\""; - } else if (pair.second.type() == typeid(int)) { - json += std::to_string(std::any_cast(pair.second)); - } else if (pair.second.type() == typeid(double)) { - json += std::to_string(std::any_cast(pair.second)); - } else if (pair.second.type() == typeid(bool)) { - json += std::to_string(std::any_cast(pair.second)); - } else { - json += "null"; - } - json += ","; - } - json += "}"; - return json; -} \ No newline at end of file diff --git a/src/atom/type/args.hpp b/src/atom/type/args.hpp index 302e9676..d7942c10 100644 --- a/src/atom/type/args.hpp +++ b/src/atom/type/args.hpp @@ -17,7 +17,11 @@ Description: Argument Container Library for C++ #include #include +#include #include +#include +#include +#include #include #if ENABLE_FASTHASH #include "emhash/hash_table8.hpp" @@ -39,155 +43,192 @@ Description: Argument Container Library for C++ #define REMOVE_ARGUMENT(container, name) container.remove(#name) /** - * @brief 用于存储和获取参数的容器。 - * @brief A container for storing and retrieving arguments. + * @brief 通用容器,用于存储任意类型的键值对。 + * @brief A universal container for storing any type of key-value pairs. + * @note 这是ArgumentContainer的弱化版,虽然功能少,但是性能更好。 + * @note This is a weak version of ArgumentContainer, although it has fewer + * features, it has better performance. */ -class ArgumentContainer { +class Args { public: /** - * @brief 设置参数值。 - * @brief Set the value of a parameter. - * @tparam T 参数的类型。 - * @tparam T The type of the parameter. - * @param name 参数的名称。 - * @param name The name of the parameter. - * @param value 参数的值。 - * @param value The value of the parameter. - * @note 若参数已存在,则会覆盖原有的值。 - * @note If the parameter exists, it will overwrite the original value. + * @brief 设置键值对。 + * @brief Set key-value pairs. + * @param key 键。 + * @param key Key. + * @param value 值。 + * @param value Value. + * @note 如果键已存在,则会覆盖原有的值。 + * @note If the key exists, it will overwrite the original value. + */ + template + void set(std::string_view key, T &&value) { + m_data[key] = std::forward(value); + } + + /** + * @brief 获取键对应的值。 + * @brief Get the value corresponding to the key. + * @param key 键。 + * @param key Key. + * @return 值。 + * @return Value. + * @note 如果键不存在,则会抛出异常。 + * @note If the key does not exist, an exception will be thrown. */ template - void set(const std::string &name, const T &value); + T get(std::string_view key) const { + return std::any_cast(m_data.at(key)); + } /** - * @brief 获取参数值。 - * @brief Get the value of a parameter. - * @tparam T 参数的类型。 - * @tparam T The type of the parameter. - * @param name 参数的名称。 - * @param name The name of the parameter. - * @return 参数的值(如果存在),否则返回std::nullopt。 - * @return The value of the parameter (if it exists), otherwise return - * std::nullopt. + * @brief 获取键对应的值(如果键不存在,则返回默认值)。 + * @brief Get the value corresponding to the key (if the key does not exist, + * return the default value). + * @param key 键。 + * @param key Key. + * @param default_value 默认值。 + * @param default_value Default value. + * @return 值。 + * @return Value. */ template - std::optional get(const std::string &name) const; + T get_or(std::string_view key, T &&default_value) const { + if (auto it = m_data.find(key); it != m_data.end()) { + return std::any_cast(it->second); + } + return std::forward(default_value); + } /** - * @brief 移除指定的参数。 - * @brief Remove a specified parameter. - * @param name 要移除的参数的名称。 - * @param name The name of the parameter to be removed. - * @return 如果成功移除参数,则返回true;否则返回false - * @return If the parameter is successfully removed, return true; otherwise - * return false。 + * @brief 获取键对应的值(如果键不存在,则返回 std::nullopt)。 + * @brief Get the value corresponding to the key (if the key does not exist, + * return std::nullopt). + * @param key 键。 + * @param key Key. + * @return 值。 + * @return Value. */ - bool remove(const std::string &name); + template + std::optional get_optional(std::string_view key) const { + if (auto it = m_data.find(key); it != m_data.end()) { + return std::any_cast(it->second); + } + return std::nullopt; + } /** - * @brief 检查参数是否存在。 - * @brief Check if a parameter exists. - * @param name 要检查的参数的名称。 - * @param name The name of the parameter to be checked. - * @return 如果参数存在,则返回true;否则返回false。 - * @return If the parameter exists, return true; otherwise return false. + * @brief 检查键是否存在。 + * @brief Check if the key exists. + * @param key 键。 + * @param key Key. + * @return 如果键存在,则返回true;否则返回false。 + * @return If the key exists, return true; otherwise return false. */ - bool contains(const std::string &name) const; + bool contains(std::string_view key) const noexcept { + return m_data.contains(key); + } /** - * @brief 获取参数的数量。 - * @brief Get the number of parameters. - * @return 参数的数量。 - * @return The number of parameters. + * @brief 移除键值对。 + * @brief Remove key-value pairs. + * @param key 键。 + * @param key Key. */ - std::size_t size() const; + void remove(std::string_view key) { m_data.erase(key); } /** - * @brief 获取所有参数的名称。 - * @brief Get all parameter names. - * @return 包含所有参数名称的字符串向量。 - * @return A vector containing all parameter names. + * @brief 清空容器。 + * @brief Clear the container. + */ + void clear() noexcept { m_data.clear(); } + /** + * @brief 获取容器中键值对的数量。 + * @brief Get the number of key-value pairs in the container. + * @return 键值对的数量。 + * @return The number of key-value pairs. */ - std::vector getNames() const; + size_t size() const noexcept { return m_data.size(); } /** - * @brief 重载索引运算符[]以获取和设置参数值。 - * @brief Overload the index operator [] to get and set the value of a - * parameter. - * @tparam T 参数的类型。 - * @tparam T The type of the parameter. - * @param name 参数的名称。 - * @param name The name of the parameter. - * @return 参数的引用。 - * @return A reference to the parameter. + * @brief 检查容器是否为空。 + * @brief Check if the container is empty. + * @return 如果容器为空,则返回true;否则返回false。 + * @return If the container is empty, return true; otherwise return false. */ - template - T &operator[](const std::string &name) { - return std::any_cast(m_arguments[name]); + bool empty() const noexcept { return m_data.empty(); } + +#if ENABLE_FASTHASH + emhash8::HashMap data() +#else + std::unordered_map data() +#endif + const noexcept { + return m_data; } /** - * @brief 重载赋值运算符=以设置参数值。 - * @brief Overload the assignment operator = to set the value of a - * parameter. - * @tparam T 参数的类型。 - * @tparam T The type of the parameter. - * @param argument 要设置的参数(名称和值)。 - * @param argument The parameter to be set (name and value). + * @brief 获取指定键对应的值。 + * @brief Get the value corresponding to the specified key. + * @param key 键。 + * @param key Key. + * @return 值。 + * @return Value. + * @note 如果键不存在,则会插入一个新的键值对。 + * @note If the key does not exist, a new key-value pair will be inserted. */ template - void operator=(const std::pair &argument) { - set(argument.first, argument.second); + T &operator[](std::string_view key) { + return std::any_cast(m_data[key]); } /** - * @brief 重载赋值运算符=以设置参数值。 - * @brief Overload the assignment operator = to set the value of a - * parameter. - * @param container 要设置的参数容器。 - * @param container The parameter container to be set. + * @brief 获取指定键对应的值。 + * @brief Get the value corresponding to the specified key. + * @param key 键。 + * @param key Key. + * @return 值。 + * @return Value. + * @note 如果键不存在,则会抛出异常。 + * @note If the key does not exist, an exception will be thrown. */ -#if ENABLE_FASTHASH - void operator=(const emhash8::HashMap &container) -#else - void operator=(const std::unordered_map &container) -#endif - { - m_arguments = container; + template + const T &operator[](std::string_view key) const { + return std::any_cast(m_data.at(key)); } - std::string toJson() const; + /** + * @brief 获取指定键对应的值。 + * @brief Get the value corresponding to the specified key. + * @param key 键。 + * @param key Key. + * @return 值。 + * @return Value. + * @note 如果键不存在,则会插入一个新的键值对。 + * @note If the key does not exist, a new key-value pair will be inserted. + */ + std::any &operator[](std::string_view key) { return m_data[key]; } + + /** + * @brief 获取指定键对应的值。 + * @brief Get the value corresponding to the specified key. + * @param key 键。 + * @param key Key. + * @return 值。 + * @return Value. + * @note 如果键不存在,则会抛出异常。 + * @note If the key does not exist, an exception will be thrown. + */ + const std::any &operator[](std::string_view key) const { + return m_data.at(key); + } private: #if ENABLE_FASTHASH - emhash8::HashMap m_arguments; // 存储参数的容器 + emhash8::HashMap m_data; #else - std::unordered_map m_arguments; // 存储参数的容器 + std::unordered_map m_data; #endif }; -template -void ArgumentContainer::set(const std::string &name, const T &value) { - if (m_arguments.find(name) != m_arguments.end()) { - m_arguments.erase(name); - } - m_arguments[name] = value; -} - -template -std::optional ArgumentContainer::get(const std::string &name) const { - auto it = m_arguments.find(name); - if (it != m_arguments.end()) { - try { - return std::any_cast(it->second); - } catch (const std::bad_any_cast &) { - return std::nullopt; - } - } else { - return std::nullopt; - } -} - -using Args = ArgumentContainer; - #endif \ No newline at end of file diff --git a/src/atom/type/argsview.hpp b/src/atom/type/argsview.hpp new file mode 100644 index 00000000..6d23e57a --- /dev/null +++ b/src/atom/type/argsview.hpp @@ -0,0 +1,259 @@ +/* + * argsview.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-12-28 + +Description: Argument View for C++20 + +**************************************************/ + +#ifndef ATOM_TYPE_ARGSVIEW_HPP +#define ATOM_TYPE_ARGSVIEW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +template +class ArgsView { +public: + constexpr ArgsView(Args&&... args) noexcept + : args_(std::forward(args)...) {} + + template ...>, + int> = 0> + constexpr ArgsView(const std::tuple& other_tuple) + : args_(std::apply( + [](const auto&... args) { + return std::tuple(std::forward(args)...); + }, + other_tuple)) {} + + template ...>, + int> = 0> + constexpr ArgsView(ArgsView other_args_view) + : args_(std::apply( + [](const auto&... args) { + return std::tuple(std::forward(args)...); + }, + other_args_view.args_)) {} + + template + constexpr decltype(auto) get() const noexcept { + return std::get(args_); + } + + constexpr size_t size() const noexcept { return sizeof...(Args); } + + constexpr bool empty() const noexcept { return size() == 0; } + + template + constexpr void for_each(Func&& func) const { + apply([&func](auto&&... args) { + (func(std::forward(args)), ...); + }); + } + + template + constexpr auto transform(Func&& func) const { + return ArgsView(std::apply( + [&func](auto&&... args) { + return std::make_tuple( + func(std::forward(args))...); + }, + args_)); + } + + template + constexpr auto accumulate(Func&& func, Init init) const { + return apply([&func, init = std::move(init)](auto&&... args) mutable { + ((init = func(std::move(init), std::forward(args))), + ...); + return init; + }); + } + + template + constexpr auto apply(Func&& func) const { + return std::apply(std::forward(func), args_); + } + + template ...>, + int> = 0> + constexpr ArgsView& operator=(const std::tuple& other_tuple) { + args_ = std::apply( + [](const auto&... args) { + return std::tuple(std::forward(args)...); + }, + other_tuple); + return *this; + } + + template ...>, + int> = 0> + constexpr ArgsView& operator=(ArgsView other_args_view) { + args_ = std::apply( + [](const auto&... args) { + return std::tuple(std::forward(args)...); + }, + other_args_view.args_); + return *this; + } + + constexpr decltype(auto) begin() const noexcept { + return std::apply( + [](const auto&... args) { + return std::tuple(args...); + }, + args_) + .cbegin(); + } + + constexpr decltype(auto) end() const noexcept { + return std::apply( + [](const auto&... args) { + return std::tuple(args...); + }, + args_) + .cend(); + } + +private: + std::tuple args_; +}; + +template +ArgsView(Args&&...) -> ArgsView...>; + +template +using ArgsViewT = ArgsView...>; + +template +int sum(Args&&... args) { + return ArgsView{std::forward(args)...}.accumulate( + [](int a, int b) { return a + b; }, 0); +} + +template +std::string concat(Args&&... args) { + return transform( + [](const auto& arg) { + if constexpr (std::is_same_v, + const char*>) { + return std::string(arg); + } else { + return std::to_string(arg); + } + }, + make_args_view(std::forward(args)...)) + .accumulate([](std::string a, std::string b) { return a + b; }, + std::string{}); +} + +template +constexpr auto apply(Func&& func, ArgsViewT args_view) { + return args_view.apply(std::forward(func)); +} + +template +constexpr void for_each(Func&& func, ArgsView args_view) { + args_view.for_each(std::forward(func)); +} + +template +constexpr auto accumulate(Func&& func, Init init, + ArgsViewT args_view) { + return args_view.accumulate(std::forward(func), std::move(init)); +} + +template +constexpr ArgsViewT make_args_view(Args&&... args) { + return ArgsViewT(std::forward(args)...); +} + +template +constexpr decltype(auto) get(ArgsView args_view) { + return args_view.template get(); +} + +template +constexpr bool operator==(ArgsView lhs, ArgsView rhs) { + return lhs.size() == rhs.size() && + lhs.apply([&rhs](const auto&... lhs_args) { + return rhs.apply([&lhs_args...](const auto&... rhs_args) { + return ((lhs_args == rhs_args) && ...); + }); + }); +} + +template +constexpr bool operator!=(ArgsView lhs, ArgsView rhs) { + return !(lhs == rhs); +} + +template +constexpr bool operator<(ArgsView lhs, ArgsView rhs) { + return lhs.apply([&rhs](const auto&... lhs_args) { + return rhs.apply([&lhs_args...](const auto&... rhs_args) { + return std::tie(lhs_args...) < std::tie(rhs_args...); + }); + }); +} + +template +constexpr bool operator<=(ArgsView lhs, ArgsView rhs) { + return !(rhs < lhs); +} + +template +constexpr bool operator>(ArgsView lhs, ArgsView rhs) { + return rhs < lhs; +} + +template +constexpr bool operator>=(ArgsView lhs, ArgsView rhs) { + return !(lhs < rhs); +} + +template +struct std::hash> { + std::size_t operator()(ArgsView args_view) const { + return args_view.apply([](const auto&... args) { + std::size_t seed = 0; + ((seed ^= std::hash>{}(args) + + 0x9e3779b9 + (seed << 6) + (seed >> 2)), + ...); + return seed; + }); + } +}; + +#ifdef __DEBUG__ +#include +template +void print(Args&&... args) { + ArgsView{std::forward(args)...}.for_each( + [](const auto& arg) { std::cout << arg << ' '; }); + std::cout << '\n'; +} +#endif + +#endif diff --git a/src/atom/type/autotype.hpp b/src/atom/type/autotype.hpp deleted file mode 100644 index 806091e4..00000000 --- a/src/atom/type/autotype.hpp +++ /dev/null @@ -1,158 +0,0 @@ -/* - * type.hpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-12-13 - -Description: A simple type wrapper - -**************************************************/ - -#ifndef ATOM_TYPE_TYPE_HPP -#define ATOM_TYPE_TYPE_HPP - -#include -#include -#include - -namespace Atom::Type { -class Any { -public: - template - Any(const T &value) : m_ptr(new Derived(value)) {} - - template - T cast() const { - return static_cast *>(m_ptr)->m_value; - } - -private: - struct Base { - virtual ~Base() {} - }; - - template - struct Derived : Base { - explicit Derived(const T &value) : m_value(value) {} - T m_value; - }; - - Base *m_ptr; -}; - -// 优化后的自动类型类模板 -template -class AutoType { -public: - explicit AutoType(const T &value) : m_value(value) {} - - template - auto operator+(const AutoType &other) const { - return AutoType(m_value + other.m_value); - } - - template - auto operator-(const AutoType &other) const { - return AutoType(m_value - other.m_value); - } - - template - auto operator*(const AutoType &other) const { - return AutoType(m_value * other.m_value); - } - - template - auto operator/(const AutoType &other) const { - return AutoType(m_value / other.m_value); - } - - template - auto operator%(const AutoType &other) const { - return AutoType(m_value % other.m_value); - } - - template - auto operator==(const AutoType &other) const { - return m_value == other.m_value; - } - - template - auto operator!=(const AutoType &other) const { - return m_value != other.m_value; - } - - template - auto operator<(const AutoType &other) const { - return m_value < other.m_value; - } - - template - auto operator<=(const AutoType &other) const { - return m_value <= other.m_value; - } - - template - auto operator>(const AutoType &other) const { - return m_value > other.m_value; - } - - template - auto operator>=(const AutoType &other) const { - return m_value >= other.m_value; - } - - // 省略其他运算符重载 - - T m_value; // 成员变量 -}; - -// 辅助函数模板,用于创建AutoType对象 -template -AutoType makeAutoType(const T &value) { - return AutoType(value); -} - -// 元组打印类模板 -template > -struct TuplePrinter { - static void print(const Tuple &t) { - if constexpr (N > 1) { - TuplePrinter::print(t); - std::cout << ", " << std::get(t); - } else { - std::cout << std::get<0>(t); - } - } -}; -} // namespace Atom::Type - -#endif - -/* -int main() -{ - AutoType a(2); - AutoType b(3.5); - - auto c = a + b; // AutoType(5.5) - auto d = a - b; // AutoType(-1.5) - auto e = a * b; // AutoType(7.0) - auto f = a / b; // AutoType(0.571429) - - std::cout << std::boolalpha << (a == b) << '\n'; // false - std::cout << std::boolalpha << (a != b) << '\n'; // true - std::cout << std::boolalpha << (a < b) << '\n'; // true - std::cout << std::boolalpha << (a <= b) << '\n'; // true - std::cout << std::boolalpha << (a > b) << '\n'; // false - std::cout << std::boolalpha << (a >= b) << '\n'; // false - - auto t = std::make_tuple(1, 2.5, "Hello"); - TuplePrinter::print(t); // 1, 2.5, Hello - - return 0; -} -*/ \ No newline at end of file diff --git a/src/atom/type/enum_flag.hpp b/src/atom/type/enum_flag.hpp new file mode 100644 index 00000000..f9a7e9a6 --- /dev/null +++ b/src/atom/type/enum_flag.hpp @@ -0,0 +1,144 @@ +/* + * enum_flag.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-3 + +Description: Enum-based flags + +**************************************************/ + +#ifndef ATOM_TYPE_ENUM_FLAG_HPP +#define ATOM_TYPE_ENUM_FLAG_HPP + +#include +#include +#include + +namespace Atom::Type { + +template +concept EnumType = std::is_enum_v; + +//! Enum-based flags false checker +template +struct IsEnumFlags : public std::false_type {}; + +#define ENUM_FLAGS(type) \ + using Atom::Type::operator&; \ + using Atom::Type::operator|; \ + using Atom::Type::operator^; \ + namespace Atom::Type { \ + template <> \ + struct IsEnumFlags : std::true_type {}; \ + } + +template +class Flags { + //! Enum underlying type + // typedef typename std::make_unsigned::type>::type type; + using type = + std::make_unsigned_t>; // 简化了类型定义 + +public: + Flags() noexcept : _value(0) {} + Flags(type value) noexcept : _value(value) {} + Flags(TEnum value) noexcept : _value((type)value) {} + Flags(const Flags &) noexcept = default; + Flags(Flags &&) noexcept = default; + ~Flags() noexcept = default; + + Flags &operator=(type value) noexcept { + _value = value; + return *this; + } + Flags &operator=(TEnum value) noexcept { + _value = (type)value; + return *this; + } + Flags &operator=(const Flags &) noexcept = default; + Flags &operator=(Flags &&) noexcept = default; + + //! Is any flag set? + explicit operator bool() const noexcept { return isset(); } + + //! Is no flag set? + bool operator!() const noexcept { return !isset(); } + + //! Reverse all flags + Flags operator~() const noexcept { return Flags(~_value); } + + //! Flags logical assign operators + Flags &operator&=(const Flags &flags) noexcept { + _value &= flags._value; + return *this; + } + Flags &operator|=(const Flags &flags) noexcept { + _value |= flags._value; + return *this; + } + Flags &operator^=(const Flags &flags) noexcept { + _value ^= flags._value; + return *this; + } + + //! Flags logical friend operators + friend Flags operator&(const Flags &flags1, const Flags &flags2) noexcept { + return Flags(flags1._value & flags2._value); + } + friend Flags operator|(const Flags &flags1, const Flags &flags2) noexcept { + return Flags(flags1._value | flags2._value); + } + friend Flags operator^(const Flags &flags1, const Flags &flags2) noexcept { + return Flags(flags1._value ^ flags2._value); + } + + // Flags comparison + friend bool operator==(const Flags &flags1, const Flags &flags2) noexcept { + return flags1._value == flags2._value; + } + friend bool operator!=(const Flags &flags1, const Flags &flags2) noexcept { + return flags1._value != flags2._value; + } + + //! Convert to the enum value + operator TEnum() const noexcept { return (TEnum)_value; } + + //! Is any flag set? + bool isset() const noexcept { return (_value != 0); } + //! Is the given flag set? + bool isset(type value) const noexcept { return (_value & value) != 0; } + //! Is the given flag set? + bool isset(TEnum value) const noexcept { + return (_value & (type)value) != 0; + } + + //! Get the enum value + TEnum value() const noexcept { return (TEnum)_value; } + //! Get the underlying enum value + type underlying() const noexcept { return _value; } + //! Get the bitset value + std::bitset bitset() const noexcept { return {_value}; } + + //! Swap two instances + void swap(Flags &flags) noexcept { + using std::swap; + swap(_value, flags._value); + } + template + friend void swap(Flags &flags1, Flags &flags2) noexcept; + +private: + type _value; +}; + +} // namespace Atom::Type + +#include "enum_flag.inl" + +#endif diff --git a/src/atom/type/enum_flag.inl b/src/atom/type/enum_flag.inl new file mode 100644 index 00000000..ea3c3d54 --- /dev/null +++ b/src/atom/type/enum_flag.inl @@ -0,0 +1,40 @@ +/* + * enum_flag.inl + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-3 + +Description: Enum-based flags + +**************************************************/ + +namespace Atom::Type { + +template +inline void swap(Flags& flags1, Flags& flags2) noexcept { + flags1.swap(flags2); +} + +template +constexpr auto operator&(TEnum value1, TEnum value2) noexcept -> + typename std::enable_if::value, Flags>::type { + return Flags(value1) & value2; +} + +template +constexpr auto operator|(TEnum value1, TEnum value2) noexcept -> + typename std::enable_if::value, Flags>::type { + return Flags(value1) | value2; +} + +template +constexpr auto operator^(TEnum value1, TEnum value2) noexcept -> + typename std::enable_if::value, Flags>::type { + return Flags(value1) ^ value2; +} + +} // namespace Atom::Type diff --git a/src/atom/type/flatset.hpp b/src/atom/type/flatset.hpp new file mode 100644 index 00000000..232c63db --- /dev/null +++ b/src/atom/type/flatset.hpp @@ -0,0 +1,269 @@ +/* + * flatset.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-16 + +Description: A FlatSet + +**************************************************/ + +#ifndef ATOM_TYPE_FLAT_SET_HPP +#define ATOM_TYPE_FLAT_SET_HPP + +#include +#include +#include +#include +#include +#include +#include + +template > +class FlatSet { +public: + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = typename std::vector::const_iterator; + using const_iterator = typename std::vector::const_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + using key_compare = Compare; + using value_compare = Compare; + +private: + std::vector m_data; + Compare m_comp; + +public: + FlatSet() = default; + + explicit FlatSet(const Compare& comp) : m_comp(comp) {} + + template + FlatSet(InputIt first, InputIt last, const Compare& comp = Compare()) + : m_data(first, last), m_comp(comp) { + std::sort(m_data.begin(), m_data.end(), m_comp); + m_data.erase(std::unique(m_data.begin(), m_data.end()), m_data.end()); + } + + FlatSet(std::initializer_list init, const Compare& comp = Compare()) + : FlatSet(init.begin(), init.end(), comp) {} + + FlatSet(const FlatSet& other) = default; + FlatSet(FlatSet&& other) noexcept = default; + + FlatSet& operator=(const FlatSet& other) = default; + FlatSet& operator=(FlatSet&& other) noexcept = default; + + iterator begin() noexcept { return m_data.begin(); } + const_iterator begin() const noexcept { return m_data.begin(); } + const_iterator cbegin() const noexcept { return m_data.cbegin(); } + + iterator end() noexcept { return m_data.end(); } + const_iterator end() const noexcept { return m_data.end(); } + const_iterator cend() const noexcept { return m_data.cend(); } + + reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator(end()); + } + const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator(cend()); + } + + reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + const_reverse_iterator rend() const noexcept { + return const_reverse_iterator(begin()); + } + const_reverse_iterator crend() const noexcept { + return const_reverse_iterator(cbegin()); + } + + bool empty() const noexcept { return m_data.empty(); } + size_type size() const noexcept { return m_data.size(); } + size_type max_size() const noexcept { return m_data.max_size(); } + + void clear() noexcept { m_data.clear(); } + + std::pair insert(const T& value) { + auto it = std::lower_bound(m_data.begin(), m_data.end(), value, m_comp); + if (it != m_data.end() && !m_comp(value, *it)) { + return {it, false}; + } + return {m_data.insert(it, value), true}; + } + + std::pair insert(T&& value) { + auto it = std::lower_bound(m_data.begin(), m_data.end(), value, m_comp); + if (it != m_data.end() && !m_comp(value, *it)) { + return {it, false}; + } + return {m_data.insert(it, std::move(value)), true}; + } + + iterator insert(const_iterator hint, const T& value) { + if (hint == m_data.end() || m_comp(value, *hint)) { + if (hint == m_data.begin() || m_comp(*(hint - 1), value)) { + return m_data.insert(hint, value); + } + return insert(value).first; + } + return hint; + } + + iterator insert(const_iterator hint, T&& value) { + if (hint == m_data.end() || m_comp(value, *hint)) { + if (hint == m_data.begin() || m_comp(*(hint - 1), value)) { + return m_data.insert(hint, std::move(value)); + } + return insert(std::move(value)).first; + } + return hint; + } + + template + void insert(InputIt first, InputIt last) { + while (first != last) { + insert(*first++); + } + } + + void insert(std::initializer_list ilist) { + insert(ilist.begin(), ilist.end()); + } + + template + std::pair emplace(Args&&... args) { + return insert(T(std::forward(args)...)); + } + + template + iterator emplace_hint(const_iterator hint, Args&&... args) { + return insert(hint, T(std::forward(args)...)); + } + + iterator erase(const_iterator pos) { return m_data.erase(pos); } + + iterator erase(const_iterator first, const_iterator last) { + return m_data.erase(first, last); + } + + size_type erase(const T& value) { + auto it = find(value); + if (it != m_data.end()) { + erase(it); + return 1; + } + return 0; + } + + void swap(FlatSet& other) noexcept { + std::swap(m_data, other.m_data); + std::swap(m_comp, other.m_comp); + } + + size_type count(const T& value) const { + return find(value) != m_data.end() ? 1 : 0; + } + + iterator find(const T& value) { + auto it = std::lower_bound(m_data.begin(), m_data.end(), value, m_comp); + if (it != m_data.end() && !m_comp(value, *it)) { + return it; + } + return m_data.end(); + } + + const_iterator find(const T& value) const { + auto it = std::lower_bound(m_data.begin(), m_data.end(), value, m_comp); + if (it != m_data.end() && !m_comp(value, *it)) { + return it; + } + return m_data.end(); + } + + std::pair equal_range(const T& value) { + return std::equal_range(m_data.begin(), m_data.end(), value, m_comp); + } + + std::pair equal_range( + const T& value) const { + return std::equal_range(m_data.begin(), m_data.end(), value, m_comp); + } + + iterator lower_bound(const T& value) { + return std::lower_bound(m_data.begin(), m_data.end(), value, m_comp); + } + + const_iterator lower_bound(const T& value) const { + return std::lower_bound(m_data.begin(), m_data.end(), value, m_comp); + } + + iterator upper_bound(const T& value) { + return std::upper_bound(m_data.begin(), m_data.end(), value, m_comp); + } + + const_iterator upper_bound(const T& value) const { + return std::upper_bound(m_data.begin(), m_data.end(), value, m_comp); + } + + key_compare key_comp() const { return m_comp; } + + value_compare value_comp() const { return m_comp; } + + bool contains(const T& value) const { return find(value) != m_data.end(); } +}; + +template +bool operator==(const FlatSet& lhs, + const FlatSet& rhs) { + return lhs.size() == rhs.size() && + std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template +bool operator!=(const FlatSet& lhs, + const FlatSet& rhs) { + return !(lhs == rhs); +} + +template +bool operator<(const FlatSet& lhs, const FlatSet& rhs) { + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), + rhs.end()); +} + +template +bool operator<=(const FlatSet& lhs, + const FlatSet& rhs) { + return !(rhs < lhs); +} + +template +bool operator>(const FlatSet& lhs, const FlatSet& rhs) { + return rhs < lhs; +} + +template +bool operator>=(const FlatSet& lhs, + const FlatSet& rhs) { + return !(lhs < rhs); +} + +template +void swap(FlatSet& lhs, + FlatSet& rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} + +#endif // ATOM_TYPE_FLAT_SET_HPP \ No newline at end of file diff --git a/src/atom/type/meson.build b/src/atom/type/meson.build new file mode 100644 index 00000000..dfa592d4 --- /dev/null +++ b/src/atom/type/meson.build @@ -0,0 +1,54 @@ +project('Atom-Type', 'cpp') + +# Define sources and headers +sources = [ + 'args.cpp', + 'ini.cpp', + 'message.cpp' +] +headers = [ + 'abi.hpp', + 'args.hpp', + 'enum_flag.hpp', + 'enum_flag.inl', + 'flatset.hpp', + 'ini_impl.hpp', + 'ini.hpp', + 'json.hpp', + 'message.hpp', + 'pointer.hpp', + 'small_vector.hpp' +] + +# Create object library +atom_type_obj = library('atom_type', + sources + headers, + cpp_std: c_std +) + +# Create static library +atom_type_static = static_library('atom_type_static', atom_type_obj) + +# Set version properties +version = '1.0.0' # You should define your version here +soversion = '1' + +atom_type_static.version = version +atom_type_static.soversion = soversion +atom_type_static.basename = 'Atom-Type' + +# Link to other libraries if necessary +atom_type_static_link_libs = [] +foreach lib : ['atom-utils'] + atom_type_static_link_libs += dependency(lib) +endforeach + +atom_type_static_link_libs = [] +foreach lib : ['atom-utils'] + atom_type_static_link_libs += dependency(lib) +endforeach + +atom_type_static.link_with(atom_type_static_link_libs) + +# Install target +install_targets(atom_type_static) diff --git a/src/atom/type/no_offset_ptr.hpp b/src/atom/type/no_offset_ptr.hpp new file mode 100644 index 00000000..f15368d5 --- /dev/null +++ b/src/atom/type/no_offset_ptr.hpp @@ -0,0 +1,148 @@ +/* + * no_offset_ptr.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-3 + +Description: No Offset Pointer + +**************************************************/ + +#ifndef ATOM_TYPE_NoOffsetPtr_HPP +#define ATOM_TYPE_NoOffsetPtr_HPP + +#include +#include +#include +#include +#include +#include + +template +class UnshiftedPtr { +public: + UnshiftedPtr() { new (&storage) T; } + + UnshiftedPtr(const T& value) { new (&storage) T(value); } + + UnshiftedPtr(const UnshiftedPtr& other) { new (&storage) T(get()); } + + UnshiftedPtr(UnshiftedPtr&& other) { new (&storage) T(std::move(get())); } + + ~UnshiftedPtr() { get().~T(); } + + UnshiftedPtr& operator=(const UnshiftedPtr& other) { + if (this != &other) { + get() = other.get(); + } + return *this; + } + + UnshiftedPtr& operator=(UnshiftedPtr&& other) { + if (this != &other) { + get() = std::move(other.get()); + } + return *this; + } + + T* operator->() { return &get(); } + + const T* operator->() const { return &get(); } + + T& operator*() { return get(); } + + const T& operator*() const { return get(); } + +private: + T& get() { return reinterpret_cast(storage); } + + const T& get() const { return reinterpret_cast(storage); } + + std::aligned_storage_t storage; +}; + +template +class NoOffsetPtr { +public: + constexpr NoOffsetPtr() noexcept = default; + constexpr NoOffsetPtr(std::nullptr_t) noexcept {} + + constexpr explicit NoOffsetPtr(T* ptr) noexcept : ptr_(ptr) {} + + constexpr NoOffsetPtr(const NoOffsetPtr&) noexcept = default; + constexpr NoOffsetPtr& operator=(const NoOffsetPtr&) noexcept = default; + + constexpr NoOffsetPtr(NoOffsetPtr&&) noexcept = default; + constexpr NoOffsetPtr& operator=(NoOffsetPtr&&) noexcept = default; + + constexpr T& operator*() const noexcept { return *ptr_; } + constexpr T* operator->() const noexcept { return ptr_; } + + constexpr explicit operator bool() const noexcept { + return ptr_ != nullptr; + } + + constexpr T* get() const noexcept { return ptr_; } + + constexpr void reset(T* ptr = nullptr) noexcept { ptr_ = ptr; } + + constexpr void swap(NoOffsetPtr& other) noexcept { + std::swap(ptr_, other.ptr_); + } + + NoOffsetPtr& operator++() = delete; + NoOffsetPtr& operator--() = delete; + NoOffsetPtr operator++(int) = delete; + NoOffsetPtr operator--(int) = delete; + NoOffsetPtr& operator+=(std::ptrdiff_t) = delete; + NoOffsetPtr& operator-=(std::ptrdiff_t) = delete; + NoOffsetPtr operator+(std::ptrdiff_t) const = delete; + NoOffsetPtr operator-(std::ptrdiff_t) const = delete; + std::ptrdiff_t operator-(const NoOffsetPtr&) const = delete; + +private: + T* ptr_ = nullptr; +}; + +template +constexpr bool operator==(const NoOffsetPtr& lhs, + const NoOffsetPtr& rhs) noexcept { + return lhs.get() == rhs.get(); +} + +template +constexpr bool operator==(const NoOffsetPtr& lhs, std::nullptr_t) noexcept { + return lhs.get() == nullptr; +} + +template +constexpr bool operator==(std::nullptr_t, const NoOffsetPtr& rhs) noexcept { + return nullptr == rhs.get(); +} + +template +constexpr bool operator!=(const NoOffsetPtr& lhs, + const NoOffsetPtr& rhs) noexcept { + return !(lhs == rhs); +} + +template +constexpr bool operator!=(const NoOffsetPtr& lhs, std::nullptr_t) noexcept { + return !(lhs == nullptr); +} + +template +constexpr bool operator!=(std::nullptr_t, const NoOffsetPtr& rhs) noexcept { + return !(nullptr == rhs); +} + +template +constexpr void swap(NoOffsetPtr& lhs, NoOffsetPtr& rhs) noexcept { + lhs.swap(rhs); +} + +#endif diff --git a/src/atom/type/pointer.hpp b/src/atom/type/pointer.hpp new file mode 100644 index 00000000..7c1aa05a --- /dev/null +++ b/src/atom/type/pointer.hpp @@ -0,0 +1,134 @@ +/* + * pointer.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-3 + +Description: Pointer Sentinel for Atom + +**************************************************/ + +#ifndef ATOM_TYPE_POINTER_HPP +#define ATOM_TYPE_POINTER_HPP + +#include +#include +#include +#include +#include + +/** + * @brief Concept to check if a type is a pointer type, including raw pointers, + * std::shared_ptr, and std::unique_ptr. + * + * @tparam T The type to check. + */ +template +concept PointerType = + std::is_pointer_v || + std::is_same_v::type>> || + std::is_same_v::type>>; + +/** + * @brief A class template to hold different types of pointers using + * std::variant. + * + * @tparam T The type of the pointed-to object. + */ +template +class PointerSentinel { +public: + // Using std::variant to store different types of pointers + std::variant, std::unique_ptr, T*> ptr; + + /** + * @brief Construct a new Pointer Sentinel object from a shared pointer. + * + * @param p The shared pointer. + */ + explicit PointerSentinel(std::shared_ptr p) : ptr(p) {} + + /** + * @brief Construct a new Pointer Sentinel object from a unique pointer. + * + * @param p The unique pointer. + */ + explicit PointerSentinel(std::unique_ptr&& p) : ptr(std::move(p)) {} + + /** + * @brief Construct a new Pointer Sentinel object from a raw pointer. + * + * @param p The raw pointer. + */ + explicit PointerSentinel(T* p) : ptr(p) {} + + /** + * @brief Copy constructor. + * + * @param other The other Pointer Sentinel object to copy from. + */ + PointerSentinel(const PointerSentinel& other) + : ptr(std::visit( + [](const auto& p) + -> std::variant, std::unique_ptr, T*> { + if constexpr (std::is_same_v, + std::shared_ptr>) { + return p; + } else if constexpr (std::is_same_v, + std::unique_ptr>) { + return std::make_unique(*p); + } else { + return new T(*p); + } + }, + other.ptr)) {} + + /** + * @brief Get the raw pointer stored in the variant. + * + * @return T* The raw pointer. + */ + [[nodiscard]] T* get() const { + return std::visit( + [](auto&& arg) -> T* { + using U = std::decay_t; + if constexpr (std::is_pointer_v) { + return arg; // 原始指针 + } else { + return arg.get(); // 智能指针 + } + }, + ptr); + } + + /** + * @brief Helper method to invoke member functions on the pointed-to object. + * + * @tparam Func The type of the member function pointer. + * @tparam Args The types of the arguments to the member function. + * @param func The member function pointer. + * @param args The arguments to the member function. + * @return auto The return type of the member function. + */ + template + [[nodiscard]] auto invoke(Func func, Args&&... args) { + static_assert(std::is_member_function_pointer_v, + "Func must be a member function pointer"); + return std::visit( + [func, &args...](auto&& arg) -> decltype(auto) { + using U = std::decay_t; + if constexpr (std::is_pointer_v) { + return ((*arg).*func)(std::forward(args)...); + } else { + return ((*arg.get()).*func)(std::forward(args)...); + } + }, + ptr); + } +}; + +#endif diff --git a/src/atom/type/small_list.hpp b/src/atom/type/small_list.hpp new file mode 100644 index 00000000..d86570b3 --- /dev/null +++ b/src/atom/type/small_list.hpp @@ -0,0 +1,252 @@ +/* + * small_list.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-12-17 + +Description: A Small List Implementation + +**************************************************/ + +#ifndef ATOM_TYPE_SMALL_LIST_HPP +#define ATOM_TYPE_SMALL_LIST_HPP + +#include +#include +#include +#include + +template +class small_list { +private: + struct Node { + T data; + Node* prev; + Node* next; + + Node(const T& value) : data(value), prev(nullptr), next(nullptr) {} + }; + + Node* head; + Node* tail; + size_t list_size; + +public: + small_list() : head(nullptr), tail(nullptr), list_size(0) {} + + small_list(std::initializer_list init_list) : small_list() { + for (const auto& value : init_list) { + push_back(value); + } + } + + ~small_list() { clear(); } + + void push_back(const T& value) { + Node* new_node = new Node(value); + if (empty()) { + head = tail = new_node; + } else { + tail->next = new_node; + new_node->prev = tail; + tail = new_node; + } + ++list_size; + } + + void push_front(const T& value) { + Node* new_node = new Node(value); + if (empty()) { + head = tail = new_node; + } else { + head->prev = new_node; + new_node->next = head; + head = new_node; + } + ++list_size; + } + + void pop_back() { + if (!empty()) { + Node* temp = tail; + tail = tail->prev; + if (tail) { + tail->next = nullptr; + } else { + head = nullptr; + } + delete temp; + --list_size; + } + } + + void pop_front() { + if (!empty()) { + Node* temp = head; + head = head->next; + if (head) { + head->prev = nullptr; + } else { + tail = nullptr; + } + delete temp; + --list_size; + } + } + + T& front() { return head->data; } + + const T& front() const { return head->data; } + + T& back() { return tail->data; } + + const T& back() const { return tail->data; } + + bool empty() const { return list_size == 0; } + + size_t size() const { return list_size; } + + void clear() { + while (!empty()) { + pop_front(); + } + } + + class iterator { + public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using reference = T&; + + iterator(Node* ptr = nullptr) : node_ptr(ptr) {} + + iterator& operator++() { + node_ptr = node_ptr->next; + return *this; + } + + iterator operator++(int) { + iterator temp = *this; + ++(*this); + return temp; + } + + iterator& operator--() { + node_ptr = node_ptr->prev; + return *this; + } + + iterator operator--(int) { + iterator temp = *this; + --(*this); + return temp; + } + + bool operator==(const iterator& other) const { + return node_ptr == other.node_ptr; + } + + bool operator!=(const iterator& other) const { + return !(*this == other); + } + + T& operator*() const { return node_ptr->data; } + + T* operator->() const { return &(node_ptr->data); } + + Node* node_ptr; + }; + + iterator begin() { return iterator(head); } + + iterator end() { return iterator(nullptr); } + + void insert(iterator pos, const T& value) { + if (pos == begin()) { + push_front(value); + } else if (pos == end()) { + push_back(value); + } else { + Node* new_node = new Node(value); + Node* prev_node = pos.node_ptr->prev; + new_node->prev = prev_node; + new_node->next = pos.node_ptr; + prev_node->next = new_node; + pos.node_ptr->prev = new_node; + ++list_size; + } + } + + iterator erase(iterator pos) { + if (pos == begin()) { + pop_front(); + return begin(); + } else if (pos == --end()) { + pop_back(); + return end(); + } else { + Node* prev_node = pos.node_ptr->prev; + Node* next_node = pos.node_ptr->next; + prev_node->next = next_node; + next_node->prev = prev_node; + delete pos.node_ptr; + --list_size; + return iterator(next_node); + } + } + + void remove(const T& value) { + for (auto it = begin(); it != end();) { + if (*it == value) { + it = erase(it); + } else { + ++it; + } + } + } + + void unique() { + for (auto it = begin(); it != end();) { + auto next_it = std::next(it); + if (next_it != end() && *it == *next_it) { + it = erase(next_it); + } else { + ++it; + } + } + } + + void sort() { + if (size() <= 1) { + return; + } + small_list temp; + while (!empty()) { + auto it = begin(); + T min_val = *it; + for (auto curr = ++begin(); curr != end(); ++curr) { + if (*curr < min_val) { + min_val = *curr; + it = curr; + } + } + temp.push_back(min_val); + erase(it); + } + swap(temp); + } + + void swap(small_list& other) { + std::swap(head, other.head); + std::swap(tail, other.tail); + std::swap(list_size, other.list_size); + } +}; + +#endif // ATOM_TYPE_SMALL_LIST_HPP \ No newline at end of file diff --git a/src/atom/type/small_vector.hpp b/src/atom/type/small_vector.hpp index 4640f31d..a27fdc16 100644 --- a/src/atom/type/small_vector.hpp +++ b/src/atom/type/small_vector.hpp @@ -15,114 +15,307 @@ Description: A Small Vector Implementation #ifndef ATOM_TYPE_SMALL_VECTOR_HPP #define ATOM_TYPE_SMALL_VECTOR_HPP -#include -#include +#include +#include +#include +#include +#include +#include -template +// 内部存储的最大容量 +constexpr std::size_t InternalBufferSize = 16; + +template class SmallVector { public: - // 构造函数 - SmallVector() : m_size(0), m_capacity(N) { m_data = m_inlineData; } - - // 复制构造函数 - SmallVector(const SmallVector &other) - : m_size(other.m_size), m_capacity(other.m_capacity) { - if (other.m_size > N) { - m_data = new T[other.m_capacity]; - std::copy(other.m_data, other.m_data + other.m_size, m_data); - } else { - m_data = m_inlineData; - std::copy(other.m_data, other.m_data + other.m_size, m_data); - } + SmallVector() noexcept : m_size(0), m_isExternal(false) {} + + SmallVector(std::initializer_list ilist) : SmallVector() { + reserve(ilist.size()); + std::uninitialized_copy(ilist.begin(), ilist.end(), + m_isExternal ? m_external : m_internal); + m_size = ilist.size(); } - // 移动构造函数 - SmallVector(SmallVector &&other) noexcept - : m_size(other.m_size), - m_capacity(other.m_capacity), - m_data(other.m_data) { - if (other.m_size > N) { - other.m_data = nullptr; - } else { - std::copy(other.m_inlineData, other.m_inlineData + other.m_size, - m_inlineData); - } + SmallVector(const SmallVector& other) : SmallVector() { + reserve(other.m_size); + std::uninitialized_copy(other.begin(), other.end(), + m_isExternal ? m_external : m_internal); + m_size = other.m_size; } - // 析构函数 - ~SmallVector() { - if (m_data != m_inlineData) { - delete[] m_data; - } + SmallVector(SmallVector&& other) noexcept : SmallVector() { + moveAssign(std::move(other)); } - // 赋值操作符重载 - SmallVector &operator=(const SmallVector &other) { + ~SmallVector() { clear(); } + + SmallVector& operator=(const SmallVector& other) { if (this != &other) { + clear(); + reserve(other.m_size); + std::uninitialized_copy(other.begin(), other.end(), + m_isExternal ? m_external : m_internal); m_size = other.m_size; - m_capacity = other.m_capacity; - if (other.m_size > N) { - if (m_data != m_inlineData) { - delete[] m_data; - } - m_data = new T[other.m_capacity]; - std::copy(other.m_data, other.m_data + other.m_size, m_data); - } else { - if (m_data != m_inlineData) { - delete[] m_data; - } - m_data = m_inlineData; - std::copy(other.m_data, other.m_data + other.m_size, m_data); - } } return *this; } - // 添加元素 - void push_back(const T &value) { - if (m_size == m_capacity) { - if (m_capacity == N) { - m_capacity *= 2; - m_data = new T[m_capacity]; - std::copy(m_inlineData, m_inlineData + m_size, m_data); + SmallVector& operator=(SmallVector&& other) noexcept { + if (this != &other) { + moveAssign(std::move(other)); + } + return *this; + } + + T& operator[](std::size_t index) { + return const_cast(static_cast(*this)[index]); + } + + const T& operator[](std::size_t index) const { + return m_isExternal ? m_external[index] : m_internal[index]; + } + + bool operator==(const SmallVector& other) const { + if (m_size != other.m_size) { + return false; + } + for (std::size_t i = 0; i < m_size; ++i) { + if ((*this)[i] != other[i]) { + return false; + } + } + return true; + } + + bool operator!=(const SmallVector& other) const { + return !(*this == other); + } + + T* data() noexcept { return m_isExternal ? m_external : m_internal; } + + const T* data() const noexcept { + return m_isExternal ? m_external : m_internal; + } + + std::size_t size() const noexcept { return m_size; } + + std::size_t capacity() const noexcept { + return m_isExternal ? m_externalCapacity : N; + } + + bool empty() const noexcept { return m_size == 0; } + + void clear() noexcept { + if (m_isExternal) { + std::destroy(m_external, m_external + m_size); + ::operator delete(m_external, m_externalCapacity * sizeof(T)); + m_external = nullptr; + m_externalCapacity = 0; + } else { + std::destroy(m_internal, m_internal + m_size); + } + m_size = 0; + m_isExternal = false; + } + + void reserve(std::size_t newCapacity) { + if (newCapacity <= capacity()) { + return; + } + + if (newCapacity > N) { + if (m_isExternal) { + m_external = + static_cast(::operator new(newCapacity * sizeof(T))); + std::uninitialized_copy( + std::make_move_iterator(m_external), + std::make_move_iterator(m_external + m_size), m_external); + std::destroy(m_external, m_external + m_size); + ::operator delete(m_external, m_externalCapacity * sizeof(T)); + m_externalCapacity = newCapacity; } else { - m_capacity *= 2; - T *newData = new T[m_capacity]; - std::copy(m_data, m_data + m_size, newData); - delete[] m_data; - m_data = newData; + T* newBuffer = + static_cast(::operator new(newCapacity * sizeof(T))); + std::uninitialized_copy( + std::make_move_iterator(m_internal), + std::make_move_iterator(m_internal + m_size), newBuffer); + std::destroy(m_internal, m_internal + m_size); + m_external = newBuffer; + m_externalCapacity = newCapacity; + m_isExternal = true; } } - m_data[m_size++] = value; } - // 删除最后一个元素 - void pop_back() { - if (m_size > 0) { - --m_size; + void resize(std::size_t newSize) { + reserve(newSize); + if (newSize > m_size) { + std::uninitialized_fill( + m_isExternal ? m_external + m_size : m_internal + m_size, + m_isExternal ? m_external + newSize : m_internal + newSize, + T()); + } else { + std::destroy( + m_isExternal ? m_external + newSize : m_internal + newSize, + m_isExternal ? m_external + m_size : m_internal + m_size); } + m_size = newSize; } - // 清空向量 - void clear() { m_size = 0; } + void push_back(const T& value) { + reserve(m_size + 1); + ::new (m_isExternal ? m_external + m_size : m_internal + m_size) + T(value); + ++m_size; + } + + void push_back(T&& value) { + reserve(m_size + 1); + ::new (m_isExternal ? m_external + m_size : m_internal + m_size) + T(std::move(value)); + ++m_size; + } + + template + T& emplace_back(Args&&... args) { + reserve(m_size + 1); + T* newElement = + m_isExternal ? m_external + m_size : m_internal + m_size; + ::new (newElement) T(std::forward(args)...); + ++m_size; + return *newElement; + } + + void pop_back() { + --m_size; + std::destroy_at(m_isExternal ? m_external + m_size + : m_internal + m_size); + } + +public: + // 迭代器类型别名 + using iterator = T*; + using const_iterator = const T*; + + // 迭代器方法 + iterator begin() noexcept { return data(); } + + const_iterator begin() const noexcept { return data(); } + + iterator end() noexcept { return data() + m_size; } + + const_iterator end() const noexcept { return data() + m_size; } + + const_iterator cbegin() const noexcept { return begin(); } + + const_iterator cend() const noexcept { return end(); } + + // 反向迭代器方法 + std::reverse_iterator rbegin() noexcept { + return std::reverse_iterator(end()); + } + + std::reverse_iterator rbegin() const noexcept { + return std::reverse_iterator(end()); + } + + std::reverse_iterator rend() noexcept { + return std::reverse_iterator(begin()); + } - // 获取元素个数 - size_t size() const { return m_size; } + std::reverse_iterator rend() const noexcept { + return std::reverse_iterator(begin()); + } - // 获取容量大小 - size_t capacity() const { return m_capacity; } + std::reverse_iterator crbegin() const noexcept { + return rbegin(); + } - // 判断向量是否为空 - bool empty() const { return m_size == 0; } + std::reverse_iterator crend() const noexcept { + return rend(); + } - // 通过索引访问元素 - T &operator[](size_t index) { return m_data[index]; } +public: + // 三路比较运算符 +public: + /* + // 三路比较运算符 + auto operator<=>(const SmallVector& other) const { + if constexpr (std::is_arithmetic_v) { + // 对于内置算术类型,使用 std::is_eq 和 std::is_neq + for (std::size_t i = 0; i < std::min(m_size, other.m_size); ++i) + { if (std::is_neq((*this)[i], other[i])) { return (*this)[i] < other[i] ? + std::partial_ordering::less : (*this)[i] > other[i] ? + std::partial_ordering::greater : std::partial_ordering::unordered; + } + } + + return m_size < other.m_size ? std::partial_ordering::less + : m_size > other.m_size ? std::partial_ordering::greater + : + std::partial_ordering::equivalent; } else { + // 对于自定义类型,使用 operator<=> + using BuiltinThreeWay = + std::partial_ordering (*)(const T&, const T&); + BuiltinThreeWay cmp = &T::operator<=>; + + for (std::size_t i = 0; i < std::min(m_size, other.m_size); ++i) + { auto result = cmp((*this)[i], other[i]); if (result != + std::partial_ordering::equivalent) { return result; + } + } + + return m_size < other.m_size ? std::partial_ordering::less + : m_size > other.m_size ? std::partial_ordering::greater + : + std::partial_ordering::equivalent; + } + } + */ + +public: + static std::string partialOrderingToString(std::partial_ordering order) { + switch (order) { + case std::partial_ordering::less: + return "less"; + case std::partial_ordering::equivalent: + return "equivalent"; + case std::partial_ordering::greater: + return "greater"; + case std::partial_ordering::unordered: + return "unordered"; + default: + return "unknown"; + } + } private: - size_t m_size; // 元素个数 - size_t m_capacity; // 容量大小 - T *m_data; // 存储数据的指针 - T m_inlineData[N]; // 内置的数据存储空间 + void moveAssign(SmallVector&& other) noexcept { + clear(); + if (other.m_isExternal) { + m_external = other.m_external; + m_externalCapacity = other.m_externalCapacity; + other.m_external = nullptr; + other.m_externalCapacity = 0; + } else { + std::uninitialized_copy( + std::make_move_iterator(other.m_internal), + std::make_move_iterator(other.m_internal + other.m_size), + m_internal); + } + m_size = other.m_size; + m_isExternal = other.m_isExternal; + other.m_size = 0; + other.m_isExternal = false; + } + + alignas(T) std::byte m_internalBuffer[N * sizeof(T)]; + T* m_internal = reinterpret_cast(m_internalBuffer); + T* m_external = nullptr; + std::size_t m_externalCapacity = 0; + std::size_t m_size = 0; + bool m_isExternal = false; }; #endif diff --git a/src/atom/type/trackable.hpp b/src/atom/type/trackable.hpp new file mode 100644 index 00000000..30382ac9 --- /dev/null +++ b/src/atom/type/trackable.hpp @@ -0,0 +1,142 @@ +#ifndef ATOM_TYPE_TRACKABLE_HPP +#define ATOM_TYPE_TRACKABLE_HPP + +#include +#include +#include +#include +#include + +#include "atom/error/exception.hpp" +#include "atom/utils/cstring.hpp" + +/** + * @brief A class template for creating trackable objects that notify observers + * when their value changes. + * + * @tparam T The type of the value being tracked. + */ +template +class Trackable { +public: + /** + * @brief Constructor to initialize the trackable object with an initial + * value. + * + * @param initialValue The initial value of the trackable object. + */ + explicit Trackable(T initialValue) + : value_(std::move(initialValue)), notifyDeferred_(false) {} + + /** + * @brief Subscribe a callback function to be called when the value changes. + * + * @param onChange The callback function to be called when the value + * changes. It takes two const references: the old value and the new value. + */ + void subscribe(std::function onChange) { + std::scoped_lock lock(mutex_); + observers_.emplace_back(std::move(onChange)); + } + + /** + * @brief Unsubscribe all observer functions. + */ + void unsubscribeAll() { + std::scoped_lock lock(mutex_); + observers_.clear(); + } + + /** + * @brief Overloaded assignment operator to update the value and notify + * observers. + * + * @param newValue The new value to be assigned. + * @return Trackable& Reference to the trackable object. + */ + Trackable& operator=(T newValue) { + std::scoped_lock lock(mutex_); + if (value_ != newValue) { + T oldValue = std::exchange(value_, std::move(newValue)); + if (!notifyDeferred_) { + notifyObservers(oldValue, value_); + } + } + return *this; + } + + /** + * @brief Control whether notifications are deferred or not. + * + * @param defer If true, notifications will be deferred until + * deferNotifications(false) is called. + */ + void deferNotifications(bool defer) { + std::scoped_lock lock(mutex_); + notifyDeferred_ = defer; + if (!defer) { + notifyObservers(std::exchange(lastOldValue_, value_), value_); + } + } + + /** + * @brief Overloaded += operator to increment the value and notify + * observers. + * + * @param rhs The value to be added. + * @return Trackable& Reference to the trackable object. + */ + Trackable& operator+=(const T& rhs) { + std::scoped_lock lock(mutex_); + T newValue = value_ + rhs; + if (value_ != newValue) { + T oldValue = std::exchange(value_, std::move(newValue)); + if (!notifyDeferred_) { + notifyObservers(oldValue, value_); + } + } + return *this; + } + + /** + * @brief Conversion operator to convert the trackable object to its value + * type. + * + * @return T The value of the trackable object. + */ + operator T() const { + std::scoped_lock lock(mutex_); + return value_; + } + +private: + T value_; ///< The stored value. + std::vector> + observers_; ///< List of observer functions. + mutable std::mutex mutex_; ///< Mutex for thread safety. + bool notifyDeferred_; ///< Flag to control deferred notifications. + T lastOldValue_; ///< Last old value for deferred notifications. + + /** + * @brief Notifies all observers about the value change. + * + * @param oldVal The old value. + * @param newVal The new value. + */ + void notifyObservers(const T& oldVal, const T& newVal) { + auto localObservers = observers_; + mutex_.unlock(); // 解锁,以避免在调用回调时持有锁 + for (const auto& observer : localObservers) { + try { + observer(oldVal, newVal); + } catch (const std::exception& e) { + THROW_EXCEPTION(concat("Exception in observer.", e.what())); + } catch (...) { + THROW_EXCEPTION("Unknown exception in observer."); + } + } + mutex_.lock(); + } +}; + +#endif \ No newline at end of file diff --git a/src/atom/type/xmake.lua b/src/atom/type/xmake.lua new file mode 100644 index 00000000..57ad5eaf --- /dev/null +++ b/src/atom/type/xmake.lua @@ -0,0 +1,55 @@ +-- xmake.lua for Atom-Type +-- This project is licensed under the terms of the GPL3 license. +-- +-- Project Name: Atom-Type +-- Description: All of the self-implement types +-- Author: Max Qian +-- License: GPL3 + +add_rules("mode.debug", "mode.release") + +set_project("atom-type") +set_version("1.0.0") +set_license("GPL3") + +-- Sources +local sources = { + "args.cpp", + "ini.cpp", + "message.cpp" +} + +-- Headers +local headers = { + "abi.hpp", + "args.hpp", + "enum_flag.hpp", + "enum_flag.inl", + "flatset.hpp", + "ini_impl.hpp", + "ini.hpp", + "json.hpp", + "message.hpp", + "pointer.hpp", + "small_vector.hpp" +} + +-- Build Object Library +target("atom-type-object") + set_kind("object") + add_files(headers, {public = true}) + add_files(sources, {public = false}) + +-- Build Static Library +target("atom-type") + set_kind("static") + add_deps("atom-type-object", "atom-utils") + add_includedirs(".", {public = true}) + + set_targetdir("$(buildir)/lib") + set_objectdir("$(buildir)/obj") + + after_build(function (target) + os.cp("$(buildir)/lib", "$(projectdir)/lib") + os.cp("$(projectdir)/*.hpp", "$(projectdir)/include") + end) \ No newline at end of file diff --git a/src/atom/utils/CMakeLists.txt b/src/atom/utils/CMakeLists.txt index 0b76c16b..969743e0 100644 --- a/src/atom/utils/CMakeLists.txt +++ b/src/atom/utils/CMakeLists.txt @@ -13,7 +13,6 @@ project(atom-utils C CXX) set(${PROJECT_NAME}_SOURCES aes.cpp env.cpp - hash_util.cpp random.cpp string.cpp stopwatcher.cpp @@ -26,7 +25,6 @@ set(${PROJECT_NAME}_SOURCES set(${PROJECT_NAME}_HEADERS aes.hpp env.hpp - hash_util.hpp random.hpp refl.hpp string.hpp diff --git a/src/atom/utils/_script.hpp b/src/atom/utils/_script.hpp new file mode 100644 index 00000000..b47e36c4 --- /dev/null +++ b/src/atom/utils/_script.hpp @@ -0,0 +1,174 @@ +/* + * _script.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-23 + +Description: Carbon binding for Atom-Utils + +**************************************************/ + +#ifndef ATOM_TYPE_SCRIPT_HPP +#define ATOM_TYPE_SCRIPT_HPP + +#include "carbon/carbon.hpp" + +#include "aes.hpp" +#include "argsview.hpp" +#include "env.hpp" +#include "random.hpp" +#include "stopwatcher.hpp" +#include "string.hpp" +#include "time.hpp" +#include "utf.hpp" +#include "uuid.hpp" +#include "xml.hpp" + +using namespace Atom::Utils; + +namespace Atom::_Script::Utils { +/** + * Adds the String Methods to the given Carbon module. + */ +Carbon::ModulePtr bootstrap( + Carbon::ModulePtr m = std::make_shared()) { + m->add(Carbon::fun(&encryptAES), "encrypt_aes"); + m->add(Carbon::fun(&decryptAES), "decrypt_aes"); + m->add(Carbon::fun(&compress), "compress"); + m->add(Carbon::fun(&decompress), "decompress"); + m->add(Carbon::fun(&calculateSha256), "calculate_sha256"); + + m->add(user_type(), "ArgsView"); + m->add(Carbon::fun (ArgsView::*)( + std::string_view) const>(&ArgsView::get), + "get_string"); + m->add(Carbon::fun(&ArgsView::get), "get_int"); + m->add(Carbon::fun(&ArgsView::get), "get_double"); + m->add(Carbon::fun(&ArgsView::get), "get_bool"); + m->add(Carbon::fun(&ArgsView::has), "has"); + m->add(Carbon::fun(&ArgsView::hasFlag), "has_flag"); + m->add(Carbon::fun(&ArgsView::addRule), "add_rule"); + m->add(Carbon::fun(&ArgsView::getFlags), "get_flags"); + + m->add(user_type(), "Env"); + m->add(Carbon::fun(&Env::setEnv), "set_env"); + m->add(Carbon::fun(&Env::getEnv), "get_env"); + m->add(Carbon::fun(&Env::add), "add"); + m->add(Carbon::fun(&Env::del), "del"); + m->add(Carbon::fun(&Env::get), "get"); + m->add(Carbon::fun(&Env::getAbsolutePath), "get_absolute_path"); + m->add(Carbon::fun(&Env::getAbsoluteWorkPath), "get_absolute_work_path"); + m->add(Carbon::fun(&Env::getConfigPath), "get_config_path"); + m->add(Carbon::fun(&Env::removeHelp), "remove_help"); + m->add(Carbon::fun(&Env::addHelp), "add_help"); + m->add(Carbon::fun(&Env::printHelp), "print_help"); + m->add(Carbon::fun(&Env::createShared), "create_shared"); + m->add(Carbon::fun(&Env::createUnique), "create_unique"); + + m->add(user_type(), "default_random_engine"); + m->add(user_type>(), + "uniform_int_distribution"); + m->add(user_type>(), "random"); + m->add(Carbon::fun( + static_cast< + typename random>:: + result_type (random>::*)( + const typename random< + std::default_random_engine, + std::uniform_int_distribution<>>::param_type &)>( + &random>::operator())), + "operator()"); + m->add(Carbon::fun(&generateRandomString), "generate_random_string"); + + m->add(user_type(), "StopWatcher"); + m->add(Carbon::fun(&StopWatcher::start), "start"); + m->add(Carbon::fun(&StopWatcher::pause), "pause"); + m->add(Carbon::fun(&StopWatcher::stop), "stop"); + m->add(Carbon::fun(&StopWatcher::resume), "resume"); + m->add(Carbon::fun(&StopWatcher::reset), "reset"); + m->add(Carbon::fun(&StopWatcher::elapsedMilliseconds), "elapsed_ms"); + m->add(Carbon::fun(&StopWatcher::elapsedSeconds), "elapsed_s"); + m->add(Carbon::fun(&StopWatcher::elapsedFormatted), "elapsed_formatted"); + m->add(Carbon::fun(&StopWatcher::registerCallback), "register_callback"); + + m->add(Carbon::fun(&hasUppercase), "has_uppercase"); + m->add(Carbon::fun(&toCamelCase), "to_camel_case"); + m->add(Carbon::fun(&toUnderscore), "to_underscore"); + + m->add(Carbon::fun(&urlEncode), "url_encode"); + m->add(Carbon::fun(&urlDecode), "url_decode"); + m->add(Carbon::fun(&replaceString), "replace_string"); + m->add(Carbon::fun(&replaceStrings), "replace_strings"); + m->add(Carbon::fun(&startsWith), "starts_with"); + m->add(Carbon::fun(&endsWith), "ends_with"); + m->add(Carbon::fun(&joinStrings), "join_strings"); + m->add(Carbon::fun(&splitString), "split_string"); + + m->add(user_type(), "tm"); + m->add_global_const(Carbon::const_var(&std::tm::tm_sec), "tm_sec"); + m->add_global_const(Carbon::const_var(&std::tm::tm_min), "tm_min"); + m->add_global_const(Carbon::const_var(&std::tm::tm_hour), "tm_hour"); + m->add_global_const(Carbon::const_var(&std::tm::tm_mday), "tm_mday"); + m->add_global_const(Carbon::const_var(&std::tm::tm_mon), "tm_mon"); + m->add_global_const(Carbon::const_var(&std::tm::tm_year), "tm_year"); + m->add_global_const(Carbon::const_var(&std::tm::tm_wday), "tm_wday"); + m->add_global_const(Carbon::const_var(&std::tm::tm_yday), "tm_yday"); + m->add_global_const(Carbon::const_var(&std::tm::tm_isdst), "tm_isdst"); + m->add(Carbon::fun(&timeStampToString), "time_stamp_to_string"); + m->add(Carbon::fun(&getTimestampString), "get_timestamp_string"); + m->add(Carbon::fun(&getChinaTimestampString), "get_china_timestamp_string"); + m->add(Carbon::fun(&getUtcTime), "get_utc_time"); + m->add(Carbon::fun(×tampToTime), "timestamp_to_time"); + m->add(Carbon::fun(&toString), "to_string"); + m->add(Carbon::fun(&convertToChinaTime), "convert_to_china_time"); + + m->add(Carbon::fun(&toUTF8), "to_utf8"); + m->add(Carbon::fun(&fromUTF8), "from_utf8"); + m->add(Carbon::fun(&UTF8toUTF16), "utf8_to_utf16"); + m->add(Carbon::fun(&UTF16toUTF8), "utf16_to_utf8"); + m->add(Carbon::fun(&UTF8toUTF32), "utf8_to_utf32"); + m->add(Carbon::fun(&UTF32toUTF8), "utf32_to_utf8"); + m->add(Carbon::fun(&UTF16toUTF32), "utf16_to_utf32"); + m->add(Carbon::fun(&UTF32toUTF16), "utf32_to_utf16"); + + m->add(user_type(), "UUIDGenerator"); + m->add(Carbon::fun(&UUIDGenerator::generateUUID), "generate_uuid"); + m->add(Carbon::fun(&UUIDGenerator::getRandomNumber), "get_random_number"); + m->add(Carbon::fun(&UUIDGenerator::seed), "seed"); + m->add(Carbon::fun(&generateSystemUUID), "generate_system_uuid"); + + m->add(user_type(), "XMLReader"); + m->add(Carbon::fun(&XMLReader::getChildElementText), + "get_child_element_text"); + m->add(Carbon::fun(&XMLReader::getChildElementAttributeValue), + "get_child_element_attribute_value"); + m->add(Carbon::fun(&XMLReader::getChildElementAttributeValueByPath), + "get_child_element_attribute_value_by_path"); + m->add(Carbon::fun(&XMLReader::getChildElementTextByPath), + "get_child_element_text_by_path"); + m->add(Carbon::fun(&XMLReader::getChildElementNames), + "get_child_element_names"); + m->add(Carbon::fun(&XMLReader::getRootElementNames), + "get_root_element_names"); + m->add(Carbon::fun(&XMLReader::getAttributeValue), "get_attribute_value"); + m->add(Carbon::fun(&XMLReader::getAttributeValueByPath), + "get_attribute_value_by_path"); + m->add(Carbon::fun(&XMLReader::getValueByPath), "get_value_by_path"); + m->add(Carbon::fun(&XMLReader::hasChildElement), "has_child_element"); + m->add(Carbon::fun(&XMLReader::hasChildElementByPath), + "has_child_element_by_path"); + m->add(Carbon::fun(&XMLReader::saveToFile), "save_to_file"); + m->add(Carbon::fun(&XMLReader::getElementText), "get_element_text"); + + return m; +} +} // namespace Atom::_Script::Utils + +#endif \ No newline at end of file diff --git a/src/atom/utils/aes.cpp b/src/atom/utils/aes.cpp index b9a3c838..274adea6 100644 --- a/src/atom/utils/aes.cpp +++ b/src/atom/utils/aes.cpp @@ -14,29 +14,33 @@ Description: Simple implementation of AES encryption #include "aes.hpp" +#include +#include +#include + #include #include #include -#include -#include + +#include "atom/io/io.hpp" +#include "atom/log/loguru.hpp" const int AES_BLOCK_SIZE = 16; namespace Atom::Utils { -std::string encryptAES(const std::string &plaintext, const std::string &key) { +std::string encryptAES(std::string_view plaintext, std::string_view key) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), nullptr, - reinterpret_cast(key.c_str()), + reinterpret_cast(key.data()), nullptr); int c_len = plaintext.length() + AES_BLOCK_SIZE; unsigned char *ciphertext = new unsigned char[c_len]; int len; - EVP_EncryptUpdate( - ctx, ciphertext, &len, - reinterpret_cast(plaintext.c_str()), - plaintext.length()); + EVP_EncryptUpdate(ctx, ciphertext, &len, + reinterpret_cast(plaintext.data()), + plaintext.length()); int ciphertext_len = len; EVP_EncryptFinal_ex(ctx, ciphertext + len, &len); @@ -50,10 +54,10 @@ std::string encryptAES(const std::string &plaintext, const std::string &key) { return result; } -std::string decryptAES(const std::string &ciphertext, const std::string &key) { +std::string decryptAES(std::string_view ciphertext, std::string_view key) { EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); EVP_DecryptInit_ex(ctx, EVP_aes_128_ecb(), nullptr, - reinterpret_cast(key.c_str()), + reinterpret_cast(key.data()), nullptr); int p_len = ciphertext.length() + AES_BLOCK_SIZE; @@ -62,7 +66,7 @@ std::string decryptAES(const std::string &ciphertext, const std::string &key) { int len; EVP_DecryptUpdate( ctx, plaintext, &len, - reinterpret_cast(ciphertext.c_str()), + reinterpret_cast(ciphertext.data()), ciphertext.length()); int plaintext_len = len; @@ -77,7 +81,7 @@ std::string decryptAES(const std::string &ciphertext, const std::string &key) { return result; } -std::string compress(const std::string &data) { +std::string compress(std::string_view data) { z_stream zs; memset(&zs, 0, sizeof(zs)); @@ -107,7 +111,7 @@ std::string compress(const std::string &data) { return compressed; } -std::string decompress(const std::string &data) { +std::string decompress(std::string_view data) { z_stream zs; memset(&zs, 0, sizeof(zs)); @@ -136,4 +140,42 @@ std::string decompress(const std::string &data) { return decompressed; } + +std::string calculateSha256(std::string_view filename) { + if (!Atom::IO::isFileExists(std::string(filename))) { + LOG_F(ERROR, "File not exist: {}", filename); + return ""; + } + std::ifstream file(filename.data(), std::ios::binary); + if (!file || !file.good()) { + return ""; + } + + EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); + EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL); + + char buffer[1024]; + while (file.read(buffer, sizeof(buffer))) { + EVP_DigestUpdate(mdctx, buffer, sizeof(buffer)); + } + + if (file.gcount() > 0) { + EVP_DigestUpdate(mdctx, buffer, file.gcount()); + } + + unsigned char hash[EVP_MAX_MD_SIZE]; + unsigned int hash_len = 0; + EVP_DigestFinal_ex(mdctx, hash, &hash_len); + EVP_MD_CTX_free(mdctx); + + // 转换为十六进制字符串 + std::string sha256_val; + for (unsigned int i = 0; i < hash_len; ++i) { + char hex_str[3]; + sprintf(hex_str, "%02x", hash[i]); + sha256_val += hex_str; + } + + return sha256_val; +} } // namespace Atom::Utils diff --git a/src/atom/utils/aes.hpp b/src/atom/utils/aes.hpp index 20063973..ef8d7013 100644 --- a/src/atom/utils/aes.hpp +++ b/src/atom/utils/aes.hpp @@ -25,8 +25,8 @@ namespace Atom::Utils { * @param key 加密密钥 * @return 加密后的密文数据 */ -[[maybe_unused]] [[nodiscard]] std::string encryptAES( - const std::string &plaintext, const std::string &key); +[[nodiscard]] std::string encryptAES(std::string_view plaintext, + std::string_view key); /** * @brief 使用AES算法对输入的密文进行解密。 @@ -35,8 +35,8 @@ namespace Atom::Utils { * @param key 解密密钥 * @return 解密后的明文数据 */ -[[maybe_unused]] [[nodiscard]] std::string decryptAES( - const std::string &ciphertext, const std::string &key); +[[nodiscard]] std::string decryptAES(std::string_view ciphertext, + std::string_view key); /** * @brief 使用Zlib库对输入的数据进行压缩。 @@ -44,7 +44,7 @@ namespace Atom::Utils { * @param data 待压缩的数据 * @return 压缩后的数据 */ -[[maybe_unused]] [[nodiscard]] std::string compress(const std::string &data); +[[nodiscard]] std::string compress(std::string_view data); /** * @brief 使用Zlib库对输入的数据进行解压。 @@ -52,7 +52,15 @@ namespace Atom::Utils { * @param data 待解压的数据 * @return 解压后的数据 */ -[[maybe_unused]] [[nodiscard]] std::string decompress(const std::string &data); +[[nodiscard]] std::string decompress(std::string_view data); + +/** + * @brief 计算文件的SHA-256哈希值。 + * + * @param filename 文件名 + * @return 文件的SHA-256哈希值 + */ +[[nodiscard]] std::string calculateSha256(std::string_view filename); } // namespace Atom::Utils #endif diff --git a/src/atom/utils/argsview.cpp b/src/atom/utils/argsview.cpp new file mode 100644 index 00000000..f2410a27 --- /dev/null +++ b/src/atom/utils/argsview.cpp @@ -0,0 +1,70 @@ +/* + * argsview.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-19 + +Description: ArgsView Class for C++ + +**************************************************/ + +#include "argsview.hpp" + +namespace Atom::Utils { +ArgsView::ArgsView(int argc, char** argv) : m_argc(argc), m_argv(argv) { + for (int i = 1; i < m_argc; ++i) { + std::string_view arg(m_argv[i]); + if (arg.substr(0, 2) == "--") { + auto pos = arg.find('='); + if (pos != std::string_view::npos) { + auto key = arg.substr(2, pos - 2); + if (!has(key)) { + m_args.emplace(key, arg.substr(pos + 1)); + } + } else { + auto flag = arg.substr(2); + if (!hasFlag(flag)) { + m_flags.emplace_back(flag); + } + } + } else { + for (const auto& rule : m_rules) { + if (arg.substr(0, rule.first.size()) == rule.first) { + rule.second(arg.substr(rule.first.size())); + break; + } + } + } + } +} + +std::optional ArgsView::get(std::string_view key) const { + if (auto it = m_args.find(key); it != m_args.end()) { + return it->second; + } + throw std::runtime_error(std::string("Key not found: ") + std::string(key)); +} + +bool ArgsView::has(std::string_view key) const { return m_args.count(key) > 0; } + +bool ArgsView::hasFlag(std::string_view flag) const { + return std::find(m_flags.begin(), m_flags.end(), flag) != m_flags.end(); +} + +std::vector ArgsView::getFlags() const { return m_flags; } + +std::unordered_map ArgsView::getArgs() + const { + return m_args; +} + +void ArgsView::addRule(std::string_view prefix, + std::function handler) { + m_rules.emplace_back(prefix, handler); +} + +} // namespace Atom::Utils diff --git a/src/atom/utils/argsview.hpp b/src/atom/utils/argsview.hpp new file mode 100644 index 00000000..ea890d66 --- /dev/null +++ b/src/atom/utils/argsview.hpp @@ -0,0 +1,113 @@ +/* + * argsview.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-19 + +Description: ArgsView Class for C++ + +**************************************************/ + +#ifndef ATOM_UTILS_ARGSVIEW_HPP +#define ATOM_UTILS_ARGSVIEW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Atom::Utils { +/** + * @brief Represents a view of command-line arguments. + */ +class ArgsView { +public: + /** + * @brief Constructs an ArgsView object from argc and argv. + * + * @param argc The number of command-line arguments. + * @param argv The array of command-line arguments. + */ + explicit ArgsView(int argc, char** argv); + + /** + * @brief Gets the value associated with the specified key. + * + * @param key The key to search for. + * @return An optional containing the value if found, otherwise nullopt. + * @throws std::runtime_error if the key is not found. + */ + std::optional get(std::string_view key) const; + + /** + * @brief Gets the value associated with the specified key and converts it + * to the specified type. + * + * @tparam T The type to convert the value to. + * @param key The key to search for. + * @return An optional containing the converted value if found and + * successfully converted, otherwise nullopt. + */ + template + std::optional get(std::string_view key) const; + + /** + * @brief Checks if the specified key exists. + * + * @param key The key to search for. + * @return true if the key exists, otherwise false. + */ + bool has(std::string_view key) const; + + /** + * @brief Checks if the specified flag exists. + * + * @param flag The flag to search for. + * @return true if the flag exists, otherwise false. + */ + bool hasFlag(std::string_view flag) const; + + /** + * @brief Gets all the flags. + * + * @return A vector containing all the flags. + */ + std::vector getFlags() const; + + /** + * @brief Gets all the key-value pairs. + * + * @return An unordered_map containing all the key-value pairs. + */ + std::unordered_map getArgs() const; + + /** + * @brief Adds a custom rule with the specified prefix and handler function. + * + * @param prefix The prefix of the rule. + * @param handler The handler function for the rule. + */ + void addRule(std::string_view prefix, + std::function handler); + +private: + int m_argc; + char** m_argv; + std::unordered_map m_args; + std::vector m_flags; + std::vector< + std::pair>> + m_rules; +}; + +} // namespace Atom::Utils + +#endif \ No newline at end of file diff --git a/src/atom/utils/convert.cpp b/src/atom/utils/convert.cpp new file mode 100644 index 00000000..f0754900 --- /dev/null +++ b/src/atom/utils/convert.cpp @@ -0,0 +1,69 @@ +/* + * convert.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-18 + +Description: Convert Utils for Windows + +**************************************************/ + +#ifdef _WIN32 + +#include "convert.hpp" + +#include +#include + +namespace Atom::Utils { +LPWSTR CharToLPWSTR(std::string_view charString) { + const int size = + MultiByteToWideChar(CP_ACP, 0, charString.data(), + static_cast(charString.size()), nullptr, 0); + if (size == 0) { + throw std::runtime_error("Error converting char string to LPWSTR"); + } + std::vector buffer(size + 1); + MultiByteToWideChar(CP_ACP, 0, charString.data(), + static_cast(charString.size()), buffer.data(), + size); + buffer[size] = L'\0'; + return buffer.data(); +} + +LPWSTR StringToLPWSTR(const std::string& str) { return CharToLPWSTR(str); } + +std::string LPWSTRToString(LPWSTR lpwstr) { + const int size = WideCharToMultiByte(CP_ACP, 0, lpwstr, -1, nullptr, 0, + nullptr, nullptr); + if (size == 0) { + throw std::runtime_error("Error converting LPWSTR to std::string"); + } + std::string str(size - 1, '\0'); + WideCharToMultiByte(CP_ACP, 0, lpwstr, -1, str.data(), size, nullptr, + nullptr); + return str; +} + +std::string LPCWSTRToString(LPCWSTR lpcwstr) { + return LPWSTRToString(const_cast(lpcwstr)); +} + +LPWSTR WStringToLPWSTR(const std::wstring& wstr) { + std::vector buffer(wstr.size() + 1); + std::copy(wstr.begin(), wstr.end(), buffer.begin()); + buffer[wstr.size()] = L'\0'; + return buffer.data(); +} + +std::wstring LPWSTRToWString(LPWSTR lpwstr) { return std::wstring(lpwstr); } + +std::wstring LPCWSTRToWString(LPCWSTR lpcwstr) { return std::wstring(lpcwstr); } + +} // namespace Atom::Utils + +#endif \ No newline at end of file diff --git a/src/atom/utils/convert.hpp b/src/atom/utils/convert.hpp new file mode 100644 index 00000000..b884e2df --- /dev/null +++ b/src/atom/utils/convert.hpp @@ -0,0 +1,82 @@ +/* + * convert.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-18 + +Description: Convert Utils for Windows + +**************************************************/ + +#ifndef ATOM_UTILS_CONVERT_HPP +#define ATOM_UTILS_CONVERT_HPP + +#ifdef _WIN32 + +#include +#include +#include + +namespace Atom::Utils { +/** + * @brief Converts a string_view containing char characters to LPWSTR (wide + * character string). + * @param charString The string_view containing char characters to be converted. + * @return LPWSTR representing the wide character version of the input string. + */ +[[nodiscard]] LPWSTR CharToLPWSTR(std::string_view charString); + +/** + * @brief Converts a string containing char characters to LPWSTR (wide character + * string). + * @param str The string containing char characters to be converted. + * @return LPWSTR representing the wide character version of the input string. + */ +[[nodiscard]] LPWSTR StringToLPWSTR(const std::string& str); + +/** + * @brief Converts LPWSTR (wide character string) to a string containing char + * characters. + * @param lpwstr The LPWSTR to be converted to a string. + * @return std::string containing char characters. + */ +[[nodiscard]] std::string LPWSTRToString(LPWSTR lpwstr); + +/** + * @brief Converts LPCWSTR (const wide character string) to a string containing + * char characters. + * @param lpcwstr The LPCWSTR to be converted to a string. + * @return std::string containing char characters. + */ +[[nodiscard]] std::string LPCWSTRToString(LPCWSTR lpcwstr); + +/** + * @brief Converts a wstring to LPWSTR (wide character string). + * @param wstr The wstring to be converted. + * @return LPWSTR representing the wide character version of the input wstring. + */ +[[nodiscard]] LPWSTR WStringToLPWSTR(const std::wstring& wstr); + +/** + * @brief Converts LPWSTR (wide character string) to a wstring. + * @param lpwstr The LPWSTR to be converted to a wstring. + * @return std::wstring. + */ +[[nodiscard]] std::wstring LPWSTRToWString(LPWSTR lpwstr); + +/** + * @brief Converts LPCWSTR (const wide character string) to a wstring. + * @param lpcwstr The LPCWSTR to be converted to a wstring. + * @return std::wstring. + */ +[[nodiscard]] std::wstring LPCWSTRToWString(LPCWSTR lpcwstr); + +} // namespace Atom::Utils + +#endif + +#endif diff --git a/src/atom/utils/cstring.hpp b/src/atom/utils/cstring.hpp new file mode 100644 index 00000000..39b95758 --- /dev/null +++ b/src/atom/utils/cstring.hpp @@ -0,0 +1,167 @@ +/* + * cstring.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-18 + +Description: String methods in compilation time + +**************************************************/ + +#ifndef ATOM_UTILS_CSTRING_HPP +#define ATOM_UTILS_CSTRING_HPP + + +#include +#include + +template +constexpr auto deduplicate(const char (&str)[N]) { + std::array result{}; + std::size_t index = 0; + + for (std::size_t i = 0; i < N - 1; ++i) { + bool duplicate = false; + for (std::size_t j = 0; j < index; ++j) { + if (str[i] == result[j]) { + duplicate = true; + break; + } + } + if (!duplicate) { + result[index++] = str[i]; + } + } + + result[index] = '\0'; + return result; +} + +constexpr auto split(std::string_view str, char delimiter) { + std::array result; + std::size_t pos = 0; + std::size_t index = 0; + while ((pos = str.find(delimiter)) != std::string_view::npos && + index < result.size()) { + result[index++] = str.substr(0, pos); + str.remove_prefix(pos + 1); + } + if (index < result.size()) { + result[index++] = str; + } + return result; +} + +template +constexpr auto replace(const char (&str)[N], char oldChar, char newChar) { + std::array result{}; + for (std::size_t i = 0; i < N - 1; ++i) { + result[i] = (str[i] == oldChar) ? newChar : str[i]; + } + result[N - 1] = '\0'; + return result; +} + +template +constexpr auto toLower(const char (&str)[N]) { + std::array result{}; + for (std::size_t i = 0; i < N - 1; ++i) { + result[i] = + (str[i] >= 'A' && str[i] <= 'Z') ? str[i] + ('a' - 'A') : str[i]; + } + result[N - 1] = '\0'; + return result; +} + +template +constexpr auto toUpper(const char (&str)[N]) { + std::array result{}; + for (std::size_t i = 0; i < N - 1; ++i) { + result[i] = + (str[i] >= 'a' && str[i] <= 'z') ? str[i] - ('a' - 'A') : str[i]; + } + result[N - 1] = '\0'; + return result; +} + +template +constexpr auto concat(const char (&str1)[N1], const char (&str2)[N2]) { + std::array result{}; + std::size_t index = 0; + for (std::size_t i = 0; i < N1 - 1; ++i) { + result[index++] = str1[i]; + } + for (std::size_t i = 0; i < N2 - 1; ++i) { + result[index++] = str2[i]; + } + return result; +} + +template +constexpr auto trim(const char (&str)[N]) { + std::array result{}; + std::size_t index = 0; + for (std::size_t i = 0; i < N - 1; ++i) { + if (str[i] != ' ') { + result[index++] = str[i]; + } + } + result[index] = '\0'; + return result; +} + +template +constexpr auto substring(const char (&str)[N], std::size_t start, + std::size_t length) { + std::array result{}; + std::size_t index = 0; + for (std::size_t i = start; i < start + length && i < N - 1; ++i) { + result[index++] = str[i]; + } + result[index] = '\0'; + return result; +} + +template +constexpr bool equal(const char (&str1)[N1], const char (&str2)[N2]) { + if (N1 != N2) { + return false; + } + for (std::size_t i = 0; i < N1 - 1; ++i) { + if (str1[i] != str2[i]) { + return false; + } + } + return true; +} + +template +constexpr std::size_t find(const char (&str)[N], char ch) { + for (std::size_t i = 0; i < N - 1; ++i) { + if (str[i] == ch) { + return i; + } + } + return N - 1; +} + +template +constexpr std::size_t length(const char (&str)[N]) { + return N - 1; +} + +template +constexpr auto reverse(const char (&str)[N]) { + std::array result{}; + for (std::size_t i = 0; i < N - 1; ++i) { + result[i] = str[N - 2 - i]; + } + result[N - 1] = '\0'; + return result; +} + +#endif diff --git a/src/atom/utils/hash_util.cpp b/src/atom/utils/hash_util.cpp deleted file mode 100644 index e4fb309e..00000000 --- a/src/atom/utils/hash_util.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - * hash_util.cpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-12-16 - -Description: Implementation of murmur3 hash and quick hash - -**************************************************/ - -#include "hash_util.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ROTL(x, r) ((x << r) | (x >> (32 - r))) - -namespace Atom::Utils { -static inline uint32_t fmix32(uint32_t h) { - h ^= h >> 16; - h *= 0x85ebca6b; - h ^= h >> 13; - h *= 0xc2b2ae35; - h ^= h >> 16; - - return h; -} - -uint32_t murmur3Hash(const char *str, const uint32_t &seed = 1060627423) { - if (!str) - return 0; - - int len = strlen(str); - return murmur3Hash(str, len, seed); -} - -uint32_t murmur3Hash(const void *data, const uint32_t &size, - const uint32_t &seed = 1060627423) { - if (!data) - return 0; - - const char *str = (const char *)data; - int len = size; - - uint32_t s, h = seed, seed1 = 0xcc9e2d51, seed2 = 0x1b873593, - *ptr = (uint32_t *)str; - - // handle begin blocks - int blk = len / 4; - for (int i = 0; i < blk; i++) { - s = ptr[i]; - s *= seed1; - s = ROTL(s, 15); - s *= seed2; - - h ^= s; - h = ROTL(h, 13); - h *= 5; - h += 0xe6546b64; - } - - // handle tail - s = 0; - uint8_t *tail = (uint8_t *)(str + blk * 4); - switch (len & 3) { - case 3: - s |= tail[2] << 16; - case 2: - s |= tail[1] << 8; - case 1: - s |= tail[0]; - - s *= seed1; - s = ROTL(s, 15); - s *= seed2; - h ^= s; - }; - - return fmix32(h ^ len); -} - -uint32_t quickHash(const char *str) { - if (!str) - return 0; - - unsigned int h = 0; - for (; *str; str++) { - h = 31 * h + *str; - } - return h; -} - -uint32_t quickHash(const void *tmp, uint32_t size) { - if (!tmp) - return 0; - - const char *str = (const char *)tmp; - unsigned int h = 0; - for (uint32_t i = 0; i < size; ++i, ++str) { - h = 31 * h + *str; - } - return h; -} - -uint64_t murmur3Hash64(const void *str, const uint32_t &size, - const uint32_t &seed = 1060627423, - const uint32_t &seed2 = 1050126127) { - return (((uint64_t)murmur3Hash(str, size, seed)) << 32 | - murmur3Hash(str, size, seed2)); -} - -uint64_t murmur3Hash64(const char *str, const uint32_t &seed = 1060627423, - const uint32_t &seed2 = 1050126127) { - return (((uint64_t)murmur3Hash(str, seed)) << 32 | murmur3Hash(str, seed2)); -} - -std::string base64decode(const std::string &src) { - std::string result; - result.resize(src.size() * 3 / 4); - char *writeBuf = &result[0]; - - const char *ptr = src.c_str(); - const char *end = ptr + src.size(); - - while (ptr < end) { - int i = 0; - int padding = 0; - int packed = 0; - for (; i < 4 && ptr < end; ++i, ++ptr) { - if (*ptr == '=') { - ++padding; - packed <<= 6; - continue; - } - - // padding with "=" only - if (padding > 0) { - return ""; - } - - int val = 0; - if (*ptr >= 'A' && *ptr <= 'Z') { - val = *ptr - 'A'; - } else if (*ptr >= 'a' && *ptr <= 'z') { - val = *ptr - 'a' + 26; - } else if (*ptr >= '0' && *ptr <= '9') { - val = *ptr - '0' + 52; - } else if (*ptr == '+') { - val = 62; - } else if (*ptr == '/') { - val = 63; - } else { - return ""; // invalid character - } - - packed = (packed << 6) | val; - } - if (i != 4) { - return ""; - } - if (padding > 0 && ptr != end) { - return ""; - } - if (padding > 2) { - return ""; - } - - *writeBuf++ = (char)((packed >> 16) & 0xff); - if (padding != 2) { - *writeBuf++ = (char)((packed >> 8) & 0xff); - } - if (padding == 0) { - *writeBuf++ = (char)(packed & 0xff); - } - } - - result.resize(writeBuf - result.c_str()); - return result; -} - -void hexstring_from_data(const void *data, size_t len, char *output) { - const unsigned char *buf = (const unsigned char *)data; - size_t i, j; - for (i = j = 0; i < len; ++i) { - char c; - c = (buf[i] >> 4) & 0xf; - c = (c > 9) ? c + 'a' - 10 : c + '0'; - output[j++] = c; - c = (buf[i] & 0xf); - c = (c > 9) ? c + 'a' - 10 : c + '0'; - output[j++] = c; - } -} - -std::string hexstring_from_data(const char *data, size_t len) { - if (len == 0) { - return std::string(); - } - std::string result; - result.resize(len * 2); - hexstring_from_data(data, len, &result[0]); - return result; -} - -std::string hexstring_from_data(const std::string &data) { - return hexstring_from_data(data.c_str(), data.size()); -} - -void data_from_hexstring(const char *hexstring, size_t length, void *output) { - unsigned char *buf = (unsigned char *)output; - unsigned char byte; - if (length % 2 != 0) { - throw std::invalid_argument("data_from_hexstring length % 2 != 0"); - } - for (size_t i = 0; i < length; ++i) { - switch (hexstring[i]) { - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - byte = (hexstring[i] - 'a' + 10) << 4; - break; - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - byte = (hexstring[i] - 'A' + 10) << 4; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - byte = (hexstring[i] - '0') << 4; - break; - default: - throw std::invalid_argument( - "data_from_hexstring invalid hexstring"); - } - ++i; - switch (hexstring[i]) { - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - byte |= hexstring[i] - 'a' + 10; - break; - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - byte |= hexstring[i] - 'A' + 10; - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - byte |= hexstring[i] - '0'; - break; - default: - throw std::invalid_argument( - "data_from_hexstring invalid hexstring"); - } - *buf++ = byte; - } -} - -std::string data_from_hexstring(const char *hexstring, size_t length) { - if (length % 2 != 0) { - throw std::invalid_argument("data_from_hexstring length % 2 != 0"); - } - if (length == 0) { - return std::string(); - } - std::string result; - result.resize(length / 2); - data_from_hexstring(hexstring, length, &result[0]); - return result; -} - -std::string data_from_hexstring(const std::string &hexstring) { - return data_from_hexstring(hexstring.c_str(), hexstring.size()); -} -} // namespace Atom::Utils diff --git a/src/atom/utils/hash_util.hpp b/src/atom/utils/hash_util.hpp deleted file mode 100644 index 22d605c1..00000000 --- a/src/atom/utils/hash_util.hpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * hash_util.hpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-12-16 - -Description: Implementation of murmur3 hash and quick hash - -**************************************************/ - -#ifndef ATOM_UTILS_HASH_UTIL_HPP -#define ATOM_UTILS_HASH_UTIL_HPP - -#include -#include -#include - -namespace Atom::Utils { -/** - * @brief Calculates the MurmurHash3 hash value for a given string. - * - * @param str The input string. - * @param seed The seed value (optional, default is 1060627423). - * @return uint32_t The calculated hash value. - */ -[[nodiscard]] uint32_t murmur3Hash(const char *str, const uint32_t &seed); - -/** - * @brief Calculates the MurmurHash3 hash value for a given data buffer. - * - * @param str The input data buffer. - * @param size The size of the data buffer. - * @param seed The seed value (optional, default is 1060627423). - * @return uint32_t The calculated hash value. - */ -[[nodiscard]] uint32_t murmur3Hash(const void *str, const uint32_t &size, - const uint32_t &seed); - -/** - * @brief Calculates the 64-bit MurmurHash3 hash value for a given string. - * - * @param str The input string. - * @param seed The first seed value (optional, default is 1060627423). - * @param seed2 The second seed value (optional, default is 1050126127). - * @return uint64_t The calculated hash value. - */ -[[nodiscard]] uint64_t murmur3Hash64(const char *str, const uint32_t &seed, - const uint32_t &seed2); - -/** - * @brief Calculates the 64-bit MurmurHash3 hash value for a given data buffer. - * - * @param str The input data buffer. - * @param size The size of the data buffer. - * @param seed The first seed value (optional, default is 1060627423). - * @param seed2 The second seed value (optional, default is 1050126127). - * @return uint64_t The calculated hash value. - */ -[[nodiscard]] uint64_t murmur3Hash64(const void *str, const uint32_t &size, - const uint32_t &seed, - const uint32_t &seed2); - -/** - * @brief Calculates the quick hash value for a given string. - * - * @param str The input string. - * @return uint32_t The calculated hash value. - */ -[[nodiscard]] uint32_t quickHash(const char *str); - -/** - * @brief Calculates the quick hash value for a given data buffer. - * - * @param str The input data buffer. - * @param size The size of the data buffer. - * @return uint32_t The calculated hash value. - */ -[[nodiscard]] uint32_t quickHash(const void *str, uint32_t size); - -/** - * @brief Converts binary data to a hexadecimal string representation. - * - * @param data The input data buffer. - * @param len The length of the data buffer. - * @param output The output buffer to store the hexadecimal string (length must - * be len * 2). - */ -void hexstring_from_data(const void *data, size_t len, - char *output); - -/** - * @brief Converts binary data to a hexadecimal string representation. - * - * @param data The input data buffer. - * @param len The length of the data buffer. - * @return std::string The hexadecimal string representation. - */ -[[nodiscard]] std::string hexstring_from_data(const void *data, size_t len); - -/** - * @brief Converts a string to a hexadecimal string representation. - * - * @param data The input string. - * @return std::string The hexadecimal string representation. - */ -[[nodiscard]] std::string hexstring_from_data(const std::string &data); - -/** - * @brief Converts a hexadecimal string representation to binary data. - * - * @param hexstring The input hexadecimal string. - * @param length The length of the hexadecimal string. - * @param output The output buffer to store the binary data (length must be - * length / 2). - */ -void data_from_hexstring(const char *hexstring, size_t length, - void *output); - -/** - * @brief Converts a hexadecimal string representation to binary data. - * - * @param hexstring The input hexadecimal string. - * @param length The length of the hexadecimal string. - * @return std::string The binary data. - * @throw std::invalid_argument If the input hexstring is not a valid - * hexadecimal string. - */ -[[nodiscard]] std::string data_from_hexstring(const char *hexstring, - size_t length); - -/** - * @brief Converts a hexadecimal string representation to binary data. - * - * @param data The input hexadecimal string. - * @return std::string The binary data. - * @throw std::invalid_argument If the input hexstring is not a valid - * hexadecimal string. - */ -[[nodiscard]] std::string data_from_hexstring(const std::string &data); -} // namespace Atom::Utils - -#endif \ No newline at end of file diff --git a/src/atom/utils/leak.hpp b/src/atom/utils/leak.hpp new file mode 100644 index 00000000..52cae42e --- /dev/null +++ b/src/atom/utils/leak.hpp @@ -0,0 +1,33 @@ +/* + * leak.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-12-16 + +Description: Memory Leak Detection + +**************************************************/ + +#ifndef ATOM_UTILS_LEAK_HPP +#define ATOM_UTILS_LEAK_HPP + +#if defined(__clang__) +#pragma clang system_header +#elif defined(__GNUC__) +#pragma GCC system_header +#elif defined(_MSC_VER) +#pragma system_header +#endif + +//! @cond INTERNALS +#if defined(_MSC_VER) +#define VLD_FORCE_ENABLE +#include +#endif +//! @endcond + +#endif // ATOM_UTILS_LEAK_HPP diff --git a/src/atom/utils/meson.build b/src/atom/utils/meson.build new file mode 100644 index 00000000..c7d9b01a --- /dev/null +++ b/src/atom/utils/meson.build @@ -0,0 +1,59 @@ +project('Atom-Utils', 'cpp') + +# Define sources and headers +sources = [ + 'aes.cpp', + 'env.cpp', + 'hash_util.cpp', + 'random.cpp', + 'string.cpp', + 'stopwatcher.cpp', + 'time.cpp', + 'uuid.cpp', + 'xml.cpp' +] +headers = [ + 'aes.hpp', + 'env.hpp', + 'hash_util.hpp', + 'random.hpp', + 'refl.hpp', + 'string.hpp', + 'stopwatcher.hpp', + 'switch.hpp', + 'time.hpp', + 'uuid.hpp', + 'xml.hpp' +] + +# Private headers +private_headers = [ +] + +# Create object library +atom_utils_obj = library('atom_utils', + sources + headers + private_headers, + cpp_std: c_std +) + +# Link to other libraries if necessary +atom_utils_obj_link_libs = [] +foreach lib : ['loguru', 'tinyxml2'] + atom_utils_obj_link_libs += dependency(lib) +endforeach + +atom_utils_obj.link_with(atom_utils_obj_link_libs) + +# Create static library +atom_utils_static = static_library('atom_utils_static', atom_utils_obj) + +# Set version properties +version = '1.0.0' +soversion = '1' + +atom_utils_static.version = version +atom_utils_static.soversion = soversion +atom_utils_static.basename = 'Atom-Utils' + +# Install target +install_targets(atom_utils_static) diff --git a/src/atom/utils/string.cpp b/src/atom/utils/string.cpp index 0c3716de..d534777d 100644 --- a/src/atom/utils/string.cpp +++ b/src/atom/utils/string.cpp @@ -18,144 +18,165 @@ Description: Some useful string functions #include #include +#include #include "atom/error/exception.hpp" namespace Atom::Utils { -bool hasUppercase(const std::string &str) { - // 判断字符串中是否存在大写字母 - // 参数: - // - str: 要检查的字符串 - // 返回值: - // - 如果字符串中存在大写字母,返回true;否则,返回false +bool hasUppercase(std::string_view str) { return std::any_of(str.begin(), str.end(), - [](char ch) { return std::isupper(ch); }); + [](unsigned char ch) { return std::isupper(ch); }); } -std::string toUnderscore(const std::string &str) { - std::string result; // 生成一个空字符串用于存储转换结果 - for (char ch : str) // 遍历输入字符串中的每个字符 - { - if (std::isupper(ch)) // 如果字符是大写字母 - { - result += '_'; // 在结果字符串前加上下划线 - result += - std::tolower(ch); // 将大写字母转换为小写字母并添加到结果字符串 - } else // 如果字符不是大写字母 - { - result += ch; // 直接将字符添加到结果字符串 +std::string toUnderscore(std::string_view str) { + std::string result; + result.reserve(str.size() + + std::count_if(str.begin(), str.end(), [](unsigned char ch) { + return std::isupper(ch); + })); + + for (char ch : str) { + if (std::isupper(ch)) { + result.push_back('_'); + result.push_back(std::tolower(ch)); + } else { + result.push_back(ch); } } - return result; // 返回转换后的字符串结果 + + return result; } -std::string toCamelCase(const std::string &str) { - std::string result; // 用于保存转换后的字符串结果 - bool capitalize = false; // 用于标记是否需要大写转换 - - for (char ch : str) // 遍历输入字符串 - { - if (ch == '_') // 如果遇到下划线 - { - capitalize = true; // 设置大写标记为true - } else // 如果不是下划线 - { - if (capitalize) // 如果需要大写转换 - { - result += - std::toupper(ch); // 将字符转换为大写并添加到结果字符串 - capitalize = false; // 清除大写标记 - } else // 如果不需要大写转换 - { - result += ch; // 直接将字符添加到结果字符串 - } +std::string toCamelCase(std::string_view str) { + std::string result; + result.reserve(str.size()); + + bool capitalize = false; + for (char ch : str) { + if (ch == '_') { + capitalize = true; + } else if (capitalize) { + result.push_back(std::toupper(ch)); + capitalize = false; + } else { + result.push_back(ch); } } - return result; // 返回转换后的字符串结果 + + return result; } -std::string urlEncode(const std::string &str) { - // 创建一个输出字符串流对象escaped,设置填充字符为'0',输出基数为十六进制 +std::string urlEncode(std::string_view str) { std::ostringstream escaped; escaped.fill('0'); escaped << std::hex; - // 遍历输入字符串str的每个字符 for (auto c : str) { - // 如果字符是alnum字符,或者字符是'-'、'_'、'.'、'~'之一,则直接将字符输出到escaped if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { escaped << c; - } - // 如果字符是空格,则将字符替换为'+' - else if (c == ' ') { + } else if (c == ' ') { escaped << '+'; - } - // 其他情况下,将字符替换为字符对应的百分比表示形式,例如 '%' + - // 十六进制字符的字符串形式 - else { - escaped << '%' << std::setw(2) << int((unsigned char)c); + } else { + escaped << '%' << std::setw(2) + << static_cast(static_cast(c)); } } - // 返回经过编码处理的字符串 return escaped.str(); } -std::string urlDecode(const std::string &str) { - // 初始化一个空字符串用于存储解码后的结果 +std::string urlDecode(std::string_view str) { std::string result; + result.reserve(str.size()); - // 遍历输入的字符串的每个字符 for (size_t i = 0; i < str.size(); ++i) { - // 如果当前字符是 '%',则进行编码解析 if (str[i] == '%') { + if (i + 2 >= str.size()) { + throw std::invalid_argument("urlDecode failed"); + } + int value; - std::istringstream is(str.substr(i + 1, 2)); - - // 将当前字符的后两位作为十六进制字符串,转换为整数 - if (is >> std::hex >> value) { - // 将整数转换为字符,并添加到结果中 - result += static_cast(value); - - // 跳过解析后的两位字符,继续下一个字符的处理 - i += 2; - } else { - // 如果解析失败,则抛出异常 - throw Error::WrongArgument("urlDecode failed"); + if (auto [p, ec] = + std::from_chars(&str[i + 1], &str[i + 3], value, 16); + ec != std::errc()) { + throw std::invalid_argument("urlDecode failed"); } + + result.push_back(static_cast(value)); + i += 2; + } else if (str[i] == '+') { + result.push_back(' '); + } else { + result.push_back(str[i]); } - // 如果当前字符是 '+',则将其替换为空格并添加到结果中 - else if (str[i] == '+') { - result += ' '; - } - // 对于其他字符,直接将其添加到结果中 - else { - result += str[i]; + } + + return result; +} + +bool startsWith(std::string_view str, std::string_view prefix) { + return str.size() >= prefix.size() && + str.substr(0, prefix.size()) == prefix; +} + +bool endsWith(std::string_view str, std::string_view suffix) { + return str.size() >= suffix.size() && + str.substr(str.size() - suffix.size()) == suffix; +} + +std::vector splitString(const std::string &str, + char delimiter) { + std::vector result; + std::string_view view(str); + size_t pos = 0; + + while (true) { + size_t next_pos = view.find(delimiter, pos); + if (next_pos == std::string_view::npos) { + result.emplace_back(view.substr(pos)); + break; } + result.emplace_back(view.substr(pos, next_pos - pos)); + pos = next_pos + 1; } - // 返回解码后的结果 return result; } -std::vector splitString(const std::string &input, char delimiter) { - std::vector tokens; -#ifndef ATOM_USE_OBSOLETE - std::istringstream ss(input); - std::string token; - while (std::getline(ss, token, delimiter)) { - tokens.push_back(token); +std::string joinStrings(const std::vector &strings, + const std::string_view &delimiter) { + std::ostringstream oss; + bool first = true; + + for (const auto &str : strings) { + if (!first) { + oss << delimiter; + } + oss << str; + first = false; } - return tokens; -#else + + return oss.str(); +} + +std::string replaceString(std::string_view text, std::string_view oldStr, + std::string_view newStr) { + std::string result = text.data(); size_t pos = 0; - size_t foundPos = input.find(delimiter); - while (foundPos != std::string::npos) { - tokens.push_back(input.substr(pos, foundPos - pos)); - pos = foundPos + 1; - foundPos = input.find(delimiter, pos); + while ((pos = result.find(std::string(oldStr), pos)) != std::string::npos) { + result.replace(pos, oldStr.length(), std::string(newStr)); + pos += newStr.length(); + } + return result; +} + +std::string replaceStrings( + std::string_view text, + const std::vector> + &replacements) { + std::string result(text); + for (const auto &[oldStr, newStr] : replacements) { + result = replaceString(result, oldStr, newStr); } - tokens.push_back(input.substr(pos)); -#endif - return tokens; + return result; } } // namespace Atom::Utils \ No newline at end of file diff --git a/src/atom/utils/string.hpp b/src/atom/utils/string.hpp index 50774ad5..e47b0cd0 100644 --- a/src/atom/utils/string.hpp +++ b/src/atom/utils/string.hpp @@ -16,43 +16,67 @@ Description: Some useful string functions #define ATOM_UTILS_STRING_HPP #include +#include #include namespace Atom::Utils { /** - * @brief 检查字符串中是否包含大写字母。 - * @param str 输入字符串。 - * @return 如果字符串中至少包含一个大写字母,则返回 true,否则返回 false。 + * @brief Checks if the given string contains any uppercase characters. + * + * @param str The string to check. + * @return true if the string contains uppercase characters, otherwise false. */ -[[nodiscard]] bool hasUppercase(const std::string &str); +[[nodiscard]] bool hasUppercase(std::string_view str); /** - * @brief 将字符串转换为下划线命名法(underscore)。 - * @param str 输入字符串。 - * @return 转换后的下划线命名法字符串。 + * @brief Converts the given string to snake_case format. + * + * @param str The string to convert. + * @return The string converted to snake_case. */ -[[nodiscard]] std::string toUnderscore(const std::string &str); +[[nodiscard]] std::string toUnderscore(std::string_view str); /** - * @brief 将字符串转换为驼峰命名法(camel case)。 - * @param str 输入字符串。 - * @return 转换后的驼峰命名法字符串。 + * @brief Converts the given string to camelCase format. + * + * @param str The string to convert. + * @return The string converted to camelCase. */ -[[nodiscard]] std::string toCamelCase(const std::string &str); +[[nodiscard]] std::string toCamelCase(std::string_view str); /** - * @brief 对字符串进行 URL 编码。 - * @param str 输入字符串。 - * @return URL 编码后的字符串。 + * @brief Encodes the given string using URL encoding. + * + * @param str The string to encode. + * @return The URL encoded string. */ -[[nodiscard]] std::string urlEncode(const std::string &str); +[[nodiscard]] std::string urlEncode(std::string_view str); /** - * @brief 对 URL 编码的字符串进行解码。 - * @param str 输入字符串。 - * @return 解码后的字符串。 + * @brief Decodes the given URL encoded string. + * + * @param str The URL encoded string to decode. + * @return The decoded string. + */ +[[nodiscard]] std::string urlDecode(std::string_view str); + +/** + * @brief Checks if the given string starts with the specified prefix. + * + * @param str The string to check. + * @param prefix The prefix to search for. + * @return true if the string starts with the prefix, otherwise false. + */ +[[nodiscard]] bool startsWith(std::string_view str, std::string_view prefix); + +/** + * @brief Checks if the given string ends with the specified suffix. + * + * @param str The string to check. + * @param suffix The suffix to search for. + * @return true if the string ends with the suffix, otherwise false. */ -[[nodiscard]] std::string urlDecode(const std::string &str); +[[nodiscard]] bool endsWith(std::string_view str, std::string_view suffix); /** * @brief 将字符串分割为多个字符串。 @@ -60,8 +84,49 @@ namespace Atom::Utils { * @param delimiter 分隔符。 * @return 分割后的字符串数组。 */ -[[nodiscard]] std::vector splitString(const std::string &input, - char delimiter); +[[nodiscard( + "the result of splitString is not used")]] std::vector +splitString(const std::string& str, char delimiter); + +/** + * @brief Concatenates an array of strings into a single string with a specified + * delimiter. + * + * @param strings The array of strings to concatenate. + * @param delimiter The delimiter to use for concatenation. + * @return The concatenated string. + */ +[[nodiscard("the result of joinStrings is not used")]] std::string joinStrings( + const std::vector& strings, + const std::string_view& delimiter); + +/** + * @brief Replaces all occurrences of a substring with another substring in a + * given text. + * + * @param text The text in which replacements will be made. + * @param oldStr The substring to replace. + * @param newStr The substring to replace with. + * @return The text with replacements made. + */ +[[nodiscard("the result of replaceString is not used")]] std::string +replaceString(std::string_view text, std::string_view oldStr, + std::string_view newStr); + +/** + * @brief Replaces multiple substrings with their corresponding replacements in + * a given text. + * + * @param text The text in which replacements will be made. + * @param replacements A vector of pairs, where each pair represents the + * substring to replace and its replacement. + * @return The text with replacements made. + */ +[[nodiscard("the result of replaceStrings is not used")]] std::string +replaceStrings(std::string_view text, + const std::vector>& + replacements); + } // namespace Atom::Utils #endif \ No newline at end of file diff --git a/src/atom/utils/switch.hpp b/src/atom/utils/switch.hpp index 261f47f0..f3e9ae43 100644 --- a/src/atom/utils/switch.hpp +++ b/src/atom/utils/switch.hpp @@ -18,6 +18,8 @@ Description: Smart Switch just like javascript #include #include #include +#include +#include #if ENABLE_FASTHASH #include "emhash/hash_table8.hpp" #else @@ -25,122 +27,90 @@ Description: Smart Switch just like javascript #endif #include "atom/error/exception.hpp" +#include "atom/experiment/noncopyable.hpp" namespace Atom::Utils { /** - * @brief A class for implementing a switch statement with string cases. + * @brief A class for implementing a switch statement with string cases, + * enhanced with C++17/20 features. * * @tparam DefaultFunc The function type for handling the default case. * @tparam Args The types of additional arguments to pass to the functions. */ template -class StringSwitch { +class StringSwitch : public NonCopyable { public: - using Func = std::function; /**< The function type for - handling a case. */ - using DefaultFunc = std::optional; // Optional default function - - /** - * @brief Registers a case with the given string and function. - * - * @param str The string to match against. - * @param func The function to call if the string matches. - */ - void registerCase(const std::string &str, Func func); - - /** - * @brief Unregisters a case with the given string. - * - * @param str The string to match against. - */ - void unregisterCase(const std::string &str); - - /** - * @brief Clears all registered cases. - */ - void clearCases(); - - /** - * @brief Matches the given string against the registered cases. - * - * @param str The string to match against. - * @param args Additional arguments to pass to the function. - * @return true if a match was found, false otherwise. - */ - bool match(const std::string &str, Args... args); - - /** - * @brief Sets the default function to be called if no match is found. - * - * @param func The function to call for the default case. - */ - void setDefault(DefaultFunc func); - - /** - * @brief Returns a vector of all registered cases. - * - * @return A vector of all registered cases. - */ - std::vector getCases() const; + using Func = std::function; + using DefaultFunc = std::optional; -private: -#if ENABLE_FASTHASH - emhash8::HashMap cases_; -#else - std::unordered_map - cases_; /**< The map of registered cases. */ -#endif - DefaultFunc - defaultFunc_; /**< The default function to call if no match is found. */ -}; + StringSwitch() = default; -template -void StringSwitch::registerCase(const std::string &str, Func func) { - if (cases_.find(str) != cases_.end()) { - throw Error::ObjectAlreadyExist("Case already registered"); + // Register a case with the given string and function + void registerCase(const std::string &str, Func func) { + if (cases_.find(str) != cases_.end()) { + throw Error::ObjectAlreadyExist("Case already registered"); + } + cases_[str] = std::move(func); // Use move semantics for efficiency } - cases_[str] = func; -} -template -void StringSwitch::unregisterCase(const std::string &str) { - cases_.erase(str); -} + // Unregister a case with the given string + void unregisterCase(const std::string &str) { cases_.erase(str); } -template -void StringSwitch::clearCases() { - cases_.clear(); -} + // Clear all registered cases + void clearCases() { cases_.clear(); } -template -bool StringSwitch::match(const std::string &str, Args... args) { - auto iter = cases_.find(str); - if (iter != cases_.end()) { - std::invoke(iter->second, args...); - return true; - } + // Match the given string against the registered cases + bool match(const std::string &str, Args... args) { + auto iter = cases_.find(str); + if (iter != cases_.end()) { + std::invoke(iter->second, args...); + return true; + } + + if (defaultFunc_) { + std::invoke(*defaultFunc_, + args...); // Use optional's value() for clarity + return true; + } - if constexpr (!std::is_void_v) { - std::invoke(defaultFunc_.value(), args...); - return true; + return false; } - return false; -} + // Set the default function to be called if no match is found + void setDefault(DefaultFunc func) { defaultFunc_ = std::move(func); } + + // Get a vector of all registered cases + std::vector getCases() const { + std::vector caseList; + for (const auto &[key, value] : + cases_) { // Use structured bindings for clarity + caseList.push_back(key); + } + return caseList; + } -template -void StringSwitch::setDefault(DefaultFunc func) { - defaultFunc_ = func; -} + // C++17 deduction guide for easier initialization + template >> + void registerCase(const std::string &str, T &&func) { + registerCase(str, std::forward(func)); + } -template -std::vector StringSwitch::getCases() const { - std::vector caseList; - for (const auto &entry : cases_) { - caseList.push_back(entry.first); + // C++20 designated initializers for easier case registration + StringSwitch(std::initializer_list> initList) { + for (auto [str, func] : initList) { + registerCase(str, std::move(func)); + } } - return caseList; -} + +private: +#if ENABLE_FASTHASH + emhash8::HashMap cases_; +#else + std::unordered_map cases_; +#endif + DefaultFunc defaultFunc_; +}; } // namespace Atom::Utils diff --git a/src/atom/utils/utf.cpp b/src/atom/utils/utf.cpp new file mode 100644 index 00000000..9287674b --- /dev/null +++ b/src/atom/utils/utf.cpp @@ -0,0 +1,137 @@ +/* + * utf.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-3 + +Description: Some useful functions about utf string + +**************************************************/ + +#include "utf.hpp" + +#include +#include +#include +#include +#include +#include + +namespace Atom::Utils { +std::string toUTF8(std::wstring_view wstr) { +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) + std::wstring_convert, char16_t> convert; + return convert.to_bytes((char16_t*)wstr.data(), + (char16_t*)wstr.data() + wstr.size()); +#elif defined(unix) || defined(__unix) || defined(__unix__) || \ + defined(__APPLE__) + std::wstring_convert > convert; + return convert.to_bytes(wstr.data(), wstr.data() + wstr.size()); +#elif defined(_WIN32) || defined(_WIN64) + std::wstring_convert > convert; + return convert.to_bytes(wstr.data(), wstr.data() + wstr.size()); +#endif +} + +std::wstring fromUTF8(std::string_view str) { +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) + std::wstring_convert, char16_t> convert; + auto tmp = convert.from_bytes(str.data(), str.data() + str.size()); + return std::wstring(tmp.data(), tmp.data() + tmp.size()); +#elif defined(unix) || defined(__unix) || defined(__unix__) || \ + defined(__APPLE__) + std::wstring_convert > convert; + return convert.from_bytes(str.data(), str.data() + str.size()); +#elif defined(_WIN32) || defined(_WIN64) + std::wstring_convert > convert; + return convert.from_bytes(str.data(), str.data() + str.size()); +#endif +} + +std::u16string UTF8toUTF16(std::string_view str) { +#if defined(_MSC_VER) + std::wstring_convert, uint16_t> convert; + auto tmp = convert.from_bytes(str.data(), str.data() + str.size()); + return std::u16string(tmp.data(), tmp.data() + tmp.size()); +#else + std::wstring_convert, char16_t> convert; + return convert.from_bytes(str.data(), str.data() + str.size()); +#endif +} + +std::u32string UTF8toUTF32(std::string_view str) { +#if defined(_MSC_VER) + std::wstring_convert, uint32_t> convert; + auto tmp = convert.from_bytes(str.data(), str.data() + str.size()); + return std::u32string(tmp.data(), tmp.data() + tmp.size()); +#else + std::wstring_convert, char32_t> convert; + return convert.from_bytes(str.data(), str.data() + str.size()); +#endif +} + +std::string UTF16toUTF8(std::u16string_view str) { +#if defined(_MSC_VER) + std::wstring_convert, uint16_t> convert; + return convert.to_bytes((uint16_t*)str.data(), + (uint16_t*)str.data() + str.size()); +#else + std::wstring_convert, char16_t> convert; + return convert.to_bytes(str.data(), str.data() + str.size()); +#endif +} + +std::u32string UTF16toUTF32(std::u16string_view str) { + std::string bytes; + bytes.reserve(str.size() * 2); + + for (const char16_t ch : str) { + bytes.push_back((uint8_t)(ch / 256)); + bytes.push_back((uint8_t)(ch % 256)); + } + +#if defined(_MSC_VER) + std::wstring_convert, uint32_t> convert; + auto tmp = convert.from_bytes(bytes); + return std::u32string(tmp.data(), tmp.data() + tmp.size()); +#else + std::wstring_convert, char32_t> convert; + return convert.from_bytes(bytes); +#endif +} + +std::string UTF32toUTF8(std::u32string_view str) { +#if defined(_MSC_VER) + std::wstring_convert, uint32_t> convert; + return convert.to_bytes((uint32_t*)str.data(), + (uint32_t*)str.data() + str.size()); +#else + std::wstring_convert, char32_t> convert; + return convert.to_bytes(str.data(), str.data() + str.size()); +#endif +} + +std::u16string UTF32toUTF16(std::u32string_view str) { +#if defined(_MSC_VER) + std::wstring_convert, uint32_t> convert; + std::string bytes = convert.to_bytes((uint32_t*)str.data(), + (uint32_t*)str.data() + str.size()); +#else + std::wstring_convert, char32_t> convert; + std::string bytes = convert.to_bytes(str.data(), str.data() + str.size()); +#endif + + std::u16string result; + result.reserve(bytes.size() / 2); + + for (size_t i = 0; i < bytes.size(); i += 2) + result.push_back( + (char16_t)((uint8_t)(bytes[i]) * 256 + (uint8_t)(bytes[i + 1]))); + + return result; +} +} // namespace Atom::Utils diff --git a/src/atom/utils/utf.hpp b/src/atom/utils/utf.hpp new file mode 100644 index 00000000..0436af49 --- /dev/null +++ b/src/atom/utils/utf.hpp @@ -0,0 +1,86 @@ +/* + * utf.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-3 + +Description: Some useful functions about utf string + +**************************************************/ + +#ifndef ATOM_UTILS_UTF_HPP +#define ATOM_UTILS_UTF_HPP + +#include + +namespace Atom::Utils { +/** + * @brief 将宽字符字符串转换为 UTF-8 字符串 + * + * @param wstr 宽字符字符串视图 + * @return std::string 转换后的 UTF-8 字符串 + */ +std::string toUTF8(std::wstring_view wstr); + +/** + * @brief 将 UTF-8 字符串转换为宽字符字符串 + * + * @param str UTF-8 字符串视图 + * @return std::wstring 转换后的宽字符字符串 + */ +std::wstring fromUTF8(std::string_view str); + +/** + * @brief 将 UTF-8 字符串转换为 UTF-16 字符串 + * + * @param str UTF-8 字符串视图 + * @return std::u16string 转换后的 UTF-16 字符串 + */ +std::u16string UTF8toUTF16(std::string_view str); + +/** + * @brief 将 UTF-8 字符串转换为 UTF-32 字符串 + * + * @param str UTF-8 字符串视图 + * @return std::u32string 转换后的 UTF-32 字符串 + */ +std::u32string UTF8toUTF32(std::string_view str); + +/** + * @brief 将 UTF-16 字符串转换为 UTF-8 字符串 + * + * @param str UTF-16 字符串视图 + * @return std::string 转换后的 UTF-8 字符串 + */ +std::string UTF16toUTF8(std::u16string_view str); + +/** + * @brief 将 UTF-16 字符串转换为 UTF-32 字符串 + * + * @param str UTF-16 字符串视图 + * @return std::u32string 转换后的 UTF-32 字符串 + */ +std::u32string UTF16toUTF32(std::u16string_view str); + +/** + * @brief 将 UTF-32 字符串转换为 UTF-8 字符串 + * + * @param str UTF-32 字符串视图 + * @return std::string 转换后的 UTF-8 字符串 + */ +std::string UTF32toUTF8(std::u32string_view str); + +/** + * @brief 将 UTF-32 字符串转换为 UTF-16 字符串 + * + * @param str UTF-32 字符串视图 + * @return std::u16string 转换后的 UTF-16 字符串 + */ +std::u16string UTF32toUTF16(std::u32string_view str); +} // namespace Atom::Utils + +#endif diff --git a/src/atom/utils/validate_aligned_storage.hpp b/src/atom/utils/validate_aligned_storage.hpp index dc29f29d..b044d155 100644 --- a/src/atom/utils/validate_aligned_storage.hpp +++ b/src/atom/utils/validate_aligned_storage.hpp @@ -15,6 +15,7 @@ Description: Validate aligned storage #ifndef ATOM_UTILS_VALIDATE_ALIGNED_STORAGE_HPP #define ATOM_UTILS_VALIDATE_ALIGNED_STORAGE_HPP +#include #include namespace Atom::Utils { diff --git a/src/atom/utils/xmake.lua b/src/atom/utils/xmake.lua new file mode 100644 index 00000000..071e7bdf --- /dev/null +++ b/src/atom/utils/xmake.lua @@ -0,0 +1,67 @@ +-- xmake.lua for Atom-Utils +-- This project is licensed under the terms of the GPL3 license. +-- +-- Project Name: Atom-Utils +-- Description: A collection of useful functions +-- Author: Max Qian +-- License: GPL3 + +add_rules("mode.debug", "mode.release") + +set_project("atom-utils") +set_version("1.0.0") +set_license("GPL3") + +-- Sources +local sources = { + "aes.cpp", + "env.cpp", + "hash_util.cpp", + "random.cpp", + "string.cpp", + "stopwatcher.cpp", + "time.cpp", + "uuid.cpp", + "xml.cpp" +} + +-- Headers +local headers = { + "aes.hpp", + "env.hpp", + "hash_util.hpp", + "random.hpp", + "refl.hpp", + "string.hpp", + "stopwatcher.hpp", + "switch.hpp", + "time.hpp", + "uuid.hpp", + "xml.hpp" +} + +-- Private Headers +local private_headers = { +} + +-- Build Object Library +target("atom-utils-object") + set_kind("object") + add_headerfiles(headers, {public = true}) + add_files(sources, private_headers, {public = false}) + add_packages("loguru", "tinyxml2") + +-- Build Static Library +target("atom-utils") + set_kind("static") + add_deps("atom-utils-object") + add_packages("loguru", "tinyxml2") + add_includedirs(".", {public = true}) + + set_targetdir("$(buildir)/lib") + set_objectdir("$(buildir)/obj") + + after_build(function (target) + os.cp("$(buildir)/lib", "$(projectdir)/lib") + os.cp("$(projectdir)/*.hpp", "$(projectdir)/include") + end) \ No newline at end of file diff --git a/src/atom/web/meson.build b/src/atom/web/meson.build new file mode 100644 index 00000000..b866d8c7 --- /dev/null +++ b/src/atom/web/meson.build @@ -0,0 +1,47 @@ +project('Atom-Web', 'cpp') + +# Define sources and headers +sources = [ + 'address.cpp', + 'downloader.cpp', + 'httpclient.cpp', + 'httplite.cpp', + 'utils.cpp', + 'time.cpp' +] +headers = [ + 'address.hpp', + 'downloader.hpp', + 'httpclient.hpp', + 'httplite.hpp', + 'utils.hpp', + 'time.hpp' +] + +# Create object library +atom_web_obj = library('atom_web', + sources + headers, + cpp_std: c_std +) + +# Link to other libraries if necessary +atom_web_obj_link_libs = [] +foreach lib : ['loguru', 'cpp_httplib'] + atom_web_obj_link_libs += dependency(lib) +endforeach + +atom_web_obj.link_with(atom_web_obj_link_libs) + +# Create static library +atom_web_static = static_library('atom_web_static', atom_web_obj) + +# Set version properties +version = '1.0.0' +soversion = '1' + +atom_web_static.version = version +atom_web_static.soversion = soversion +atom_web_static.basename = 'Atom-Web' + +# Install target +install_targets(atom_web_static) diff --git a/src/atom/web/time.cpp b/src/atom/web/time.cpp index e0b441ac..9c47319e 100644 --- a/src/atom/web/time.cpp +++ b/src/atom/web/time.cpp @@ -22,9 +22,9 @@ Description: Time #include #ifdef _WIN32 // Windows +#include #include #include -#include #include #else // Linux #include @@ -197,7 +197,7 @@ bool syncTimeFromRTC() { FILETIME ft; ft.dwLowDateTime = (DWORD)new_timestamp; ft.dwHighDateTime = (DWORD)(new_timestamp >> 32); - if (!System::IsRoot()) { + if (!System::isRoot()) { LOG_F(ERROR, "Permission denied. Need root privilege to set system time."); return false; @@ -210,7 +210,7 @@ bool syncTimeFromRTC() { void setSystemTime(int year, int month, int day, int hour, int minute, int second) { - if (!System::IsRoot()) { + if (!System::isRoot()) { LOG_F(ERROR, "Permission denied. Need root privilege to set system time."); return; diff --git a/src/atom/web/xmake.lua b/src/atom/web/xmake.lua new file mode 100644 index 00000000..d6d4e0e7 --- /dev/null +++ b/src/atom/web/xmake.lua @@ -0,0 +1,59 @@ +-- xmake.lua for Atom-Web +-- This project is licensed under the terms of the GPL3 license. +-- +-- Project Name: Atom-Web +-- Description: Web API +-- Author: Max Qian +-- License: GPL3 + +add_rules("mode.debug", "mode.release") + +set_project("atom-web") +set_version("1.0.0") +set_license("GPL3") + +-- Sources +local sources = { + "address.cpp", + "downloader.cpp", + "httpclient.cpp", + "httplite.cpp", + "utils.cpp", + "time.cpp" +} + +-- Headers +local headers = { + "address.hpp", + "downloader.hpp", + "httpclient.hpp", + "httplite.hpp", + "utils.hpp", + "time.hpp" +} + +-- Build Object Library +target("atom-web-object") + set_kind("object") + add_files(headers, {public = true}) + add_files(sources, {public = false}) + add_packages("loguru") + +-- Build Static Library +target("atom-web") + set_kind("static") + add_deps("atom-web-object") + add_packages("loguru", "cpp_httplib") + add_includedirs(".", {public = true}) + + if is_plat("windows") then + add_syslinks("wsock32", "ws2_32") + end + + set_targetdir("$(buildir)/lib") + set_objectdir("$(buildir)/obj") + + after_build(function (target) + os.cp("$(buildir)/lib", "$(projectdir)/lib") + os.cp("$(projectdir)/*.hpp", "$(projectdir)/include") + end) \ No newline at end of file diff --git a/src/atom/xmake.lua b/src/atom/xmake.lua new file mode 100644 index 00000000..64dc0a13 --- /dev/null +++ b/src/atom/xmake.lua @@ -0,0 +1,94 @@ +-- xmake.lua for Atom +-- This project is licensed under the terms of the GPL3 license. +-- +-- Project Name: Atom +-- Description: Atom Library for all of the Element Astro Project +-- Author: Max Qian +-- License: GPL3 + +add_rules("mode.debug", "mode.release") + +set_project("atom") +set_version("1.0.0") +set_license("GPL3") + +option("atom_build_python", {description = "Build Atom with Python support", default = false}) + +if has_config("atom_build_python") then + add_requires("python 3.x", {kind = "binary"}) + add_requires("pybind11") +end + +add_subdirs("algorithm", "async", "components", "connection", "driver", "event", "experiment", "io", "log", "server", "search", "system", "task", "type", "utils", "web") + +if not has_config("HAS_STD_FORMAT") then + add_requires("fmt") +end + +-- Sources +local sources = { + "error/error_stack.cpp", + "log/logger.cpp", + "log/global_logger.cpp", + "log/syslog.cpp" +} + +-- Headers +local headers = { + "error/error_code.hpp", + "error/error_stack.hpp", + "log/logger.hpp", + "log/global_logger.hpp", + "log/syslog.hpp" +} + +-- Private Headers +local private_headers = { +} + +-- Dependencies +local dependencies = { + "loguru", + "cpp_httplib", + "libzippp", + "atom-async", + "atom-task", + "atom-io", + "atom-driver", + "atom-event", + "atom-experiment", + "atom-component", + "atom-type", + "atom-utils", + "atom-search", + "atom-web", + "atom-system", + "atom-server" +} + +-- Build Object Library +target("atom-object") + set_kind("object") + add_files(headers, {public = true}) + add_files(sources, private_headers, {public = false}) + add_defines("HAVE_LIBNOVA") + add_packages(dependencies) + + if is_plat("windows") then + add_syslinks("setupapi", "wsock32", "ws2_32", "shlwapi", "iphlpapi") + end + +-- Build Static Library +target("atom") + set_kind("static") + add_deps("atom-object") + add_packages(dependencies) + add_includedirs(".", {public = true}) + + set_targetdir("$(buildir)/lib") + set_objectdir("$(buildir)/obj") + + after_build(function (target) + os.cp("$(buildir)/lib", "$(projectdir)/lib") + os.cp("$(projectdir)/*.hpp", "$(projectdir)/include") + end) \ No newline at end of file diff --git a/src/auth/AuthHandler.cpp b/src/auth/AuthHandler.cpp deleted file mode 100644 index 0eb06a93..00000000 --- a/src/auth/AuthHandler.cpp +++ /dev/null @@ -1,12 +0,0 @@ - -#include "AuthHandler.hpp" - -AuthHandler::AuthHandler(const std::shared_ptr& jwt) - : oatpp::web::server::handler::BearerAuthorizationHandler( - "API" /* Realm */), - m_jwt(jwt) {} - -std::shared_ptr AuthHandler::authorize( - const oatpp::String& token) { - return m_jwt->readAndVerifyToken(token); -} \ No newline at end of file diff --git a/src/auth/AuthHandler.hpp b/src/auth/AuthHandler.hpp deleted file mode 100644 index 91ffb28c..00000000 --- a/src/auth/AuthHandler.hpp +++ /dev/null @@ -1,19 +0,0 @@ - -#ifndef HEAL_AUTH_JWT_AUTHHANDLER_HPP -#define HEAL_AUTH_JWT_AUTHHANDLER_HPP - -#include "JWT.hpp" - -class AuthHandler - : public oatpp::web::server::handler::BearerAuthorizationHandler { -private: - std::shared_ptr m_jwt; - -public: - AuthHandler(const std::shared_ptr& jwt); - - std::shared_ptr authorize( - const oatpp::String& token) override; -}; - -#endif // HEAL_AUTH_JWT_AUTHHANDLER_HPP diff --git a/src/auth/JWT.cpp b/src/auth/JWT.cpp deleted file mode 100644 index ece67ad4..00000000 --- a/src/auth/JWT.cpp +++ /dev/null @@ -1,32 +0,0 @@ - -#include "JWT.hpp" - -JWT::JWT(const oatpp::String& secret, const oatpp::String& issuer) - : m_secret(secret), - m_issuer(issuer), - m_verifier(jwt::verify() - .allow_algorithm(jwt::algorithm::hs256{secret}) - .with_issuer(issuer)) {} - -oatpp::String JWT::createToken(const std::shared_ptr& payload) { - auto token = jwt::create() - .set_issuer(m_issuer) - .set_type("JWS") - - .set_payload_claim("userId", jwt::claim(payload->userId)) - - .sign(jwt::algorithm::hs256{m_secret}); - return token; -} - -std::shared_ptr JWT::readAndVerifyToken( - const oatpp::String& token) { - auto decoded = jwt::decode(token); - m_verifier.verify(decoded); - - auto payload = std::make_shared(); - payload->userId = - decoded.get_payload_claim("").to_json().get("userId").to_str(); - - return payload; -} \ No newline at end of file diff --git a/src/auth/JWT.hpp b/src/auth/JWT.hpp deleted file mode 100644 index 6e37e192..00000000 --- a/src/auth/JWT.hpp +++ /dev/null @@ -1,29 +0,0 @@ - -#ifndef HEAL_AUTH_JWT_HPP -#define HEAL_AUTH_JWT_HPP - -#include "oatpp/core/Types.hpp" -#include "oatpp/web/server/handler/AuthorizationHandler.hpp" - -#include - -class JWT { -public: - struct Payload : public oatpp::web::server::handler::AuthorizationObject { - oatpp::String userId; - }; - -private: - oatpp::String m_secret; - oatpp::String m_issuer; - jwt::verifier m_verifier; - -public: - JWT(const oatpp::String& secret, const oatpp::String& issuer); - - oatpp::String createToken(const std::shared_ptr& payload); - - std::shared_ptr readAndVerifyToken(const oatpp::String& token); -}; - -#endif // HEAL_AUTH_JWT_HPP diff --git a/src/carbon/CMakeLists.txt b/src/carbon/CMakeLists.txt new file mode 100644 index 00000000..441f8695 --- /dev/null +++ b/src/carbon/CMakeLists.txt @@ -0,0 +1,419 @@ +cmake_minimum_required(VERSION 3.12) + +project(Carbon) + +cmake_policy(SET CMP0054 NEW) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# required since cmake 3.4 at least for libc++ +set(CMAKE_ENABLE_EXPORTS ON) + +option(MULTITHREAD_SUPPORT_ENABLED "Multithreaded Support Enabled" TRUE) +option(DYNLOAD_ENABLED "Dynamic Loading Support Enabled" TRUE) +option(BUILD_MODULES "Build Extra Modules (stl)" TRUE) +option(BUILD_SAMPLES "Build Samples Folder" FALSE) +option(RUN_FUZZY_TESTS "Run tests generated by AFL" FALSE) +option(USE_STD_MAKE_SHARED "Use std::make_shared instead of Carbon::make_shared" FALSE) +option(RUN_PERFORMANCE_TESTS "Run Performance Tests" FALSE) + +mark_as_advanced(USE_STD_MAKE_SHARED) + +if(USE_STD_MAKE_SHARED) + add_definitions(-DCarbon_USE_STD_MAKE_SHARED) +endif() + +if(CMAKE_COMPILER_IS_GNUCC) + option(ENABLE_COVERAGE "Enable Coverage Reporting in GCC" FALSE) + + if(ENABLE_COVERAGE) + add_definitions(--coverage -O0) + set(LINKER_FLAGS "${LINKER_FLAGS} --coverage") + endif() +endif() + +if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + option(ENABLE_THREAD_SANITIZER "Enable thread sanitizer testing in gcc/clang" FALSE) + if(ENABLE_THREAD_SANITIZER) + add_definitions(-fsanitize=thread -g) + set(LINKER_FLAGS "${LINKER_FLAGS} -fsanitize=thread") + endif() + + option(ENABLE_ADDRESS_SANITIZER "Enable address sanitizer testing in gcc/clang" FALSE) + if(ENABLE_ADDRESS_SANITIZER) + add_definitions(-fsanitize=address -g) + set(LINKER_FLAGS "${LINKER_FLAGS} -fsanitize=address") + + option(BUILD_LIBFUZZ_TESTER "Build libfuzzer tool" FALSE) + endif() + + option(ENABLE_MEMORY_SANITIZER "Enable memory sanitizer testing in gcc/clang" FALSE) + if(ENABLE_MEMORY_SANITIZER) + add_definitions(-fsanitize=memory -fsanitize-memory-track-origins -g) + set(LINKER_FLAGS "${LINKER_FLAGS} -fsanitize=memory -fsanitize-memory-track-origins ") + endif() + + option(ENABLE_UNDEFINED_SANITIZER "Enable undefined behavior sanitizer testing in gcc/clang" FALSE) + if(ENABLE_UNDEFINED_SANITIZER) + add_definitions(-fsanitize=undefined -g) + set(LINKER_FLAGS "${LINKER_FLAGS} -fsanitize=undefined") + endif() + + option(ENABLE_LTO "Enable Link Time Optimization" FALSE) + if(ENABLE_LTO) + check_ipo_supported() + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + endif() + + option(GPROF_OUTPUT "Generate profile data" FALSE) + if(GPROF_OUTPUT) + add_definitions(-pg) + set(LINKER_FLAGS "${LINKER_FLAGS} -pg") + endif() + + + option(PROFILE_GENERATE "Generate profile data" FALSE) + if(PROFILE_GENERATE) + add_definitions(-fprofile-generate) + set(LINKER_FLAGS "${LINKER_FLAGS} -fprofile-generate") + endif() + + option(PROFILE_USE "Use profile data" FALSE) + if(PROFILE_USE) + add_definitions(-fprofile-use) + set(LINKER_FLAGS "${LINKER_FLAGS} -fprofile-use") + endif() + + +endif() + +list(APPEND CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_BINARY_DIR}") +list(APPEND CPACK_SOURCE_IGNORE_FILES "\\\\.svn") +list(APPEND CPACK_SOURCE_IGNORE_FILES "\\\\.git") +list(APPEND CPACK_SOURCE_IGNORE_FILES ".swp") +list(APPEND CPACK_SOURCE_IGNORE_FILES ".*~") + +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") +set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") +#set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/description.txt") + +set(CPACK_PACKAGE_VERSION_MAJOR 7) +set(CPACK_PACKAGE_VERSION_MINOR 0) +set(CPACK_PACKAGE_VERSION_PATCH 0) + +set(CPACK_PACKAGE_EXECUTABLES "chai;Carbon Eval") +set(CPACK_PACKAGE_VENDOR "Carbon.com") +set(CPACK_PACKAGE_CONTACT "contact@Carbon.com") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "An embedded scripting language for C++") + +set(CPACK_DEBIAN_PACKAGE_SECTION "devel") +set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") + +set(CPACK_RPM_PACKAGE_LICENSE "BSD") +set(CPACK_RPM_PACKAGE_GROUP "Programming") + +set(CHAI_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}) + +# configure_file(Doxyfile.in ${CMAKE_BINARY_DIR}/Doxyfile) + +include(CTest) +include(CPack) + #include(cmake/Catch.cmake) + +if(NOT MINGW) + find_library(READLINE_LIBRARY NAMES readline PATH /usr/lib /usr/local/lib /opt/local/lib) +endif() + +if(UNIX AND NOT APPLE) + find_program(VALGRIND NAMES valgrind PATH /usr/bin /usr/local/bin) +endif() + +enable_testing() + + +message(STATUS "Detecting readline support") +if(READLINE_LIBRARY) + message(STATUS "Found: ${READLINE_LIBRARY}") + set(READLINE_LIB readline) + add_definitions(/DREADLINE_AVAILABLE) +else() + message(STATUS "Not Found") + set(READLINE_LIB) + set(READLINE_FLAG) +endif() + +if(MSVC) + add_definitions(/W4 /w14545 /w34242 /w34254 /w34287 /w44263 /w44265 /w44296 /w44311 /w44826 /we4289 /w14546 /w14547 /w14549 /w14555 /w14619 /w14905 /w14906 /w14928) + + if(MSVC_VERSION STREQUAL "1800") + # VS2013 doesn't have magic statics + add_definitions(/w44640) + else() + # enum warnings are too noisy on MSVC2013 + add_definitions(/w34062) + endif() + + add_definitions(/bigobj /permissive- /utf-8) + # Note on MSVC compiler flags. + # The code base selective disables warnings as necessary when the compiler is complaining too much + # about something that is perfectly valid, or there is simply no technical way around it + # This particular warning, C4503 is in regards to the decorated names that MSVC generates internally. + # The error did not come up until the move to C++11, but the compiler doesn't give enough information + # to determine where the error is coming from, and the internet provides no real information for + # how to workaround or fix the error. So I'm disabling it globally. + add_definitions(/wd4503) +else() + add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -Wno-noexcept-type -Wpedantic -Werror=return-type) + + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command -Wno-unused-template -Wno-undef -Wno-double-promotion) + else() + add_definitions(-Wnoexcept) + endif() + + if(APPLE) + add_definitions(-Wno-sign-compare) + endif() +endif() + +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + option(USE_LIBCXX "Use clang's libcxx" FALSE) + + if(USE_LIBCXX) + add_definitions(-stdlib=libc++) + set(LINKER_FLAGS "${LINKER_FLAGS} -stdlib=libc++") + endif() +endif() + +# limitations in MinGW require us to make an optimized build +# for the sake of object sizes or something +if(MINGW OR CYGWIN) + add_definitions(-O3) +endif() + +include_directories(include) + + +set(Chai_INCLUDES carbon.hpp threading.hpp command/bad_boxed_cast.hpp command/bind_first.hpp command/bootstrap.hpp command/bootstrap_stl.hpp command/boxed_cast.hpp command/boxed_cast_helper.hpp command/boxed_number.hpp command/boxed_value.hpp command/dispatchkit.hpp command/type_conversions.hpp command/dynamic_object.hpp command/exception_specification.hpp command/function_call.hpp command/function_call_detail.hpp command/handle_return.hpp command/operators.hpp command/proxy_constructors.hpp command/proxy_functions.hpp command/proxy_functions_detail.hpp command/register_function.hpp command/type_info.hpp language/algebraic.hpp language/common.hpp language/engine.hpp language/eval.hpp language/parser.hpp language/prelude.hpp language/prelude_docs.hpp utils/utility.hpp utils/json.hpp utils/json_wrap.hpp) + +set_source_files_properties(${Chai_INCLUDES} PROPERTIES HEADER_FILE_ONLY TRUE) + +if(NOT MULTITHREAD_SUPPORT_ENABLED) + add_definitions(-DCarbon_NO_THREADS) +endif() + +if(NOT DYNLOAD_ENABLED) + add_definitions(-DCarbon_NO_DYNLOAD) +endif() + +if(CMAKE_HOST_UNIX) + if(DYNLOAD_ENABLED) + if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Haiku") + list(APPEND LIBS "dl") + endif() + endif() + + if(MULTITHREAD_SUPPORT_ENABLED) + if(CMAKE_COMPILER_IS_GNUCC) + execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE GCC_FULL_VERSION) + if(GCC_FULL_VERSION MATCHES "4.8.1.*ubuntu") + set(LINKER_FLAGS "${LINKER_FLAGS} -Wl,--no-as-needed -pthread") + else() + set(LINKER_FLAGS "${LINKER_FLAGS} -pthread") + endif() + else() + set(LINKER_FLAGS "${LINKER_FLAGS} -pthread") + endif() + + add_definitions(-pthread) + endif() + +endif() + +list(APPEND LIBS ${READLINE_LIB}) + +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LINKER_FLAGS}") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINKER_FLAGS}") +set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LINKER_FLAGS}") + +#add_library(stdlib STATIC static_libs/Carbon_stdlib.cpp) +#add_library(parser STATIC static_libs/Carbon_parser.cpp) + +#add_library(Carbon_stdlib-${CHAI_VERSION} MODULE src/Carbon_stdlib_module.cpp) +#target_link_libraries(Carbon_stdlib-${CHAI_VERSION} ${LIBS} ${CMAKE_THREAD_LIBS_INIT}) + +set(Carbon_LIBS stdlib parser) + +set(Carbon_SRC + command/bad_boxed_cast.cpp + command/boxed_number.cpp + command/dispatchkit.cpp + command/dynamic_object_detail.cpp + command/dynamic_object.cpp + command/proxy_functions.cpp + + language/common.cpp + language/engine.cpp + language/prelude.cpp +) +# We will build a static library for Carbon +add_library(carbon STATIC ${Carbon_SRC}) +# add_executable(chai src/main.cpp ${Carbon_SRC} ${Chai_INCLUDES}) +# target_link_libraries(chai ${LIBS} ${Carbon_LIBS}) + +add_library(Carbon INTERFACE) +target_include_directories(Carbon INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) + +if(BUILD_SAMPLES) + add_executable(sanity_checks src/sanity_checks.cpp) + target_link_libraries(sanity_checks ${LIBS}) + add_executable(test_num_exceptions samples/test_num_exceptions.cpp) + target_link_libraries(test_num_exceptions ${LIBS} ${Carbon_LIBS}) + add_executable(memory_leak_test samples/memory_leak_test.cpp) + target_link_libraries(memory_leak_test ${LIBS} ${Carbon_LIBS}) + add_executable(inheritance samples/inheritance.cpp) + target_link_libraries(inheritance ${LIBS} ${Carbon_LIBS}) + add_executable(factory samples/factory.cpp) + target_link_libraries(factory ${LIBS} ${Carbon_LIBS}) + add_executable(fun_call_performance samples/fun_call_performance.cpp) + target_link_libraries(fun_call_performance ${LIBS} ${Carbon_LIBS}) +endif() + + +if(BUILD_MODULES) + #add_library(test_module MODULE src/test_module.cpp) + #target_link_libraries(test_module ${LIBS}) + + #add_library(stl_extra MODULE src/stl_extra.cpp) + #target_link_libraries(stl_extra ${LIBS}) + + #set(MODULES stl_extra) +endif() + + +file(GLOB UNIT_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/unittests/ ${CMAKE_CURRENT_SOURCE_DIR}/unittests/*.chai ${CMAKE_CURRENT_SOURCE_DIR}/unittests/3.x/*.chai) +list(SORT UNIT_TESTS) + +file(GLOB PERFORMANCE_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/performance_tests/ ${CMAKE_CURRENT_SOURCE_DIR}/performance_tests/*.chai) +list(SORT PERFORMANCE_TESTS) + + +if(RUN_FUZZY_TESTS) + + file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/unittests") + + execute_process( + COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2017-07-20.tar.bz2 + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittests + ) + + + file(GLOB FUZZY_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/MINIMIZED/*) + list(SORT FUZZY_TESTS) + + foreach(filename ${FUZZY_TESTS}) + message(STATUS "Adding test ${filename}") + add_test(fuzz.${filename} chai "-e" "--exception" "--any-exception" ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzz_unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename}) + endforeach() + + set_property(TEST ${FUZZY_EXCEPTION_TESTS} + PROPERTY ENVIRONMENT + "CHAI_USE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/unittests/" + "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" + ) + +endif() + + +if(BUILD_TESTING) + + option(UNIT_TEST_LIGHT "Unit tests light (expect module loading failures)" FALSE) + + add_test(version_check chai -c "if(\"\\\${ version() };\\\${version_major()};\\\${version_minor()};\\\${version_patch()}\" != \"${CHAI_VERSION};${CPACK_PACKAGE_VERSION_MAJOR};${CPACK_PACKAGE_VERSION_MINOR};${CPACK_PACKAGE_VERSION_PATCH}\") { exit(-1) }") + set_property(TEST version_check + PROPERTY ENVIRONMENT + "CHAI_USE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/unittests/" + "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" + ) + + add_test(version_check_2 chai --version ) + set_property(TEST version_check_2 + PROPERTY ENVIRONMENT + "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" + PROPERTY PASS_REGULAR_EXPRESSION "${CHAI_VERSION}" + ) + + add_test(help chai --help ) + set_property(TEST help + PROPERTY ENVIRONMENT + "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" + ) + + + set(TESTS "") + + foreach(filename ${UNIT_TESTS}) + message(STATUS "Adding unit test ${filename}") + add_test(unit.${filename} chai ${CMAKE_CURRENT_SOURCE_DIR}/unittests/unit_test.inc ${CMAKE_CURRENT_SOURCE_DIR}/unittests/${filename}) + list(APPEND TESTS unit.${filename}) + endforeach() + + if(RUN_PERFORMANCE_TESTS) + foreach(filename ${PERFORMANCE_TESTS}) + message(STATUS "Adding performance test ${filename}") + + add_test(NAME performance.${filename} COMMAND ${VALGRIND} --tool=callgrind --callgrind-out-file=callgrind.performance.${filename} $ ${CMAKE_CURRENT_SOURCE_DIR}/performance_tests/${filename}) + list(APPEND TESTS performance.${filename}) + endforeach() + + add_executable(profile_cpp_calls_2 performance_tests/profile_cpp_calls_2.cpp) + target_link_libraries(profile_cpp_calls_2 ${LIBS}) + add_test(NAME performance.profile_cpp_calls_2 COMMAND ${VALGRIND} --tool=callgrind --callgrind-out-file=callgrind.performance.profile_cpp_calls_2 $) + + add_executable(profile_fun_wrappers performance_tests/profile_fun_wrappers.cpp) + target_link_libraries(profile_fun_wrappers ${LIBS}) + add_test(NAME performance.profile_fun_wrappers COMMAND ${VALGRIND} --tool=callgrind --callgrind-out-file=callgrind.performance.profile_fun_wrappers $) + endif() + + set_property(TEST ${TESTS} + PROPERTY ENVIRONMENT + "CHAI_USE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/unittests/" + "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" + ) + + +endif() + + +if(BUILD_LIBFUZZ_TESTER) + add_executable(fuzzer src/libfuzzer_client.cpp src/sha3.cpp) + target_compile_options(fuzzer PRIVATE "-fsanitize=fuzzer,address") + target_link_libraries(fuzzer PRIVATE ${LIBS} "-fsanitize=fuzzer,address") +endif() + + +# install(TARGETS chai Carbon_stdlib-${CHAI_VERSION} ${MODULES} RUNTIME DESTINATION bin LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/Carbon") + +install(DIRECTORY include/Carbon DESTINATION include + PATTERN "*.hpp" + PATTERN "*/.svn*" EXCLUDE + PATTERN "*/.git*" EXCLUDE + PATTERN "*~" EXCLUDE) +install(DIRECTORY unittests DESTINATION share/Carbon + PATTERN "*.chai" + PATTERN "*.inc" + PATTERN "*/.svn*" EXCLUDE + PATTERN "*/.git*" EXCLUDE + PATTERN "*~" EXCLUDE) +install(DIRECTORY samples DESTINATION share/Carbon + PATTERN "*.chai" + PATTERN "*/.svn*" EXCLUDE + PATTERN "*/.git*" EXCLUDE + PATTERN "*~" EXCLUDE) + +#configure_file(contrib/pkgconfig/Carbon.pc.in lib/pkgconfig/Carbon.pc @ONLY) +#install(FILES "${Carbon_BINARY_DIR}/lib/pkgconfig/Carbon.pc" +# DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + + diff --git a/src/carbon/LICENSE b/src/carbon/LICENSE new file mode 100644 index 00000000..73e79394 --- /dev/null +++ b/src/carbon/LICENSE @@ -0,0 +1,31 @@ +BSD-3-Clause License + +Copyright 2009-2018 Jason Turner +Copyright 2009-2012 Jonathan Turner. + +All Rights Reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of Jason Turner nor Jonathan Turner nor the + name of contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"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 THE COPYRIGHT +OWNER 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. \ No newline at end of file diff --git a/src/carbon/README.md b/src/carbon/README.md new file mode 100644 index 00000000..4d39744e --- /dev/null +++ b/src/carbon/README.md @@ -0,0 +1,3 @@ +# Carbon + +A super enhanced script engine based on ChaiScript. diff --git a/src/carbon/basic.hpp b/src/carbon/basic.hpp new file mode 100644 index 00000000..671e3f19 --- /dev/null +++ b/src/carbon/basic.hpp @@ -0,0 +1,34 @@ + + +#ifndef CARBON_BASIC_HPP +#define CARBON_BASIC_HPP + +#include "defines.hpp" + +#include "command/boxed_number.hpp" +#include "command/dispatchkit.hpp" +#include "command/dynamic_object.hpp" +#include "command/function_call.hpp" + +#include "language/engine.hpp" +#include "language/eval.hpp" + +// This file includes all of the basic requirements for ChaiScript, +// to use, you might do something like: +// + +/* + +#include "language/parser.hpp" +#include "stdlib.hpp" + +Carbon_Basic chai( + Carbon::Std_Lib::library(), + std::make_unique>()); + +*/ + +// If you want a fully packaged ready to go ChaiScript, use chaiscript.hpp + +#endif /* CARBON_BASIC_HPP */ diff --git a/src/carbon/carbon.hpp b/src/carbon/carbon.hpp new file mode 100644 index 00000000..a24d3498 --- /dev/null +++ b/src/carbon/carbon.hpp @@ -0,0 +1,870 @@ +#ifndef CARBON_HPP +#define CARBON_HPP + +/// @mainpage +/// [CarbonScript](http://www.chaiscript.com") is a scripting language designed +/// specifically for integration with C++. It provides seamless integration with +/// C++ on all levels, including shared_ptr objects, functors and exceptions. +/// +/// The parts of the CarbonScript API that the average user will be concerned +/// with are contained in the chaiscript namespace and the Carbon::CarbonScript +/// class. +/// +/// The end user parts of the API are extremely simple both in size and ease of +/// use. +/// +/// Currently, all source control and project management aspects of CarbonScript +/// occur on [github](http://www.github.com/CarbonScript/CarbonScript"). +/// +/// ------------------------------------------------------------ +/// +/// @sa chaiscript +/// @sa Carbon::CarbonScript +/// @sa Carbon_Language for Built in Functions +/// @sa @ref LangGettingStarted +/// @sa @ref LangKeywordRef +/// @sa @ref LangInPlaceRef +/// @sa @ref LangObjectSystemRef +/// @sa http://www.chaiscript.com +/// @sa http://www.github.com/CarbonScript/CarbonScript +/// +/// ----------------------------------------------------------- +/// +/// @section gettingstarted API Getting Started +/// +/// - @ref basics +/// - @ref compiling +/// - @ref eval +/// - @ref adding_items +/// - @ref operatoroverloading +/// - @ref add_class +/// - @ref pointer_conversions +/// - @ref baseclasses +/// - @ref functionobjects +/// - @ref threading +/// - @ref exceptions +/// +/// +/// @subsection basics Basics +/// +/// Basic simple example: +/// +/// ~~~~~~~{.cpp} +/// //main.cpp +/// #include +/// +/// double function(int i, double j) +/// { +/// return i * j; +/// } +/// +/// int main() +/// { +/// Carbon::CarbonScript chai; +/// chai.add(Carbon::fun(&function), "function"); +/// +/// double d = chai.eval("function(3, 4.75);"); +/// } +/// ~~~~~~~ +/// +/// ------------------------------------------------------ +/// +/// @subsection compiling Compiling CarbonScript Applications +/// +/// CarbonScript is a header only library with only one dependency: The +/// operating system provided dynamic library loader, which has to be specified +/// on some platforms. +/// +/// @subsubsection compilinggcc Compiling with GCC +/// +/// To compile the above application on a Unix like operating system (MacOS, +/// Linux) with GCC you need to link the dynamic loader. For example: +/// +/// ~~~~~~~~ +/// gcc main.cpp -I/path/to/chaiscript/headers -ldl +/// ~~~~~~~~ +/// +/// Alternatively, you may compile without threading support. +/// +/// ~~~~~~~~ +/// gcc main.cpp -I/path/to/chaiscript/headers -ldl -DCARBON_NO_THREADS +/// ~~~~~~~~ +/// +/// ------------------------------------------ +/// +/// @subsection eval Evaluating Scripts +/// +/// Scripts can be evaluated with the () operator, eval method or eval_file +/// method. +/// +/// @subsubsection parenoperator () Operator +/// +/// operator() can be used as a handy shortcut for evaluating CarbonScript +/// snippets. +/// +/// ~~~~~~~~{.cpp} +/// Carbon::CarbonScript chai; +/// chai("print(@"hello world@")"); +/// ~~~~~~~~ +/// +/// @sa Carbon::CarbonScript::operator()(const std::string &) +/// +/// @subsubsection evalmethod Method 'eval' +/// +/// The eval method is somewhat more verbose and can be used to get type safely +/// return values from the script. +/// +/// ~~~~~~~~{.cpp} +/// Carbon::CarbonScript chai; +/// chai.eval("callsomefunc()"); +/// int result = chai.eval("1 + 3"); +/// // result now equals 4 +/// ~~~~~~~~ +/// +/// @sa Carbon::CarbonScript::eval +/// +/// @subsubsection evalfilemethod Method 'eval_file' +/// +/// The 'eval_file' method loads a file from disk and executes the script in it +/// +/// ~~~~~~~~{.cpp} +/// Carbon::CarbonScript chai; +/// chai.eval_file("myfile.chai"); +/// std::string result = chai.eval_file("myfile.chai") // extract +/// the last value returned from the file +/// ~~~~~~~~ +/// +/// @sa Carbon::CarbonScript::eval_file +/// +/// -------------------------------------------------- +/// +/// @subsection adding_items Adding Items to CarbonScript +/// +/// CarbonScript supports 4 basic things that can be added: objects, functions, +/// type infos and Modules +/// +/// @subsubsection adding_objects Adding Objects +/// +/// Named objects can be created with the Carbon::var function. Note: adding +/// a object adds it to the current thread scope, not to a global scope. If you +/// have multiple threads that need to access the same variables you will need +/// to add them separately for each thread, from the thread itself. +/// +/// ~~~~~~~~~{.cpp} +/// using namespace Carbon; +/// CarbonScript chai; +/// int i = 5; +/// chai.add(var(i), "i"); +/// chai("print(i)"); +/// ~~~~~~~~~ +/// +/// Immutable objects can be created with the Carbon::const_var function. +/// +/// ~~~~~~~~~{.cpp} +/// chai.add(const_var(i), "i"); +/// chai("i = 5"); // exception throw, cannot assign const var +/// ~~~~~~~~~ +/// +/// Named variables can only be accessed from the context they are created in. +/// If you want a global variable, it must be const, and created with the +/// Carbon::CarbonScript::add_global_const function. +/// +/// ~~~~~~~~~{.cpp} +/// chai.add_global_const(const_var(i), "i"); +/// chai("def somefun() { print(i); }; somefun();"); +/// ~~~~~~~~~ +/// +/// @subsubsection adding_functions Adding Functions +/// +/// Functions, methods and members are all added using the same function: +/// Carbon::fun. +/// +/// ~~~~~~~~~{.cpp} +/// using namespace Carbon; +/// +/// class MyClass { +/// public: +/// int memberdata; +/// void method(); +/// void method2(int); +/// static void staticmethod(); +/// void overloadedmethod(); +/// void overloadedmethod(const std::string &); +/// }; +/// +/// CarbonScript chai; +/// chai.add(fun(&MyClass::memberdata), "memberdata"); +/// chai.add(fun(&MyClass::method), "method"); +/// chai.add(fun(&MyClass::staticmethod), "staticmethod"); +/// ~~~~~~~~~ +/// +/// Overloaded methods will need some help, to hint the compiler as to which +/// overload you want: +/// +/// ~~~~~~~~~{.cpp} +/// chai.add(fun(&MyClass::overloadedmethod), +/// "overloadedmethod"); chai.add(fun(&MyClass::overloadedmethod), "overloadedmethod"); +/// ~~~~~~~~~ +/// +/// There are also shortcuts built into Carbon::fun for binding up to the +/// first two parameters of the function. +/// +/// ~~~~~~~~~{.cpp} +/// MyClass obj; +/// chai.add(fun(&MyClass::method, &obj), "method"); +/// chai("method()"); // equiv to obj.method() +/// chai.add(fun(&MyClass::method2, &obj, 3), "method2"); +/// chai("method2()"); // equiv to obj.method2(3) +/// ~~~~~~~~~ +/// +/// @subsubsection addingtypeinfo Adding Type Info +/// +/// CarbonScript will automatically support any type implicitly provided to it +/// in the form of objects and function parameters / return types. However, it +/// can be nice to let CarbonScript know more details about the types you are +/// giving it. For instance, the "clone" functionality cannot work unless there +/// is a copy constructor registered and the name of the type is known (so that +/// CarbonScript can look up the copy constructor). +/// +/// Continuing with the example "MyClass" from above: +/// +/// ~~~~~~~~{.cpp} +/// chai.add(user_type(), "MyClass"); +/// ~~~~~~~~ +/// +/// @subsubsection adding_modules Adding Modules +/// +/// Modules are holders for collections of CarbonScript registrations. +/// +/// ~~~~~~~~{.cpp} +/// ModulePtr module = get_sum_module(); +/// chai.add(module); +/// ~~~~~~~~ +/// +/// @sa Carbon::Module +/// +/// ----------------------------------------------------------------------- +/// +/// @subsection operatoroverloading Operator Overloading +/// +/// Operators are just like any other function in CarbonScript, to overload an +/// operator, simply register it. +/// +/// ~~~~~~~~{.cpp} +/// class MyClass { +/// MyClass operator+(const MyClass &) const; +/// }; +/// +/// chai.add(fun(&MyClass::operator+), "+"); +/// +/// std::string append_string_int(const std::string &t_lhs, int t_rhs) +/// { +/// std::stringstream ss; +/// ss << t_lhs << t_rhs; +/// return ss.str(); +/// } +/// +/// chai.add(fun(append_string_int), "+"); +/// ~~~~~~~~ +/// +/// @sa @ref adding_functions +/// +/// ----------------------------------------------------------------------- +/// +/// @subsection add_class Class Helper Utility +/// +/// Much of the work of adding new classes to CarbonScript can be reduced with +/// the help of the add_class helper utility. +/// +/// ~~~~~~~~{.cpp} +/// class Test +/// { +/// public: +/// void function() {} +/// std::string function2() { return "Function2"; } +/// void function3() {} +/// std::string functionOverload(double) { return "double"; } +/// std::string functionOverload(int) { return "int"; } +/// }; +/// +/// int main() +/// { +/// Carbon::ModulePtr m = Carbon::ModulePtr(new Carbon::Module()); +/// +/// Carbon::utility::add_class(*m, +/// "Test", +/// { constructor(), +/// constructor() }, +/// { {fun(&Test::function), "function"}, +/// {fun(&Test::function2), "function2"}, +/// {fun(&Test::function2), "function3"} +/// {fun(static_cast(&Test::functionOverload)), "functionOverload"} +/// {fun(static_cast(&Test::functionOverload)), +/// "functionOverload"} } +/// ); +/// +/// +/// Carbon::CarbonScript chai; +/// chai.add(m); +/// } +/// ~~~~~~~~ +/// +/// @sa @ref adding_modules +/// +/// ----------------------------------------------------------------------- +/// +/// @subsection pointer_conversions Pointer / Object Conversions +/// +/// As much as possible, CarbonScript attempts to convert between &, *, const &, +/// const *, std::shared_ptr, std::shared_ptr, +/// std::reference_wrapper, std::reference_wrapper and value types +/// automatically. +/// +/// If a Carbon::var object was created in C++ from a pointer, it cannot be +/// converted to a shared_ptr (this would add invalid reference counting). Const +/// may be added, but never removed. +/// +/// The take away is that you can pretty much expect function calls to Just Work +/// when you need them to. +/// +/// ~~~~~~~~{.cpp} +/// void fun1(const int *); +/// void fun2(int *); +/// void fun3(int); +/// void fun4(int &); +/// void fun5(const int &); +/// void fun5(std::shared_ptr); +/// void fun6(std::shared_ptr); +/// void fun7(const std::shared_ptr &); +/// void fun8(const std::shared_ptr &); +/// void fun9(std::reference_wrapper); +/// void fun10(std::reference_wrapper); +/// +/// int main() +/// { +/// using namespace Carbon +/// Carbon::CarbonScript chai; +/// chai.add(fun(fun1), "fun1"); +/// chai.add(fun(fun2), "fun2"); +/// chai.add(fun(fun3), "fun3"); +/// chai.add(fun(fun4), "fun4"); +/// chai.add(fun(fun5), "fun5"); +/// chai.add(fun(fun6), "fun6"); +/// chai.add(fun(fun7), "fun7"); +/// chai.add(fun(fun8), "fun8"); +/// chai.add(fun(fun9), "fun9"); +/// chai.add(fun(fun10), "fun10"); +/// +/// chai("var i = 10;"); +/// chai("fun1(i)"); +/// chai("fun2(i)"); +/// chai("fun3(i)"); +/// chai("fun4(i)"); +/// chai("fun5(i)"); +/// chai("fun6(i)"); +/// chai("fun7(i)"); +/// chai("fun8(i)"); +/// chai("fun9(i)"); +/// chai("fun10(i)"); +/// } +/// ~~~~~~~~ +/// +/// See the unit test unittests/boxed_cast_test.cpp for a complete breakdown of +/// the automatic casts that available and tested. +/// +/// ----------------------------------------------------------------------- +/// +/// @subsection baseclasses Base Classes +/// +/// CarbonScript supports handling of passing a derived class object to a +/// function expecting a base class object. For the process to work, the +/// base/derived relationship must be registered with the engine. +/// +/// ~~~~~~~~{.cpp} +/// class Base {}; +/// class Derived : public Base {}; +/// void myfunction(Base *b); +/// +/// int main() +/// { +/// Carbon::CarbonScript chai; +/// chai.add(Carbon::base_class()); +/// Derived d; +/// chai.add(Carbon::var(&d), "d"); +/// chai.add(Carbon::fun(&myfunction), "myfunction"); +/// chai("myfunction(d)"); +/// } +/// ~~~~~~~~ +/// +/// ----------------------------------------------------------------------- +/// +/// +/// @subsection functionobjects Function Objects +/// +/// Functions are first class objects in CarbonScript and CarbonScript supports +/// automatic conversion between CarbonScript functions and std::function +/// objects. +/// +/// ~~~~~~~~{.cpp} +/// void callafunc(const std::function &t_func) +/// { +/// t_func("bob"); +/// } +/// +/// int main() +/// { +/// Carbon::CarbonScript chai; +/// chai.add(Carbon::fun(&callafunc), "callafunc"); +/// chai("callafunc(fun(x) { print(x); })"); // pass a lambda function to the +/// registered function +/// // which expects a typed +/// std::function +/// +/// std::function f = chai.eval +/// >("dump_system"); f(); // call the CarbonScript function dump_system, from +/// C++ +/// } +/// ~~~~~~~~ +/// +/// ----------------------------------------------------------------------- +/// +/// +/// @subsection threading Threading +/// +/// Thread safety is automatically handled within the CarbonScript system. +/// Objects can be added and scripts executed from multiple threads. For each +/// thread that executes scripts, a new context is created and managed by the +/// engine. +/// +/// Thread safety can be disabled by defining CARBON_NO_THREADS when using the +/// library. +/// +/// Disabling thread safety increases performance in many cases. +/// +/// ----------------------------------------------------------------------- +/// +/// +/// @subsection exceptions Exception Handling +/// +/// @subsubsection exceptionsbasics Exception Handling Basics +/// +/// Exceptions can be thrown in CarbonScript and caught in C++ or thrown in C++ +/// and caught in CarbonScript. +/// +/// ~~~~~~~~{.cpp} +/// void throwexception() +/// { +/// throw std::runtime_error("err"); +/// } +/// +/// int main() +/// { +/// // Throw in C++, catch in CarbonScript +/// Carbon::CarbonScript chai; +/// chai.add(Carbon::fun(&throwexception), "throwexception"); +/// chai("try { throwexception(); } catch (e) { print(e.what()); }"); // +/// prints "err" +/// +/// // Throw in CarbonScript, catch in C++ +/// try { +/// chai("throw(1)"); +/// } catch (Carbon::Boxed_Value bv) { +/// int i = Carbon::boxed_cast(bv); +/// // i == 1 +/// } +/// } +/// ~~~~~~~~ +/// +/// @subsubsection exceptionsautomatic Exception Handling Automatic Unboxing +/// +/// As an alternative to the manual unboxing of exceptions shown above, +/// exception specifications allow the user to tell CarbonScript what possible +/// exceptions are expected from the script being executed. +/// +/// Example: +/// ~~~~~~~~{.cpp} +/// Carbon::CarbonScript chai; +/// +/// try { +/// chai.eval("throw(runtime_error(@"error@"))", +/// Carbon::exception_specification()); +/// } catch (const double e) { +/// } catch (int) { +/// } catch (float) { +/// } catch (const std::string &) { +/// } catch (const std::exception &e) { +/// // This is the one what will be called in the specific throw() above +/// } +/// ~~~~~~~~ +/// +/// @sa Carbon::Exception_Handler for details on automatic exception +/// unboxing +/// @sa Carbon::exception_specification + +/// @page LangObjectSystemRef CarbonScript Language Object Model Reference +/// +/// +/// CarbonScript has an object system built in, for types defined within the +/// CarbonScript system. +/// +/// ~~~~~~~~~ +/// attr Rectangle::height +/// attr Rectangle::width +/// def Rectangle::Rectangle() { this.height = 10; this.width = 20 } +/// def Rectangle::area() { this.height * this.width } +/// +/// var rect = Rectangle() +/// rect.height = 30 +/// print(rect.area()) +/// ~~~~~~~~~ +/// +/// Since CarbonScript 5.4.0 it has been possible to use the "class" keyword to +/// simplify this code. +/// +/// ~~~~~~~~~ +/// class Rectangle { +/// attr height +/// attr width +/// def Rectangle() { this.height = 10; this.width = 20 } +/// def area() { this.height * this.width } +/// } +/// +/// var rect = Rectangle() +/// rect.height = 30 +/// print(rect.area()) +/// ~~~~~~~~~ +/// +/// @sa @ref keywordattr +/// @sa @ref keyworddef + +/// @page LangInPlaceRef CarbonScript Language In-Place Creation Reference +/// @section inplacevector Vector +/// +/// ~~~~~~~~~ +/// In-place Vector ::= "[" [expression ("," expression)*] "]" +/// ~~~~~~~~~ +/// +/// @section inplacerangedvector Ranged Vector +/// +/// ~~~~~~~~~ +/// In-place Ranged Vector ::= "[" value ".." value "]" +/// ~~~~~~~~~ +/// +/// Creates a vector over a range (eg. 1..10) +/// +/// @section inplacemap Map +/// +/// ~~~~~~~~ +/// In-place Map ::= "[" (string ":" expression)+ "]" +/// ~~~~~~~~ + +/// @page LangGettingStarted CarbonScript Language Getting Started +/// +/// CarbonScript is a simple language that should feel familiar to anyone who +/// knows C++ or ECMAScript (JavaScript). +/// +/// ----------------------------------------------------------------------- +/// +/// @section chaiscriptloops Loops +/// +/// Common looping constructs exist in CarbonScript +/// +/// ~~~~~~~~ +/// var i = 0; +/// while (i < 10) +/// { +/// // do something +/// ++i; +/// } +/// ~~~~~~~~ +/// +/// ~~~~~~~~ +/// for (var i = 0; i < 10; ++i) +/// { +/// // do something +/// } +/// ~~~~~~~~ +/// +/// @sa @ref keywordfor +/// @sa @ref keywordwhile +/// +/// ----------------------------------------------------------------------- +/// +/// @section chaiscriptifs Conditionals +/// +/// If statements work as expected +/// +/// ~~~~~~~~ +/// var b = true; +/// +/// if (b) { +/// // do something +/// } else if (c < 10) { +/// // do something else +/// } else { +/// // or do this +/// } +/// ~~~~~~~~ +/// +/// @sa @ref keywordif +/// +/// ----------------------------------------------------------------------- +/// +/// @section chaiscriptfunctions Functions +/// +/// Functions are defined with the def keyword +/// +/// ~~~~~~~~ +/// def myfun(x) { print(x); } +/// +/// myfun(10); +/// ~~~~~~~~ +/// +/// Functions may have "guards" which determine if which is called. +/// +/// ~~~~~~~~ +/// eval> def myfun2(x) : x < 10 { print("less than 10"); } +/// eval> def myfun2(x) : x >= 10 { print("10 or greater"); } +/// eval> myfun2(5) +/// less than 10 +/// eval> myfun2(12) +/// 10 or greater +/// ~~~~~~~~ +/// +/// @sa @ref keyworddef +/// @sa @ref keywordattr +/// @sa @ref LangObjectSystemRef +/// +/// ----------------------------------------------------------------------- +/// +/// @section chaiscriptfunctionobjects Function Objects +/// +/// Functions are first class types in CarbonScript and can be used as +/// variables. +/// +/// ~~~~~~~~ +/// eval> var p = print; +/// eval> p(1); +/// 1 +/// ~~~~~~~~ +/// +/// They can also be passed to functions. +/// +/// ~~~~~~~~ +/// eval> def callfunc(f, lhs, rhs) { return f(lhs, rhs); } +/// eval> def do_something(lhs, rhs) { print("lhs: ${lhs}, rhs: ${rhs}"); } +/// eval> callfunc(do_something, 1, 2); +/// lhs: 1, rhs: 2 +/// ~~~~~~~~ +/// +/// Operators can also be treated as functions by using the back tick operator. +/// Building on the above example: +/// +/// ~~~~~~~~ +/// eval> callfunc(`+`, 1, 4); +/// 5 +/// eval> callfunc(`*`, 3, 2); +/// 6 +/// ~~~~~~~~ +/// +/// ----------------------------------------------------------------------- +/// +/// @sa @ref LangKeywordRef +/// @sa Carbon_Language for Built in Functions + +/// @page LangKeywordRef CarbonScript Language Keyword Reference +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordattr attr +/// Defines a CarbonScript object attribute +/// +/// ~~~~~~~~ +/// Attribute Definition ::= "attr" class_name "::" attribute_name +/// ~~~~~~~~ +/// +/// @sa @ref LangObjectSystemRef +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordauto auto +/// +/// Defines a variable +/// +/// ~~~~~~~~ +/// Variable ::= "auto" identifier +/// ~~~~~~~~ +/// +/// Synonym for @ref keywordvar +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordbreak break +/// Stops execution of a looping block. +/// +/// ~~~~~~~~ +/// Break Statement ::= "break" +/// ~~~~~~~~ +/// +/// @sa @ref keywordfor +/// @sa @ref keywordwhile +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keyworddef def +/// Begins a function or method definition +/// +/// ~~~~~~~~ +/// Function Definition ::= "def" identifier "(" [[type] arg ("," [type] arg)*] +/// ")" [":" guard] block Method Definition ::= "def" class_name "::" +/// method_name "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block +/// ~~~~~~~~ +/// +/// identifier: name of function. Required. +/// args: comma-delimited list of parameter names with optional type specifiers. +/// Optional. guards: guarding statement that act as a prerequisite for the +/// function. Optional. { }: scoped block as function body. Required. +/// +/// Functions return values in one of two ways: +/// +/// By using an explicit return call, optionally passing the value to be +/// returned. By implicitly returning the value of the last expression (if it is +/// not a while or for loop). +/// +/// Method definitions for known types extend those types with new methods. This +/// includes C++ and CarbonScript defined types. Method definitions for unknown +/// types implicitly define the named type. +/// +/// @sa @ref LangObjectSystemRef +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordelse else +/// @sa @ref keywordif +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordfor for +/// ~~~~~~~~ +/// For Block ::= "for" "(" [initial] ";" stop_condition ";" loop_expression ")" +/// block +/// ~~~~~~~~ +/// This loop can be broken using the @ref keywordbreak command. +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordfun fun +/// Begins an anonymous function declaration (sometimes called a lambda). +/// +/// ~~~~~~~~ +/// Lambda ::= "fun" "(" [variable] ("," variable)* ")" block +/// ~~~~~~~~ +/// +/// _Example_ +/// +/// ~~~~~~~~ +/// // Generate an anonymous function object that adds 2 to its parameter +/// var f = fun(x) { x + 2; } +/// ~~~~~~~~ +/// +/// @sa @ref keyworddef for more details on CarbonScript functions +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordif if +/// Begins a conditional block of code that only executes if the condition +/// evaluates as true. +/// ~~~~~~~~ +/// If Block ::= "if" "(" condition ")" block +/// Else If Block ::= "else if" "(" condition ")" block +/// Else Block ::= "else" block +/// ~~~~~~~~ +/// +/// _Example_ +/// +/// ~~~~~~~~ +/// if (true) { +/// // do something +/// } else if (false) { +/// // do something else +/// } else { +/// // otherwise do this +/// } +/// ~~~~~~~~ +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordtry try +/// ~~~~~~~~ +/// Try Block ::= "try" block +/// ("catch" ["(" [type] variable ")"] [":" guards] block)+ +/// ["finally" block] +/// ~~~~~~~~ +/// +/// @sa Carbon_Language::throw +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordwhile while +/// +/// Begins a conditional block of code that loops 0 or more times, as long as +/// the condition is true +/// +/// ~~~~~~~~ +/// While Block ::= "while" "(" condition ")" block +/// ~~~~~~~~ +/// +/// This loop can be broken using the @ref keywordbreak command. +/// +/// +/// ----------------------------------------------------------------------- +/// +/// @section keywordvar var +/// +/// Defines a variable +/// +/// ~~~~~~~~ +/// Variable ::= "var" identifier +/// ~~~~~~~~ +/// +/// Synonym for @ref keywordauto + +/// @namespace Carbon +/// @brief Namespace chaiscript contains every API call that the average user +/// will be concerned with. + +/// @namespace Carbon::detail +/// @brief Classes and functions reserved for internal use. Items in this +/// namespace are not supported. + +#include "basic.hpp" +#include "language/parser.hpp" +#include "stdlib.hpp" + +namespace Carbon { +class CarbonScript : public Carbon_Basic { +public: + CarbonScript(std::vector t_modulepaths = {}, + std::vector t_usepaths = {}, + std::vector t_opts = Carbon::default_options()) + : Carbon_Basic(Carbon::Std_Lib::library(), + std::make_unique>(), + std::move(t_modulepaths), std::move(t_usepaths), + std::move(t_opts)) {} +}; +} // namespace Carbon + +#endif /* CARBON_HPP */ diff --git a/src/carbon/command/any.hpp b/src/carbon/command/any.hpp new file mode 100644 index 00000000..0511a30e --- /dev/null +++ b/src/carbon/command/any.hpp @@ -0,0 +1,112 @@ + + +#ifndef CARBON_ANY_HPP +#define CARBON_ANY_HPP + +#include +#include +#include + +namespace Carbon { +namespace detail { +namespace exception { +/// \brief Thrown in the event that an Any cannot be cast to the desired type +/// +/// It is used internally during function dispatch. +/// +/// \sa Carbon::detail::Any +class bad_any_cast : public std::bad_cast { +public: + /// \brief Description of what error occurred + const char *what() const noexcept override { return "bad any cast"; } +}; +} // namespace exception + +class Any { +private: + struct Data { + constexpr explicit Data(const std::type_info &t_type) noexcept + : m_type(t_type) {} + + Data &operator=(const Data &) = delete; + + virtual ~Data() noexcept = default; + + virtual void *data() noexcept = 0; + + const std::type_info &type() const noexcept { return m_type; } + + virtual std::unique_ptr clone() const = 0; + const std::type_info &m_type; + }; + + template + struct Data_Impl : Data { + explicit Data_Impl(T t_type) + : Data(typeid(T)), m_data(std::move(t_type)) {} + + void *data() noexcept override { return &m_data; } + + std::unique_ptr clone() const override { + return std::make_unique>(m_data); + } + + Data_Impl &operator=(const Data_Impl &) = delete; + + T m_data; + }; + + std::unique_ptr m_data; + +public: + // construct/copy/destruct + constexpr Any() noexcept = default; + Any(Any &&) noexcept = default; + Any &operator=(Any &&t_any) = default; + + Any(const Any &t_any) + : m_data(t_any.empty() ? nullptr : t_any.m_data->clone()) {} + + template >>> + explicit Any(ValueType &&t_value) + : m_data(std::make_unique>>( + std::forward(t_value))) {} + + Any &operator=(const Any &t_any) { + Any copy(t_any); + swap(copy); + return *this; + } + + template + ToType &cast() const { + if (m_data && typeid(ToType) == m_data->type()) { + return *static_cast(m_data->data()); + } else { + throw Carbon::detail::exception::bad_any_cast(); + } + } + + // modifiers + Any &swap(Any &t_other) { + std::swap(t_other.m_data, m_data); + return *this; + } + + // queries + bool empty() const noexcept { return !static_cast(m_data); } + + const std::type_info &type() const noexcept { + if (m_data) { + return m_data->type(); + } else { + return typeid(void); + } + } +}; + +} // namespace detail +} // namespace Carbon + +#endif diff --git a/src/carbon/command/bad_boxed_cast.cpp b/src/carbon/command/bad_boxed_cast.cpp new file mode 100644 index 00000000..cbe9a028 --- /dev/null +++ b/src/carbon/command/bad_boxed_cast.cpp @@ -0,0 +1,17 @@ +#include "bad_boxed_cast.hpp" + +namespace Carbon::exception { +bad_boxed_cast::bad_boxed_cast(Type_Info t_from, const std::type_info &t_to, + Static_String t_what) noexcept + : from(t_from), to(&t_to), m_what(std::move(t_what)) {} + +bad_boxed_cast::bad_boxed_cast(Type_Info t_from, + const std::type_info &t_to) noexcept + : from(t_from), to(&t_to), m_what("Cannot perform boxed_cast") {} + +bad_boxed_cast::bad_boxed_cast(Static_String t_what) noexcept + : m_what(std::move(t_what)) {} + +/// \brief Description of what error occurred +const char *bad_boxed_cast::what() const noexcept { return m_what.c_str(); } +} // namespace Carbon::exception diff --git a/src/carbon/command/bad_boxed_cast.hpp b/src/carbon/command/bad_boxed_cast.hpp new file mode 100644 index 00000000..c417e9e6 --- /dev/null +++ b/src/carbon/command/bad_boxed_cast.hpp @@ -0,0 +1,77 @@ + + +#ifndef CARBON_BAD_BOXED_CAST_HPP +#define CARBON_BAD_BOXED_CAST_HPP + +#include +#include + +#include "../defines.hpp" +#include "atom/experiment/sstring.hpp" +#include "atom/experiment/type_info.hpp" + +namespace Carbon { +namespace exception { +/** + * @brief Exception thrown to indicate a failed cast operation involving + * Boxed_Value. + * + * This exception is thrown when an attempt to cast a Boxed_Value to another + * type fails. It provides information about the source type (from), the target + * type (to), and a description of the error. + */ +class bad_boxed_cast : public std::bad_cast { +public: + /** + * @brief Constructs a bad_boxed_cast object with detailed information. + * + * @param t_from The Type_Info of the source type contained in the + * Boxed_Value. + * @param t_to The std::type_info of the desired (but failed) result type. + * @param t_what A Static_String providing additional description of the + * error. + */ + bad_boxed_cast(Type_Info t_from, const std::type_info &t_to, + Static_String t_what) noexcept; + + /** + * @brief Constructs a bad_boxed_cast object with type information only. + * + * @param t_from The Type_Info of the source type contained in the + * Boxed_Value. + * @param t_to The std::type_info of the desired (but failed) result type. + */ + bad_boxed_cast(Type_Info t_from, const std::type_info &t_to) noexcept; + + /** + * @brief Constructs a bad_boxed_cast object with a custom error message. + * + * @param t_what A Static_String providing additional description of the + * error. + */ + explicit bad_boxed_cast(Static_String t_what) noexcept; + + /** + * @brief Destructor for bad_boxed_cast. + */ + ~bad_boxed_cast() noexcept override = default; + + /** + * @brief Retrieves a C-style string describing the error. + * + * @return A pointer to a constant C-style string describing the error. + */ + const char *what() const noexcept override; + + Type_Info from; ///< Type_Info contained in the Boxed_Value + const std::type_info *to = + nullptr; ///< std::type_info of the desired (but failed) result type + +private: + Static_String m_what; ///< Additional description of the error +}; + +} // namespace exception +} // namespace Carbon + +#endif diff --git a/src/carbon/command/bootstrap.hpp b/src/carbon/command/bootstrap.hpp new file mode 100644 index 00000000..5d5650b5 --- /dev/null +++ b/src/carbon/command/bootstrap.hpp @@ -0,0 +1,573 @@ + + +#ifndef CARBON_BOOTSTRAP_HPP +#define CARBON_BOOTSTRAP_HPP + +#include "../utils/utility.hpp" +#include "register_function.hpp" + +/// \brief Classes and functions useful for bootstrapping of ChaiScript and +/// adding of new types +namespace Carbon::bootstrap { +template ::value>::type> +void array(const std::string &type, Module &m) { + using ReturnType = typename std::remove_extent::type; + + m.add(user_type(), type); + m.add( + fun([](T &t, size_t index) -> ReturnType & { + constexpr const auto extent = std::extent::value; + if (extent > 0 && index >= extent) { + throw std::range_error("Array index out of range. Received: " + + std::to_string(index) + " expected < " + + std::to_string(extent)); + } else { + return t[index]; + } + }), + "[]"); + + m.add( + fun([](const T &t, size_t index) -> const ReturnType & { + constexpr const auto extent = std::extent::value; + if (extent > 0 && index >= extent) { + throw std::range_error("Array index out of range. Received: " + + std::to_string(index) + " expected < " + + std::to_string(extent)); + } else { + return t[index]; + } + }), + "[]"); + + m.add(fun([](const T &) { return std::extent::value; }), "size"); +} + +/// \brief Adds a copy constructor for the given type to the given Model +/// \param[in] type The name of the type. The copy constructor will be named +/// "type". \param[in,out] m The Module to add the copy constructor to \tparam T +/// The type to add a copy constructor for \returns The passed in Module +template +void copy_constructor(const std::string &type, Module &m) { + m.add(constructor(), type); +} + +/// \brief Add all comparison operators for the templated type. Used during +/// bootstrap, also available to users. \tparam T Type to create comparison +/// operators for \param[in,out] m module to add comparison operators to +/// \returns the passed in Module. +template +void opers_comparison(Module &m) { + operators::equal(m); + operators::greater_than(m); + operators::greater_than_equal(m); + operators::less_than(m); + operators::less_than_equal(m); + operators::not_equal(m); +} + +/// \brief Adds default and copy constructors for the given type +/// \param[in] type The name of the type to add the constructors for. +/// \param[in,out] m The Module to add the basic constructors to +/// \tparam T Type to generate basic constructors for +/// \returns The passed in Module +/// \sa copy_constructor +/// \sa constructor +template +void basic_constructors(const std::string &type, Module &m) { + m.add(constructor(), type); + copy_constructor(type, m); +} + +/// \brief Adds a constructor for a POD type +/// \tparam T The type to add the constructor for +/// \param[in] type The name of the type +/// \param[in,out] m The Module to add the constructor to +template +void construct_pod(const std::string &type, Module &m) { + m.add(fun([](const Boxed_Number &bn) { return bn.get_as(); }), type); +} + +/// Internal function for converting from a string to a value +/// uses ostream operator >> to perform the conversion +template +Input parse_string(const std::string &i) { + if constexpr (!std::is_same::value && + !std::is_same::value && + !std::is_same::value) { + std::stringstream ss(i); + Input t; + ss >> t; + return t; + } else { + throw std::runtime_error( + "Parsing of wide characters is not yet supported"); + } +} + +/// Add all common functions for a POD type. All operators, and +/// common conversions +template +void bootstrap_pod_type(const std::string &name, Module &m) { + m.add(user_type(), name); + m.add(constructor(), name); + construct_pod(name, m); + + m.add(fun(&parse_string), "to_" + name); + m.add(fun([](const T t) { return t; }), "to_" + name); +} + +/// "clone" function for a shared_ptr type. This is used in the case +/// where you do not want to make a deep copy of an object during cloning +/// but want to instead maintain the shared_ptr. It is needed internally +/// for handling of Proxy_Function object (that is, +/// function variables. +template +auto shared_ptr_clone(const std::shared_ptr &p) { + return p; +} + +/// Specific version of shared_ptr_clone just for Proxy_Functions +template +std::shared_ptr> shared_ptr_unconst_clone( + const std::shared_ptr> &p) { + return std::const_pointer_cast::type>(p); +} + +/// Assignment function for shared_ptr objects, does not perform a copy of the +/// object pointed to, instead maintains the shared_ptr concept. +/// Similar to shared_ptr_clone. Used for Proxy_Function. +template +Boxed_Value ptr_assign(Boxed_Value lhs, const std::shared_ptr &rhs) { + if (lhs.is_undef() || + (!lhs.get_type_info().is_const() && + lhs.get_type_info().bare_equal( + Get_Type_Info::get()))) { + lhs.assign(Boxed_Value(rhs)); + return lhs; + } else { + throw exception::bad_boxed_cast("type mismatch in pointer assignment"); + } +} + +/// Class consisting of only static functions. All default bootstrapping occurs +/// from this class. +class Bootstrap { +private: + /// Function allowing for assignment of an unknown type to any other value + static Boxed_Value unknown_assign(Boxed_Value lhs, Boxed_Value rhs) { + if (lhs.is_undef()) { + return (lhs.assign(rhs)); + } else { + throw exception::bad_boxed_cast( + "boxed_value has a set type already"); + } + } + + static void print(const std::string &s) noexcept { + fwrite(s.c_str(), 1, s.size(), stdout); + } + + static void println(const std::string &s) noexcept { puts(s.c_str()); } + + /// Add all arithmetic operators for PODs + static void opers_arithmetic_pod(Module &m) { + m.add(fun(&Boxed_Number::equals), "=="); + m.add(fun(&Boxed_Number::less_than), "<"); + m.add(fun(&Boxed_Number::greater_than), ">"); + m.add(fun(&Boxed_Number::greater_than_equal), ">="); + m.add(fun(&Boxed_Number::less_than_equal), "<="); + m.add(fun(&Boxed_Number::not_equal), "!="); + + m.add(fun(&Boxed_Number::pre_decrement), "--"); + m.add(fun(&Boxed_Number::pre_increment), "++"); + m.add(fun(&Boxed_Number::sum), "+"); + m.add(fun(&Boxed_Number::unary_plus), "+"); + m.add(fun(&Boxed_Number::unary_minus), "-"); + m.add(fun(&Boxed_Number::difference), "-"); + m.add(fun(&Boxed_Number::assign_bitwise_and), "&="); + m.add(fun(&Boxed_Number::assign), "="); + m.add(fun(&Boxed_Number::assign_bitwise_or), "|="); + m.add(fun(&Boxed_Number::assign_bitwise_xor), "^="); + m.add(fun(&Boxed_Number::assign_remainder), "%="); + m.add(fun(&Boxed_Number::assign_shift_left), "<<="); + m.add(fun(&Boxed_Number::assign_shift_right), ">>="); + m.add(fun(&Boxed_Number::bitwise_and), "&"); + m.add(fun(&Boxed_Number::bitwise_complement), "~"); + m.add(fun(&Boxed_Number::bitwise_xor), "^"); + m.add(fun(&Boxed_Number::bitwise_or), "|"); + m.add(fun(&Boxed_Number::assign_product), "*="); + m.add(fun(&Boxed_Number::assign_quotient), "/="); + m.add(fun(&Boxed_Number::assign_sum), "+="); + m.add(fun(&Boxed_Number::assign_difference), "-="); + m.add(fun(&Boxed_Number::quotient), "/"); + m.add(fun(&Boxed_Number::shift_left), "<<"); + m.add(fun(&Boxed_Number::product), "*"); + m.add(fun(&Boxed_Number::remainder), "%"); + m.add(fun(&Boxed_Number::shift_right), ">>"); + } + + /// Create a bound function object. The first param is the function to bind + /// the remaining parameters are the args to bind into the result + static Boxed_Value bind_function(const Function_Params ¶ms) { + if (params.empty()) { + throw exception::arity_error(0, 1); + } + + Const_Proxy_Function f = boxed_cast(params[0]); + + if (f->get_arity() != -1 && + size_t(f->get_arity()) != params.size() - 1) { + throw exception::arity_error(static_cast(params.size()), + f->get_arity()); + } + + return Boxed_Value( + Const_Proxy_Function(std::make_shared( + std::move(f), + std::vector(params.begin() + 1, params.end())))); + } + + static bool has_guard(const Const_Proxy_Function &t_pf) noexcept { + auto pf = + std::dynamic_pointer_cast( + t_pf); + return pf && pf->has_guard(); + } + + static Const_Proxy_Function get_guard(const Const_Proxy_Function &t_pf) { + const auto pf = + std::dynamic_pointer_cast( + t_pf); + if (pf && pf->get_guard()) { + return pf->get_guard(); + } else { + throw std::runtime_error("Function does not have a guard"); + } + } + + template + static std::vector do_return_boxed_value_vector( + FunctionType f, const dispatch::Proxy_Function_Base *b) { + auto v = (b->*f)(); + + std::vector vbv; + + for (const auto &o : v) { + vbv.push_back(const_var(o)); + } + + return vbv; + } + + static bool has_parse_tree( + const Carbon::Const_Proxy_Function &t_pf) noexcept { + const auto pf = std::dynamic_pointer_cast< + const Carbon::dispatch::Dynamic_Proxy_Function>(t_pf); + return bool(pf); + } + + static const Carbon::AST_Node &get_parse_tree( + const Carbon::Const_Proxy_Function &t_pf) { + const auto pf = std::dynamic_pointer_cast< + const Carbon::dispatch::Dynamic_Proxy_Function>(t_pf); + if (pf) { + return pf->get_parse_tree(); + } else { + throw std::runtime_error("Function does not have a parse tree"); + } + } + + template + static auto return_boxed_value_vector(const Function &f) { + return [f](const dispatch::Proxy_Function_Base *b) { + return do_return_boxed_value_vector(f, b); + }; + } + +public: + /// \brief perform all common bootstrap functions for std::string, void and + /// POD types \param[in,out] m Module to add bootstrapped functions to + /// \returns passed in Module + static void bootstrap(Module &m) { + m.add(user_type(), "void"); + m.add(user_type(), "bool"); + m.add(user_type(), "Object"); + m.add(user_type(), "Number"); + m.add(user_type(), "Function"); + m.add(user_type(), + "Assignable_Function"); + m.add(user_type(), "exception"); + + m.add(fun(&dispatch::Proxy_Function_Base::get_arity), "get_arity"); + m.add(fun(&dispatch::Proxy_Function_Base::operator==), "=="); + + m.add(fun(return_boxed_value_vector( + &dispatch::Proxy_Function_Base::get_param_types)), + "get_param_types"); + m.add(fun(return_boxed_value_vector( + &dispatch::Proxy_Function_Base::get_contained_functions)), + "get_contained_functions"); + + m.add( + fun([](const std::exception &e) { return std::string(e.what()); }), + "what"); + + m.add(user_type(), "out_of_range"); + m.add(user_type(), "logic_error"); + m.add(Carbon::base_class()); + m.add(Carbon::base_class()); + m.add(Carbon::base_class()); + + m.add(user_type(), "runtime_error"); + m.add(Carbon::base_class()); + + m.add(constructor(), + "runtime_error"); + + m.add(user_type(), "Dynamic_Object"); + m.add(constructor(), + "Dynamic_Object"); + m.add(constructor(), "Dynamic_Object"); + m.add(fun(&dispatch::Dynamic_Object::get_type_name), "get_type_name"); + m.add(fun(&dispatch::Dynamic_Object::get_attrs), "get_attrs"); + m.add(fun(&dispatch::Dynamic_Object::set_explicit), "set_explicit"); + m.add(fun(&dispatch::Dynamic_Object::is_explicit), "is_explicit"); + m.add(fun(&dispatch::Dynamic_Object::has_attr), "has_attr"); + + m.add(fun(static_cast( + &dispatch::Dynamic_Object::get_attr)), + "get_attr"); + m.add(fun(static_cast( + &dispatch::Dynamic_Object::get_attr)), + "get_attr"); + + m.add(fun(static_cast( + &dispatch::Dynamic_Object::method_missing)), + "method_missing"); + m.add(fun(static_cast( + &dispatch::Dynamic_Object::method_missing)), + "method_missing"); + + m.add(fun(static_cast( + &dispatch::Dynamic_Object::get_attr)), + "[]"); + m.add(fun(static_cast( + &dispatch::Dynamic_Object::get_attr)), + "[]"); + + m.eval(R"chaiscript( + def Dynamic_Object::clone() { + auto &new_o = Dynamic_Object(this.get_type_name()); + for_each(this.get_attrs(), fun[new_o](x) { new_o.get_attr(x.first) = x.second; } ); + new_o; + } + + def `=`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name() + { + for_each(rhs.get_attrs(), fun[lhs](x) { lhs.get_attr(x.first) = clone(x.second); } ); + } + + def `!=`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name() + { + var rhs_attrs := rhs.get_attrs(); + var lhs_attrs := lhs.get_attrs(); + + if (rhs_attrs.size() != lhs_attrs.size()) { + true; + } else { + return any_of(rhs_attrs, fun[lhs](x) { !lhs.has_attr(x.first) || lhs.get_attr(x.first) != x.second; } ); + } + } + + def `==`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name() + { + var rhs_attrs := rhs.get_attrs(); + var lhs_attrs := lhs.get_attrs(); + + if (rhs_attrs.size() != lhs_attrs.size()) { + false; + } else { + return all_of(rhs_attrs, fun[lhs](x) { lhs.has_attr(x.first) && lhs.get_attr(x.first) == x.second; } ); + } + } + )chaiscript"); + + m.add(fun(&has_guard), "has_guard"); + m.add(fun(&get_guard), "get_guard"); + + m.add(fun(&Boxed_Value::is_undef), "is_var_undef"); + m.add(fun(&Boxed_Value::is_null), "is_var_null"); + m.add(fun(&Boxed_Value::is_const), "is_var_const"); + m.add(fun(&Boxed_Value::is_ref), "is_var_reference"); + m.add(fun(&Boxed_Value::is_pointer), "is_var_pointer"); + m.add(fun(&Boxed_Value::is_return_value), "is_var_return_value"); + m.add(fun(&Boxed_Value::reset_return_value), "reset_var_return_value"); + m.add(fun(&Boxed_Value::is_type), "is_type"); + m.add(fun(&Boxed_Value::get_attr), "get_var_attr"); + m.add(fun(&Boxed_Value::copy_attrs), "copy_var_attrs"); + m.add(fun(&Boxed_Value::clone_attrs), "clone_var_attrs"); + + m.add(fun(&Boxed_Value::get_type_info), "get_type_info"); + m.add(user_type(), "Type_Info"); + m.add(constructor(), "Type_Info"); + + operators::equal(m); + + m.add(fun(&Type_Info::is_const), "is_type_const"); + m.add(fun(&Type_Info::is_reference), "is_type_reference"); + m.add(fun(&Type_Info::is_void), "is_type_void"); + m.add(fun(&Type_Info::is_undef), "is_type_undef"); + m.add(fun(&Type_Info::is_pointer), "is_type_pointer"); + m.add(fun(&Type_Info::is_arithmetic), "is_type_arithmetic"); + m.add(fun(&Type_Info::name), "cpp_name"); + m.add(fun(&Type_Info::bare_name), "cpp_bare_name"); + m.add(fun(&Type_Info::bare_equal), "bare_equal"); + + basic_constructors("bool", m); + operators::assign(m); + operators::equal(m); + operators::not_equal(m); + + m.add(fun([](const std::string &s) { return s; }), "to_string"); + m.add( + fun([](const bool b) { return std::string(b ? "true" : "false"); }), + "to_string"); + m.add(fun(&unknown_assign), "="); + m.add(fun([](const Boxed_Value &bv) { throw bv; }), "throw"); + + m.add(fun([](const char c) { return std::string(1, c); }), "to_string"); + m.add(fun(&Boxed_Number::to_string), "to_string"); + + bootstrap_pod_type("double", m); + bootstrap_pod_type("long_double", m); + bootstrap_pod_type("float", m); + bootstrap_pod_type("int", m); + bootstrap_pod_type("long", m); + bootstrap_pod_type("unsigned_int", m); + bootstrap_pod_type("unsigned_long", m); + bootstrap_pod_type("long_long", m); + bootstrap_pod_type("unsigned_long_long", m); + bootstrap_pod_type("size_t", m); + bootstrap_pod_type("char", m); + bootstrap_pod_type("wchar_t", m); + bootstrap_pod_type("char16_t", m); + bootstrap_pod_type("char32_t", m); + bootstrap_pod_type("int8_t", m); + bootstrap_pod_type("int16_t", m); + bootstrap_pod_type("int32_t", m); + bootstrap_pod_type("int64_t", m); + bootstrap_pod_type("uint8_t", m); + bootstrap_pod_type("uint16_t", m); + bootstrap_pod_type("uint32_t", m); + bootstrap_pod_type("uint64_t", m); + + operators::logical_compliment(m); + + opers_arithmetic_pod(m); + + m.add(fun(&Build_Info::version_major), "version_major"); + m.add(fun(&Build_Info::version_minor), "version_minor"); + m.add(fun(&Build_Info::version_patch), "version_patch"); + m.add(fun(&Build_Info::version), "version"); + m.add(fun(&Build_Info::compiler_version), "compiler_version"); + m.add(fun(&Build_Info::compiler_name), "compiler_name"); + m.add(fun(&Build_Info::compiler_id), "compiler_id"); + m.add(fun(&Build_Info::debug_build), "debug_build"); + + m.add(fun(&print), "print_string"); + m.add(fun(&println), "println_string"); + + m.add(dispatch::make_dynamic_proxy_function(&bind_function), "bind"); + + m.add(fun(&shared_ptr_unconst_clone), + "clone"); + m.add(fun(&ptr_assign< + std::remove_const::type>), + "="); + m.add(fun(&ptr_assign< + std::add_const::type>), + "="); + m.add(Carbon::base_class()); + m.add(fun([](dispatch::Assignable_Proxy_Function &t_lhs, + const std::shared_ptr + &t_rhs) { t_lhs.assign(t_rhs); }), + "="); + + m.add(fun(&Boxed_Value::type_match), "type_match"); + + m.add(Carbon::fun(&has_parse_tree), "has_parse_tree"); + m.add(Carbon::fun(&get_parse_tree), "get_parse_tree"); + + m.add(Carbon::base_class()); + m.add(Carbon::base_class()); + + m.add(user_type(), + "arithmetic_error"); + m.add( + Carbon::base_class()); + m.add( + Carbon::base_class()); + + // Carbon::bootstrap::standard_library::vector_type + // > + // >("AST_NodeVector", m); + + Carbon::utility::add_class( + m, "eval_error", {}, + {{fun(&Carbon::exception::eval_error::reason), "reason"}, + {fun(&Carbon::exception::eval_error::pretty_print), + "pretty_print"}, + {fun([](const Carbon::exception::eval_error &t_eval_error) { + std::vector retval; + std::transform( + t_eval_error.call_stack.begin(), + t_eval_error.call_stack.end(), std::back_inserter(retval), + &Carbon::var); + return retval; + }), + "call_stack"}}); + + Carbon::utility::add_class( + m, "File_Position", + {constructor(), + constructor()}, + {{fun(&File_Position::line), "line"}, + {fun(&File_Position::column), "column"}}); + + Carbon::utility::add_class( + m, "AST_Node", {}, + {{fun(&AST_Node::text), "text"}, + {fun(&AST_Node::identifier), "identifier"}, + {fun(&AST_Node::filename), "filename"}, + {fun(&AST_Node::start), "start"}, + {fun(&AST_Node::end), "end"}, + {fun(&AST_Node::to_string), "to_string"}, + {fun([](const Carbon::AST_Node &t_node) + -> std::vector { + std::vector retval; + const auto children = t_node.get_children(); + std::transform(children.begin(), children.end(), + std::back_inserter(retval), + &Carbon::var &>); + return retval; + }), + "children"}}); + } +}; +} // namespace Carbon::bootstrap + +#endif diff --git a/src/carbon/command/bootstrap_stl.hpp b/src/carbon/command/bootstrap_stl.hpp new file mode 100644 index 00000000..21b3e383 --- /dev/null +++ b/src/carbon/command/bootstrap_stl.hpp @@ -0,0 +1,718 @@ + + +#ifndef CARBON_BOOTSTRAP_STL_HPP +#define CARBON_BOOTSTRAP_STL_HPP + +#include +#include +#include +#include +#include + +#include "atom/experiment/type_info.hpp" +#include "bootstrap.hpp" +#include "boxed_value.hpp" +#include "dispatchkit.hpp" +#include "operators.hpp" +#include "proxy_constructors.hpp" +#include "register_function.hpp" + +namespace Carbon::bootstrap::standard_library { +/// Bidir_Range, based on the D concept of ranges. +/// \todo Update the Range code to base its capabilities on +/// the user_typetraits of the iterator passed in +template +struct Bidir_Range { + using container_type = Container; + + constexpr Bidir_Range(Container &c) : m_begin(c.begin()), m_end(c.end()) {} + + constexpr bool empty() const noexcept { return m_begin == m_end; } + + constexpr void pop_front() { + if (empty()) { + throw std::range_error("Range empty"); + } + ++m_begin; + } + + constexpr void pop_back() { + if (empty()) { + throw std::range_error("Range empty"); + } + --m_end; + } + + constexpr decltype(auto) front() const { + if (empty()) { + throw std::range_error("Range empty"); + } + return (*m_begin); + } + + constexpr decltype(auto) back() const { + if (empty()) { + throw std::range_error("Range empty"); + } + auto pos = m_end; + --pos; + return (*(pos)); + } + + IterType m_begin; + IterType m_end; +}; + +namespace detail { +template +size_t count(const T &t_target, const typename T::key_type &t_key) { + return t_target.count(t_key); +} + +template +void insert(T &t_target, const T &t_other) { + t_target.insert(t_other.begin(), t_other.end()); +} + +template +void insert_ref(T &t_target, const typename T::value_type &t_val) { + t_target.insert(t_val); +} + +template +void emplace(T &t_target, const typename T::value_type &t_val) { + t_target.emplace(t_val); +} + +template +void emplace_hint(T &t_target, const typename T::value_type &t_val) { + t_target.emplace_hint(t_target.end(), t_val); +} + +/// Add Bidir_Range support for the given ContainerType +template +void input_range_type_impl(const std::string &type, Module &m) { + m.add(user_type(), type + "_Range"); + + copy_constructor(type + "_Range", m); + + m.add(constructor(), + "range_internal"); + + m.add(fun(&Bidir_Type::empty), "empty"); + m.add(fun(&Bidir_Type::pop_front), "pop_front"); + m.add(fun(&Bidir_Type::front), "front"); + m.add(fun(&Bidir_Type::pop_back), "pop_back"); + m.add(fun(&Bidir_Type::back), "back"); +} + +/// Algorithm for inserting at a specific position into a container +template +void insert_at(Type &container, int pos, const typename Type::value_type &v) { + auto itr = container.begin(); + auto end = container.end(); + + if (pos < 0 || std::distance(itr, end) < pos) { + throw std::range_error("Cannot insert past end of range"); + } + + std::advance(itr, pos); + container.insert(itr, v); +} + +/// Algorithm for erasing a specific position from a container +template +void erase_at(Type &container, int pos) { + auto itr = container.begin(); + auto end = container.end(); + + if (pos < 0 || std::distance(itr, end) <= pos) { + throw std::range_error("Cannot erase past end of range"); + } + + std::advance(itr, pos); + container.erase(itr); +} +} // namespace detail + +/** + * Add Bidir_Range support for the given ContainerType + * @tparam ContainerType The container type to add support for + * @param m The module to add the type to + * @note This function is a no-op if the type has already been added + * @attention empty, pop_front, front, pop_back, back + */ +template +void input_range_type(const std::string &type, Module &m) { + detail::input_range_type_impl< + Bidir_Range>(type, m); + detail::input_range_type_impl>( + "Const_" + type, m); +} + +/** + * Add random_access_container concept to the given ContainerType + * @tparam ContainerType The container type to add support for + * @param m The module to add the type to + * @note This function is a no-op if the type has already been added + * @attention [index] operator + */ +template +void random_access_container_type(const std::string & /*type*/, Module &m) { + // In the interest of runtime safety for the m, we prefer the at() method + // for [] access, to throw an exception in an out of bounds condition. + m.add(fun([](ContainerType &c, int index) -> + typename ContainerType::reference { + /// \todo we are preferring to keep the key as 'int' to avoid + /// runtime conversions during dispatch. reevaluate + return c.at( + static_cast(index)); + }), + "[]"); + + m.add(fun([](const ContainerType &c, int index) -> + typename ContainerType::const_reference { + /// \todo we are preferring to keep the key as 'int' to avoid + /// runtime conversions during dispatch. reevaluate + return c.at( + static_cast(index)); + }), + "[]"); +} + +/// Add assignable concept to the given ContainerType +/// http://www.sgi.com/tech/stl/Assignable.html +template +void assignable_type(const std::string &type, Module &m) { + copy_constructor(type, m); + operators::assign(m); +} + +/** + * Add resizable concept to the given ContainerType + * @tparam ContainerType The container type to add support for + * @param m The module to add the type to + * @note This function is a no-op if the type has already been added + * @attention resize + */ +template +void resizable_type(const std::string & /*type*/, Module &m) { + m.add(fun([](ContainerType *a, typename ContainerType::size_type n, + const typename ContainerType::value_type &val) { + return a->resize(n, val); + }), + "resize"); + m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { + return a->resize(n); + }), + "resize"); +} + +/** + * Add reservable concept to the given ContainerType + * @tparam ContainerType The container type to add support for + * @param m The module to add the type to + * @note This function is a no-op if the type has already been added + * @attention reserve, capacity + */ +template +void reservable_type(const std::string & /*type*/, Module &m) { + m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { + return a->reserve(n); + }), + "reserve"); + m.add(fun([](const ContainerType *a) { return a->capacity(); }), + "capacity"); +} + +/** + * Add container concept to the given ContainerType + * @tparam ContainerType The container type to add support for + * @param m The module to add the type to + * @note This function is a no-op if the type has already been added + * @attention size, empty, clear + */ +template +void container_type(const std::string & /*type*/, Module &m) { + m.add(fun([](const ContainerType *a) { return a->size(); }), "size"); + m.add(fun([](const ContainerType *a) { return a->empty(); }), "empty"); + m.add(fun([](ContainerType *a) { a->clear(); }), "clear"); +} + +/// Add default constructable concept to the given Type +/// http://www.sgi.com/tech/stl/DefaultConstructible.html +template +void default_constructible_type(const std::string &type, Module &m) { + m.add(constructor(), type); +} + +template +concept SequenceContainer = requires(T t) { + typename T::value_type; + { + t.insert(t.begin(), typename T::value_type{}) + } -> std::same_as; + { t.erase(t.begin()) } -> std::same_as; +}; + +/** + * Add sequence concept to the given ContainerType + * @tparam ContainerType The container type to add support for + * @param m The module to add the type to + * @note This function is a no-op if the type has already been added + * @attention insert_at, erase_at + */ +template +void sequence_type(const std::string & /*type*/, Module &m) { + m.add(fun(&detail::insert_at), []() -> std::string { + if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { + return "insert_ref_at"; + } else { + return "insert_at"; + } + }()); + + m.add(fun(&detail::erase_at), "erase_at"); +} + +template +concept SequenceContainerWithEmplace = requires(T t) { + typename T::value_type; + { + t.insert(t.begin(), typename T::value_type{}) + } -> std::same_as; + { t.erase(t.begin()) } -> std::same_as; + { + t.emplace(typename T::value_type{}) + } -> std::same_as>; + { + t.emplace_hint(t.begin(), typename T::value_type{}) + } -> std::same_as; +}; + +template +concept BackInsertionSequence = requires(T t) { + typename T::value_type; + { t.push_back(typename T::value_type{}) } -> std::same_as; + { t.pop_back() } -> std::same_as; +}; +/** + * Add back insertion sequence concept to the given ContainerType + * @tparam ContainerType The container type to add support for + * @param m The module to add the type to + * @note This function is a no-op if the type has already been added + * @attention back, push_back, pop_back + */ +template +void back_insertion_sequence_type(const std::string &type, Module &m) { + m.add(fun([](ContainerType &container) -> decltype(auto) { + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.back()); + } + }), + "back"); + m.add(fun([](const ContainerType &container) -> decltype(auto) { + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.back()); + } + }), + "back"); + + using push_back = + void (ContainerType::*)(const typename ContainerType::value_type &); + m.add(fun(static_cast(&ContainerType::push_back)), + [&]() -> std::string { + if (typeid(typename ContainerType::value_type) == + typeid(Boxed_Value)) { + m.eval( + "# Pushes the second value onto the container while " + "making a clone of the value\n" + "def push_back(" + + type + + " container, x)\n" + "{ \n" + " if (x.is_var_return_value()) {\n" + " x.reset_var_return_value() \n" + " container.push_back_ref(x) \n" + " } else { \n" + " container.push_back_ref(clone(x)); \n" + " }\n" + "} \n"); + + return "push_back_ref"; + } else { + return "push_back"; + } + }()); + + m.add(fun(&ContainerType::pop_back), "pop_back"); +} + +template +concept FrontInsertionSequence = requires(T t) { + typename T::value_type; + { t.push_front(typename T::value_type{}) } -> std::same_as; + { t.pop_front() } -> std::same_as; +}; +/** + * Add front insertion sequence concept to the given ContainerType + * @tparam ContainerType The container type to add support for + * @param m The module to add the type to + * @note This function is a no-op if the type has already been added + * @attention front, push_front, pop_front + */ +template +void front_insertion_sequence_type(const std::string &type, Module &m) { + using push_ptr = + void (ContainerType::*)(typename ContainerType::const_reference); + using pop_ptr = void (ContainerType::*)(); + + m.add(fun([](ContainerType &container) -> decltype(auto) { + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + }), + "front"); + + m.add(fun([](const ContainerType &container) -> decltype(auto) { + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + }), + "front"); + + m.add(fun(static_cast(&ContainerType::push_front)), + [&]() -> std::string { + if (typeid(typename ContainerType::value_type) == + typeid(Boxed_Value)) { + m.eval( + "# Pushes the second value onto the front of container " + "while making a clone of the value\n" + "def push_front(" + + type + + " container, x)\n" + "{ \n" + " if (x.is_var_return_value()) {\n" + " x.reset_var_return_value() \n" + " container.push_front_ref(x) \n" + " } else { \n" + " container.push_front_ref(clone(x)); \n" + " }\n" + "} \n"); + return "push_front_ref"; + } else { + return "push_front"; + } + }()); + + m.add(fun(static_cast(&ContainerType::pop_front)), "pop_front"); +} + +template +concept Pair = requires() { + typename T::first_type; + typename T::second_type; +}; +/** + * Add pair type to the given ContainerType + * @tparam PairType The pair type to add support for + * @param m The module to add the type to + * @note This function is a no-op if the type has already been added + * @attention first, second + */ +template +void pair_type(const std::string &type, Module &m) { + m.add(user_type(), type); + + m.add(fun(&PairType::first), "first"); + m.add(fun(&PairType::second), "second"); + + basic_constructors(type, m); + m.add(constructor(), + type); +} + +/// Add pair associative container concept to the given ContainerType +/// http://www.sgi.com/tech/stl/PairAssociativeContainer.html + +template +void pair_associative_container_type(const std::string &type, Module &m) { + pair_type(type + "_Pair", m); +} + +template +concept UniqueAssociativeContainer = requires(T t) { + typename T::key_type; + typename T::mapped_type; + { t.count(typename T::key_type{}) } -> std::same_as; + { t.erase(typename T::key_type{}) } -> std::same_as; + { + t.insert(typename T::value_type{}) + } -> std::same_as>; +}; +/** + * Add unique associative container concept to the given ContainerType + * @tparam ContainerType The container type to add support for + * @param m The module to add the type to + * @note This function is a no-op if the type has already been added + * @attention count, erase, insert, insert_ref + */ +template +void unique_associative_container_type(const std::string & /*type*/, + Module &m) { + m.add(fun(detail::count), "count"); + + using erase_ptr = + size_t (ContainerType::*)(const typename ContainerType::key_type &); + + m.add(fun(static_cast(&ContainerType::erase)), "erase"); + + m.add(fun(&detail::insert), "insert"); + + m.add(fun(&detail::insert_ref), []() -> std::string { + if (typeid(typename ContainerType::mapped_type) == + typeid(Boxed_Value)) { + return "insert_ref"; + } else { + return "insert"; + } + }()); +} + +/// Add a MapType container +/// http://www.sgi.com/tech/stl/Map.html +template +void map_type(const std::string &type, Module &m) { + m.add(user_type(), type); + + using elem_access = typename MapType::mapped_type &( + MapType::*)(const typename MapType::key_type &); + using const_elem_access = const typename MapType::mapped_type &( + MapType::*)(const typename MapType::key_type &) const; + + m.add(fun(static_cast(&MapType::operator[])), "[]"); + + m.add(fun(static_cast(&MapType::at)), "at"); + m.add(fun(static_cast(&MapType::at)), "at"); + + if (typeid(MapType) == typeid(std::map)) { + m.eval(R"( + def Map::`==`(Map rhs) { + if ( rhs.size() != this.size() ) { + return false; + } else { + auto r1 = range(this); + auto r2 = range(rhs); + while (!r1.empty()) + { + if (!eq(r1.front().first, r2.front().first) || !eq(r1.front().second, r2.front().second)) + { + return false; + } + r1.pop_front(); + r2.pop_front(); + } + true; + } + } )"); + } + + container_type(type, m); + default_constructible_type(type, m); + assignable_type(type, m); + unique_associative_container_type(type, m); + pair_associative_container_type(type, m); + input_range_type(type, m); +} + +/// http://www.sgi.com/tech/stl/List.html +template +void list_type(const std::string &type, Module &m) { + m.add(user_type(), type); + + front_insertion_sequence_type(type, m); + back_insertion_sequence_type(type, m); + sequence_type(type, m); + resizable_type(type, m); + container_type(type, m); + default_constructible_type(type, m); + assignable_type(type, m); + input_range_type(type, m); +} + +/// Create a vector type with associated concepts +/// http://www.sgi.com/tech/stl/Vector.html +template +void vector_type(const std::string &type, Module &m) { + m.add(user_type(), type); + + m.add(fun([](VectorType &container) -> decltype(auto) { + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + }), + "front"); + + m.add(fun([](const VectorType &container) -> decltype(auto) { + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + }), + "front"); + + back_insertion_sequence_type(type, m); + sequence_type(type, m); + random_access_container_type(type, m); + resizable_type(type, m); + reservable_type(type, m); + container_type(type, m); + default_constructible_type(type, m); + assignable_type(type, m); + input_range_type(type, m); + + if (typeid(VectorType) == typeid(std::vector)) { + m.eval(R"( + def Vector::`==`(Vector rhs) { + if ( rhs.size() != this.size() ) { + return false; + } else { + auto r1 = range(this); + auto r2 = range(rhs); + while (!r1.empty()) + { + if (!eq(r1.front(), r2.front())) + { + return false; + } + r1.pop_front(); + r2.pop_front(); + } + true; + } + } )"); + } +} + +template +concept SetLike = requires(T t) { + std::is_default_constructible_v; + requires std::same_as())), + std::pair>; + requires std::is_convertible_v< + decltype(t.find(std::declval())), + typename T::const_iterator>; + requires std::is_convertible_v< + decltype(t.erase(std::declval())), + typename T::iterator>; + requires std::is_convertible_v; + requires std::is_convertible_v; +}; + +template +void set_type(const std::string &type, Module &m) { + m.add(user_type(), type); + + sequence_type(type, m); + random_access_container_type(type, m); + reservable_type(type, m); + container_type(type, m); + default_constructible_type(type, m); + assignable_type(type, m); + input_range_type(type, m); +} + +/// Add a String container +/// http://www.sgi.com/tech/stl/basic_string.html +template +void string_type(const std::string &type, Module &m) { + m.add(user_type(), type); + operators::addition(m); + operators::assign_sum(m); + opers_comparison(m); + random_access_container_type(type, m); + sequence_type(type, m); + default_constructible_type(type, m); + // container_type(type, m); + assignable_type(type, m); + input_range_type(type, m); + + // Special case: add push_back to string (which doesn't support other + // back_insertion operations + m.add(fun(&String::push_back), []() -> std::string { + if (typeid(typename String::value_type) == typeid(Boxed_Value)) { + return "push_back_ref"; + } else { + return "push_back"; + } + }()); + + m.add(fun([](const String *s, const String &f, size_t pos) { + return s->find(f, pos); + }), + "find"); + m.add(fun([](const String *s, const String &f, size_t pos) { + return s->rfind(f, pos); + }), + "rfind"); + m.add(fun([](const String *s, const String &f, size_t pos) { + return s->find_first_of(f, pos); + }), + "find_first_of"); + m.add(fun([](const String *s, const String &f, size_t pos) { + return s->find_last_of(f, pos); + }), + "find_last_of"); + m.add(fun([](const String *s, const String &f, size_t pos) { + return s->find_last_not_of(f, pos); + }), + "find_last_not_of"); + m.add(fun([](const String *s, const String &f, size_t pos) { + return s->find_first_not_of(f, pos); + }), + "find_first_not_of"); + + m.add(fun([](String *s, typename String::value_type c) -> decltype(auto) { + return (*s += c); + }), + "+="); + + m.add(fun([](String *s) { s->clear(); }), "clear"); + m.add(fun([](const String *s) { return s->empty(); }), "empty"); + m.add(fun([](const String *s) { return s->size(); }), "size"); + + m.add(fun([](const String *s) { return s->c_str(); }), "c_str"); + m.add(fun([](const String *s) { return s->data(); }), "data"); + m.add(fun([](const String *s, size_t pos, size_t len) { + return s->substr(pos, len); + }), + "substr"); +} + +/// Add a MapType container +/// http://www.sgi.com/tech/stl/Map.html +template +void future_type(const std::string &type, Module &m) { + m.add(user_type(), type); + + m.add(fun([](const FutureType &t) { return t.valid(); }), "valid"); + m.add(fun([](FutureType &t) { return t.get(); }), "get"); + m.add(fun(&FutureType::wait), "wait"); +} +} // namespace Carbon::bootstrap::standard_library + +#endif diff --git a/src/carbon/command/boxed_cast.hpp b/src/carbon/command/boxed_cast.hpp new file mode 100644 index 00000000..0957a6f8 --- /dev/null +++ b/src/carbon/command/boxed_cast.hpp @@ -0,0 +1,109 @@ + + +#ifndef CARBON_BOXED_CAST_HPP +#define CARBON_BOXED_CAST_HPP + +#include "../defines.hpp" +#include "bad_boxed_cast.hpp" +#include "boxed_cast_helper.hpp" +#include "boxed_value.hpp" +#include "type_conversions.hpp" +#include "atom/experiment/type_info.hpp" + +namespace Carbon { +class Type_Conversions; +} +namespace Carbon::detail::exception { +class bad_any_cast; +} // namespace Carbon::detail::exception + +namespace Carbon { +/// \brief Function for extracting a value stored in a Boxed_Value object +/// \tparam Type The type to extract from the Boxed_Value +/// \param[in] bv The Boxed_Value to extract a typed value from +/// \returns Type equivalent to the requested type +/// \throws exception::bad_boxed_cast If the requested conversion is not +/// possible +/// +/// boxed_cast will attempt to make conversions between value, &, *, +/// std::shared_ptr, std::reference_wrapper, and std::function (const and +/// non-const) where possible. boxed_cast is used internally during function +/// dispatch. This means that all of these conversions will be attempted +/// automatically for you during ChaiScript function calls. +/// +/// \li non-const values can be extracted as const or non-const +/// \li const values can be extracted only as const +/// \li Boxed_Value constructed from pointer or std::reference_wrapper can be +/// extracted as reference, +/// pointer or value types +/// \li Boxed_Value constructed from std::shared_ptr or value types can be +/// extracted as reference, +/// pointer, value, or std::shared_ptr types +/// +/// Conversions to std::function objects are attempted as well +/// +/// Example: +/// \code +/// // All of the following should succeed +/// Carbon::Boxed_Value bv(1); +/// std::shared_ptr spi = Carbon::boxed_cast +/// >(bv); int i = Carbon::boxed_cast(bv); int *ip = +/// Carbon::boxed_cast(bv); int &ir = Carbon::boxed_cast(bv); std::shared_ptr cspi = +/// Carbon::boxed_cast >(bv); const int ci = +/// Carbon::boxed_cast(bv); const int *cip = +/// Carbon::boxed_cast(bv); const int &cir = +/// Carbon::boxed_cast(bv); \endcode +/// +/// std::function conversion example +/// \code +/// Carbon::ChaiScript chai; +/// Boxed_Value bv = chai.eval("`+`"); // Get the functor for the + operator +/// which is built in std::function f = +/// Carbon::boxed_cast >(bv); int i = f(2,3); +/// assert(i == 5); +/// \endcode +template +decltype(auto) boxed_cast( + const Boxed_Value &bv, + const Type_Conversions_State *t_conversions = nullptr) { + if (!t_conversions || bv.get_type_info().bare_equal(user_type()) || + (t_conversions && !(*t_conversions)->convertable_type())) { + try { + return detail::Cast_Helper::cast(bv, t_conversions); + } catch (const Carbon::detail::exception::bad_any_cast &) { + } + } + + if (t_conversions && (*t_conversions)->convertable_type()) { + try { + // We will not catch any bad_boxed_dynamic_cast that is thrown, let + // the user get it either way, we are not responsible if it doesn't + // work + return (detail::Cast_Helper::cast( + (*t_conversions) + ->boxed_type_conversion(t_conversions->saves(), bv), + t_conversions)); + } catch (...) { + try { + // try going the other way + return (detail::Cast_Helper::cast( + (*t_conversions) + ->boxed_type_down_conversion( + t_conversions->saves(), bv), + t_conversions)); + } catch (const Carbon::detail::exception::bad_any_cast &) { + throw exception::bad_boxed_cast(bv.get_type_info(), + typeid(Type)); + } + } + } else { + // If it's not convertable, just throw the error, don't waste the time + // on the attempted dynamic_cast + throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); + } +} + +} // namespace Carbon + +#endif diff --git a/src/carbon/command/boxed_cast_helper.hpp b/src/carbon/command/boxed_cast_helper.hpp new file mode 100644 index 00000000..d8d71736 --- /dev/null +++ b/src/carbon/command/boxed_cast_helper.hpp @@ -0,0 +1,278 @@ + + +#ifndef CARBON_BOXED_CAST_HELPER_HPP +#define CARBON_BOXED_CAST_HELPER_HPP + +#include +#include + +#include "boxed_value.hpp" +#include "atom/experiment/type_info.hpp" + +namespace Carbon { +class Type_Conversions_State; + +namespace detail { +// Cast_Helper_Inner helper classes + +template +constexpr T *throw_if_null(T *t) { + if (t) { + return t; + } + throw std::runtime_error("Attempted to dereference null Boxed_Value"); +} + +template +static const T *verify_type_no_throw(const Boxed_Value &ob, + const std::type_info &ti, const T *ptr) { + if (ob.get_type_info() == ti) { + return ptr; + } else { + throw Carbon::detail::exception::bad_any_cast(); + } +} + +template +static T *verify_type_no_throw(const Boxed_Value &ob, const std::type_info &ti, + T *ptr) { + if (!ob.is_const() && ob.get_type_info() == ti) { + return ptr; + } else { + throw Carbon::detail::exception::bad_any_cast(); + } +} + +template +static const T *verify_type(const Boxed_Value &ob, const std::type_info &ti, + const T *ptr) { + if (ob.get_type_info().bare_equal_type_info(ti)) { + return throw_if_null(ptr); + } else { + throw Carbon::detail::exception::bad_any_cast(); + } +} + +template +static T *verify_type(const Boxed_Value &ob, const std::type_info &ti, T *ptr) { + if (!ob.is_const() && ob.get_type_info().bare_equal_type_info(ti)) { + return throw_if_null(ptr); + } else { + throw Carbon::detail::exception::bad_any_cast(); + } +} + +/// Generic Cast_Helper_Inner, for casting to any type +template +struct Cast_Helper_Inner { + static Result cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return *static_cast( + verify_type(ob, typeid(Result), ob.get_const_ptr())); + } +}; + +template +struct Cast_Helper_Inner : Cast_Helper_Inner {}; + +/// Cast_Helper_Inner for casting to a const * type +template +struct Cast_Helper_Inner { + static const Result *cast(const Boxed_Value &ob, + const Type_Conversions_State *) { + return static_cast( + verify_type_no_throw(ob, typeid(Result), ob.get_const_ptr())); + } +}; + +/// Cast_Helper_Inner for casting to a * type +template +struct Cast_Helper_Inner { + static Result *cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return static_cast( + verify_type_no_throw(ob, typeid(Result), ob.get_ptr())); + } +}; + +template +struct Cast_Helper_Inner : public Cast_Helper_Inner { +}; + +template +struct Cast_Helper_Inner + : public Cast_Helper_Inner {}; + +/// Cast_Helper_Inner for casting to a & type +template +struct Cast_Helper_Inner { + static const Result &cast(const Boxed_Value &ob, + const Type_Conversions_State *) { + return *static_cast( + verify_type(ob, typeid(Result), ob.get_const_ptr())); + } +}; + +/// Cast_Helper_Inner for casting to a & type +template +struct Cast_Helper_Inner { + static Result &cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return *static_cast( + verify_type(ob, typeid(Result), ob.get_ptr())); + } +}; + +/// Cast_Helper_Inner for casting to a && type +template +struct Cast_Helper_Inner { + static Result &&cast(const Boxed_Value &ob, + const Type_Conversions_State *) { + return std::move(*static_cast( + verify_type(ob, typeid(Result), ob.get_ptr()))); + } +}; + +/// Cast_Helper_Inner for casting to a std::unique_ptr<> && type +/// \todo Fix the fact that this has to be in a shared_ptr for now +template +struct Cast_Helper_Inner &&> { + static std::unique_ptr &&cast(const Boxed_Value &ob, + const Type_Conversions_State *) { + return std::move( + *(ob.get().cast>>())); + } +}; + +/// Cast_Helper_Inner for casting to a std::unique_ptr<> & type +/// \todo Fix the fact that this has to be in a shared_ptr for now +template +struct Cast_Helper_Inner &> { + static std::unique_ptr &cast(const Boxed_Value &ob, + const Type_Conversions_State *) { + return *(ob.get().cast>>()); + } +}; + +/// Cast_Helper_Inner for casting to a std::unique_ptr<> & type +/// \todo Fix the fact that this has to be in a shared_ptr for now +template +struct Cast_Helper_Inner &> { + static std::unique_ptr &cast(const Boxed_Value &ob, + const Type_Conversions_State *) { + return *(ob.get().cast>>()); + } +}; + +/// Cast_Helper_Inner for casting to a std::shared_ptr<> type +template +struct Cast_Helper_Inner> { + static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return ob.get().cast>(); + } +}; + +/// Cast_Helper_Inner for casting to a std::shared_ptr type +template +struct Cast_Helper_Inner> { + static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) { + if (!ob.get_type_info().is_const()) { + return std::const_pointer_cast( + ob.get().cast>()); + } else { + return ob.get().cast>(); + } + } +}; + +/// Cast_Helper_Inner for casting to a const std::shared_ptr<> & type +template +struct Cast_Helper_Inner> + : Cast_Helper_Inner> {}; + +template +struct Cast_Helper_Inner &> + : Cast_Helper_Inner> {}; + +template +struct Cast_Helper_Inner &> { + static_assert( + !std::is_const::value, + "Non-const reference to std::shared_ptr is not supported"); + static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) { + std::shared_ptr &res = ob.get().cast>(); + return ob.pointer_sentinel(res); + } +}; + +/// Cast_Helper_Inner for casting to a const std::shared_ptr & type +template +struct Cast_Helper_Inner> + : Cast_Helper_Inner> {}; + +template +struct Cast_Helper_Inner &> + : Cast_Helper_Inner> {}; + +/// Cast_Helper_Inner for casting to a Boxed_Value type +template <> +struct Cast_Helper_Inner { + static Boxed_Value cast(const Boxed_Value &ob, + const Type_Conversions_State *) { + return ob; + } +}; + +/// Cast_Helper_Inner for casting to a Boxed_Value & type +template <> +struct Cast_Helper_Inner { + static std::reference_wrapper cast( + const Boxed_Value &ob, const Type_Conversions_State *) { + return std::ref(const_cast(ob)); + } +}; + +/// Cast_Helper_Inner for casting to a const Boxed_Value & type +template <> +struct Cast_Helper_Inner : Cast_Helper_Inner {}; + +template <> +struct Cast_Helper_Inner : Cast_Helper_Inner { +}; + +/// Cast_Helper_Inner for casting to a std::reference_wrapper type +template +struct Cast_Helper_Inner> + : Cast_Helper_Inner {}; + +template +struct Cast_Helper_Inner> + : Cast_Helper_Inner {}; + +template +struct Cast_Helper_Inner &> + : Cast_Helper_Inner {}; + +template +struct Cast_Helper_Inner> + : Cast_Helper_Inner {}; + +template +struct Cast_Helper_Inner> + : Cast_Helper_Inner {}; + +template +struct Cast_Helper_Inner &> + : Cast_Helper_Inner {}; + +/// The exposed Cast_Helper object that by default just calls the +/// Cast_Helper_Inner +template +struct Cast_Helper { + static decltype(auto) cast(const Boxed_Value &ob, + const Type_Conversions_State *t_conversions) { + return (Cast_Helper_Inner::cast(ob, t_conversions)); + } +}; +} // namespace detail + +} // namespace Carbon + +#endif diff --git a/src/carbon/command/boxed_number.cpp b/src/carbon/command/boxed_number.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/carbon/command/boxed_number.hpp b/src/carbon/command/boxed_number.hpp new file mode 100644 index 00000000..3a4c26a4 --- /dev/null +++ b/src/carbon/command/boxed_number.hpp @@ -0,0 +1,857 @@ + + +#ifndef CARBON_BOXED_NUMERIC_HPP +#define CARBON_BOXED_NUMERIC_HPP + +#include +#include +#include + +#include "../language/algebraic.hpp" +#include "any.hpp" +#include "boxed_cast.hpp" +#include "boxed_cast_helper.hpp" +#include "boxed_value.hpp" +#include "atom/experiment/type_info.hpp" + +namespace Carbon { +class Type_Conversions; +} // namespace Carbon + +namespace Carbon::exception { +struct arithmetic_error : std::runtime_error { + explicit arithmetic_error(const std::string &reason) + : std::runtime_error("Arithmetic error: " + reason) {} + arithmetic_error(const arithmetic_error &) = default; + ~arithmetic_error() noexcept override = default; +}; +} // namespace Carbon::exception + +namespace Carbon { +// Due to the nature of generating every possible arithmetic operation, there +// are going to be warnings generated on every platform regarding size and sign, +// this is OK, so we're disabling size/and sign type warnings +#ifdef CARBON_MSVC +#pragma warning(push) +#pragma warning(disable : 4244 4018 4389 4146 4365 4267 4242) +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wfloat-equal" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wfloat-conversion" +#pragma GCC diagnostic ignored "-Wswitch" +#endif + +/// \brief Represents any numeric type, generically. Used internally for generic +/// operations between POD values +class Boxed_Number { +private: + enum class Common_Types { + t_int32, + t_double, + t_uint8, + t_int8, + t_uint16, + t_int16, + t_uint32, + t_uint64, + t_int64, + t_float, + t_long_double + }; + + template + constexpr static inline void check_divide_by_zero([[maybe_unused]] T t) { +#ifndef CARBON_NO_PROTECT_DIVIDEBYZERO + if constexpr (!std::is_floating_point::value) { + if (t == 0) { + throw Carbon::exception::arithmetic_error("divide by zero"); + } + } +#endif + } + + constexpr static Common_Types get_common_type(size_t t_size, + bool t_signed) noexcept { + return (t_size == 1 && t_signed) ? (Common_Types::t_int8) + : (t_size == 1) ? (Common_Types::t_uint8) + : (t_size == 2 && t_signed) ? (Common_Types::t_int16) + : (t_size == 2) ? (Common_Types::t_uint16) + : (t_size == 4 && t_signed) ? (Common_Types::t_int32) + : (t_size == 4) ? (Common_Types::t_uint32) + : (t_size == 8 && t_signed) ? (Common_Types::t_int64) + : (Common_Types::t_uint64); + } + + /* + const Type_Info &inp_ = t_bv.get_type_info(); + + // 一个泛型lambda函数,用于检查类型并返回相应的Common_Types + auto check_type = [&inp_](Common_Types type_if_match) -> Common_Types { + if (inp_ == user_type()) { + return type_if_match; + } + throw std::invalid_argument("Type not supported"); + }; + + try { + // 使用lambda函数替代if-else链 + return std::visit([&](auto&& arg) -> Common_Types { + using T = std::decay_t; + if constexpr (std::is_same_v) { + return check_type.template operator()(Common_Types::t_int); + } else if constexpr (std::is_same_v) { + return Common_Types::t_double; + } else if constexpr (std::is_same_v) { + return Common_Types::t_long_double; + } else if constexpr (std::is_same_v) { + return Common_Types::t_float; + } else if constexpr (std::is_same_v) { + return check_type.template operator()(Common_Types::t_int8); + } else if constexpr (std::is_same_v) { + return check_type.template operator()(Common_Types::t_uint8); + } else if constexpr (std::is_same_v) { + return check_type.template operator()(Common_Types::t_uint32); + } else if constexpr (std::is_same_v) { + return check_type.template operator()(Common_Types::t_int64); + } else if constexpr (std::is_same_v) { + return check_type.template operator()(Common_Types::t_int64); + } else if constexpr (std::is_same_v) { + return check_type.template operator()(Common_Types::t_uint64); + } else if constexpr (std::is_same_v) { + return check_type.template operator()(Common_Types::t_uint64); + } else if constexpr (std::is_same_v) { + return Common_Types::t_int8; + } else if constexpr (std::is_same_v) { + return Common_Types::t_int16; + } else if constexpr (std::is_same_v) { + return Common_Types::t_int32; + } else if constexpr (std::is_same_v) { + return Common_Types::t_int64; + } else if constexpr (std::is_same_v) { + return Common_Types::t_uint8; + } else if constexpr (std::is_same_v) { + return Common_Types::t_uint16; + } else if constexpr (std::is_same_v) { + return Common_Types::t_uint32; + } else if constexpr (std::is_same_v) { + return Common_Types::t_uint64; + } else if constexpr (std::is_same_v) { + return check_type.template operator()(Common_Types:: + */ + + static Common_Types get_common_type(const Boxed_Value &t_bv) { + const Type_Info &inp_ = t_bv.get_type_info(); + + if (inp_ == user_type()) { + return get_common_type(sizeof(int), true); + } else if (inp_ == user_type()) { + return Common_Types::t_double; + } else if (inp_ == user_type()) { + return Common_Types::t_long_double; + } else if (inp_ == user_type()) { + return Common_Types::t_float; + } else if (inp_ == user_type()) { + return get_common_type(sizeof(char), std::is_signed::value); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(unsigned char), false); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(unsigned int), false); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(long), true); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(long long), true); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(unsigned long), false); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(unsigned long long), false); + } else if (inp_ == user_type()) { + return Common_Types::t_int8; + } else if (inp_ == user_type()) { + return Common_Types::t_int16; + } else if (inp_ == user_type()) { + return Common_Types::t_int32; + } else if (inp_ == user_type()) { + return Common_Types::t_int64; + } else if (inp_ == user_type()) { + return Common_Types::t_uint8; + } else if (inp_ == user_type()) { + return Common_Types::t_uint16; + } else if (inp_ == user_type()) { + return Common_Types::t_uint32; + } else if (inp_ == user_type()) { + return Common_Types::t_uint64; + } else if (inp_ == user_type()) { + return get_common_type(sizeof(wchar_t), + std::is_signed::value); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(char16_t), + std::is_signed::value); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(char32_t), + std::is_signed::value); + } else { + throw Carbon::detail::exception::bad_any_cast(); + } + } + + template + static auto go(Operators::Opers t_oper, const Boxed_Value &t_bv, LHS *t_lhs, + const LHS &c_lhs, const RHS &c_rhs) { + switch (t_oper) { + case Operators::Opers::equals: + return const_var(c_lhs == c_rhs); + case Operators::Opers::less_than: + return const_var(c_lhs < c_rhs); + case Operators::Opers::greater_than: + return const_var(c_lhs > c_rhs); + case Operators::Opers::less_than_equal: + return const_var(c_lhs <= c_rhs); + case Operators::Opers::greater_than_equal: + return const_var(c_lhs >= c_rhs); + case Operators::Opers::not_equal: + return const_var(c_lhs != c_rhs); + case Operators::Opers::sum: + return const_var(c_lhs + c_rhs); + case Operators::Opers::quotient: + check_divide_by_zero(c_rhs); + return const_var(c_lhs / c_rhs); + case Operators::Opers::product: + return const_var(c_lhs * c_rhs); + case Operators::Opers::difference: + return const_var(c_lhs - c_rhs); + default: + break; + } + + if constexpr (!std::is_floating_point::value && + !std::is_floating_point::value) { + switch (t_oper) { + case Operators::Opers::shift_left: + return const_var(c_lhs << c_rhs); + case Operators::Opers::shift_right: + return const_var(c_lhs >> c_rhs); + case Operators::Opers::remainder: + check_divide_by_zero(c_rhs); + return const_var(c_lhs % c_rhs); + case Operators::Opers::bitwise_and: + return const_var(c_lhs & c_rhs); + case Operators::Opers::bitwise_or: + return const_var(c_lhs | c_rhs); + case Operators::Opers::bitwise_xor: + return const_var(c_lhs ^ c_rhs); + default: + break; + } + } + + if (t_lhs) { + switch (t_oper) { + case Operators::Opers::assign: + *t_lhs = c_rhs; + return t_bv; + case Operators::Opers::assign_product: + *t_lhs *= c_rhs; + return t_bv; + case Operators::Opers::assign_sum: + *t_lhs += c_rhs; + return t_bv; + case Operators::Opers::assign_quotient: + check_divide_by_zero(c_rhs); + *t_lhs /= c_rhs; + return t_bv; + case Operators::Opers::assign_difference: + *t_lhs -= c_rhs; + return t_bv; + default: + break; + } + + if constexpr (!std::is_floating_point::value && + !std::is_floating_point::value) { + switch (t_oper) { + case Operators::Opers::assign_bitwise_and: + check_divide_by_zero(c_rhs); + *t_lhs &= c_rhs; + return t_bv; + case Operators::Opers::assign_bitwise_or: + *t_lhs |= c_rhs; + return t_bv; + case Operators::Opers::assign_shift_left: + *t_lhs <<= c_rhs; + return t_bv; + case Operators::Opers::assign_shift_right: + *t_lhs >>= c_rhs; + return t_bv; + case Operators::Opers::assign_remainder: + *t_lhs %= c_rhs; + return t_bv; + case Operators::Opers::assign_bitwise_xor: + *t_lhs ^= c_rhs; + return t_bv; + default: + break; + } + } + } + + throw Carbon::detail::exception::bad_any_cast(); + } + + template + inline static auto visit(const Boxed_Value &bv, Callable &&callable) { + switch (get_common_type(bv)) { + case Common_Types::t_int32: + return callable( + *static_cast(bv.get_const_ptr())); + case Common_Types::t_uint8: + return callable( + *static_cast(bv.get_const_ptr())); + case Common_Types::t_int8: + return callable( + *static_cast(bv.get_const_ptr())); + case Common_Types::t_uint16: + return callable( + *static_cast(bv.get_const_ptr())); + case Common_Types::t_int16: + return callable( + *static_cast(bv.get_const_ptr())); + case Common_Types::t_uint32: + return callable( + *static_cast(bv.get_const_ptr())); + case Common_Types::t_uint64: + return callable( + *static_cast(bv.get_const_ptr())); + case Common_Types::t_int64: + return callable( + *static_cast(bv.get_const_ptr())); + case Common_Types::t_double: + return callable( + *static_cast(bv.get_const_ptr())); + case Common_Types::t_float: + return callable( + *static_cast(bv.get_const_ptr())); + case Common_Types::t_long_double: + return callable( + *static_cast(bv.get_const_ptr())); + } + throw Carbon::detail::exception::bad_any_cast(); + } + + inline static Boxed_Value oper(Operators::Opers t_oper, + const Boxed_Value &t_lhs) { + auto unary_operator = [t_oper, &t_lhs](const auto &c_lhs) { + auto *lhs = + static_cast *>(t_lhs.get_ptr()); + + if (lhs) { + switch (t_oper) { + case Operators::Opers::pre_increment: + ++(*lhs); + return t_lhs; + case Operators::Opers::pre_decrement: + --(*lhs); + return t_lhs; + default: + break; + } + } + + switch (t_oper) { + case Operators::Opers::unary_minus: + return const_var(-c_lhs); + case Operators::Opers::unary_plus: + return const_var(+c_lhs); + default: + break; + } + + if constexpr (!std::is_floating_point_v< + std::decay_t>) { + switch (t_oper) { + case Operators::Opers::bitwise_complement: + return const_var(~c_lhs); + default: + break; + } + } + + throw Carbon::detail::exception::bad_any_cast(); + }; + + return visit(t_lhs, unary_operator); + } + + inline static Boxed_Value oper(Operators::Opers t_oper, + const Boxed_Value &t_lhs, + const Boxed_Value &t_rhs) { + auto lhs_visit = [t_oper, &t_lhs, &t_rhs](const auto &c_lhs) { + auto *lhs = t_lhs.is_return_value() + ? nullptr + : static_cast *>( + t_lhs.get_ptr()); + + auto rhs_visit = [t_oper, &t_lhs, lhs, &c_lhs](const auto &c_rhs) { + return go(t_oper, t_lhs, lhs, c_lhs, c_rhs); + }; + + return visit(t_rhs, rhs_visit); + }; + + return visit(t_lhs, lhs_visit); + } + + template + static inline Target get_as_aux(const Boxed_Value &t_bv) { + return static_cast( + *static_cast(t_bv.get_const_ptr())); + } + + template + static std::string to_string_aux(const Boxed_Value &v) { + std::ostringstream oss; + oss << *static_cast(v.get_const_ptr()); + return oss.str(); + } + +public: + Boxed_Number() : bv(Boxed_Value(0)) {} + + explicit Boxed_Number(Boxed_Value v) : bv(std::move(v)) { + validate_boxed_number(bv); + } + + Boxed_Number(const Boxed_Number &) = default; + Boxed_Number(Boxed_Number &&) = default; + Boxed_Number &operator=(Boxed_Number &&) = default; + + template + explicit Boxed_Number(T t) : bv(Boxed_Value(t)) { + validate_boxed_number(bv); + } + + static Boxed_Value clone(const Boxed_Value &t_bv) { + return Boxed_Number(t_bv).get_as(t_bv.get_type_info()).bv; + } + + static bool is_floating_point(const Boxed_Value &t_bv) { + const Type_Info &inp_ = t_bv.get_type_info(); + + if (inp_ == user_type()) { + return true; + } else if (inp_ == user_type()) { + return true; + } else if (inp_ == user_type()) { + return true; + } else { + return false; + } + } + + Boxed_Number get_as(const Type_Info &inp_) const { + if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else { + throw Carbon::detail::exception::bad_any_cast(); + } + } + + template + static void check_type() { +#ifdef CARBON_MSVC +// MSVC complains about this being redundant / tautologica l +#pragma warning(push) +#pragma warning(disable : 4127 6287) +#endif + if (sizeof(Source) != sizeof(Target) || + std::is_signed() != std::is_signed() || + std::is_floating_point() != + std::is_floating_point()) { + throw Carbon::detail::exception::bad_any_cast(); + } +#ifdef CARBON_MSVC +#pragma warning(pop) +#endif + } + + template + Target get_as_checked() const { + switch (get_common_type(bv)) { + case Common_Types::t_int32: + check_type(); + return get_as_aux(bv); + case Common_Types::t_uint8: + check_type(); + return get_as_aux(bv); + case Common_Types::t_int8: + check_type(); + return get_as_aux(bv); + case Common_Types::t_uint16: + check_type(); + return get_as_aux(bv); + case Common_Types::t_int16: + check_type(); + return get_as_aux(bv); + case Common_Types::t_uint32: + check_type(); + return get_as_aux(bv); + case Common_Types::t_uint64: + check_type(); + return get_as_aux(bv); + case Common_Types::t_int64: + check_type(); + return get_as_aux(bv); + case Common_Types::t_double: + check_type(); + return get_as_aux(bv); + case Common_Types::t_float: + check_type(); + return get_as_aux(bv); + case Common_Types::t_long_double: + check_type(); + return get_as_aux(bv); + } + + throw Carbon::detail::exception::bad_any_cast(); + } + + template + Target get_as() const { + switch (get_common_type(bv)) { + case Common_Types::t_int32: + return get_as_aux(bv); + case Common_Types::t_uint8: + return get_as_aux(bv); + case Common_Types::t_int8: + return get_as_aux(bv); + case Common_Types::t_uint16: + return get_as_aux(bv); + case Common_Types::t_int16: + return get_as_aux(bv); + case Common_Types::t_uint32: + return get_as_aux(bv); + case Common_Types::t_uint64: + return get_as_aux(bv); + case Common_Types::t_int64: + return get_as_aux(bv); + case Common_Types::t_double: + return get_as_aux(bv); + case Common_Types::t_float: + return get_as_aux(bv); + case Common_Types::t_long_double: + return get_as_aux(bv); + } + + throw Carbon::detail::exception::bad_any_cast(); + } + + std::string to_string() const { + switch (get_common_type(bv)) { + case Common_Types::t_int32: + return std::to_string(get_as()); + case Common_Types::t_uint8: + return std::to_string(get_as()); + case Common_Types::t_int8: + return std::to_string(get_as()); + case Common_Types::t_uint16: + return std::to_string(get_as()); + case Common_Types::t_int16: + return std::to_string(get_as()); + case Common_Types::t_uint32: + return std::to_string(get_as()); + case Common_Types::t_uint64: + return std::to_string(get_as()); + case Common_Types::t_int64: + return std::to_string(get_as()); + case Common_Types::t_double: + return to_string_aux(bv); + case Common_Types::t_float: + return to_string_aux(bv); + case Common_Types::t_long_double: + return to_string_aux(bv); + } + + throw Carbon::detail::exception::bad_any_cast(); + } + + static void validate_boxed_number(const Boxed_Value &v) { + const Type_Info &inp_ = v.get_type_info(); + if (inp_ == user_type()) { + throw Carbon::detail::exception::bad_any_cast(); + } + + if (!inp_.is_arithmetic()) { + throw Carbon::detail::exception::bad_any_cast(); + } + } + + static bool equals(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return boxed_cast( + oper(Operators::Opers::equals, t_lhs.bv, t_rhs.bv)); + } + + static bool less_than(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return boxed_cast( + oper(Operators::Opers::less_than, t_lhs.bv, t_rhs.bv)); + } + + static bool greater_than(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return boxed_cast( + oper(Operators::Opers::greater_than, t_lhs.bv, t_rhs.bv)); + } + + static bool greater_than_equal(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return boxed_cast( + oper(Operators::Opers::greater_than_equal, t_lhs.bv, t_rhs.bv)); + } + + static bool less_than_equal(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return boxed_cast( + oper(Operators::Opers::less_than_equal, t_lhs.bv, t_rhs.bv)); + } + + static bool not_equal(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return boxed_cast( + oper(Operators::Opers::not_equal, t_lhs.bv, t_rhs.bv)); + } + + static Boxed_Number pre_decrement(Boxed_Number t_lhs) { + return Boxed_Number(oper(Operators::Opers::pre_decrement, t_lhs.bv)); + } + + static Boxed_Number pre_increment(Boxed_Number t_lhs) { + return Boxed_Number(oper(Operators::Opers::pre_increment, t_lhs.bv)); + } + + static const Boxed_Number sum(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::sum, t_lhs.bv, t_rhs.bv)); + } + + static const Boxed_Number unary_plus(const Boxed_Number &t_lhs) { + return Boxed_Number(oper(Operators::Opers::unary_plus, t_lhs.bv)); + } + + static const Boxed_Number unary_minus(const Boxed_Number &t_lhs) { + return Boxed_Number(oper(Operators::Opers::unary_minus, t_lhs.bv)); + } + + static const Boxed_Number difference(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::difference, t_lhs.bv, t_rhs.bv)); + } + + static Boxed_Number assign_bitwise_and(Boxed_Number t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::assign_bitwise_and, t_lhs.bv, t_rhs.bv)); + } + + static Boxed_Number assign(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::assign, t_lhs.bv, t_rhs.bv)); + } + + static Boxed_Number assign_bitwise_or(Boxed_Number t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::assign_bitwise_or, t_lhs.bv, t_rhs.bv)); + } + + static Boxed_Number assign_bitwise_xor(Boxed_Number t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::assign_bitwise_xor, t_lhs.bv, t_rhs.bv)); + } + + static Boxed_Number assign_remainder(Boxed_Number t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::assign_remainder, t_lhs.bv, t_rhs.bv)); + } + + static Boxed_Number assign_shift_left(Boxed_Number t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::assign_shift_left, t_lhs.bv, t_rhs.bv)); + } + + static Boxed_Number assign_shift_right(Boxed_Number t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::assign_shift_right, t_lhs.bv, t_rhs.bv)); + } + + static const Boxed_Number bitwise_and(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::bitwise_and, t_lhs.bv, t_rhs.bv)); + } + + static const Boxed_Number bitwise_complement(const Boxed_Number &t_lhs) { + return Boxed_Number(oper(Operators::Opers::bitwise_complement, t_lhs.bv, + Boxed_Value(0))); + } + + static const Boxed_Number bitwise_xor(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::bitwise_xor, t_lhs.bv, t_rhs.bv)); + } + + static const Boxed_Number bitwise_or(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::bitwise_or, t_lhs.bv, t_rhs.bv)); + } + + static Boxed_Number assign_product(Boxed_Number t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::assign_product, t_lhs.bv, t_rhs.bv)); + } + + static Boxed_Number assign_quotient(Boxed_Number t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::assign_quotient, t_lhs.bv, t_rhs.bv)); + } + + static Boxed_Number assign_sum(Boxed_Number t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::assign_sum, t_lhs.bv, t_rhs.bv)); + } + static Boxed_Number assign_difference(Boxed_Number t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::assign_difference, t_lhs.bv, t_rhs.bv)); + } + + static const Boxed_Number quotient(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::quotient, t_lhs.bv, t_rhs.bv)); + } + + static const Boxed_Number shift_left(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::shift_left, t_lhs.bv, t_rhs.bv)); + } + + static const Boxed_Number product(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::product, t_lhs.bv, t_rhs.bv)); + } + + static const Boxed_Number remainder(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::remainder, t_lhs.bv, t_rhs.bv)); + } + + static const Boxed_Number shift_right(const Boxed_Number &t_lhs, + const Boxed_Number &t_rhs) { + return Boxed_Number( + oper(Operators::Opers::shift_right, t_lhs.bv, t_rhs.bv)); + } + + static Boxed_Value do_oper(Operators::Opers t_oper, + const Boxed_Value &t_lhs, + const Boxed_Value &t_rhs) { + return oper(t_oper, t_lhs, t_rhs); + } + + static Boxed_Value do_oper(Operators::Opers t_oper, + const Boxed_Value &t_lhs) { + return oper(t_oper, t_lhs); + } + + Boxed_Value bv; +}; + +namespace detail { +/// Cast_Helper for converting from Boxed_Value to Boxed_Number +template <> +struct Cast_Helper { + static Boxed_Number cast(const Boxed_Value &ob, + const Type_Conversions_State *) { + return Boxed_Number(ob); + } +}; + +/// Cast_Helper for converting from Boxed_Value to Boxed_Number +template <> +struct Cast_Helper : Cast_Helper {}; + +/// Cast_Helper for converting from Boxed_Value to Boxed_Number +template <> +struct Cast_Helper : Cast_Helper {}; +} // namespace detail + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#ifdef CARBON_MSVC +#pragma warning(pop) +#endif + +} // namespace Carbon + +#endif diff --git a/src/carbon/command/boxed_value.cpp b/src/carbon/command/boxed_value.cpp new file mode 100644 index 00000000..7a6013d8 --- /dev/null +++ b/src/carbon/command/boxed_value.cpp @@ -0,0 +1,123 @@ +#include "boxed_value.hpp" + +namespace Carbon { +Boxed_Value::Data::Data(const Type_Info &ti, Carbon::detail::Any to, + bool is_ref, const void *t_void_ptr, + bool t_return_value) + : m_type_info(ti), + m_obj(std::move(to)), + m_data_ptr(ti.is_const() ? nullptr : const_cast(t_void_ptr)), + m_const_data_ptr(t_void_ptr), + m_is_ref(is_ref), + m_return_value(t_return_value) {} + +Boxed_Value::Data &Boxed_Value::Data::operator=(const Data &rhs) { + m_type_info = rhs.m_type_info; + m_obj = rhs.m_obj; + m_is_ref = rhs.m_is_ref; + m_data_ptr = rhs.m_data_ptr; + m_const_data_ptr = rhs.m_const_data_ptr; + m_return_value = rhs.m_return_value; + + if (rhs.m_attrs) { + m_attrs = + std::make_unique>>( + *rhs.m_attrs); + } + + return *this; +} + +Boxed_Value::Boxed_Value(std::shared_ptr t_data, Internal_Construction) + : m_data(std::move(t_data)) {} + +void Boxed_Value::swap(Boxed_Value &rhs) noexcept { + std::swap(m_data, rhs.m_data); +} + +Boxed_Value Boxed_Value::assign(const Boxed_Value &rhs) noexcept { + (*m_data) = (*rhs.m_data); + return *this; +} + +const Type_Info &Boxed_Value::get_type_info() const noexcept { + return m_data->m_type_info; +} + +/// return true if the object is uninitialized +bool Boxed_Value::is_undef() const noexcept { + return m_data->m_type_info.is_undef(); +} + +bool Boxed_Value::is_const() const noexcept { + return m_data->m_type_info.is_const(); +} + +bool Boxed_Value::is_type(const Type_Info &ti) const noexcept { + return m_data->m_type_info.bare_equal(ti); +} + +bool Boxed_Value::is_null() const noexcept { + return (m_data->m_data_ptr == nullptr && + m_data->m_const_data_ptr == nullptr); +} + +const Carbon::detail::Any &Boxed_Value::get() const noexcept { + return m_data->m_obj; +} + +bool Boxed_Value::is_ref() const noexcept { return m_data->m_is_ref; } + +bool Boxed_Value::is_return_value() const noexcept { + return m_data->m_return_value; +} + +void Boxed_Value::reset_return_value() const noexcept { + m_data->m_return_value = false; +} + +bool Boxed_Value::is_pointer() const noexcept { return !is_ref(); } + +void *Boxed_Value::get_ptr() const noexcept { return m_data->m_data_ptr; } + +const void *Boxed_Value::get_const_ptr() const noexcept { + return m_data->m_const_data_ptr; +} + +Boxed_Value Boxed_Value::get_attr(const std::string &t_name) { + if (!m_data->m_attrs) { + m_data->m_attrs = + std::make_unique>>(); + } + + auto &attr = (*m_data->m_attrs)[t_name]; + if (attr) { + return Boxed_Value(attr, Internal_Construction()); + } else { + Boxed_Value bv; // default construct a new one + attr = bv.m_data; + return bv; + } +} + +Boxed_Value &Boxed_Value::copy_attrs(const Boxed_Value &t_obj) { + if (t_obj.m_data->m_attrs) { + m_data->m_attrs = + std::make_unique>>( + *t_obj.m_data->m_attrs); + } + return *this; +} + +Boxed_Value &Boxed_Value::clone_attrs(const Boxed_Value &t_obj) { + copy_attrs(t_obj); + reset_return_value(); + return *this; +} + +/// \returns true if the two Boxed_Values share the same internal type +bool Boxed_Value::type_match(const Boxed_Value &l, + const Boxed_Value &r) noexcept { + return l.get_type_info() == r.get_type_info(); +} +} // namespace Carbon diff --git a/src/carbon/command/boxed_value.hpp b/src/carbon/command/boxed_value.hpp new file mode 100644 index 00000000..bdfbb479 --- /dev/null +++ b/src/carbon/command/boxed_value.hpp @@ -0,0 +1,323 @@ +#ifndef CARBON_BOXED_VALUE_HPP +#define CARBON_BOXED_VALUE_HPP + +#include +#include +#include + +#include "../defines.hpp" +#include "any.hpp" +#include "atom/experiment/type_info.hpp" +#include "atom/type/pointer.hpp" + +namespace Carbon { +/// \brief A wrapper for holding any valid C++ type. All types in ChaiScript are +/// Boxed_Value objects \sa Carbon::boxed_cast +class Boxed_Value { +public: + /// used for explicitly creating a "void" object + struct Void_Type {}; + +private: + /// structure which holds the internal state of a Boxed_Value + /// \todo Get rid of Any and merge it with this, reducing an allocation in + /// the process + struct Data { + Data(const Type_Info &ti, Carbon::detail::Any to, bool is_ref, + const void *t_void_ptr, bool t_return_value); + + Data &operator=(const Data &rhs); + + Data(const Data &) = delete; + + Data(Data &&) = default; + Data &operator=(Data &&rhs) = default; + + Type_Info m_type_info; + Carbon::detail::Any m_obj; + void *m_data_ptr; + const void *m_const_data_ptr; + std::unique_ptr>> m_attrs; + bool m_is_ref; + bool m_return_value; + }; + + struct Object_Data { + static auto get(Boxed_Value::Void_Type, bool t_return_value) { + return std::make_shared(Get_Type_Info::get(), + Carbon::detail::Any(), false, + nullptr, t_return_value); + } + + template + static auto get(const std::shared_ptr *obj, bool t_return_value) { + return get(*obj, t_return_value); + } + + template + static auto get(const std::shared_ptr &obj, bool t_return_value) { + return std::make_shared(Get_Type_Info::get(), + Carbon::detail::Any(obj), false, + obj.get(), t_return_value); + } + + template + static auto get(std::shared_ptr &&obj, bool t_return_value) { + auto ptr = obj.get(); + return std::make_shared( + Get_Type_Info::get(), + Carbon::detail::Any(std::move(obj)), false, ptr, + t_return_value); + } + + template + static auto get(T *t, bool t_return_value) { + return get(std::ref(*t), t_return_value); + } + + template + static auto get(const T *t, bool t_return_value) { + return get(std::cref(*t), t_return_value); + } + + template + static auto get(std::reference_wrapper obj, bool t_return_value) { + auto p = &obj.get(); + return std::make_shared( + Get_Type_Info::get(), + Carbon::detail::Any(std::move(obj)), true, p, + t_return_value); + } + + template + static auto get(std::unique_ptr &&obj, bool t_return_value) { + auto ptr = obj.get(); + return std::make_shared( + Get_Type_Info::get(), + Carbon::detail::Any( + std::make_shared>(std::move(obj))), + true, ptr, t_return_value); + } + + template + static auto get(T t, bool t_return_value) { + auto p = std::make_shared(std::move(t)); + auto ptr = p.get(); + return std::make_shared(Get_Type_Info::get(), + Carbon::detail::Any(std::move(p)), + false, ptr, t_return_value); + } + + static std::shared_ptr get() { + return std::make_shared( + Type_Info(), Carbon::detail::Any(), false, nullptr, false); + } + }; + +public: + /// Basic Boxed_Value constructor + template >>> + explicit Boxed_Value(T &&t, bool t_return_value = false) + : m_data(Object_Data::get(std::forward(t), t_return_value)) {} + + /// Unknown-type constructor + Boxed_Value() = default; + + Boxed_Value(Boxed_Value &&) = default; + Boxed_Value &operator=(Boxed_Value &&) = default; + Boxed_Value(const Boxed_Value &) = default; + Boxed_Value &operator=(const Boxed_Value &) = default; + + void swap(Boxed_Value &rhs); + + /// Copy the values stored in rhs.m_data to m_data. + /// m_data pointers are not shared in this case + Boxed_Value assign(const Boxed_Value &rhs) noexcept; + + const Type_Info &get_type_info() const noexcept; + + /// return true if the object is uninitialized + bool is_undef() const noexcept; + + bool is_const() const noexcept; + + bool is_type(const Type_Info &ti) const noexcept; + + template + auto pointer_sentinel(std::shared_ptr &ptr) const noexcept { + struct Sentinel { + Sentinel(std::shared_ptr &t_ptr, Data &data) + : m_ptr(t_ptr), m_data(data) {} + + ~Sentinel() { + // save new pointer data + const auto ptr_ = m_ptr.get().get(); + m_data.get().m_data_ptr = ptr_; + m_data.get().m_const_data_ptr = ptr_; + } + + Sentinel &operator=(Sentinel &&s) = default; + Sentinel(Sentinel &&s) = default; + + operator std::shared_ptr &() const noexcept { + return m_ptr.get(); + } + + Sentinel &operator=(const Sentinel &) = delete; + Sentinel(Sentinel &) = delete; + + std::reference_wrapper> m_ptr; + std::reference_wrapper m_data; + }; + + return Sentinel(ptr, *(m_data.get())); + } + + bool is_null() const noexcept; + + const Carbon::detail::Any &get() const noexcept; + + bool is_ref() const noexcept; + + bool is_return_value() const noexcept; + + void reset_return_value() const noexcept; + + bool is_pointer() const noexcept; + + void *get_ptr() const noexcept; + + const void *get_const_ptr() const noexcept; + + Boxed_Value get_attr(const std::string &t_name); + + Boxed_Value ©_attrs(const Boxed_Value &t_obj); + + Boxed_Value &clone_attrs(const Boxed_Value &t_obj); + + /// \returns true if the two Boxed_Values share the same internal type + static bool type_match(const Boxed_Value &l, + const Boxed_Value &r) noexcept; + +private: + // necessary to avoid hitting the templated && constructor of Boxed_Value + struct Internal_Construction {}; + + Boxed_Value(std::shared_ptr t_data, Internal_Construction); + + std::shared_ptr m_data = Object_Data::get(); +}; + +/// @brief Creates a Boxed_Value. If the object passed in is a value type, it is +/// copied. If it is a pointer, std::shared_ptr, or std::reference_type +/// a copy is not made. +/// @param t The value to box +/// +/// Example: +/// +/// ~~~{.cpp} +/// int i; +/// Carbon::ChaiScript chai; +/// chai.add(Carbon::var(i), "i"); +/// chai.add(Carbon::var(&i), "ip"); +/// ~~~ +/// +/// @sa @ref adding_objects +template +Boxed_Value var(T &&t) { + return Boxed_Value(std::forward(t)); +} + +namespace detail { +/// \brief Takes a value, copies it and returns a Boxed_Value object that is +/// immutable \param[in] t Value to copy and make const \returns Immutable +/// Boxed_Value \sa Boxed_Value::is_const +template +Boxed_Value const_var_impl(const T &t) { + return Boxed_Value(std::make_shared::type>(t)); +} + +/// \brief Takes a pointer to a value, adds const to the pointed to type and +/// returns an immutable Boxed_Value. +/// Does not copy the pointed to value. +/// \param[in] t Pointer to make immutable +/// \returns Immutable Boxed_Value +/// \sa Boxed_Value::is_const +template +Boxed_Value const_var_impl(T *t) { + return Boxed_Value(const_cast::type *>(t)); +} + +/// \brief Takes a std::shared_ptr to a value, adds const to the pointed to type +/// and returns an immutable Boxed_Value. +/// Does not copy the pointed to value. +/// \param[in] t Pointer to make immutable +/// \returns Immutable Boxed_Value +/// \sa Boxed_Value::is_const +template +Boxed_Value const_var_impl(const std::shared_ptr &t) { + return Boxed_Value( + std::const_pointer_cast::type>(t)); +} + +/// \brief Takes a std::reference_wrapper value, adds const to the referenced +/// type and returns an immutable Boxed_Value. +/// Does not copy the referenced value. +/// \param[in] t Reference object to make immutable +/// \returns Immutable Boxed_Value +/// \sa Boxed_Value::is_const +template +Boxed_Value const_var_impl(const std::reference_wrapper &t) { + return Boxed_Value(std::cref(t.get())); +} +} // namespace detail + +/// \brief Takes an object and returns an immutable Boxed_Value. If the object +/// is a std::reference or pointer type +/// the value is not copied. If it is an object type, it is copied. +/// \param[in] t Object to make immutable +/// \returns Immutable Boxed_Value +/// \sa Carbon::Boxed_Value::is_const +/// \sa Carbon::var +/// +/// Example: +/// \code +/// enum Colors +/// { +/// Blue, +/// Green, +/// Red +/// }; +/// Carbon::ChaiScript chai +/// chai.add(Carbon::const_var(Blue), "Blue"); // add immutable constant +/// chai.add(Carbon::const_var(Red), "Red"); +/// chai.add(Carbon::const_var(Green), "Green"); +/// \endcode +/// +/// \todo support C++11 strongly typed enums +/// \sa \ref adding_objects +template +Boxed_Value const_var(const T &t) { + return detail::const_var_impl(t); +} + +inline Boxed_Value void_var() { + static const auto v = Boxed_Value(Boxed_Value::Void_Type()); + return v; +} + +inline Boxed_Value const_var(bool b) { + static const auto t = detail::const_var_impl(true); + static const auto f = detail::const_var_impl(false); + + if (b) { + return t; + } else { + return f; + } +} + +} // namespace Carbon + +#endif diff --git a/src/carbon/command/dispatchkit.cpp b/src/carbon/command/dispatchkit.cpp new file mode 100644 index 00000000..2c9043ec --- /dev/null +++ b/src/carbon/command/dispatchkit.cpp @@ -0,0 +1,1055 @@ +#include "dispatchkit.hpp" +#include "boxed_number.hpp" + +namespace Carbon { + +Module &Module::add(Type_Info ti, std::string name) { + m_typeinfos.emplace_back(ti, std::move(name)); + return *this; +} + +Module &Module::add(Type_Conversion d) { + m_conversions.push_back(std::move(d)); + return *this; +} + +Module &Module::add(Proxy_Function f, std::string name) { + m_funcs.emplace_back(std::move(f), std::move(name)); + return *this; +} + +Module &Module::add_global_const(Boxed_Value t_bv, std::string t_name) { + if (!t_bv.is_const()) { + throw Carbon::exception::global_non_const(); + } + + m_globals.emplace_back(std::move(t_bv), std::move(t_name)); + return *this; +} + +// Add a bit of ChaiScript to eval during module implementation +Module &Module::eval(std::string str) { + m_evals.push_back(std::move(str)); + return *this; +} + +bool Module::has_function(const Proxy_Function &new_f, + std::string_view name) noexcept { + return std::any_of( + m_funcs.begin(), m_funcs.end(), + [&](const std::pair &existing_f) { + return existing_f.second == name && *(existing_f.first) == *(new_f); + }); +} + +namespace detail { +Dispatch_Function::Dispatch_Function(std::vector t_funcs) + : Proxy_Function_Base(build_type_infos(t_funcs), calculate_arity(t_funcs)), + m_funcs(std::move(t_funcs)) {} + +bool Dispatch_Function::operator==( + const dispatch::Proxy_Function_Base &rhs) const noexcept { + try { + const auto &dispatch_fun = dynamic_cast(rhs); + return m_funcs == dispatch_fun.m_funcs; + } catch (const std::bad_cast &) { + return false; + } +} + +std::vector Dispatch_Function::get_contained_functions() + const { + return std::vector(m_funcs.begin(), m_funcs.end()); +} + +int Dispatch_Function::calculate_arity( + const std::vector &t_funcs) noexcept { + if (t_funcs.empty()) { + return -1; + } + + const auto arity = t_funcs.front()->get_arity(); + + for (const auto &func : t_funcs) { + if (arity != func->get_arity()) { + // The arities in the list do not match, so it's unspecified + return -1; + } + } + + return arity; +} + +bool Dispatch_Function::call_match( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const noexcept { + return std::any_of(std::begin(m_funcs), std::end(m_funcs), + [&vals, &t_conversions](const Proxy_Function &f) { + return f->call_match(vals, t_conversions); + }); +} + +Boxed_Value Dispatch_Function::do_call( + const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const { + return dispatch::dispatch(m_funcs, params, t_conversions); +} + +std::vector Dispatch_Function::build_type_infos( + const std::vector &t_funcs) { + auto begin = t_funcs.cbegin(); + const auto &end = t_funcs.cend(); + + if (begin != end) { + std::vector type_infos = (*begin)->get_param_types(); + + ++begin; + + bool size_mismatch = false; + + while (begin != end) { + std::vector param_types = (*begin)->get_param_types(); + + if (param_types.size() != type_infos.size()) { + size_mismatch = true; + } + + for (size_t i = 0; i < type_infos.size() && i < param_types.size(); + ++i) { + if (!(type_infos[i] == param_types[i])) { + type_infos[i] = Get_Type_Info::get(); + } + } + + ++begin; + } + + assert(!type_infos.empty() && + " type_info vector size is < 0, this is only possible if " + "something else is broken"); + + if (size_mismatch) { + type_infos.resize(1); + } + + return type_infos; + } + + return std::vector(); +} +} // namespace detail + +namespace detail { + +Dispatch_Engine::Dispatch_Engine(Carbon::parser::Carbon_Parser_Base &parser) + : m_stack_holder(), m_parser(parser) {} + +void Dispatch_Engine::add(const Type_Conversion &d) { + m_conversions.add_conversion(d); +} + +void Dispatch_Engine::add(const Proxy_Function &f, const std::string &name) { + add_function(f, name); +} + +void Dispatch_Engine::add(Boxed_Value obj, const std::string &name) { + auto &stack = get_stack_data(); + + for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); + ++stack_elem) { + if (auto itr = stack_elem->find(name); itr != stack_elem->end()) { + itr->second = std::move(obj); + return; + } + } + + add_object(name, std::move(obj)); +} + +Boxed_Value &Dispatch_Engine::add_get_object(std::string t_name, + Boxed_Value obj, + Stack_Holder &t_holder) { + auto &stack_elem = get_stack_data(t_holder).back(); + + if (auto result = + stack_elem.insert(std::pair{std::move(t_name), std::move(obj)}); + result.second) { + return result.first->second; + } else { + // insert failed + throw Carbon::exception::name_conflict_error(result.first->first); + } +} + +void Dispatch_Engine::add_object(std::string t_name, Boxed_Value obj, + Stack_Holder &t_holder) { + auto &stack_elem = get_stack_data(t_holder).back(); + + if (auto result = + stack_elem.insert(std::pair{std::move(t_name), std::move(obj)}); + !result.second) { + // insert failed + throw Carbon::exception::name_conflict_error(result.first->first); + } +} + +void Dispatch_Engine::add_object(const std::string &name, Boxed_Value obj) { + add_object(name, std::move(obj), get_stack_holder()); +} + +/// Adds a new global shared object, between all the threads +void Dispatch_Engine::add_global_const(const Boxed_Value &obj, + const std::string &name) { + if (!obj.is_const()) { + throw Carbon::exception::global_non_const(); + } + + Carbon::detail::threading::unique_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) { + throw Carbon::exception::name_conflict_error(name); + } else { + m_state.m_global_objects.insert(std::make_pair(name, obj)); + } +} + +/// Adds a new global (non-const) shared object, between all the threads +Boxed_Value Dispatch_Engine::add_global_no_throw(Boxed_Value obj, + std::string name) { + Carbon::detail::threading::unique_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + return m_state.m_global_objects + .insert(std::pair{std::move(name), std::move(obj)}) + .first->second; +} + +/// Adds a new global (non-const) shared object, between all the threads +void Dispatch_Engine::add_global(Boxed_Value obj, std::string name) { + Carbon::detail::threading::unique_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + if (auto result = m_state.m_global_objects.insert( + std::pair{std::move(name), std::move(obj)}); + !result.second) { + // insert failed + throw Carbon::exception::name_conflict_error(result.first->first); + } +} + +void Dispatch_Engine::set_global(Boxed_Value obj, std::string name) { + Carbon::detail::threading::unique_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + m_state.m_global_objects.insert_or_assign(std::move(name), std::move(obj)); +} + +void Dispatch_Engine::new_scope() { new_scope(*m_stack_holder); } + +void Dispatch_Engine::pop_scope() { pop_scope(*m_stack_holder); } + +void Dispatch_Engine::new_scope(Stack_Holder &t_holder) { + t_holder.push_stack_data(); + t_holder.push_call_params(); +} + +void Dispatch_Engine::pop_scope(Stack_Holder &t_holder) { + t_holder.call_params.pop_back(); + StackData &stack = get_stack_data(t_holder); + + assert(!stack.empty()); + + stack.pop_back(); +} + +void Dispatch_Engine::new_stack(Stack_Holder &t_holder) { + // add a new Stack with 1 element + t_holder.push_stack(); +} + +void Dispatch_Engine::pop_stack(Stack_Holder &t_holder) { + t_holder.stacks.pop_back(); +} + +Boxed_Value Dispatch_Engine::get_object(std::string_view name, + std::atomic_uint_fast32_t &t_loc, + Stack_Holder &t_holder) const { + enum class Loc : uint_fast32_t { + located = 0x80000000, + is_local = 0x40000000, + stack_mask = 0x0FFF0000, + loc_mask = 0x0000FFFF + }; + + uint_fast32_t loc = t_loc; + + if (loc == 0) { + auto &stack = get_stack_data(t_holder); + + // Is it in the stack? + for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); + ++stack_elem) { + for (auto s = stack_elem->begin(); s != stack_elem->end(); ++s) { + if (s->first == name) { + t_loc = + static_cast( + std::distance(stack.rbegin(), stack_elem) << 16) | + static_cast( + std::distance(stack_elem->begin(), s)) | + static_cast(Loc::located) | + static_cast(Loc::is_local); + return s->second; + } + } + } + + t_loc = static_cast(Loc::located); + } else if ((loc & static_cast(Loc::is_local)) != 0u) { + auto &stack = get_stack_data(t_holder); + + return stack[stack.size() - 1 - + ((loc & static_cast(Loc::stack_mask)) >> + 16)] + .at_index(loc & static_cast(Loc::loc_mask)); + } + + // Is the value we are looking for a global or function? + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + const auto itr = m_state.m_global_objects.find(name); + if (itr != m_state.m_global_objects.end()) { + return itr->second; + } + + // no? is it a function object? + auto obj = get_function_object_int(name, loc); + if (obj.first != loc) { + t_loc = uint_fast32_t(obj.first); + } + + return obj.second; +} + +void Dispatch_Engine::add(const Type_Info &ti, const std::string &name) { + add_global_const(const_var(ti), name + "_type"); + + Carbon::detail::threading::unique_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + m_state.m_types.insert(std::make_pair(name, ti)); +} + +Type_Info Dispatch_Engine::get_type(std::string_view name, + bool t_throw = true) const { + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + const auto itr = m_state.m_types.find(name); + + if (itr != m_state.m_types.end()) { + return itr->second; + } + + if (t_throw) { + throw std::range_error("Type Not Known: " + std::string(name)); + } else { + return Type_Info(); + } +} + +std::string Dispatch_Engine::get_type_name(const Type_Info &ti) const { + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + for (const auto &elem : m_state.m_types) { + if (elem.second.bare_equal(ti)) { + return elem.first; + } + } + + return ti.bare_name(); +} + +std::vector> Dispatch_Engine::get_types() + const { + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + return std::vector>( + m_state.m_types.begin(), m_state.m_types.end()); +} + +std::shared_ptr> +Dispatch_Engine::get_method_missing_functions() const { + uint_fast32_t method_missing_loc = m_method_missing_loc; + auto method_missing_funs = + get_function("method_missing", method_missing_loc); + if (method_missing_funs.first != method_missing_loc) { + m_method_missing_loc = uint_fast32_t(method_missing_funs.first); + } + + return std::move(method_missing_funs.second); +} + +std::pair>> +Dispatch_Engine::get_function(std::string_view t_name, + const size_t t_hint) const { + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + const auto &funs = get_functions_int(); + + if (const auto itr = funs.find(t_name, t_hint); itr != funs.end()) { + return std::make_pair(std::distance(funs.begin(), itr), itr->second); + } else { + return std::make_pair(size_t(0), + std::make_shared>()); + } +} + +Boxed_Value Dispatch_Engine::get_function_object( + const std::string &t_name) const { + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + return get_function_object_int(t_name, 0).second; +} + +std::pair Dispatch_Engine::get_function_object_int( + std::string_view t_name, const size_t t_hint) const { + const auto &funs = get_boxed_functions_int(); + + if (const auto itr = funs.find(t_name, t_hint); itr != funs.end()) { + return std::make_pair(std::distance(funs.begin(), itr), itr->second); + } else { + throw std::range_error("Object not found: " + std::string(t_name)); + } +} + +bool Dispatch_Engine::function_exists(std::string_view name) const { + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + return get_functions_int().count(name) > 0; +} + +std::map Dispatch_Engine::get_parent_locals() const { + auto &stack = get_stack_data(); + if (stack.size() > 1) { + return std::map(stack[1].begin(), + stack[1].end()); + } else { + return std::map(stack[0].begin(), + stack[0].end()); + } +} + +std::map Dispatch_Engine::get_locals() const { + auto &stack = get_stack_data(); + auto &scope = stack.front(); + return std::map(scope.begin(), scope.end()); +} + +void Dispatch_Engine::set_locals( + const std::map &t_locals) { + auto &stack = get_stack_data(); + auto &scope = stack.front(); + scope.assign(t_locals.begin(), t_locals.end()); +} + +std::map Dispatch_Engine::get_scripting_objects() + const { + const Stack_Holder &s = *m_stack_holder; + + // We don't want the current context, but one up if it exists + const StackData &stack = (s.stacks.size() == 1) + ? (s.stacks.back()) + : (s.stacks[s.stacks.size() - 2]); + + std::map retval; + + // note: map insert doesn't overwrite existing values, which is why this + // works + for (auto itr = stack.rbegin(); itr != stack.rend(); ++itr) { + retval.insert(itr->begin(), itr->end()); + } + + // add the global values + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + retval.insert(m_state.m_global_objects.begin(), + m_state.m_global_objects.end()); + + return retval; +} + +std::map Dispatch_Engine::get_function_objects() + const { + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + const auto &funs = get_function_objects_int(); + + std::map objs; + + for (const auto &fun : funs) { + objs.insert(std::make_pair(fun.first, const_var(fun.second))); + } + + return objs; +} + +/// Get a vector of all registered functions +std::vector> +Dispatch_Engine::get_functions() const { + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + std::vector> rets; + + const auto &functions = get_functions_int(); + + for (const auto &function : functions) { + for (const auto &internal_func : *function.second) { + rets.emplace_back(function.first, internal_func); + } + } + + return rets; +} + +const Type_Conversions &Dispatch_Engine::conversions() const noexcept { + return m_conversions; +} + +bool Dispatch_Engine::is_attribute_call( + const std::vector &t_funs, const Function_Params &t_params, + bool t_has_params, const Type_Conversions_State &t_conversions) noexcept { + if (!t_has_params || t_params.empty()) { + return false; + } + + return std::any_of( + std::begin(t_funs), std::end(t_funs), [&](const auto &fun) { + return fun->is_attribute_function() && + fun->compare_first_type(t_params[0], t_conversions); + }); +} + +#ifdef CARBON_MSVC +// MSVC is unable to recognize that "rethrow_exception" causes the function to +// return so we must disable it here. +#pragma warning(push) +#pragma warning(disable : 4715) +#endif +Boxed_Value Dispatch_Engine::call_member( + const std::string &t_name, std::atomic_uint_fast32_t &t_loc, + const Function_Params ¶ms, bool t_has_params, + const Type_Conversions_State &t_conversions) { + uint_fast32_t loc = t_loc; + const auto funs = get_function(t_name, loc); + if (funs.first != loc) { + t_loc = uint_fast32_t(funs.first); + } + + const auto do_attribute_call = + [this](int l_num_params, Function_Params l_params, + const std::vector &l_funs, + const Type_Conversions_State &l_conversions) -> Boxed_Value { + Function_Params attr_params(l_params.begin(), + l_params.begin() + l_num_params); + Boxed_Value bv = dispatch::dispatch(l_funs, attr_params, l_conversions); + if (l_num_params < int(l_params.size()) || + bv.get_type_info().bare_equal( + user_type())) { + struct This_Foist { + This_Foist(Dispatch_Engine &e, const Boxed_Value &t_bv) + : m_e(e) { + m_e.get().new_scope(); + m_e.get().add_object("__this", t_bv); + } + + ~This_Foist() { m_e.get().pop_scope(); } + + std::reference_wrapper m_e; + }; + + This_Foist fi(*this, l_params.front()); + + try { + auto func = + boxed_cast(bv); + try { + return (*func)( + {l_params.begin() + l_num_params, l_params.end()}, + l_conversions); + } catch (const Carbon::exception::bad_boxed_cast &) { + } catch (const Carbon::exception::arity_error &) { + } catch (const Carbon::exception::guard_error &) { + } + throw Carbon::exception::dispatch_error( + {l_params.begin() + l_num_params, l_params.end()}, + std::vector{ + boxed_cast(bv)}); + } catch (const Carbon::exception::bad_boxed_cast &) { + // unable to convert bv into a Proxy_Function_Base + throw Carbon::exception::dispatch_error( + {l_params.begin() + l_num_params, l_params.end()}, + std::vector(l_funs.begin(), + l_funs.end())); + } + } else { + return bv; + } + }; + + if (is_attribute_call(*funs.second, params, t_has_params, t_conversions)) { + return do_attribute_call(1, params, *funs.second, t_conversions); + } else { + std::exception_ptr except; + + if (!funs.second->empty()) { + try { + return dispatch::dispatch(*funs.second, params, t_conversions); + } catch (Carbon::exception::dispatch_error &) { + except = std::current_exception(); + } + } + + // If we get here we know that either there was no method with that + // name, or there was no matching method + + const auto functions = [&]() -> std::vector { + std::vector fs; + + const auto method_missing_funs = get_method_missing_functions(); + + for (const auto &f : *method_missing_funs) { + if (f->compare_first_type(params[0], t_conversions)) { + fs.push_back(f); + } + } + + return fs; + }(); + + const bool is_no_param = [&]() -> bool { + for (const auto &f : functions) { + if (f->get_arity() != 2) { + return false; + } + } + return true; + }(); + + if (!functions.empty()) { + try { + if (is_no_param) { + auto tmp_params = params.to_vector(); + tmp_params.insert(tmp_params.begin() + 1, var(t_name)); + return do_attribute_call(2, Function_Params(tmp_params), + functions, t_conversions); + } else { + std::array p{ + params[0], var(t_name), + var(std::vector(params.begin() + 1, + params.end()))}; + return dispatch::dispatch(functions, Function_Params{p}, + t_conversions); + } + } catch (const dispatch::option_explicit_set &e) { + throw Carbon::exception::dispatch_error( + params, + std::vector(funs.second->begin(), + funs.second->end()), + e.what()); + } + } + + // If we get all the way down here we know there was no + // "method_missing" method at all. + if (except) { + std::rethrow_exception(except); + } else { + throw Carbon::exception::dispatch_error( + params, std::vector(funs.second->begin(), + funs.second->end())); + } + } +} +#ifdef CARBON_MSVC +#pragma warning(pop) +#endif + +Boxed_Value Dispatch_Engine::call_function( + std::string_view t_name, std::atomic_uint_fast32_t &t_loc, + const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const { + uint_fast32_t loc = t_loc; + const auto [func_loc, func] = get_function(t_name, loc); + if (func_loc != loc) { + t_loc = uint_fast32_t(func_loc); + } + return dispatch::dispatch(*func, params, t_conversions); +} + +/// Dump object info to stdout +void Dispatch_Engine::dump_object(const Boxed_Value &o) const { + std::cout << (o.is_const() ? "const " : "") << type_name(o) << '\n'; +} + +/// Dump type info to stdout +void Dispatch_Engine::dump_type(const Type_Info &type) const { + std::cout << (type.is_const() ? "const " : "") << get_type_name(type); +} + +/// Dump function to stdout +void Dispatch_Engine::dump_function( + const std::pair &f) const { + const auto params = f.second->get_param_types(); + + dump_type(params.front()); + std::cout << " " << f.first << "("; + + for (auto itr = params.begin() + 1; itr != params.end();) { + dump_type(*itr); + ++itr; + + if (itr != params.end()) { + std::cout << ", "; + } + } + + std::cout << ") \n"; +} + +/// Returns true if a call can be made that consists of the first parameter +/// (the function) with the remaining parameters as its arguments. +Boxed_Value Dispatch_Engine::call_exists(const Function_Params ¶ms) const { + if (params.empty()) { + throw Carbon::exception::arity_error(static_cast(params.size()), + 1); + } + + const auto &f = this->boxed_cast(params[0]); + const Type_Conversions_State convs(m_conversions, + m_conversions.conversion_saves()); + + return const_var(f->call_match( + Function_Params(params.begin() + 1, params.end()), convs)); +} + +/// Dump all system info to stdout +void Dispatch_Engine::dump_system() const { + std::cout << "Registered Types: \n"; + for (const auto &[type_name, type] : get_types()) { + std::cout << type_name << ": " << type.bare_name() << '\n'; + } + + std::cout << '\n'; + + std::cout << "Functions: \n"; + for (const auto &func : get_functions()) { + dump_function(func); + } + std::cout << '\n'; +} + +/// return true if the Boxed_Value matches the registered type by name +bool Dispatch_Engine::is_type(const Boxed_Value &r, + std::string_view user_typename) const noexcept { + try { + if (get_type(user_typename).bare_equal(r.get_type_info())) { + return true; + } + } catch (const std::range_error &) { + } + + try { + const dispatch::Dynamic_Object &d = + boxed_cast(r); + return d.get_type_name() == user_typename; + } catch (const std::bad_cast &) { + } + + return false; +} + +std::string Dispatch_Engine::type_name(const Boxed_Value &obj) const { + return get_type_name(obj.get_type_info()); +} + +Dispatch_Engine::State Dispatch_Engine::get_state() const { + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + return m_state; +} + +void Dispatch_Engine::set_state(const State &t_state) { + Carbon::detail::threading::unique_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + m_state = t_state; +} + +void Dispatch_Engine::save_function_params( + Stack_Holder &t_s, std::vector &&t_params) { + for (auto &¶m : t_params) { + t_s.call_params.back().insert(t_s.call_params.back().begin(), + std::move(param)); + } +} + +void Dispatch_Engine::save_function_params(Stack_Holder &t_s, + const Function_Params &t_params) { + t_s.call_params.back().insert(t_s.call_params.back().begin(), + t_params.begin(), t_params.end()); +} + +void Dispatch_Engine::save_function_params( + std::vector &&t_params) { + save_function_params(*m_stack_holder, std::move(t_params)); +} + +void Dispatch_Engine::save_function_params(const Function_Params &t_params) { + save_function_params(*m_stack_holder, t_params); +} + +void Dispatch_Engine::new_function_call( + Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves) { + if (t_s.call_depth == 0) { + m_conversions.enable_conversion_saves(t_saves, true); + } + + ++t_s.call_depth; + + save_function_params(m_conversions.take_saves(t_saves)); +} + +void Dispatch_Engine::pop_function_call( + Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves) { + --t_s.call_depth; + + assert(t_s.call_depth >= 0); + + if (t_s.call_depth == 0) { + t_s.call_params.back().clear(); + m_conversions.enable_conversion_saves(t_saves, false); + } +} + +void Dispatch_Engine::new_function_call() { + new_function_call(*m_stack_holder, m_conversions.conversion_saves()); +} + +void Dispatch_Engine::pop_function_call() { + pop_function_call(*m_stack_holder, m_conversions.conversion_saves()); +} + +Stack_Holder &Dispatch_Engine::get_stack_holder() noexcept { + return *m_stack_holder; +} + +const Dispatch_Engine::StackData &Dispatch_Engine::get_stack_data() + const noexcept { + return m_stack_holder->stacks.back(); +} + +Dispatch_Engine::StackData &Dispatch_Engine::get_stack_data( + Stack_Holder &t_holder) noexcept { + return t_holder.stacks.back(); +} + +Dispatch_Engine::StackData &Dispatch_Engine::get_stack_data() noexcept { + return m_stack_holder->stacks.back(); +} + +parser::Carbon_Parser_Base &Dispatch_Engine::get_parser() noexcept { + return m_parser.get(); +} + +bool Dispatch_Engine::function_less_than(const Proxy_Function &lhs, + const Proxy_Function &rhs) noexcept { + auto dynamic_lhs( + std::dynamic_pointer_cast(lhs)); + auto dynamic_rhs( + std::dynamic_pointer_cast(rhs)); + + if (dynamic_lhs && dynamic_rhs) { + if (dynamic_lhs->get_guard()) { + return dynamic_rhs->get_guard() ? false : true; + } else { + return false; + } + } + + if (dynamic_lhs && !dynamic_rhs) { + return false; + } + + if (!dynamic_lhs && dynamic_rhs) { + return true; + } + + const auto &lhsparamtypes = lhs->get_param_types(); + const auto &rhsparamtypes = rhs->get_param_types(); + + const auto lhssize = lhsparamtypes.size(); + const auto rhssize = rhsparamtypes.size(); + + const auto boxed_type = user_type(); + const auto boxed_pod_type = user_type(); + + for (size_t i = 1; i < lhssize && i < rhssize; ++i) { + const Type_Info < = lhsparamtypes[i]; + const Type_Info &rt = rhsparamtypes[i]; + + if (lt.bare_equal(rt) && lt.is_const() == rt.is_const()) { + continue; // The first two types are essentially the same, next + // iteration + } + + // const is after non-const for the same type + if (lt.bare_equal(rt) && lt.is_const() && !rt.is_const()) { + return false; + } + + if (lt.bare_equal(rt) && !lt.is_const()) { + return true; + } + + // boxed_values are sorted last + if (lt.bare_equal(boxed_type)) { + return false; + } + + if (rt.bare_equal(boxed_type)) { + return true; + } + + if (lt.bare_equal(boxed_pod_type)) { + return false; + } + + if (rt.bare_equal(boxed_pod_type)) { + return true; + } + + // otherwise, we want to sort by typeid + return lt < rt; + } + + return false; +} + +/// Implementation detail for adding a function. +/// \throws exception::name_conflict_error if there's a function matching +/// the given one being added +void Dispatch_Engine::add_function(const Proxy_Function &t_f, + const std::string &t_name) { + Carbon::detail::threading::unique_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + Proxy_Function new_func = [&]() -> Proxy_Function { + auto &funcs = get_functions_int(); + auto itr = funcs.find(t_name); + + if (itr != funcs.end()) { + auto vec = *itr->second; + for (const auto &func : vec) { + if ((*t_f) == *(func)) { + throw Carbon::exception::name_conflict_error(t_name); + } + } + + vec.reserve(vec.size() + 1); // tightly control vec growth + vec.push_back(t_f); + std::stable_sort(vec.begin(), vec.end(), &function_less_than); + itr->second = std::make_shared>(vec); + return std::make_shared(std::move(vec)); + } else if (t_f->has_arithmetic_param()) { + // if the function is the only function but it also contains + // arithmetic operators, we must wrap it in a dispatch function + // to allow for automatic arithmetic type conversions + std::vector vec; + vec.push_back(t_f); + funcs.insert(std::pair{ + t_name, std::make_shared>(vec)}); + return std::make_shared(std::move(vec)); + } else { + auto vec = std::make_shared>(); + vec->push_back(t_f); + funcs.insert(std::pair{t_name, vec}); + return t_f; + } + }(); + + get_boxed_functions_int().insert_or_assign(t_name, const_var(new_func)); + get_function_objects_int().insert_or_assign(t_name, std::move(new_func)); +} + +Dispatch_State::Dispatch_State(Dispatch_Engine &t_engine) + : m_engine(t_engine), + m_stack_holder(t_engine.get_stack_holder()), + m_conversions(t_engine.conversions(), + t_engine.conversions().conversion_saves()) {} + +Dispatch_Engine *Dispatch_State::operator->() const noexcept { + return &m_engine.get(); +} + +Dispatch_Engine &Dispatch_State::operator*() const noexcept { + return m_engine.get(); +} + +Stack_Holder &Dispatch_State::stack_holder() const noexcept { + return m_stack_holder.get(); +} + +const Type_Conversions_State &Dispatch_State::conversions() const noexcept { + return m_conversions; +} + +Type_Conversions::Conversion_Saves &Dispatch_State::conversion_saves() + const noexcept { + return m_conversions.saves(); +} + +Boxed_Value &Dispatch_State::add_get_object(const std::string &t_name, + Boxed_Value obj) const { + return m_engine.get().add_get_object(t_name, std::move(obj), + m_stack_holder.get()); +} + +void Dispatch_State::add_object(const std::string &t_name, + Boxed_Value obj) const { + m_engine.get().add_object(t_name, std::move(obj), m_stack_holder.get()); +} + +Boxed_Value Dispatch_State::get_object(std::string_view t_name, + std::atomic_uint_fast32_t &t_loc) const { + return m_engine.get().get_object(t_name, t_loc, m_stack_holder.get()); +} + +} // namespace detail +} // namespace Carbon diff --git a/src/carbon/command/dispatchkit.hpp b/src/carbon/command/dispatchkit.hpp new file mode 100644 index 00000000..42a63d24 --- /dev/null +++ b/src/carbon/command/dispatchkit.hpp @@ -0,0 +1,584 @@ + + +#ifndef CARBON_DISPATCHKIT_HPP +#define CARBON_DISPATCHKIT_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../defines.hpp" +#include "../threading.hpp" +#include "atom/experiment/flatmap.hpp" +#include "atom/experiment/short_alloc.hpp" +#include "atom/experiment/type_info.hpp" +#include "bad_boxed_cast.hpp" +#include "boxed_cast.hpp" +#include "boxed_cast_helper.hpp" +#include "boxed_value.hpp" +#include "dynamic_object.hpp" +#include "proxy_constructors.hpp" +#include "proxy_functions.hpp" +#include "type_conversions.hpp" + +namespace Carbon { +class Boxed_Number; +} // namespace Carbon + +namespace Carbon { +namespace parser { +class Carbon_Parser_Base; +} +namespace dispatch { +class Dynamic_Proxy_Function; +class Proxy_Function_Base; +struct Placeholder_Object; +} // namespace dispatch +} // namespace Carbon + +/// \namespace Carbon::dispatch +/// \brief Classes and functions specific to the runtime dispatch side of +/// ChaiScript. Some items may be of use to the end user. + +namespace Carbon { +namespace exception { +/// Exception thrown in the case that an object name is invalid because it is a +/// reserved word +class reserved_word_error : public std::runtime_error { +public: + explicit reserved_word_error(const std::string &t_word) noexcept + : std::runtime_error("Reserved word not allowed in object name: " + + t_word), + m_word(t_word) {} + + reserved_word_error(const reserved_word_error &) = default; + + ~reserved_word_error() noexcept override = default; + + std::string word() const { return m_word; } + +private: + std::string m_word; +}; + +/// Exception thrown in the case that an object name is invalid because it +/// contains illegal characters +class illegal_name_error : public std::runtime_error { +public: + explicit illegal_name_error(const std::string &t_name) noexcept + : std::runtime_error("Reserved name not allowed in object name: " + + t_name), + m_name(t_name) {} + + illegal_name_error(const illegal_name_error &) = default; + + ~illegal_name_error() noexcept override = default; + + std::string name() const { return m_name; } + +private: + std::string m_name; +}; + +/// Exception thrown in the case that an object name is invalid because it +/// already exists in current context +class name_conflict_error : public std::runtime_error { +public: + explicit name_conflict_error(const std::string &t_name) noexcept + : std::runtime_error("Name already exists in current context " + + t_name), + m_name(t_name) {} + + name_conflict_error(const name_conflict_error &) = default; + + ~name_conflict_error() noexcept override = default; + + std::string name() const { return m_name; } + +private: + std::string m_name; +}; + +/// Exception thrown in the case that a non-const object was added as a shared +/// object +class global_non_const : public std::runtime_error { +public: + global_non_const() noexcept + : std::runtime_error("a global object must be const") {} + + global_non_const(const global_non_const &) = default; + ~global_non_const() noexcept override = default; +}; +} // namespace exception + +/// \brief Holds a collection of ChaiScript settings which can be applied to the +/// ChaiScript runtime. +/// Used to implement loadable module support. +class Module { +public: + Module &add(Type_Info ti, std::string name); + + Module &add(Type_Conversion d); + + Module &add(Proxy_Function f, std::string name); + + Module &add_global_const(Boxed_Value t_bv, std::string t_name); + + // Add a bit of ChaiScript to eval during module implementation + Module &eval(std::string str); + + template + void apply(Eval &t_eval, Engine &t_engine) const { + apply(m_typeinfos.begin(), m_typeinfos.end(), t_engine); + apply(m_funcs.begin(), m_funcs.end(), t_engine); + apply_eval(m_evals.begin(), m_evals.end(), t_eval); + apply_single(m_conversions.begin(), m_conversions.end(), t_engine); + apply_globals(m_globals.begin(), m_globals.end(), t_engine); + } + + bool has_function(const Proxy_Function &new_f, + std::string_view name) noexcept; + +private: + std::vector> m_typeinfos; + std::vector> m_funcs; + std::vector> m_globals; + std::vector m_evals; + std::vector m_conversions; + + template + static void apply(InItr begin, const InItr end, T &t) { + for_each(begin, end, [&t](const auto &obj) { + try { + t.add(obj.first, obj.second); + } catch (const Carbon::exception::name_conflict_error &) { + /// \todo Should we throw an error if there's a name conflict + /// while applying a module? + } + }); + } + + template + static void apply_globals(InItr begin, InItr end, T &t) { + while (begin != end) { + t.add_global_const(begin->first, begin->second); + ++begin; + } + } + + template + static void apply_single(InItr begin, InItr end, T &t) { + while (begin != end) { + t.add(*begin); + ++begin; + } + } + + template + static void apply_eval(InItr begin, InItr end, T &t) { + while (begin != end) { + t.eval(*begin); + ++begin; + } + } +}; + +/// Convenience typedef for Module objects to be added to the ChaiScript runtime +using ModulePtr = std::shared_ptr; + +namespace detail { +/// A Proxy_Function implementation that is able to take +/// a vector of Proxy_Functions and perform a dispatch on them. It is +/// used specifically in the case of dealing with Function object variables +class Dispatch_Function final : public dispatch::Proxy_Function_Base { +public: + explicit Dispatch_Function(std::vector t_funcs); + + bool operator==( + const dispatch::Proxy_Function_Base &rhs) const noexcept override; + + std::vector get_contained_functions() const override; + + static int calculate_arity( + const std::vector &t_funcs) noexcept; + + bool call_match( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const noexcept override; + +protected: + Boxed_Value do_call( + const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const override; + +private: + std::vector m_funcs; + + static std::vector build_type_infos( + const std::vector &t_funcs); +}; +} // namespace detail + +namespace detail { +struct Stack_Holder { + // template + // using SmallVector = std::vector>; + + template + using SmallVector = std::vector; + + using Scope = QuickFlatMap; + using StackData = SmallVector; + using Stacks = SmallVector; + using Call_Param_List = SmallVector; + using Call_Params = SmallVector; + + Stack_Holder() { + push_stack(); + push_call_params(); + } + + void push_stack_data() { + stacks.back().emplace_back(); + // stacks.back().emplace_back(Scope(scope_allocator)); + } + + void push_stack() { stacks.emplace_back(1); } + + void push_call_params() { call_params.emplace_back(); } + + Stacks stacks; + Call_Params call_params; + + int call_depth = 0; +}; + +/// Main class for the dispatchkit. Handles management +/// of the object stack, functions and registered types. +class Dispatch_Engine { +public: + using Type_Name_Map = std::map; + using Scope = QuickFlatMap; + using StackData = Stack_Holder::StackData; + + struct State { + QuickFlatMap>, + str_equal> + m_functions; + QuickFlatMap m_function_objects; + QuickFlatMap m_boxed_functions; + std::map m_global_objects; + Type_Name_Map m_types; + }; + + explicit Dispatch_Engine(Carbon::parser::Carbon_Parser_Base &parser); + + /// \brief casts an object while applying any Dynamic_Conversion available + template + decltype(auto) boxed_cast(const Boxed_Value &bv) const { + Type_Conversions_State state(m_conversions, + m_conversions.conversion_saves()); + return (Carbon::boxed_cast(bv, &state)); + } + + /// Add a new conversion for upcasting to a base class + void add(const Type_Conversion &d); + + /// Add a new named Proxy_Function to the system + void add(const Proxy_Function &f, const std::string &name); + + /// Set the value of an object, by name. If the object + /// is not available in the current scope it is created + void add(Boxed_Value obj, const std::string &name); + + /// Adds a named object to the current scope + /// \warning This version does not check the validity of the name + /// it is meant for internal use only + Boxed_Value &add_get_object(std::string t_name, Boxed_Value obj, + Stack_Holder &t_holder); + + /// Adds a named object to the current scope + /// \warning This version does not check the validity of the name + /// it is meant for internal use only + void add_object(std::string t_name, Boxed_Value obj, + Stack_Holder &t_holder); + + /// Adds a named object to the current scope + /// \warning This version does not check the validity of the name + /// it is meant for internal use only + void add_object(const std::string &name, Boxed_Value obj); + + /// Adds a new global shared object, between all the threads + void add_global_const(const Boxed_Value &obj, const std::string &name); + + /// Adds a new global (non-const) shared object, between all the threads + Boxed_Value add_global_no_throw(Boxed_Value obj, std::string name); + + /// Adds a new global (non-const) shared object, between all the threads + void add_global(Boxed_Value obj, std::string name); + + /// Updates an existing global shared object or adds a new global shared + /// object if not found + void set_global(Boxed_Value obj, std::string name); + + /// Adds a new scope to the stack + void new_scope(); + + /// Pops the current scope from the stack + void pop_scope(); + + /// Adds a new scope to the stack + static void new_scope(Stack_Holder &t_holder); + + /// Pops the current scope from the stack + static void pop_scope(Stack_Holder &t_holder); + + /// Pushes a new stack on to the list of stacks + static void new_stack(Stack_Holder &t_holder); + + static void pop_stack(Stack_Holder &t_holder); + + /// Searches the current stack for an object of the given name + /// includes a special overload for the _ place holder object to + /// ensure that it is always in scope. + Boxed_Value get_object(std::string_view name, + std::atomic_uint_fast32_t &t_loc, + Stack_Holder &t_holder) const; + + /// Registers a new named type + void add(const Type_Info &ti, const std::string &name); + + /// Returns the type info for a named type + Type_Info get_type(std::string_view name, bool t_throw) const; + + /// Returns the registered name of a known type_info object + /// compares the "bare_type_info" for the broadest possible + /// match + std::string get_type_name(const Type_Info &ti) const; + + /// Return all registered types + std::vector> get_types() const; + + std::shared_ptr> get_method_missing_functions() + const; + + /// Return a function by name + std::pair>> + get_function(std::string_view t_name, const size_t t_hint) const; + + /// \returns a function object (Boxed_Value wrapper) if it exists + /// \throws std::range_error if it does not + Boxed_Value get_function_object(const std::string &t_name) const; + + /// \returns a function object (Boxed_Value wrapper) if it exists + /// \throws std::range_error if it does not + /// \warn does not obtain a mutex lock. \sa get_function_object for public + /// version + std::pair get_function_object_int( + std::string_view t_name, const size_t t_hint) const; + + /// Return true if a function exists + bool function_exists(std::string_view name) const; + + /// \returns All values in the local thread state in the parent scope, or if + /// it doesn't exist, + /// the current scope. + std::map get_parent_locals() const; + + /// \returns All values in the local thread state, added through the add() + /// function + std::map get_locals() const; + + /// \brief Sets all of the locals for the current thread state. + /// + /// \param[in] t_locals The map set of variables to replace the + /// current state with + /// + /// Any existing locals are removed and the given set of variables is added + void set_locals(const std::map &t_locals); + + /// + /// Get a map of all objects that can be seen from the current scope in a + /// scripting context + /// + std::map get_scripting_objects() const; + + /// + /// Get a map of all functions that can be seen from a scripting context + /// + std::map get_function_objects() const; + + /// Get a vector of all registered functions + std::vector> get_functions() const; + + const Type_Conversions &conversions() const noexcept; + + static bool is_attribute_call( + const std::vector &t_funs, + const Function_Params &t_params, bool t_has_params, + const Type_Conversions_State &t_conversions) noexcept; + +#ifdef CARBON_MSVC +// MSVC is unable to recognize that "rethrow_exception" causes the function to +// return so we must disable it here. +#pragma warning(push) +#pragma warning(disable : 4715) +#endif + Boxed_Value call_member(const std::string &t_name, + std::atomic_uint_fast32_t &t_loc, + const Function_Params ¶ms, bool t_has_params, + const Type_Conversions_State &t_conversions); +#ifdef CARBON_MSVC +#pragma warning(pop) +#endif + + Boxed_Value call_function( + std::string_view t_name, std::atomic_uint_fast32_t &t_loc, + const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const; + + /// Dump object info to stdout + void dump_object(const Boxed_Value &o) const; + + /// Dump type info to stdout + void dump_type(const Type_Info &type) const; + + /// Dump function to stdout + void dump_function( + const std::pair &f) const; + + /// Returns true if a call can be made that consists of the first parameter + /// (the function) with the remaining parameters as its arguments. + Boxed_Value call_exists(const Function_Params ¶ms) const; + + /// Dump all system info to stdout + void dump_system() const; + + /// return true if the Boxed_Value matches the registered type by name + bool is_type(const Boxed_Value &r, + std::string_view user_typename) const noexcept; + + std::string type_name(const Boxed_Value &obj) const; + + State get_state() const; + + void set_state(const State &t_state); + + static void save_function_params(Stack_Holder &t_s, + std::vector &&t_params); + + static void save_function_params(Stack_Holder &t_s, + const Function_Params &t_params); + + void save_function_params(std::vector &&t_params); + + void save_function_params(const Function_Params &t_params); + + void new_function_call(Stack_Holder &t_s, + Type_Conversions::Conversion_Saves &t_saves); + + void pop_function_call(Stack_Holder &t_s, + Type_Conversions::Conversion_Saves &t_saves); + + void new_function_call(); + + void pop_function_call(); + + Stack_Holder &get_stack_holder() noexcept; + + /// Returns the current stack + /// make const/non const versions + const StackData &get_stack_data() const noexcept; + + static StackData &get_stack_data(Stack_Holder &t_holder) noexcept; + + StackData &get_stack_data() noexcept; + + parser::Carbon_Parser_Base &get_parser() noexcept; + +private: + const decltype(State::m_boxed_functions) &get_boxed_functions_int() + const noexcept { + return m_state.m_boxed_functions; + } + + decltype(State::m_boxed_functions) &get_boxed_functions_int() noexcept { + return m_state.m_boxed_functions; + } + + const decltype(State::m_function_objects) &get_function_objects_int() + const noexcept { + return m_state.m_function_objects; + } + + decltype(State::m_function_objects) &get_function_objects_int() noexcept { + return m_state.m_function_objects; + } + + const decltype(State::m_functions) &get_functions_int() const noexcept { + return m_state.m_functions; + } + + decltype(State::m_functions) &get_functions_int() noexcept { + return m_state.m_functions; + } + + static bool function_less_than(const Proxy_Function &lhs, + const Proxy_Function &rhs) noexcept; + + /// Implementation detail for adding a function. + /// \throws exception::name_conflict_error if there's a function matching + /// the given one being added + void add_function(const Proxy_Function &t_f, const std::string &t_name); + + mutable Carbon::detail::threading::shared_mutex m_mutex; + + Type_Conversions m_conversions; + Carbon::detail::threading::Thread_Storage m_stack_holder; + std::reference_wrapper m_parser; + + mutable std::atomic_uint_fast32_t m_method_missing_loc = {0}; + + State m_state; +}; + +class Dispatch_State { +public: + explicit Dispatch_State(Dispatch_Engine &t_engine); + + Dispatch_Engine *operator->() const noexcept; + + Dispatch_Engine &operator*() const noexcept; + + Stack_Holder &stack_holder() const noexcept; + + const Type_Conversions_State &conversions() const noexcept; + + Type_Conversions::Conversion_Saves &conversion_saves() const noexcept; + + Boxed_Value &add_get_object(const std::string &t_name, + Boxed_Value obj) const; + + void add_object(const std::string &t_name, Boxed_Value obj) const; + + Boxed_Value get_object(std::string_view t_name, + std::atomic_uint_fast32_t &t_loc) const; + +private: + std::reference_wrapper m_engine; + std::reference_wrapper m_stack_holder; + Type_Conversions_State m_conversions; +}; +} // namespace detail +} // namespace Carbon + +#endif diff --git a/src/carbon/command/dynamic_object.cpp b/src/carbon/command/dynamic_object.cpp new file mode 100644 index 00000000..be7e8b4b --- /dev/null +++ b/src/carbon/command/dynamic_object.cpp @@ -0,0 +1,70 @@ +#include "dynamic_object.hpp" + +namespace Carbon { +namespace dispatch { + +Dynamic_Object::Dynamic_Object(std::string t_type_name) + : m_type_name(std::move(t_type_name)), m_option_explicit(false) {} + +bool Dynamic_Object::is_explicit() const noexcept { return m_option_explicit; } + +void Dynamic_Object::set_explicit(const bool t_explicit) noexcept { + m_option_explicit = t_explicit; +} + +const std::string &Dynamic_Object::get_type_name() const noexcept { + return m_type_name; +} + +const Boxed_Value &Dynamic_Object::operator[]( + const std::string &t_attr_name) const { + return get_attr(t_attr_name); +} + +Boxed_Value &Dynamic_Object::operator[](const std::string &t_attr_name) { + return get_attr(t_attr_name); +} + +const Boxed_Value &Dynamic_Object::get_attr( + const std::string &t_attr_name) const { + auto a = m_attrs.find(t_attr_name); + + if (a != m_attrs.end()) { + return a->second; + } else { + throw std::range_error("Attr not found '" + t_attr_name + + "' and cannot be added to const obj"); + } +} + +bool Dynamic_Object::has_attr(const std::string &t_attr_name) const { + return m_attrs.find(t_attr_name) != m_attrs.end(); +} + +Boxed_Value &Dynamic_Object::get_attr(const std::string &t_attr_name) { + return m_attrs[t_attr_name]; +} + +Boxed_Value &Dynamic_Object::method_missing(const std::string &t_method_name) { + if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { + throw option_explicit_set(t_method_name); + } + + return get_attr(t_method_name); +} + +const Boxed_Value &Dynamic_Object::method_missing( + const std::string &t_method_name) const { + if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { + throw option_explicit_set(t_method_name); + } + + return get_attr(t_method_name); +} + +std::map Dynamic_Object::get_attrs() const { + return m_attrs; +} + +} // namespace dispatch +} // namespace Carbon diff --git a/src/carbon/command/dynamic_object.hpp b/src/carbon/command/dynamic_object.hpp new file mode 100644 index 00000000..469f2159 --- /dev/null +++ b/src/carbon/command/dynamic_object.hpp @@ -0,0 +1,67 @@ + +#ifndef CARBON_DYNAMIC_OBJECT_HPP +#define CARBON_DYNAMIC_OBJECT_HPP + +#include +#include +#include + +#include "boxed_value.hpp" + +namespace Carbon { +class Type_Conversions; +namespace dispatch { +class Proxy_Function_Base; +} // namespace dispatch +} // namespace Carbon + +namespace Carbon { +namespace dispatch { +struct option_explicit_set : std::runtime_error { + explicit option_explicit_set(const std::string &t_param_name) + : std::runtime_error("option explicit set and parameter '" + + t_param_name + "' does not exist") {} + + option_explicit_set(const option_explicit_set &) = default; + + ~option_explicit_set() noexcept override = default; +}; + +class Dynamic_Object { +public: + explicit Dynamic_Object(std::string t_type_name); + + Dynamic_Object() = default; + + bool is_explicit() const noexcept; + + void set_explicit(const bool t_explicit) noexcept; + + const std::string &get_type_name() const noexcept; + + const Boxed_Value &operator[](const std::string &t_attr_name) const; + + Boxed_Value &operator[](const std::string &t_attr_name); + + const Boxed_Value &get_attr(const std::string &t_attr_name) const; + + bool has_attr(const std::string &t_attr_name) const; + + Boxed_Value &get_attr(const std::string &t_attr_name); + + Boxed_Value &method_missing(const std::string &t_method_name); + + const Boxed_Value &method_missing(const std::string &t_method_name) const; + + std::map get_attrs() const; + +private: + const std::string m_type_name = ""; + bool m_option_explicit = false; + + std::map m_attrs; +}; + +} // namespace dispatch +} // namespace Carbon +#endif diff --git a/src/carbon/command/dynamic_object_detail.cpp b/src/carbon/command/dynamic_object_detail.cpp new file mode 100644 index 00000000..196ea3b3 --- /dev/null +++ b/src/carbon/command/dynamic_object_detail.cpp @@ -0,0 +1,176 @@ +#include "dynamic_object_detail.hpp" + +namespace Carbon { +namespace dispatch { +namespace detail { +Dynamic_Object_Function::Dynamic_Object_Function(std::string t_type_name, + const Proxy_Function &t_func, + bool t_is_attribute) + : Proxy_Function_Base(t_func->get_param_types(), t_func->get_arity()), + m_type_name(std::move(t_type_name)), + m_func(t_func), + m_doti(user_type()), + m_is_attribute(t_is_attribute) { + assert((t_func->get_arity() > 0 || t_func->get_arity() < 0) && + "Programming error, Dynamic_Object_Function must have at least " + "one parameter (this)"); +} + +Dynamic_Object_Function::Dynamic_Object_Function(std::string t_type_name, + const Proxy_Function &t_func, + const Type_Info &t_ti, + bool t_is_attribute) + : Proxy_Function_Base(build_param_types(t_func->get_param_types(), t_ti), + t_func->get_arity()), + m_type_name(std::move(t_type_name)), + m_func(t_func), + m_ti(t_ti.is_undef() ? nullptr : new Type_Info(t_ti)), + m_doti(user_type()), + m_is_attribute(t_is_attribute) { + assert((t_func->get_arity() > 0 || t_func->get_arity() < 0) && + "Programming error, Dynamic_Object_Function must have at least " + "one parameter (this)"); +} + +bool Dynamic_Object_Function::operator==( + const Proxy_Function_Base &f) const noexcept { + if (const auto *df = dynamic_cast(&f)) { + return df->m_type_name == m_type_name && (*df->m_func) == (*m_func); + } else { + return false; + } +} + +bool Dynamic_Object_Function::is_attribute_function() const noexcept { + return m_is_attribute; +} + +bool Dynamic_Object_Function::call_match( + const Carbon::Function_Params &vals, + const Type_Conversions_State &t_conversions) const noexcept { + if (dynamic_object_typename_match(vals, m_type_name, m_ti, t_conversions)) { + return m_func->call_match(vals, t_conversions); + } else { + return false; + } +} + +std::vector +Dynamic_Object_Function::get_contained_functions() const { + return {m_func}; +} + +Boxed_Value Dynamic_Object_Function::do_call( + const Carbon::Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const { + if (dynamic_object_typename_match(params, m_type_name, m_ti, + t_conversions)) { + return (*m_func)(params, t_conversions); + } else { + throw exception::guard_error(); + } +} + +bool Dynamic_Object_Function::compare_first_type( + const Boxed_Value &bv, + const Type_Conversions_State &t_conversions) const noexcept { + return dynamic_object_typename_match(bv, m_type_name, m_ti, t_conversions); +} + +std::vector Dynamic_Object_Function::build_param_types( + const std::vector &t_inner_types, const Type_Info &t_objectti) { + std::vector types(t_inner_types); + + assert(types.size() > 1); + // assert(types[1].bare_equal(user_type())); + types[1] = t_objectti; + return types; +} + +bool Dynamic_Object_Function::dynamic_object_typename_match( + const Boxed_Value &bv, const std::string &name, + const std::unique_ptr &ti, + const Type_Conversions_State &t_conversions) const noexcept { + if (bv.get_type_info().bare_equal(m_doti)) { + try { + const Dynamic_Object &d = + boxed_cast(bv, &t_conversions); + return name == "Dynamic_Object" || d.get_type_name() == name; + } catch (const std::bad_cast &) { + return false; + } + } else { + if (ti) { + return bv.get_type_info().bare_equal(*ti); + } else { + return false; + } + } +} + +bool Dynamic_Object_Function::dynamic_object_typename_match( + const Carbon::Function_Params &bvs, const std::string &name, + const std::unique_ptr &ti, + const Type_Conversions_State &t_conversions) const noexcept { + if (!bvs.empty()) { + return dynamic_object_typename_match(bvs[0], name, ti, t_conversions); + } else { + return false; + } +} + +Dynamic_Object_Constructor::Dynamic_Object_Constructor( + std::string t_type_name, const Proxy_Function &t_func) + : Proxy_Function_Base(build_type_list(t_func->get_param_types()), + t_func->get_arity() - 1), + m_type_name(std::move(t_type_name)), + m_func(t_func) { + assert((t_func->get_arity() > 0 || t_func->get_arity() < 0) && + "Programming error, Dynamic_Object_Function must have at least " + "one parameter (this)"); +} + +std::vector Dynamic_Object_Constructor::build_type_list( + const std::vector &tl) { + auto begin = tl.begin(); + auto end = tl.end(); + + if (begin != end) { + ++begin; + } + + return std::vector(begin, end); +} + +bool Dynamic_Object_Constructor::operator==( + const Proxy_Function_Base &f) const noexcept { + const Dynamic_Object_Constructor *dc = + dynamic_cast(&f); + return (dc != nullptr) && dc->m_type_name == m_type_name && + (*dc->m_func) == (*m_func); +} + +bool Dynamic_Object_Constructor::call_match( + const Carbon::Function_Params &vals, + const Type_Conversions_State &t_conversions) const { + std::vector new_vals{Boxed_Value(Dynamic_Object(m_type_name))}; + new_vals.insert(new_vals.end(), vals.begin(), vals.end()); + + return m_func->call_match(Carbon::Function_Params{new_vals}, t_conversions); +} + +Boxed_Value Dynamic_Object_Constructor::do_call( + const Carbon::Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const { + auto bv = Boxed_Value(Dynamic_Object(m_type_name), true); + std::vector new_params{bv}; + new_params.insert(new_params.end(), params.begin(), params.end()); + + (*m_func)(Carbon::Function_Params{new_params}, t_conversions); + + return bv; +} + +} // namespace detail +} // namespace dispatch +} // namespace Carbon diff --git a/src/carbon/command/dynamic_object_detail.hpp b/src/carbon/command/dynamic_object_detail.hpp new file mode 100644 index 00000000..7d681bf3 --- /dev/null +++ b/src/carbon/command/dynamic_object_detail.hpp @@ -0,0 +1,124 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CARBON_DYNAMIC_OBJECT_DETAIL_HPP +#define CARBON_DYNAMIC_OBJECT_DETAIL_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include "../defines.hpp" +#include "boxed_cast.hpp" +#include "boxed_cast_helper.hpp" +#include "boxed_value.hpp" +#include "dynamic_object.hpp" +#include "proxy_functions.hpp" +#include "atom/experiment/type_info.hpp" + +namespace Carbon { +class Type_Conversions; +namespace dispatch { +class Proxy_Function_Base; +} // namespace dispatch +} // namespace Carbon + +namespace Carbon { +namespace dispatch { +namespace detail { +/// A Proxy_Function implementation designed for calling a function +/// that is automatically guarded based on the first param based on the +/// param's type name +class Dynamic_Object_Function final : public Proxy_Function_Base { +public: + Dynamic_Object_Function(std::string t_type_name, + const Proxy_Function &t_func, + bool t_is_attribute = false); + + Dynamic_Object_Function(std::string t_type_name, + const Proxy_Function &t_func, const Type_Info &t_ti, + bool t_is_attribute = false); + + Dynamic_Object_Function &operator=(const Dynamic_Object_Function) = delete; + Dynamic_Object_Function(Dynamic_Object_Function &) = delete; + + bool operator==(const Proxy_Function_Base &f) const noexcept override; + + bool is_attribute_function() const noexcept override; + + bool call_match( + const Carbon::Function_Params &vals, + const Type_Conversions_State &t_conversions) const noexcept override; + + std::vector get_contained_functions() const override; + +protected: + Boxed_Value do_call( + const Carbon::Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const override; + + bool compare_first_type( + const Boxed_Value &bv, + const Type_Conversions_State &t_conversions) const noexcept override; + +private: + static std::vector build_param_types( + const std::vector &t_inner_types, + const Type_Info &t_objectti); + + bool dynamic_object_typename_match( + const Boxed_Value &bv, const std::string &name, + const std::unique_ptr &ti, + const Type_Conversions_State &t_conversions) const noexcept; + + bool dynamic_object_typename_match( + const Carbon::Function_Params &bvs, const std::string &name, + const std::unique_ptr &ti, + const Type_Conversions_State &t_conversions) const noexcept; + + std::string m_type_name; + Proxy_Function m_func; + std::unique_ptr m_ti; + const Type_Info m_doti; + const bool m_is_attribute; +}; + +/** + * A Proxy_Function implementation designed for creating a new + * Dynamic_Object + * that is automatically guarded based on the first param based on the + * param's type name + */ +class Dynamic_Object_Constructor final : public Proxy_Function_Base { +public: + Dynamic_Object_Constructor(std::string t_type_name, + const Proxy_Function &t_func); + + static std::vector build_type_list( + const std::vector &tl); + + bool operator==(const Proxy_Function_Base &f) const noexcept override; + + bool call_match(const Carbon::Function_Params &vals, + const Type_Conversions_State &t_conversions) const override; + +protected: + Boxed_Value do_call( + const Carbon::Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const override; + +private: + const std::string m_type_name; + const Proxy_Function m_func; +}; +} // namespace detail +} // namespace dispatch +} // namespace Carbon +#endif diff --git a/src/carbon/command/exception_specification.hpp b/src/carbon/command/exception_specification.hpp new file mode 100644 index 00000000..08f3bb2f --- /dev/null +++ b/src/carbon/command/exception_specification.hpp @@ -0,0 +1,108 @@ + + +#ifndef CARBON_EXCEPTION_SPECIFICATION_HPP +#define CARBON_EXCEPTION_SPECIFICATION_HPP + +#include + +#include "../defines.hpp" +#include "boxed_cast.hpp" +#include "dispatchkit.hpp" + +namespace Carbon { +namespace detail { +struct Exception_Handler_Base { + virtual void handle(const Boxed_Value &bv, + const Dispatch_Engine &t_engine) = 0; + + virtual ~Exception_Handler_Base() = default; + +protected: + template + static void throw_type(const Boxed_Value &bv, + const Dispatch_Engine &t_engine) { + try { + T t = t_engine.boxed_cast(bv); + throw t; + } catch (const Carbon::exception::bad_boxed_cast &) { + } + } +}; + +template +struct Exception_Handler_Impl : Exception_Handler_Base { + void handle(const Boxed_Value &bv, + const Dispatch_Engine &t_engine) override { + (throw_type(bv, t_engine), ...); + } +}; +} // namespace detail + +/// \brief Used in the automatic unboxing of exceptions thrown during script +/// evaluation +/// +/// Exception specifications allow the user to tell ChaiScript what possible +/// exceptions are expected from the script being executed. Exception_Handler +/// objects are created with the Carbon::exception_specification() function. +/// +/// Example: +/// \code +/// Carbon::ChaiScript chai; +/// +/// try { +/// chai.eval("throw(runtime_error(\"error\"))", +/// Carbon::exception_specification()); +/// } catch (const double e) { +/// } catch (int) { +/// } catch (float) { +/// } catch (const std::string &) { +/// } catch (const std::exception &e) { +/// // This is the one what will be called in the specific throw() above +/// } +/// \endcode +/// +/// It is recommended that if catching the generic \c std::exception& type that +/// you specifically catch the Carbon::exception::eval_error type, so that there +/// is no confusion. +/// +/// \code +/// try { +/// chai.eval("throw(runtime_error(\"error\"))", +/// Carbon::exception_specification()); +/// } catch (const Carbon::exception::eval_error &) { +/// // Error in script parsing / execution +/// } catch (const std::exception &e) { +/// // Error explicitly thrown from script +/// } +/// \endcode +/// +/// Similarly, if you are using the Carbon::eval form that unboxes the return +/// value, then Carbon::exception::bad_boxed_cast should be handled as well. +/// +/// \code +/// try { +/// chai.eval("1.0", Carbon::exception_specification()); +/// } catch (const Carbon::exception::eval_error &) { +/// // Error in script parsing / execution +/// } catch (const Carbon::exception::bad_boxed_cast &) { +/// // Error unboxing return value +/// } catch (const std::exception &e) { +/// // Error explicitly thrown from script +/// } +/// \endcode +/// +/// \sa Carbon::exception_specification for creation of +/// Carbon::Exception_Handler objects \sa \ref exceptions +using Exception_Handler = std::shared_ptr; + +/// \brief creates a Carbon::Exception_Handler which handles one type of +/// exception unboxing \sa \ref exceptions +template +Exception_Handler exception_specification() { + return std::make_shared>(); +} +} // namespace Carbon + +#endif diff --git a/src/carbon/command/function_call.hpp b/src/carbon/command/function_call.hpp new file mode 100644 index 00000000..fc9b3528 --- /dev/null +++ b/src/carbon/command/function_call.hpp @@ -0,0 +1,131 @@ + + +#ifndef CARBON_FUNCTION_CALL_HPP +#define CARBON_FUNCTION_CALL_HPP + +#include +#include +#include + +#include "boxed_cast.hpp" +#include "function_call_detail.hpp" +#include "proxy_functions.hpp" + +namespace Carbon { +class Boxed_Value; +class Type_Conversions_State; +namespace detail { +template +struct Cast_Helper; +} // namespace detail +} // namespace Carbon + +namespace Carbon { +namespace dispatch { +namespace detail { +template +constexpr auto arity(Ret (*)(Param...)) noexcept { + return sizeof...(Param); +} +} // namespace detail + +/// Build a function caller that knows how to dispatch on a set of functions +/// example: +/// std::function f = +/// build_function_caller(dispatchkit.get_function("print")); +/// \returns A std::function object for dispatching +/// \param[in] funcs the set of functions to dispatch on. +template +std::function functor( + const std::vector &funcs, + const Type_Conversions_State *t_conversions) { + const bool has_arity_match = std::any_of( + funcs.begin(), funcs.end(), [](const Const_Proxy_Function &f) { + return f->get_arity() == -1 || + size_t(f->get_arity()) == + detail::arity(static_cast(nullptr)); + }); + + if (!has_arity_match) { + throw exception::bad_boxed_cast(user_type(), + typeid(std::function)); + } + + FunctionType *p = nullptr; + return detail::build_function_caller_helper(p, funcs, t_conversions); +} + +/// Build a function caller for a particular Proxy_Function object +/// useful in the case that a function is being pass out from scripting back +/// into code +/// example: +/// void my_function(Proxy_Function f) +/// { +/// std::function local_f = +/// build_function_caller(f); +/// } +/// \returns A std::function object for dispatching +/// \param[in] func A function to execute. +template +std::function functor( + Const_Proxy_Function func, const Type_Conversions_State *t_conversions) { + return functor( + std::vector({std::move(func)}), t_conversions); +} + +/// Helper for automatically unboxing a Boxed_Value that contains a function +/// object and creating a typesafe C++ function caller from it. +template +std::function functor( + const Boxed_Value &bv, const Type_Conversions_State *t_conversions) { + return functor( + boxed_cast(bv, t_conversions), t_conversions); +} +} // namespace dispatch + +namespace detail { +/// Cast helper to handle automatic casting to const std::function & +template +struct Cast_Helper &> { + static std::function cast( + const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { + if (ob.get_type_info().bare_equal(user_type())) { + return dispatch::functor(ob, t_conversions); + } else { + return Cast_Helper_Inner &>::cast( + ob, t_conversions); + } + } +}; + +/// Cast helper to handle automatic casting to std::function +template +struct Cast_Helper> { + static std::function cast( + const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { + if (ob.get_type_info().bare_equal(user_type())) { + return dispatch::functor(ob, t_conversions); + } else { + return Cast_Helper_Inner>::cast( + ob, t_conversions); + } + } +}; + +/// Cast helper to handle automatic casting to const std::function +template +struct Cast_Helper> { + static std::function cast( + const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { + if (ob.get_type_info().bare_equal(user_type())) { + return dispatch::functor(ob, t_conversions); + } else { + return Cast_Helper_Inner>::cast( + ob, t_conversions); + } + } +}; +} // namespace detail +} // namespace Carbon + +#endif diff --git a/src/carbon/command/function_call_detail.hpp b/src/carbon/command/function_call_detail.hpp new file mode 100644 index 00000000..ef8c4d08 --- /dev/null +++ b/src/carbon/command/function_call_detail.hpp @@ -0,0 +1,101 @@ + + +#ifndef CARBON_FUNCTION_CALL_DETAIL_HPP +#define CARBON_FUNCTION_CALL_DETAIL_HPP + +#include +#include +#include +#include +#include + +#include "boxed_cast.hpp" +#include "boxed_number.hpp" +#include "boxed_value.hpp" +#include "proxy_functions.hpp" +#include "type_conversions.hpp" + +namespace Carbon::dispatch::detail { +/// used internally for unwrapping a function call's types +template +struct Build_Function_Caller_Helper { + Build_Function_Caller_Helper(std::vector t_funcs, + const Type_Conversions *t_conversions) + : m_funcs(std::move(t_funcs)), m_conversions(t_conversions) {} + + Ret call(const Carbon::Function_Params ¶ms, + const Type_Conversions_State &t_state) { + if constexpr (std::is_arithmetic_v && + !std::is_same_v< + std::remove_cv_t>, + bool>) { + return Boxed_Number(dispatch::dispatch(m_funcs, params, t_state)) + .get_as(); + } else if constexpr (std::is_same_v) { + dispatch::dispatch(m_funcs, params, t_state); + } else { + return boxed_cast(dispatch::dispatch(m_funcs, params, t_state), + &t_state); + } + } + + template + Ret operator()(P &&...param) { + std::array params{ + box

(std::forward

(param))...}; + + if (m_conversions) { + Type_Conversions_State state(*m_conversions, + m_conversions->conversion_saves()); + return call(Carbon::Function_Params{params}, state); + } else { + Type_Conversions conv; + Type_Conversions_State state(conv, conv.conversion_saves()); + return call(Carbon::Function_Params{params}, state); + } + } + + template + static Boxed_Value box(Q &&q) { + if constexpr (std::is_same_v>) { + return std::forward(q); + } else if constexpr (std::is_reference_v

) { + return Boxed_Value(std::ref(std::forward(q))); + } else { + return Boxed_Value(std::forward(q)); + } + } + + std::vector m_funcs; + const Type_Conversions *m_conversions; +}; + +/// \todo what happens if t_conversions is deleted out from under us?! +template +std::function build_function_caller_helper( + Ret(Params...), const std::vector &funcs, + const Type_Conversions_State *t_conversions) { + /* + if (funcs.size() == 1) + { + std::shared_ptr> pfi = + std::dynamic_pointer_cast > + (funcs[0]); + + if (pfi) + { + return pfi->internal_function(); + } + // looks like this either wasn't a Proxy_Function_Impl or the types didn't + match + // we cannot make any other guesses or assumptions really, so continuing + } + */ + + return std::function( + Build_Function_Caller_Helper( + funcs, t_conversions ? t_conversions->get() : nullptr)); +} +} // namespace Carbon::dispatch::detail + +#endif diff --git a/src/carbon/command/function_params.hpp b/src/carbon/command/function_params.hpp new file mode 100644 index 00000000..3e004afb --- /dev/null +++ b/src/carbon/command/function_params.hpp @@ -0,0 +1,68 @@ + + +#ifndef CARBON_FUNCTION_PARAMS_HPP +#define CARBON_FUNCTION_PARAMS_HPP + +#include "boxed_value.hpp" + +namespace Carbon { +class Function_Params { +public: + constexpr Function_Params(const Boxed_Value *const t_begin, + const Boxed_Value *const t_end) + : m_begin(t_begin), m_end(t_end) {} + + explicit Function_Params(const Boxed_Value &bv) + : m_begin(&bv), m_end(m_begin + 1) {} + + explicit Function_Params(const std::vector &vec) + : m_begin(vec.empty() ? nullptr : &vec.front()), + m_end(vec.empty() ? nullptr : &vec.front() + vec.size()) {} + + template + constexpr explicit Function_Params(const std::array &a) + : m_begin(&a.front()), m_end(&a.front() + Size) {} + + [[nodiscard]] constexpr const Boxed_Value &operator[]( + const std::size_t t_i) const noexcept { + return m_begin[t_i]; + } + + [[nodiscard]] constexpr const Boxed_Value *begin() const noexcept { + return m_begin; + } + + [[nodiscard]] constexpr const Boxed_Value &front() const noexcept { + return *m_begin; + } + + [[nodiscard]] constexpr const Boxed_Value *end() const noexcept { + return m_end; + } + + [[nodiscard]] constexpr std::size_t size() const noexcept { + return std::size_t(m_end - m_begin); + } + + [[nodiscard]] std::vector to_vector() const { + return std::vector{m_begin, m_end}; + } + + [[nodiscard]] constexpr bool empty() const noexcept { + return m_begin == m_end; + } + +private: + const Boxed_Value *m_begin = nullptr; + const Boxed_Value *m_end = nullptr; +}; + +// Constructor specialization for array of size 0 +template <> +constexpr Function_Params::Function_Params( + const std::array & /* a */) + : m_begin(nullptr), m_end(nullptr) {} + +} // namespace Carbon + +#endif diff --git a/src/carbon/command/function_signature.hpp b/src/carbon/command/function_signature.hpp new file mode 100644 index 00000000..cfbc55aa --- /dev/null +++ b/src/carbon/command/function_signature.hpp @@ -0,0 +1,186 @@ +#ifndef CARBON_FUNCTION_SIGNATURE_HPP +#define CARBON_FUNCTION_SIGNATURE_HPP + +#include + +namespace Carbon::dispatch::detail { +template +struct Function_Params {}; + +template +struct Function_Signature { + using Param_Types = Params; + using Return_Type = Ret; + constexpr static const bool is_object = IsObject; + constexpr static const bool is_member_object = IsMemberObject; + constexpr static const bool is_noexcept = IsNoExcept; + template + constexpr Function_Signature(T &&) noexcept {} + constexpr Function_Signature() noexcept = default; +}; + +// Free functions + +template +Function_Signature(Ret (*f)(Param...)) + -> Function_Signature>; + +template +Function_Signature(Ret (*f)(Param...) noexcept) + -> Function_Signature, true>; + +// no reference specifier + +template +Function_Signature(Ret (Class::*f)(Param...) volatile) + -> Function_Signature, + false, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) volatile noexcept) + -> Function_Signature, + true, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) volatile const) + -> Function_Signature< + Ret, Function_Params, false, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) volatile const noexcept) + -> Function_Signature< + Ret, Function_Params, true, true>; + +template +Function_Signature(Ret (Class::*f)(Param...)) + -> Function_Signature, false, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) noexcept) + -> Function_Signature, true, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) const) + -> Function_Signature, false, + true>; + +template +Function_Signature(Ret (Class::*f)(Param...) const noexcept) + -> Function_Signature, true, + true>; + +// & reference specifier + +template +Function_Signature(Ret (Class::*f)(Param...) volatile &) + -> Function_Signature, + false, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) volatile & noexcept) + -> Function_Signature, + true, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) volatile const &) + -> Function_Signature< + Ret, Function_Params, false, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) volatile const & noexcept) + -> Function_Signature< + Ret, Function_Params, true, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) &) + -> Function_Signature, false, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) & noexcept) + -> Function_Signature, true, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) const &) + -> Function_Signature, false, + true>; + +template +Function_Signature(Ret (Class::*f)(Param...) const & noexcept) + -> Function_Signature, true, + true>; + +// && reference specifier + +template +Function_Signature(Ret (Class::*f)(Param...) volatile &&) + -> Function_Signature, + false, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) volatile && noexcept) + -> Function_Signature, + true, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) volatile const &&) + -> Function_Signature< + Ret, Function_Params, false, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) volatile const && noexcept) + -> Function_Signature< + Ret, Function_Params, true, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) &&) + -> Function_Signature, false, + true>; + +template +Function_Signature(Ret (Class::*f)(Param...) && noexcept) + -> Function_Signature, true, true>; + +template +Function_Signature(Ret (Class::*f)(Param...) const &&) + -> Function_Signature, false, + true>; + +template +Function_Signature(Ret (Class::*f)(Param...) const && noexcept) + -> Function_Signature, true, + true>; + +template +Function_Signature(Ret Class::*f) + -> Function_Signature, true, true, true>; + +// primary template handles types that have no nested ::type member: +template > +struct has_call_operator : std::false_type {}; + +// specialization recognizes types that do have a nested ::type member: +template +struct has_call_operator> + : std::true_type {}; + +template +auto function_signature(const Func &f) { + if constexpr (has_call_operator::value) { + return Function_Signature< + typename decltype(Function_Signature{ + &std::decay_t::operator()})::Return_Type, + typename decltype(Function_Signature{ + &std::decay_t::operator()})::Param_Types, + decltype(Function_Signature{ + &std::decay_t::operator()})::is_noexcept, + false, false, true>{}; + } else { + return Function_Signature{f}; + } +} + +} // namespace Carbon::dispatch::detail + +#endif diff --git a/src/carbon/command/handle_return.hpp b/src/carbon/command/handle_return.hpp new file mode 100644 index 00000000..e6eb93a6 --- /dev/null +++ b/src/carbon/command/handle_return.hpp @@ -0,0 +1,201 @@ + +#ifndef CARBON_HANDLE_RETURN_HPP +#define CARBON_HANDLE_RETURN_HPP + +#include +#include +#include + +#include "boxed_number.hpp" +#include "boxed_value.hpp" + +namespace Carbon { +class Boxed_Number; +} // namespace Carbon + +namespace Carbon { +namespace dispatch { +template +class Proxy_Function_Callable_Impl; +template +class Assignable_Proxy_Function_Impl; + +namespace detail { +/// Used internally for handling a return value from a Proxy_Function call +template +struct Handle_Return { + template >>> + static Boxed_Value handle(T r) { + return Boxed_Value(std::move(r), true); + } + + template >)>> + static Boxed_Value handle(T &&r) { + return Boxed_Value(std::make_shared(std::forward(r)), true); + } +}; + +template +struct Handle_Return &> { + static Boxed_Value handle(const std::function &f) { + return Boxed_Value( + Carbon::make_shared>>(f)); + } +}; + +template +struct Handle_Return> + : Handle_Return &> {}; + +template +struct Handle_Return>> { + static Boxed_Value handle(const std::shared_ptr> &f) { + return Boxed_Value( + Carbon::make_shared>( + std::ref(*f), f)); + } +}; + +template +struct Handle_Return> &> + : Handle_Return>> {}; + +template +struct Handle_Return>> + : Handle_Return>> {}; + +template +struct Handle_Return &> { + static Boxed_Value handle(std::function &f) { + return Boxed_Value( + Carbon::make_shared>( + std::ref(f), std::shared_ptr>())); + } + + static Boxed_Value handle(const std::function &f) { + return Boxed_Value( + Carbon::make_shared>>(f)); + } +}; + +template +struct Handle_Return { + static Boxed_Value handle(Ret *p) { return Boxed_Value(p, true); } +}; + +template +struct Handle_Return { + static Boxed_Value handle(const Ret *p) { return Boxed_Value(p, true); } +}; + +template +struct Handle_Return { + static Boxed_Value handle(Ret *p) { return Boxed_Value(p, true); } +}; + +template +struct Handle_Return { + static Boxed_Value handle(const Ret *p) { return Boxed_Value(p, true); } +}; + +template +struct Handle_Return &> { + static Boxed_Value handle(const std::shared_ptr &r) { + return Boxed_Value(r, true); + } +}; + +template +struct Handle_Return> + : Handle_Return &> {}; + +template +struct Handle_Return &> + : Handle_Return &> {}; + +template +struct Handle_Return> + : Handle_Return &> { + static Boxed_Value handle(std::unique_ptr &&r) { + return Boxed_Value(std::move(r), true); + } +}; + +template +struct Handle_Return_Ref { + template + static Boxed_Value handle(T &&r) { + return Boxed_Value(std::cref(r), true); + } +}; + +template +struct Handle_Return_Ref { + template + static Boxed_Value handle(T &&r) { + return Boxed_Value(typename std::remove_reference::type{r}, + true); + } +}; + +template +struct Handle_Return + : Handle_Return_Ref::type>::value> {}; + +template +struct Handle_Return { + static Boxed_Value handle(Ret r) { return Boxed_Value(std::move(r)); } +}; + +template +struct Handle_Return { + static Boxed_Value handle(Ret &r) { return Boxed_Value(std::ref(r)); } +}; + +template <> +struct Handle_Return { + static Boxed_Value handle(const Boxed_Value &r) noexcept { return r; } +}; + +template <> +struct Handle_Return : Handle_Return {}; + +template <> +struct Handle_Return : Handle_Return {}; + +template <> +struct Handle_Return : Handle_Return {}; + +/** + * Used internally for handling a return value from a Proxy_Function call + */ +template <> +struct Handle_Return { + static Boxed_Value handle(const Boxed_Number &r) noexcept { return r.bv; } +}; + +template <> +struct Handle_Return : Handle_Return {}; + +/** + * Used internally for handling a return value from a Proxy_Function call + */ +template <> +struct Handle_Return { + static Boxed_Value handle() { return void_var(); } +}; +} // namespace detail +} // namespace dispatch +} // namespace Carbon + +#endif diff --git a/src/carbon/command/operators.hpp b/src/carbon/command/operators.hpp new file mode 100644 index 00000000..4b8c94f0 --- /dev/null +++ b/src/carbon/command/operators.hpp @@ -0,0 +1,211 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#ifndef CARBON_OPERATORS_HPP +#define CARBON_OPERATORS_HPP + +#include "../defines.hpp" +#include "dispatchkit.hpp" +#include "register_function.hpp" + +namespace Carbon::bootstrap::operators { +template +void assign(Module &m) { + m.add(Carbon::fun([](T &lhs, const T &rhs) -> T & { return lhs = rhs; }), + "="); +} + +template +void assign_bitwise_and(Module &m) { + m.add(Carbon::fun([](T &lhs, const T &rhs) -> T & { return lhs &= rhs; }), + "&="); +} + +template +void assign_xor(Module &m) { + m.add(Carbon::fun([](T &lhs, const T &rhs) -> T & { return lhs ^= rhs; }), + "^="); +} + +template +void assign_bitwise_or(Module &m) { + m.add(Carbon::fun([](T &lhs, const T &rhs) -> T & { return lhs |= rhs; }), + "|="); +} + +template +void assign_difference(Module &m) { + m.add(Carbon::fun([](T &lhs, const T &rhs) -> T & { return lhs -= rhs; }), + "-="); +} + +template +void assign_left_shift(Module &m) { + m.add(Carbon::fun([](T &lhs, const T &rhs) -> T & { return lhs <<= rhs; }), + "<<="); +} + +template +void assign_product(Module &m) { + m.add(Carbon::fun([](T &lhs, const T &rhs) -> T & { return lhs <<= rhs; }), + "*="); +} + +template +void assign_quotient(Module &m) { + m.add(Carbon::fun([](T &lhs, const T &rhs) -> T & { return lhs /= rhs; }), + "/="); +} + +template +void assign_remainder(Module &m) { + m.add(Carbon::fun([](T &lhs, const T &rhs) -> T & { return lhs %= rhs; }), + "%="); +} + +template +void assign_right_shift(Module &m) { + m.add(Carbon::fun([](T &lhs, const T &rhs) -> T & { return lhs >>= rhs; }), + ">>="); +} + +template +void assign_sum(Module &m) { + m.add(Carbon::fun([](T &lhs, const T &rhs) -> T & { return lhs += rhs; }), + "+="); +} + +template +void prefix_decrement(Module &m) { + m.add(Carbon::fun([](T &lhs) -> T & { return --lhs; }), "--"); +} + +template +void prefix_increment(Module &m) { + m.add(Carbon::fun([](T &lhs) -> T & { return ++lhs; }), "++"); +} + +template +void equal(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs == rhs; }), + "=="); +} + +template +void greater_than(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs > rhs; }), + ">"); +} + +template +void greater_than_equal(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs >= rhs; }), + ">="); +} + +template +void less_than(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs < rhs; }), + "<"); +} + +template +void less_than_equal(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs <= rhs; }), + "<="); +} + +template +void logical_compliment(Module &m) { + m.add(Carbon::fun([](const T &lhs) { return !lhs; }), "!"); +} + +template +void not_equal(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs != rhs; }), + "!="); +} + +template +void addition(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs + rhs; }), + "+"); +} + +template +void unary_plus(Module &m) { + m.add(Carbon::fun([](const T &lhs) { return +lhs; }), "+"); +} + +template +void subtraction(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs - rhs; }), + "-"); +} + +template +void unary_minus(Module &m) { + m.add(Carbon::fun([](const T &lhs) { return -lhs; }), "-"); +} + +template +void bitwise_and(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs & rhs; }), + "&"); +} + +template +void bitwise_compliment(Module &m) { + m.add(Carbon::fun([](const T &lhs) { return ~lhs; }), "~"); +} + +template +void bitwise_xor(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs ^ rhs; }), + "^"); +} + +template +void bitwise_or(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs | rhs; }), + "|"); +} + +template +void division(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs / rhs; }), + "/"); +} + +template +void left_shift(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs << rhs; }), + "<<"); +} + +template +void multiplication(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs * rhs; }), + "*"); +} + +template +void remainder(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs % rhs; }), + "%"); +} + +template +void right_shift(Module &m) { + m.add(Carbon::fun([](const T &lhs, const T &rhs) { return lhs >> rhs; }), + ">>"); +} +} // namespace Carbon::bootstrap::operators + +#endif diff --git a/src/carbon/command/proxy_constructors.hpp b/src/carbon/command/proxy_constructors.hpp new file mode 100644 index 00000000..82274dec --- /dev/null +++ b/src/carbon/command/proxy_constructors.hpp @@ -0,0 +1,57 @@ + + +#ifndef CARBON_PROXY_CONSTRUCTORS_HPP +#define CARBON_PROXY_CONSTRUCTORS_HPP + +#include "proxy_functions.hpp" + +namespace Carbon::dispatch::detail { +template +Proxy_Function build_constructor_(Class (*)(Params...)) { + if constexpr (!std::is_copy_constructible_v) { + auto call = [](auto &&...param) { + return std::make_shared( + std::forward(param)...); + }; + + return Proxy_Function( + Carbon::make_shared< + dispatch::Proxy_Function_Base, + dispatch::Proxy_Function_Callable_Impl< + std::shared_ptr(Params...), decltype(call)>>(call)); + } else if constexpr (true) { + auto call = [](auto &&...param) { + return Class(std::forward(param)...); + }; + + return Proxy_Function( + Carbon::make_shared>(call)); + } +} +} // namespace Carbon::dispatch::detail + +namespace Carbon { +/// \brief Generates a constructor function for use with ChaiScript +/// +/// \tparam T The signature of the constructor to generate. In the form of: +/// ClassType (ParamType1, ParamType2, ...) +/// +/// Example: +/// \code +/// Carbon::ChaiScript chai; +/// // Create a new function that creates a MyClass object using the (int, +/// float) constructor +/// // and call that function "MyClass" so that it appears as a normal +/// constructor to the user. chai.add(constructor(), +/// "MyClass"); +/// \endcode +template +Proxy_Function constructor() { + T *f = nullptr; + return (dispatch::detail::build_constructor_(f)); +} +} // namespace Carbon + +#endif diff --git a/src/carbon/command/proxy_functions.cpp b/src/carbon/command/proxy_functions.cpp new file mode 100644 index 00000000..e33a61f4 --- /dev/null +++ b/src/carbon/command/proxy_functions.cpp @@ -0,0 +1,475 @@ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "proxy_functions.hpp" + +#include "../defines.hpp" +#include "atom/experiment/type_info.hpp" +#include "boxed_cast.hpp" +#include "boxed_value.hpp" +#include "dynamic_object.hpp" +#include "function_params.hpp" +#include "proxy_functions_detail.hpp" + +namespace Carbon { + +namespace dispatch { + +Param_Types::Param_Types() : m_has_types(false) {} + +Param_Types::Param_Types(std::vector> t_types) + : m_types(std::move(t_types)), m_has_types(false) { + update_has_types(); +} + +void Param_Types::push_front(const std::string &t_name, Type_Info t_ti) { + m_types.emplace(m_types.begin(), std::move(t_name), t_ti); + update_has_types(); +} + +bool Param_Types::operator==(const Param_Types &t_rhs) const noexcept { + return m_types == t_rhs.m_types; +} + +std::vector Param_Types::convert( + Function_Params t_params, + const Type_Conversions_State &t_conversions) const { + auto vals = t_params.to_vector(); + const auto dynamic_object_type_info = user_type(); + for (size_t i = 0; i < vals.size(); ++i) { + const auto &name = m_types[i].first; + if (!name.empty()) { + const auto &bv = vals[i]; + + if (!bv.get_type_info().bare_equal(dynamic_object_type_info)) { + const auto &ti = m_types[i].second; + if (!ti.is_undef()) { + if (!bv.get_type_info().bare_equal(ti)) { + if (t_conversions->converts(ti, bv.get_type_info())) { + try { + // We will not catch any + // bad_boxed_dynamic_cast that is thrown, + // let the user get it either way, we are + // not responsible if it doesn't work + vals[i] = t_conversions->boxed_type_conversion( + m_types[i].second, t_conversions.saves(), + vals[i]); + } catch (...) { + try { + // try going the other way + vals[i] = + t_conversions + ->boxed_type_down_conversion( + m_types[i].second, + t_conversions.saves(), vals[i]); + } catch (const Carbon::detail::exception:: + bad_any_cast &) { + throw exception::bad_boxed_cast( + bv.get_type_info(), + *m_types[i].second.bare_type_info()); + } + } + } + } + } + } + } + } + + return vals; +} + +// first result: is a match +// second result: needs conversions +std::pair Param_Types::match( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const noexcept { + const auto dynamic_object_type_info = user_type(); + bool needs_conversion = false; + + if (!m_has_types) { + return std::make_pair(true, needs_conversion); + } + if (vals.size() != m_types.size()) { + return std::make_pair(false, needs_conversion); + } + + for (size_t i = 0; i < vals.size(); ++i) { + const auto &name = m_types[i].first; + if (!name.empty()) { + const auto &bv = vals[i]; + + if (bv.get_type_info().bare_equal(dynamic_object_type_info)) { + try { + const Dynamic_Object &d = + boxed_cast(bv, &t_conversions); + if (!(name == "Dynamic_Object" || + d.get_type_name() == name)) { + return std::make_pair(false, false); + } + } catch (const std::bad_cast &) { + return std::make_pair(false, false); + } + } else { + const auto &ti = m_types[i].second; + if (!ti.is_undef()) { + if (!bv.get_type_info().bare_equal(ti)) { + if (!t_conversions->converts(ti, bv.get_type_info())) { + return std::make_pair(false, false); + } else { + needs_conversion = true; + } + } + } else { + return std::make_pair(false, false); + } + } + } + } + + return std::make_pair(true, needs_conversion); +} + +const std::vector> &Param_Types::types() + const noexcept { + return m_types; +} + +void Param_Types::update_has_types() { + for (const auto &type : m_types) { + if (!type.first.empty()) { + m_has_types = true; + return; + } + } + + m_has_types = false; +} + +Boxed_Value Proxy_Function_Base::operator()( + const Function_Params ¶ms, + const Carbon::Type_Conversions_State &t_conversions) const { + if (m_arity < 0 || size_t(m_arity) == params.size()) { + return do_call(params, t_conversions); + } else { + throw exception::arity_error(static_cast(params.size()), m_arity); + } +} + +/// Returns a vector containing all of the types of the parameters the +/// function returns/takes if the function is variadic or takes no arguments +/// (arity of 0 or -1), the returned value contains exactly 1 Type_Info +/// object: the return type \returns the types of all parameters. +const std::vector &Proxy_Function_Base::get_param_types() + const noexcept { + return m_types; +} + +bool Proxy_Function_Base::is_attribute_function() const noexcept { + return false; +} + +bool Proxy_Function_Base::has_arithmetic_param() const noexcept { + return m_has_arithmetic_param; +} + +std::vector> +Proxy_Function_Base::get_contained_functions() const { + return std::vector>(); +} + +//! Return true if the function is a possible match +//! to the passed in values +bool Proxy_Function_Base::filter( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const noexcept { + assert(m_arity == -1 || + (m_arity > 0 && static_cast(vals.size()) == m_arity)); + + if (m_arity < 0) { + return true; + } else if (m_arity > 1) { + return compare_type_to_param(m_types[1], vals[0], t_conversions) && + compare_type_to_param(m_types[2], vals[1], t_conversions); + } else { + return compare_type_to_param(m_types[1], vals[0], t_conversions); + } +} + +/// \returns the number of arguments the function takes or -1 if it is +/// variadic +int Proxy_Function_Base::get_arity() const noexcept { return m_arity; } + +bool Proxy_Function_Base::compare_type_to_param( + const Type_Info &ti, const Boxed_Value &bv, + const Type_Conversions_State &t_conversions) noexcept { + const auto boxed_value_ti = user_type(); + const auto boxed_number_ti = user_type(); + const auto function_ti = + user_type>(); + + if (ti.is_undef() || ti.bare_equal(boxed_value_ti) || + (!bv.get_type_info().is_undef() && + ((ti.bare_equal(boxed_number_ti) && + bv.get_type_info().is_arithmetic()) || + ti.bare_equal(bv.get_type_info()) || + bv.get_type_info().bare_equal(function_ti) || + t_conversions->converts(ti, bv.get_type_info())))) { + return true; + } else { + return false; + } +} + +bool Proxy_Function_Base::compare_first_type( + const Boxed_Value &bv, + const Type_Conversions_State &t_conversions) const noexcept { + /// TODO is m_types guaranteed to be at least 2?? + return compare_type_to_param(m_types[1], bv, t_conversions); +} + +Proxy_Function_Base::Proxy_Function_Base(std::vector t_types, + int t_arity) + : m_types(std::move(t_types)), + m_arity(t_arity), + m_has_arithmetic_param(false) { + for (size_t i = 1; i < m_types.size(); ++i) { + if (m_types[i].is_arithmetic()) { + m_has_arithmetic_param = true; + return; + } + } +} + +bool Proxy_Function_Base::compare_types( + const std::vector &tis, const Function_Params &bvs, + const Type_Conversions_State &t_conversions) noexcept { + if (tis.size() - 1 != bvs.size()) { + return false; + } else { + const size_t size = bvs.size(); + for (size_t i = 0; i < size; ++i) { + if (!compare_type_to_param(tis[i + 1], bvs[i], t_conversions)) { + return false; + } + } + } + return true; +} + +} // namespace dispatch + +namespace dispatch { +Dynamic_Proxy_Function::Dynamic_Proxy_Function( + const int t_arity, std::shared_ptr t_parsenode, + Param_Types t_param_types = Param_Types(), + Proxy_Function t_guard = Proxy_Function()) + : Proxy_Function_Base(build_param_type_list(t_param_types), t_arity), + m_param_types(std::move(t_param_types)), + m_guard(std::move(t_guard)), + m_parsenode(std::move(t_parsenode)) { + // assert(t_parsenode); +} + +bool Dynamic_Proxy_Function::operator==( + const Proxy_Function_Base &rhs) const noexcept { + const Dynamic_Proxy_Function *prhs = + dynamic_cast(&rhs); + + return this == &rhs || + ((prhs != nullptr) && this->m_arity == prhs->m_arity && + !this->m_guard && !prhs->m_guard && + this->m_param_types == prhs->m_param_types); +} + +bool Dynamic_Proxy_Function::call_match( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const { + return call_match_internal(vals, t_conversions).first; +} + +bool Dynamic_Proxy_Function::has_guard() const noexcept { + return bool(m_guard); +} + +Proxy_Function Dynamic_Proxy_Function::get_guard() const noexcept { + return m_guard; +} + +bool Dynamic_Proxy_Function::has_parse_tree() const noexcept { + return static_cast(m_parsenode); +} + +const AST_Node &Dynamic_Proxy_Function::get_parse_tree() const { + if (m_parsenode) { + return *m_parsenode; + } else { + throw std::runtime_error( + "Dynamic_Proxy_Function does not have parse_tree"); + } +} + +bool Dynamic_Proxy_Function::test_guard( + const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const { + if (m_guard) { + try { + return boxed_cast((*m_guard)(params, t_conversions)); + } catch (const exception::arity_error &) { + return false; + } catch (const exception::bad_boxed_cast &) { + return false; + } + } else { + return true; + } +} + +// first result: is a match +// second result: needs conversions +std::pair Dynamic_Proxy_Function::call_match_internal( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const { + const auto comparison_result = [&]() { + if (m_arity < 0) { + return std::make_pair(true, false); + } else if (vals.size() == size_t(m_arity)) { + return m_param_types.match(vals, t_conversions); + } else { + return std::make_pair(false, false); + } + }(); + + return std::make_pair( + comparison_result.first && test_guard(vals, t_conversions), + comparison_result.second); +} + +std::vector Dynamic_Proxy_Function::build_param_type_list( + const Param_Types &t_types) { + // For the return type + std::vector types{Get_Type_Info::get()}; + + for (const auto &t : t_types.types()) { + if (t.second.is_undef()) { + types.push_back(Get_Type_Info::get()); + } else { + types.push_back(t.second); + } + } + + return types; +} + +Bound_Function::Bound_Function(const Const_Proxy_Function &t_f, + const std::vector &t_args) + : Proxy_Function_Base( + build_param_type_info(t_f, t_args), + (t_f->get_arity() < 0 + ? -1 + : static_cast(build_param_type_info(t_f, t_args).size()) - + 1)), + m_f(t_f), + m_args(t_args) { + assert(m_f->get_arity() < 0 || + m_f->get_arity() == static_cast(m_args.size())); +} + +bool Bound_Function::operator==(const Proxy_Function_Base &t_f) const noexcept { + return &t_f == this; +} + +bool Bound_Function::call_match( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const { + return m_f->call_match(Function_Params(build_param_list(vals)), + t_conversions); +} + +std::vector Bound_Function::get_contained_functions() + const { + return std::vector{m_f}; +} + +std::vector Bound_Function::build_param_list( + const Function_Params ¶ms) const { + auto parg = params.begin(); + auto barg = m_args.begin(); + + std::vector args; + + while (!(parg == params.end() && barg == m_args.end())) { + while (barg != m_args.end() && + !(barg->get_type_info() == + Get_Type_Info::get())) { + args.push_back(*barg); + ++barg; + } + + if (parg != params.end()) { + args.push_back(*parg); + ++parg; + } + + if (barg != m_args.end() && + barg->get_type_info() == Get_Type_Info::get()) { + ++barg; + } + } + return args; +} + +std::vector Bound_Function::build_param_type_info( + const Const_Proxy_Function &t_f, const std::vector &t_args) { + assert(t_f->get_arity() < 0 || + t_f->get_arity() == static_cast(t_args.size())); + + if (t_f->get_arity() < 0) { + return std::vector(); + } + + const auto types = t_f->get_param_types(); + assert(types.size() == t_args.size() + 1); + + // this analysis warning is invalid in MSVC12 and doesn't exist in + // MSVC14 + std::vector retval{types[0]}; + + for (size_t i = 0; i < types.size() - 1; ++i) { + if (t_args[i].get_type_info() == + Get_Type_Info::get()) { + retval.push_back(types[i + 1]); + } + } + + return retval; +} + +Boxed_Value Bound_Function::do_call( + const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const { + return (*m_f)(Function_Params{build_param_list(params)}, t_conversions); +} +Proxy_Function_Impl_Base::Proxy_Function_Impl_Base( + const std::vector &t_types) + : Proxy_Function_Base(t_types, static_cast(t_types.size()) - 1) {} + +bool Proxy_Function_Impl_Base::call_match( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const noexcept { + return static_cast(vals.size()) == get_arity() && + (compare_types(m_types, vals, t_conversions) && + compare_types_with_cast(vals, t_conversions)); +} + +} // namespace dispatch + +} // namespace Carbon diff --git a/src/carbon/command/proxy_functions.hpp b/src/carbon/command/proxy_functions.hpp new file mode 100644 index 00000000..d3c5f2aa --- /dev/null +++ b/src/carbon/command/proxy_functions.hpp @@ -0,0 +1,901 @@ + +#ifndef CARBON_PROXY_FUNCTIONS_HPP +#define CARBON_PROXY_FUNCTIONS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../defines.hpp" +#include "atom/experiment/type_info.hpp" +#include "boxed_cast.hpp" +#include "boxed_value.hpp" +#include "dynamic_object.hpp" +#include "function_params.hpp" +#include "proxy_functions_detail.hpp" + +namespace Carbon { +class Type_Conversions; +namespace exception { +class bad_boxed_cast; +struct arity_error; +} // namespace exception +class Boxed_Number; +struct AST_Node; + +using AST_NodePtr = std::unique_ptr; + +namespace dispatch { +template +std::function functor( + std::shared_ptr func, + const Type_Conversions_State *t_conversions); + +/** + * @brief A class representing the parameter types of a function. + * + * This class stores information about the types of parameters in a function. + */ +class Param_Types { +public: + /** + * @brief Default constructor. + */ + Param_Types(); + + /** + * @brief Constructor with parameters. + * + * @param t_types A vector of pairs containing parameter names and type + * information. + */ + explicit Param_Types( + std::vector> t_types); + + /** + * @brief Pushes a new parameter type to the front of the list. + * + * @param t_name The name of the parameter. + * @param t_ti The type information of the parameter. + */ + void push_front(const std::string &t_name, Type_Info t_ti); + + /** + * @brief Checks if two Param_Types objects are equal. + * + * @param t_rhs The right-hand side Param_Types object. + * @return True if the objects are equal, false otherwise. + */ + bool operator==(const Param_Types &t_rhs) const noexcept; + + /** + * @brief Converts function parameters to boxed values using type + * conversions. + * + * @param t_params The function parameters. + * @param t_conversions The state of type conversions. + * @return A vector of boxed values representing the converted parameters. + */ + std::vector convert( + Function_Params t_params, + const Type_Conversions_State &t_conversions) const; + + /** + * @brief Checks if given values match the parameter types. + * + * @param vals The values to check. + * @param t_conversions The state of type conversions. + * @return A pair of booleans indicating if there is a match and if + * conversions are needed. + */ + std::pair match( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const noexcept; + + /** + * @brief Gets the vector of parameter types. + * + * @return A vector of pairs containing parameter names and type + * information. + */ + const std::vector> &types() + const noexcept; + +private: + /** + * @brief Updates the flag indicating whether the Param_Types object has + * types. + */ + void update_has_types(); + + std::vector> + m_types; ///< Vector of parameter names and type information. + bool m_has_types; ///< Flag indicating whether the Param_Types object has + ///< types. +}; + +/** + * @brief Pure virtual base class for all Proxy_Function implementations. + * + * Proxy_Functions are a type erasure of type safe C++ function calls. At + * runtime, parameter types are expected to be tested against passed in types. + * Dispatch_Engine only knows how to work with Proxy_Function, no other + * function classes. + */ +class Proxy_Function_Base { +public: + /** + * @brief Virtual destructor. + */ + virtual ~Proxy_Function_Base() = default; + + /** + * @brief Calls the function with the given parameters. + * + * @param params The parameters to pass to the function. + * @param t_conversions The state of type conversions. + * @return The result of the function call. + */ + Boxed_Value operator()( + const Function_Params ¶ms, + const Carbon::Type_Conversions_State &t_conversions) const; + + /** + * @brief Returns the types of all parameters. + * + * If the function is variadic or takes no arguments (arity of 0 or -1), + * the returned value contains exactly 1 Type_Info object: the return type. + * + * @return A vector containing all of the types of the parameters the + * function returns/takes. + */ + const std::vector &get_param_types() const noexcept; + + /** + * @brief Checks if two Proxy_Function_Base objects are equal. + * + * @param rhs The right-hand side Proxy_Function_Base object. + * @return True if the objects are equal, false otherwise. + */ + virtual bool operator==(const Proxy_Function_Base &) const noexcept = 0; + + /** + * @brief Checks if the function matches the given parameters. + * + * @param vals The values to check. + * @param t_conversions The state of type conversions. + * @return True if the function matches, false otherwise. + */ + virtual bool call_match( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const = 0; + + /** + * @brief Checks if the function is an attribute function. + * + * @return True if the function is an attribute function, false otherwise. + */ + virtual bool is_attribute_function() const noexcept; + + /** + * @brief Checks if the function has an arithmetic parameter. + * + * @return True if the function has an arithmetic parameter, false + * otherwise. + */ + bool has_arithmetic_param() const noexcept; + + /** + * @brief Gets the contained functions. + * + * @return A vector containing shared pointers to the contained functions. + */ + virtual std::vector> + get_contained_functions() const; + + /** + * @brief Filters the function based on the passed in values. + * + * @param vals The values to filter. + * @param t_conversions The state of type conversions. + * @return True if the function is a possible match to the passed in values, + * false otherwise. + */ + bool filter(const Function_Params &vals, + const Type_Conversions_State &t_conversions) const noexcept; + + /** + * @brief Gets the number of arguments the function takes. + * + * @return The number of arguments the function takes, or -1 if it is + * variadic. + */ + int get_arity() const noexcept; + + /** + * @brief Compares a type to the first parameter. + * + * @param bv The boxed value to compare. + * @param t_conversions The state of type conversions. + * @return True if the type matches the first parameter, false otherwise. + */ + static bool compare_type_to_param( + const Type_Info &ti, const Boxed_Value &bv, + const Type_Conversions_State &t_conversions) noexcept; + + virtual bool compare_first_type( + const Boxed_Value &bv, + const Type_Conversions_State &t_conversions) const noexcept; + +protected: + /** + * @brief Performs the function call. + * + * @param params The parameters to pass to the function. + * @param t_conversions The state of type conversions. + * @return The result of the function call. + */ + virtual Boxed_Value do_call( + const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const = 0; + + /** + * @brief Constructor. + * + * @param t_types The types of the parameters. + * @param t_arity The arity of the function. + */ + Proxy_Function_Base(std::vector t_types, int t_arity); + + /** + * @brief Compares types to parameters. + * + * @param tis The types to compare. + * @param bvs The parameters to compare against. + * @param t_conversions The state of type conversions. + * @return True if the types match the parameters, false otherwise. + */ + static bool compare_types( + const std::vector &tis, const Function_Params &bvs, + const Type_Conversions_State &t_conversions) noexcept; + + std::vector m_types; ///< Vector of parameter types. + int m_arity; ///< The arity of the function. + bool m_has_arithmetic_param; ///< Flag indicating if the function has an + ///< arithmetic parameter. +}; +} // namespace dispatch + +/// \brief Common typedef used for passing of any registered function in +/// ChaiScript +using Proxy_Function = std::shared_ptr; + +/// \brief Const version of Proxy_Function. Points to a const Proxy_Function. +/// This is how most registered functions +/// are handled internally. +using Const_Proxy_Function = + std::shared_ptr; + +namespace exception { +/// \brief Exception thrown if a function's guard fails +class guard_error : public std::runtime_error { +public: + guard_error() noexcept : std::runtime_error("Guard evaluation failed") {} + + guard_error(const guard_error &) = default; + + ~guard_error() noexcept override = default; +}; +} // namespace exception + +namespace dispatch { +/** + * @brief A Proxy_Function implementation that is not type safe. + * + * The called function is expecting a vector that it works with how + * it chooses. + */ +class Dynamic_Proxy_Function : public Proxy_Function_Base { +public: + /** + * @brief Constructor. + * + * @param t_arity The arity of the function. + * @param t_parsenode The parse node. + * @param t_param_types The parameter types. + * @param t_guard The guard function. + */ + explicit Dynamic_Proxy_Function(const int t_arity, + std::shared_ptr t_parsenode, + Param_Types t_param_types, + Proxy_Function t_guard); + + /** + * @brief Checks if two Dynamic_Proxy_Function objects are equal. + * + * @param rhs The right-hand side Dynamic_Proxy_Function object. + * @return True if the objects are equal, false otherwise. + */ + bool operator==(const Proxy_Function_Base &rhs) const noexcept override; + + /** + * @brief Checks if the function matches the given parameters. + * + * @param vals The values to check. + * @param t_conversions The state of type conversions. + * @return True if the function matches, false otherwise. + */ + bool call_match(const Function_Params &vals, + const Type_Conversions_State &t_conversions) const override; + + /** + * @brief Checks if the function has a guard. + * + * @return True if the function has a guard, false otherwise. + */ + bool has_guard() const noexcept; + + /** + * @brief Gets the guard function. + * + * @return The guard function. + */ + Proxy_Function get_guard() const noexcept; + + /** + * @brief Checks if the function has a parse tree. + * + * @return True if the function has a parse tree, false otherwise. + */ + bool has_parse_tree() const noexcept; + + /** + * @brief Gets the parse tree. + * + * @return The parse tree. + */ + const AST_Node &get_parse_tree() const; + +protected: + /** + * @brief Tests the guard function. + * + * @param params The parameters to test. + * @param t_conversions The state of type conversions. + * @return True if the guard function passes, false otherwise. + */ + bool test_guard(const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const; + + /** + * @brief Checks if the function matches the given parameters internally. + * + * @param vals The values to check. + * @param t_conversions The state of type conversions. + * @return A pair of booleans indicating if there is a match and if + * conversions are needed. + */ + std::pair call_match_internal( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const; + +private: + /** + * @brief Builds a list of parameter types. + * + * @param t_types The parameter types. + * @return A vector containing the parameter types. + */ + static std::vector build_param_type_list( + const Param_Types &t_types); + +protected: + Param_Types m_param_types; ///< The parameter types. + +private: + Proxy_Function m_guard; ///< The guard function. + std::shared_ptr m_parsenode; ///< The parse node. +}; + +template +class Dynamic_Proxy_Function_Impl final : public Dynamic_Proxy_Function { +public: + Dynamic_Proxy_Function_Impl( + Callable t_f, int t_arity = -1, + std::shared_ptr t_parsenode = AST_NodePtr(), + Param_Types t_param_types = Param_Types(), + Proxy_Function t_guard = Proxy_Function()) + : Dynamic_Proxy_Function(t_arity, std::move(t_parsenode), + std::move(t_param_types), std::move(t_guard)), + m_f(std::move(t_f)) {} + +protected: + Boxed_Value do_call( + const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const override { + const auto [is_a_match, needs_conversions] = + call_match_internal(params, t_conversions); + if (is_a_match) { + if (needs_conversions) { + return m_f(Function_Params{ + m_param_types.convert(params, t_conversions)}); + } else { + return m_f(params); + } + } else { + throw exception::guard_error(); + } + } + +private: + Callable m_f; +}; + +template +Proxy_Function make_dynamic_proxy_function(Callable &&c, Arg &&...a) { + return Carbon::make_shared>( + std::forward(c), std::forward(a)...); +} + +/// An object used by Bound_Function to represent "_" parameters +/// of a binding. This allows for unbound parameters during bind. +struct Placeholder_Object {}; + +/** + * @brief An implementation of Proxy_Function that takes a Proxy_Function + * and substitutes bound parameters into the parameter list + * at runtime, when call() is executed. + * It is used for bind(function, param1, _, param2) style calls. + */ +class Bound_Function final : public Proxy_Function_Base { +public: + /** + * @brief Constructor. + * + * @param t_f The original function. + * @param t_args The arguments to bind. + */ + Bound_Function(const Const_Proxy_Function &t_f, + const std::vector &t_args); + + /** + * @brief Checks if two Bound_Function objects are equal. + * + * @param t_f The right-hand side Bound_Function object. + * @return True if the objects are equal, false otherwise. + */ + bool operator==(const Proxy_Function_Base &t_f) const noexcept override; + + /** + * @brief Checks if the function matches the given parameters. + * + * @param vals The values to check. + * @param t_conversions The state of type conversions. + * @return True if the function matches, false otherwise. + */ + bool call_match(const Function_Params &vals, + const Type_Conversions_State &t_conversions) const override; + + /** + * @brief Gets the contained functions. + * + * @return A vector containing shared pointers to the contained functions. + */ + std::vector get_contained_functions() const override; + + std::vector build_param_list( + const Function_Params ¶ms) const; + +protected: + /** + * @brief Builds parameter type information. + * + * @param t_f The original function. + * @param t_args The arguments. + * @return A vector containing the parameter type information. + */ + static std::vector build_param_type_info( + const Const_Proxy_Function &t_f, + const std::vector &t_args); + + /** + * @brief Executes the function call. + * + * @param params The parameters to pass to the function. + * @param t_conversions The state of type conversions. + * @return The result of the function call. + */ + Boxed_Value do_call( + const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const override; + +private: + Const_Proxy_Function m_f; ///< The original function. + std::vector m_args; ///< The bound arguments. +}; + +/** + * @brief Base class for Proxy_Function implementations. + */ +class Proxy_Function_Impl_Base : public Proxy_Function_Base { +public: + /** + * @brief Constructor. + * + * @param t_types The types of the parameters. + */ + explicit Proxy_Function_Impl_Base(const std::vector &t_types); + + /** + * @brief Checks if the function matches the given parameters. + * + * @param vals The values to check. + * @param t_conversions The state of type conversions. + * @return True if the function matches, false otherwise. + */ + bool call_match( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const noexcept override; + + /** + * @brief Compares types with casting. + * + * @param vals The values to compare. + * @param t_conversions The state of type conversions. + * @return True if the types match with casting, false otherwise. + */ + virtual bool compare_types_with_cast( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const noexcept = 0; +}; + +/// For any callable object +template +class Proxy_Function_Callable_Impl final : public Proxy_Function_Impl_Base { +public: + explicit Proxy_Function_Callable_Impl(Callable f) + : Proxy_Function_Impl_Base( + detail::build_param_type_list(static_cast(nullptr))), + m_f(std::move(f)) {} + + bool compare_types_with_cast( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const noexcept override { + return detail::compare_types_cast(static_cast(nullptr), vals, + t_conversions); + } + + bool operator==(const Proxy_Function_Base &t_func) const noexcept override { + return dynamic_cast< + const Proxy_Function_Callable_Impl *>( + &t_func) != nullptr; + } + +protected: + Boxed_Value do_call( + const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const override { + return detail::call_func(static_cast(nullptr), m_f, params, + t_conversions); + } + +private: + Callable m_f; +}; + +class Assignable_Proxy_Function : public Proxy_Function_Impl_Base { +public: + explicit Assignable_Proxy_Function(const std::vector &t_types) + : Proxy_Function_Impl_Base(t_types) {} + + virtual void assign( + const std::shared_ptr &t_rhs) = 0; +}; + +template +class Assignable_Proxy_Function_Impl final : public Assignable_Proxy_Function { +public: + Assignable_Proxy_Function_Impl( + std::reference_wrapper> t_f, + std::shared_ptr> t_ptr) + : Assignable_Proxy_Function( + detail::build_param_type_list(static_cast(nullptr))), + m_f(std::move(t_f)), + m_shared_ptr_holder(std::move(t_ptr)) { + assert(!m_shared_ptr_holder || m_shared_ptr_holder.get() == &m_f.get()); + } + + bool compare_types_with_cast( + const Function_Params &vals, + const Type_Conversions_State &t_conversions) const noexcept override { + return detail::compare_types_cast(static_cast(nullptr), vals, + t_conversions); + } + + bool operator==(const Proxy_Function_Base &t_func) const noexcept override { + return dynamic_cast *>( + &t_func) != nullptr; + } + + std::function internal_function() const { return m_f.get(); } + + void assign( + const std::shared_ptr &t_rhs) override { + m_f.get() = dispatch::functor(t_rhs, nullptr); + } + +protected: + Boxed_Value do_call( + const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const override { + return detail::call_func(static_cast(nullptr), m_f.get(), + params, t_conversions); + } + +private: + std::reference_wrapper> m_f; + std::shared_ptr> m_shared_ptr_holder; +}; + +/// Attribute getter Proxy_Function implementation +template +class Attribute_Access final : public Proxy_Function_Base { +public: + explicit Attribute_Access(T Class::*t_attr) + : Proxy_Function_Base(param_types(), 1), m_attr(t_attr) {} + + bool is_attribute_function() const noexcept override { return true; } + + bool operator==(const Proxy_Function_Base &t_func) const noexcept override { + const Attribute_Access *aa = + dynamic_cast *>(&t_func); + + if (aa) { + return m_attr == aa->m_attr; + } else { + return false; + } + } + + bool call_match(const Function_Params &vals, + const Type_Conversions_State &) const noexcept override { + if (vals.size() != 1) { + return false; + } + const auto class_type_info = user_type(); + return vals[0].get_type_info().bare_equal(class_type_info); + } + +protected: + Boxed_Value do_call( + const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const override { + const Boxed_Value &bv = params[0]; + if (bv.is_const()) { + const Class *o = boxed_cast(bv, &t_conversions); + return do_call_impl(o); + } else { + Class *o = boxed_cast(bv, &t_conversions); + return do_call_impl(o); + } + } + +private: + template + auto do_call_impl(Class *o) const { + if constexpr (std::is_pointer::value) { + return detail::Handle_Return::handle(o->*m_attr); + } else { + return detail::Handle_Return::type>::handle(o->*m_attr); + } + } + + template + auto do_call_impl(const Class *o) const { + if constexpr (std::is_pointer::value) { + return detail::Handle_Return::handle(o->*m_attr); + } else { + return detail::Handle_Return::type>::type>::handle(o->*m_attr); + } + } + + static std::vector param_types() { + return {user_type(), user_type()}; + } + + std::vector m_param_types{user_type(), user_type()}; + T Class::*m_attr; +}; +} // namespace dispatch + +namespace exception { +/// \brief Exception thrown in the case that a method dispatch fails +/// because no matching function was found +/// +/// May be thrown due to an arity_error, a guard_error or a bad_boxed_cast +/// exception +class dispatch_error : public std::runtime_error { +public: + dispatch_error(const Function_Params &t_parameters, + std::vector t_functions) + : std::runtime_error("Error with function dispatch"), + parameters(t_parameters.to_vector()), + functions(std::move(t_functions)) {} + + dispatch_error(const Function_Params &t_parameters, + std::vector t_functions, + const std::string &t_desc) + : std::runtime_error(t_desc), + parameters(t_parameters.to_vector()), + functions(std::move(t_functions)) {} + + dispatch_error(const dispatch_error &) = default; + ~dispatch_error() noexcept override = default; + + std::vector parameters; + std::vector functions; +}; +} // namespace exception + +namespace dispatch { +namespace detail { +template +bool types_match_except_for_arithmetic( + const FuncType &t_func, const Carbon::Function_Params &plist, + const Type_Conversions_State &t_conversions) noexcept { + const std::vector &types = t_func->get_param_types(); + + if (t_func->get_arity() == -1) { + return false; + } + + assert(plist.size() == types.size() - 1); + + return std::mismatch(plist.begin(), plist.end(), types.begin() + 1, + [&](const Boxed_Value &bv, const Type_Info &ti) { + return Proxy_Function_Base::compare_type_to_param( + ti, bv, t_conversions) || + (bv.get_type_info().is_arithmetic() && + ti.is_arithmetic()); + }) == std::make_pair(plist.end(), types.end()); +} + +template +Boxed_Value dispatch_with_conversions( + InItr begin, const InItr &end, const Carbon::Function_Params &plist, + const Type_Conversions_State &t_conversions, const Funcs &t_funcs) { + InItr matching_func(end); + + while (begin != end) { + if (types_match_except_for_arithmetic(begin->second, plist, + t_conversions)) { + if (matching_func == end) { + matching_func = begin; + } else { + // handle const members vs non-const member, which is not really + // ambiguous + const auto &mat_fun_param_types = + matching_func->second->get_param_types(); + const auto &next_fun_param_types = + begin->second->get_param_types(); + + if (plist[0].is_const() && !mat_fun_param_types[1].is_const() && + next_fun_param_types[1].is_const()) { + matching_func = + begin; // keep the new one, the const/non-const matchup + // is correct + } else if (!plist[0].is_const() && + !mat_fun_param_types[1].is_const() && + next_fun_param_types[1].is_const()) { + // keep the old one, it has a better const/non-const matchup + } else { + // ambiguous function call + throw exception::dispatch_error( + plist, std::vector( + t_funcs.begin(), t_funcs.end())); + } + } + } + + ++begin; + } + + if (matching_func == end) { + // no appropriate function to attempt arithmetic type conversion on + throw exception::dispatch_error( + plist, + std::vector(t_funcs.begin(), t_funcs.end())); + } + + std::vector newplist; + newplist.reserve(plist.size()); + + const std::vector &tis = + matching_func->second->get_param_types(); + std::transform( + tis.begin() + 1, tis.end(), plist.begin(), std::back_inserter(newplist), + [](const Type_Info &ti, const Boxed_Value ¶m) -> Boxed_Value { + if (ti.is_arithmetic() && param.get_type_info().is_arithmetic() && + param.get_type_info() != ti) { + return Boxed_Number(param).get_as(ti).bv; + } else { + return param; + } + }); + + try { + return (*(matching_func->second))(Carbon::Function_Params{newplist}, + t_conversions); + } catch (const exception::bad_boxed_cast &) { + // parameter failed to cast + } catch (const exception::arity_error &) { + // invalid num params + } catch (const exception::guard_error &) { + // guard failed to allow the function to execute + } + + throw exception::dispatch_error(plist, std::vector( + t_funcs.begin(), t_funcs.end())); +} +} // namespace detail + +/// Take a vector of functions and a vector of parameters. Attempt to execute +/// each function against the set of parameters, in order, until a matching +/// function is found or throw dispatch_error if no matching function is found +template +Boxed_Value dispatch(const Funcs &funcs, const Function_Params &plist, + const Type_Conversions_State &t_conversions) { + std::vector> ordered_funcs; + ordered_funcs.reserve(funcs.size()); + + for (const auto &func : funcs) { + const auto arity = func->get_arity(); + + if (arity == -1) { + ordered_funcs.emplace_back(plist.size(), func.get()); + } else if (arity == static_cast(plist.size())) { + size_t numdiffs = 0; + for (size_t i = 0; i < plist.size(); ++i) { + if (!func->get_param_types()[i + 1].bare_equal( + plist[i].get_type_info())) { + ++numdiffs; + } + } + ordered_funcs.emplace_back(numdiffs, func.get()); + } + } + + for (size_t i = 0; i <= plist.size(); ++i) { + for (const auto &func : ordered_funcs) { + try { + if (func.first == i && + (i == 0 || func.second->filter(plist, t_conversions))) { + return (*(func.second))(plist, t_conversions); + } + } catch (const exception::bad_boxed_cast &) { + // parameter failed to cast, try again + } catch (const exception::arity_error &) { + // invalid num params, try again + } catch (const exception::guard_error &) { + // guard failed to allow the function to execute, + // try again + } + } + } + + return detail::dispatch_with_conversions(ordered_funcs.cbegin(), + ordered_funcs.cend(), plist, + t_conversions, funcs); +} +} // namespace dispatch +} // namespace Carbon + +#endif diff --git a/src/carbon/command/proxy_functions_detail.hpp b/src/carbon/command/proxy_functions_detail.hpp new file mode 100644 index 00000000..0b3eca40 --- /dev/null +++ b/src/carbon/command/proxy_functions_detail.hpp @@ -0,0 +1,108 @@ + + +#ifndef CARBON_PROXY_FUNCTIONS_DETAIL_HPP +#define CARBON_PROXY_FUNCTIONS_DETAIL_HPP + +#include +#include +#include +#include + +#include "../defines.hpp" +#include "boxed_cast.hpp" +#include "boxed_value.hpp" +#include "function_params.hpp" +#include "handle_return.hpp" +#include "atom/experiment/type_info.hpp" + +namespace Carbon { +class Type_Conversions_State; +namespace exception { +class bad_boxed_cast; +} // namespace exception +} // namespace Carbon + +namespace Carbon { +namespace exception { +/** + * Exception thrown when there is a mismatch in number of + * parameters during Proxy_Function execution + */ +struct arity_error : std::range_error { + arity_error(int t_got, int t_expected) + : std::range_error("Function dispatch arity mismatch"), + got(t_got), + expected(t_expected) {} + + arity_error(const arity_error &) = default; + + ~arity_error() noexcept override = default; + + int got; + int expected; +}; +} // namespace exception + +namespace dispatch { +namespace detail { +/** + * Used by Proxy_Function_Impl to return a list of all param types + * it contains. + */ +template +std::vector build_param_type_list(Ret (*)(Params...)) { + /// \note somehow this is responsible for a large part of the code + /// generation + return {user_type(), user_type()...}; +} + +/** + * Used by Proxy_Function_Impl to determine if it is equivalent to another + * Proxy_Function_Impl object. This function is primarily used to prevent + * registration of two functions with the exact same signatures + */ +template +bool compare_types_cast(Ret (*)(Params...), + const Carbon::Function_Params ¶ms, + const Type_Conversions_State &t_conversions) noexcept { + try { + std::vector::size_type i = 0; + (boxed_cast(params[i++], &t_conversions), ...); + return true; + } catch (const exception::bad_boxed_cast &) { + return false; + } +} + +template +Ret call_func(Ret (*)(Params...), std::index_sequence, const Callable &f, + [[maybe_unused]] const Carbon::Function_Params ¶ms, + [[maybe_unused]] const Type_Conversions_State &t_conversions) { + return f(boxed_cast(params[I], &t_conversions)...); +} + +/// Used by Proxy_Function_Impl to perform typesafe execution of a function. +/// The function attempts to unbox each parameter to the expected type. +/// if any unboxing fails the execution of the function fails and +/// the bad_boxed_cast is passed up to the caller. +template +Boxed_Value call_func(Ret (*sig)(Params...), const Callable &f, + const Carbon::Function_Params ¶ms, + const Type_Conversions_State &t_conversions) { + if constexpr (std::is_same_v) { + call_func(sig, std::index_sequence_for{}, f, params, + t_conversions); + return Handle_Return::handle(); + } else { + return Handle_Return::handle( + call_func(sig, std::index_sequence_for{}, f, params, + t_conversions)); + } +} + +} // namespace detail +} // namespace dispatch + +} // namespace Carbon + +#endif diff --git a/src/carbon/command/register_function.hpp b/src/carbon/command/register_function.hpp new file mode 100644 index 00000000..de32bdeb --- /dev/null +++ b/src/carbon/command/register_function.hpp @@ -0,0 +1,129 @@ + +#ifndef CARBON_REGISTER_FUNCTION_HPP +#define CARBON_REGISTER_FUNCTION_HPP + +#include + +#include "atom/experiment/bind_first.hpp" +#include "function_signature.hpp" +#include "proxy_functions.hpp" + +namespace Carbon { +namespace dispatch::detail { +template +Param1 get_first_param(Function_Params, Obj &&obj) { + return static_cast(std::forward(obj)); +} + +template +auto make_callable_impl( + Func &&func, Function_Signature, Is_Noexcept, + Is_Member, Is_MemberObject, Is_Object>) { + if constexpr (Is_MemberObject) { + // we now that the Param pack will have only one element, so we are safe + // expanding it here + return Proxy_Function( + Carbon::make_shared< + dispatch::Proxy_Function_Base, + dispatch::Attribute_Access...>>( + std::forward(func))); + } else if constexpr (Is_Member) { + // TODO some kind of bug is preventing forwarding of this noexcept for + // the lambda + auto call = + [func = std::forward(func)]( + auto &&obj, + auto &&...param) /* noexcept(Is_Noexcept) */ -> decltype(auto) { + return ((get_first_param(Function_Params{}, obj).* + func)(std::forward(param)...)); + }; + return Proxy_Function( + Carbon::make_shared>( + std::move(call))); + } else { + return Proxy_Function( + Carbon::make_shared>>( + std::forward(func))); + } +} + +// this version peels off the function object itself from the function +// signature, when used on a callable object +template +auto make_callable(Func &&func, + Function_Signature, + Is_Noexcept, false, false, true>) { + return make_callable_impl( + std::forward(func), + Function_Signature, Is_Noexcept, false, + false, true>{}); +} + +template +auto make_callable( + Func &&func, Function_Signature, Is_Noexcept, + Is_Member, Is_MemberObject, false> + fs) { + return make_callable_impl(std::forward(func), fs); +} +} // namespace dispatch::detail + +/// \brief Creates a new Proxy_Function object from a free function, member +/// function or data member \param[in] t Function / member to expose +/// +/// \b Example: +/// \code +/// int myfunction(const std::string &); +/// class MyClass +/// { +/// public: +/// void memberfunction(); +/// int memberdata; +/// }; +/// +/// Carbon::Carbon chai; +/// chai.add(fun(&myfunction), "myfunction"); +/// chai.add(fun(&MyClass::memberfunction), "memberfunction"); +/// chai.add(fun(&MyClass::memberdata), "memberdata"); +/// \endcode +/// +/// \sa \ref adding_functions +template +Proxy_Function fun(T &&t) { + return dispatch::detail::make_callable( + std::forward(t), dispatch::detail::function_signature(t)); +} + +/// \brief Creates a new Proxy_Function object from a free function, member +/// function or data member and binds the first parameter of it \param[in] t +/// Function / member to expose \param[in] q Value to bind to first parameter +/// +/// \b Example: +/// \code +/// struct MyClass +/// { +/// void memberfunction(int); +/// }; +/// +/// MyClass obj; +/// Carbon::Carbon chai; +/// // Add function taking only one argument, an int, and permanently bound to +/// "obj" chai.add(fun(&MyClass::memberfunction, std::ref(obj)), +/// "memberfunction"); \endcode +/// +/// \sa \ref adding_functions +template +Proxy_Function fun(T &&t, const Q &q) { + return fun(bind_first(std::forward(t), q)); +} + +} // namespace Carbon + +#endif \ No newline at end of file diff --git a/src/carbon/command/type_conversions.hpp b/src/carbon/command/type_conversions.hpp new file mode 100644 index 00000000..9c483429 --- /dev/null +++ b/src/carbon/command/type_conversions.hpp @@ -0,0 +1,637 @@ + +#ifndef CARBON_DYNAMIC_CAST_CONVERSION_HPP +#define CARBON_DYNAMIC_CAST_CONVERSION_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../threading.hpp" +#include "atom/experiment/sstring.hpp" +#include "atom/experiment/type_info.hpp" +#include "bad_boxed_cast.hpp" +#include "boxed_cast_helper.hpp" +#include "boxed_value.hpp" + +namespace Carbon { +namespace exception { +/// \brief Error thrown when there's a problem with type conversion +class conversion_error : public bad_boxed_cast { +public: + conversion_error(const Type_Info t_to, const Type_Info t_from, + const Static_String what) noexcept + : bad_boxed_cast(t_from, (*t_to.bare_type_info()), what), + type_to(t_to) {} + + Type_Info type_to; +}; + +class bad_boxed_dynamic_cast : public bad_boxed_cast { +public: + bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to, + const Static_String &t_what) noexcept + : bad_boxed_cast(t_from, t_to, t_what) {} + + bad_boxed_dynamic_cast(const Type_Info &t_from, + const std::type_info &t_to) noexcept + : bad_boxed_cast(t_from, t_to) {} + + explicit bad_boxed_dynamic_cast(const Static_String &w) noexcept + : bad_boxed_cast(w) {} + + bad_boxed_dynamic_cast(const bad_boxed_dynamic_cast &) = default; + + ~bad_boxed_dynamic_cast() noexcept override = default; +}; + +class bad_boxed_type_cast : public bad_boxed_cast { +public: + bad_boxed_type_cast(const Type_Info &t_from, const std::type_info &t_to, + const Static_String &t_what) noexcept + : bad_boxed_cast(t_from, t_to, t_what) {} + + bad_boxed_type_cast(const Type_Info &t_from, + const std::type_info &t_to) noexcept + : bad_boxed_cast(t_from, t_to) {} + + explicit bad_boxed_type_cast(const Static_String &w) noexcept + : bad_boxed_cast(w) {} + + bad_boxed_type_cast(const bad_boxed_type_cast &) = default; + + ~bad_boxed_type_cast() noexcept override = default; +}; +} // namespace exception + +namespace detail { +class Type_Conversion_Base { +public: + virtual Boxed_Value convert(const Boxed_Value &from) const = 0; + virtual Boxed_Value convert_down(const Boxed_Value &to) const = 0; + + const Type_Info &to() const noexcept { return m_to; } + const Type_Info &from() const noexcept { return m_from; } + + virtual bool bidir() const noexcept { return true; } + + virtual ~Type_Conversion_Base() = default; + +protected: + Type_Conversion_Base(Type_Info t_to, Type_Info t_from) + : m_to(std::move(t_to)), m_from(std::move(t_from)) {} + +private: + const Type_Info m_to; + const Type_Info m_from; +}; + +template +class Static_Caster { +public: + static Boxed_Value cast(const Boxed_Value &t_from) { + if (t_from.get_type_info().bare_equal(user_type())) { + if (t_from.is_pointer()) { + // Dynamic cast out the contained boxed value, which we know is + // the type we want + if (t_from.is_const()) { + return Boxed_Value([&]() { + if (auto data = std::static_pointer_cast( + detail::Cast_Helper>::cast(t_from, nullptr))) { + return data; + } else { + throw std::bad_cast(); + } + }()); + } else { + return Boxed_Value([&]() { + if (auto data = std::static_pointer_cast( + detail::Cast_Helper< + std::shared_ptr>::cast(t_from, + nullptr))) { + return data; + } else { + throw std::bad_cast(); + } + }()); + } + } else { + // Pull the reference out of the contained boxed value, which we + // know is the type we want + if (t_from.is_const()) { + const From &d = detail::Cast_Helper::cast( + t_from, nullptr); + const To &data = static_cast(d); + return Boxed_Value(std::cref(data)); + } else { + From &d = + detail::Cast_Helper::cast(t_from, nullptr); + To &data = static_cast(d); + return Boxed_Value(std::ref(data)); + } + } + } else { + throw Carbon::exception::bad_boxed_dynamic_cast( + t_from.get_type_info(), typeid(To), + "Unknown dynamic_cast_conversion"); + } + } +}; + +template +class Dynamic_Caster { +public: + static Boxed_Value cast(const Boxed_Value &t_from) { + if (t_from.get_type_info().bare_equal(user_type())) { + if (t_from.is_pointer()) { + // Dynamic cast out the contained boxed value, which we know is + // the type we want + if (t_from.is_const()) { + return Boxed_Value([&]() { + if (auto data = std::dynamic_pointer_cast( + detail::Cast_Helper>::cast(t_from, nullptr))) { + return data; + } else { + throw std::bad_cast(); + } + }()); + } else { + return Boxed_Value([&]() { + if (auto data = std::dynamic_pointer_cast( + detail::Cast_Helper< + std::shared_ptr>::cast(t_from, + nullptr))) { + return data; + } else { +#ifdef CARBON_LIBCPP + /// \todo fix this someday after libc++ is fixed. + if (std::string(typeid(To).name()) + .find("Assignable_Proxy_Function") != + std::string::npos) { + auto from = detail::Cast_Helper< + std::shared_ptr>::cast(t_from, + nullptr); + if (std::string(typeid(*from).name()) + .find( + "Assignable_Proxy_Function_Impl") != + std::string::npos) { + return std::static_pointer_cast(from); + } + } +#endif + throw std::bad_cast(); + } + }()); + } + } else { + // Pull the reference out of the contained boxed value, which we + // know is the type we want + if (t_from.is_const()) { + const From &d = detail::Cast_Helper::cast( + t_from, nullptr); + const To &data = dynamic_cast(d); + return Boxed_Value(std::cref(data)); + } else { + From &d = + detail::Cast_Helper::cast(t_from, nullptr); + To &data = dynamic_cast(d); + return Boxed_Value(std::ref(data)); + } + } + } else { + throw Carbon::exception::bad_boxed_dynamic_cast( + t_from.get_type_info(), typeid(To), + "Unknown dynamic_cast_conversion"); + } + } +}; + +template +class Dynamic_Conversion_Impl : public Type_Conversion_Base { +public: + Dynamic_Conversion_Impl() + : Type_Conversion_Base(user_type(), user_type()) {} + + Boxed_Value convert_down(const Boxed_Value &t_base) const override { + return Dynamic_Caster::cast(t_base); + } + + Boxed_Value convert(const Boxed_Value &t_derived) const override { + return Static_Caster::cast(t_derived); + } +}; + +template +class Static_Conversion_Impl : public Type_Conversion_Base { +public: + Static_Conversion_Impl() + : Type_Conversion_Base(user_type(), user_type()) {} + + Boxed_Value convert_down(const Boxed_Value &t_base) const override { + throw Carbon::exception::bad_boxed_dynamic_cast( + t_base.get_type_info(), typeid(Derived), + "Unable to cast down inheritance hierarchy with non-polymorphic " + "types"); + } + + bool bidir() const noexcept override { return false; } + + Boxed_Value convert(const Boxed_Value &t_derived) const override { + return Static_Caster::cast(t_derived); + } +}; + +template +class Type_Conversion_Impl : public Type_Conversion_Base { +public: + Type_Conversion_Impl(Type_Info t_from, Type_Info t_to, Callable t_func) + : Type_Conversion_Base(t_to, t_from), m_func(std::move(t_func)) {} + + Boxed_Value convert_down(const Boxed_Value &) const override { + throw Carbon::exception::bad_boxed_type_cast("No conversion exists"); + } + + Boxed_Value convert(const Boxed_Value &t_from) const override { + /// \todo better handling of errors from the conversion function + return m_func(t_from); + } + + bool bidir() const noexcept override { return false; } + +private: + Callable m_func; +}; +} // namespace detail + +class Type_Conversions { +public: + struct Conversion_Saves { + bool enabled = false; + std::vector saves; + }; + + struct Less_Than { + bool operator()(const std::type_info *t_lhs, + const std::type_info *t_rhs) const noexcept { + return *t_lhs != *t_rhs && t_lhs->before(*t_rhs); + } + }; + + Type_Conversions() + : m_mutex(), m_conversions(), m_convertableTypes(), m_num_types(0) {} + + Type_Conversions(const Type_Conversions &t_other) = delete; + Type_Conversions(Type_Conversions &&) = delete; + + Type_Conversions &operator=(const Type_Conversions &) = delete; + Type_Conversions &operator=(Type_Conversions &&) = delete; + + const std::set &thread_cache() const { + auto &cache = *m_thread_cache; + if (cache.size() != m_num_types) { + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + cache = m_convertableTypes; + } + + return cache; + } + + void add_conversion( + const std::shared_ptr &conversion) { + Carbon::detail::threading::unique_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + if (find_bidir(conversion->to(), conversion->from()) != + m_conversions.end()) { + throw exception::conversion_error( + conversion->to(), conversion->from(), + "Trying to re-insert an existing conversion!"); + } + m_conversions.insert(conversion); + m_convertableTypes.insert({conversion->to().bare_type_info(), + conversion->from().bare_type_info()}); + m_num_types = m_convertableTypes.size(); + } + + template + bool convertable_type() const noexcept { + const auto type = user_type().bare_type_info(); + return thread_cache().count(type) != 0; + } + + template + bool converts() const noexcept { + return converts(user_type(), user_type()); + } + + bool converts(const Type_Info &to, const Type_Info &from) const noexcept { + const auto &types = thread_cache(); + if (types.count(to.bare_type_info()) != 0 && + types.count(from.bare_type_info()) != 0) { + return has_conversion(to, from); + } else { + return false; + } + } + + template + Boxed_Value boxed_type_conversion(Conversion_Saves &t_saves, + const Boxed_Value &from) const { + return boxed_type_conversion(user_type(), t_saves, from); + } + + template + Boxed_Value boxed_type_down_conversion(Conversion_Saves &t_saves, + const Boxed_Value &to) const { + return boxed_type_down_conversion(user_type(), t_saves, to); + } + + Boxed_Value boxed_type_conversion(const Type_Info &to, + Conversion_Saves &t_saves, + const Boxed_Value &from) const { + try { + Boxed_Value ret = + get_conversion(to, from.get_type_info())->convert(from); + if (t_saves.enabled) { + t_saves.saves.push_back(ret); + } + return ret; + } catch (const std::out_of_range &) { + throw exception::bad_boxed_dynamic_cast(from.get_type_info(), + *to.bare_type_info(), + "No known conversion"); + } catch (const std::bad_cast &) { + throw exception::bad_boxed_dynamic_cast( + from.get_type_info(), *to.bare_type_info(), + "Unable to perform dynamic_cast operation"); + } + } + + Boxed_Value boxed_type_down_conversion(const Type_Info &from, + Conversion_Saves &t_saves, + const Boxed_Value &to) const { + try { + Boxed_Value ret = + get_conversion(to.get_type_info(), from)->convert_down(to); + if (t_saves.enabled) { + t_saves.saves.push_back(ret); + } + return ret; + } catch (const std::out_of_range &) { + throw exception::bad_boxed_dynamic_cast(to.get_type_info(), + *from.bare_type_info(), + "No known conversion"); + } catch (const std::bad_cast &) { + throw exception::bad_boxed_dynamic_cast( + to.get_type_info(), *from.bare_type_info(), + "Unable to perform dynamic_cast operation"); + } + } + + static void enable_conversion_saves(Conversion_Saves &t_saves, bool t_val) { + t_saves.enabled = t_val; + } + + std::vector take_saves(Conversion_Saves &t_saves) { + std::vector ret; + std::swap(ret, t_saves.saves); + return ret; + } + + bool has_conversion(const Type_Info &to, const Type_Info &from) const { + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + return find_bidir(to, from) != m_conversions.end(); + } + + std::shared_ptr get_conversion( + const Type_Info &to, const Type_Info &from) const { + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + const auto itr = find(to, from); + + if (itr != m_conversions.end()) { + return *itr; + } else { + throw std::out_of_range( + std::string("No such conversion exists from ") + + from.bare_name() + " to " + to.bare_name()); + } + } + + Conversion_Saves &conversion_saves() const noexcept { + return *m_conversion_saves; + } + +private: + std::set>::const_iterator + find_bidir(const Type_Info &to, const Type_Info &from) const { + return std::find_if( + m_conversions.begin(), m_conversions.end(), + [&to, &from]( + const std::shared_ptr &conversion) + -> bool { + return (conversion->to().bare_equal(to) && + conversion->from().bare_equal(from)) || + (conversion->bidir() && + conversion->from().bare_equal(to) && + conversion->to().bare_equal(from)); + }); + } + + std::set>::const_iterator + find(const Type_Info &to, const Type_Info &from) const { + return std::find_if( + m_conversions.begin(), m_conversions.end(), + [&to, &from](const std::shared_ptr + &conversion) { + return conversion->to().bare_equal(to) && + conversion->from().bare_equal(from); + }); + } + + std::set> get_conversions() + const { + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l(m_mutex); + + return m_conversions; + } + + mutable Carbon::detail::threading::shared_mutex m_mutex; + std::set> m_conversions; + std::set m_convertableTypes; + std::atomic_size_t m_num_types; + mutable Carbon::detail::threading::Thread_Storage< + std::set> + m_thread_cache; + mutable Carbon::detail::threading::Thread_Storage + m_conversion_saves; +}; + +class Type_Conversions_State { +public: + Type_Conversions_State(const Type_Conversions &t_conversions, + Type_Conversions::Conversion_Saves &t_saves) + : m_conversions(t_conversions), m_saves(t_saves) {} + + const Type_Conversions *operator->() const noexcept { + return &m_conversions.get(); + } + + const Type_Conversions *get() const noexcept { + return &m_conversions.get(); + } + + Type_Conversions::Conversion_Saves &saves() const noexcept { + return m_saves; + } + +private: + std::reference_wrapper m_conversions; + std::reference_wrapper m_saves; +}; + +using Type_Conversion = std::shared_ptr; + +/// \brief Used to register a to / parent class relationship with ChaiScript. +/// Necessary if you +/// want automatic conversions up your inheritance hierarchy. +/// +/// Create a new to class registration for applying to a module or to the +/// ChaiScript engine Currently, due to limitations in module loading on +/// Windows, and for the sake of portability, if you have a type that is +/// introduced in a loadable module and is used by multiple modules (through a +/// tertiary dll that is shared between the modules, static linking the new type +/// into both loadable modules would not be portable), you need to register the +/// type relationship in all modules that use the newly added type in a +/// polymorphic way. +/// +/// Example: +/// \code +/// class Base +/// {}; +/// class Derived : public Base +/// {}; +/// +/// Carbon::ChaiScript chai; +/// chai.add(Carbon::to_class()); +/// \endcode +/// +template +Type_Conversion base_class() { + // Can only be used with related polymorphic types + // may be expanded some day to support conversions other than child -> + // parent + static_assert(std::is_base_of::value, + "Classes are not related by inheritance"); + + if constexpr (std::is_polymorphic::value && + std::is_polymorphic::value) { + return Carbon::make_shared< + detail::Type_Conversion_Base, + detail::Dynamic_Conversion_Impl>(); + } else { + return Carbon::make_shared< + detail::Type_Conversion_Base, + detail::Static_Conversion_Impl>(); + } +} + +template +Type_Conversion type_conversion(const Type_Info &t_from, const Type_Info &t_to, + const Callable &t_func) { + return Carbon::make_shared>( + t_from, t_to, t_func); +} + +template +Type_Conversion type_conversion(const Callable &t_function) { + auto func = [t_function](const Boxed_Value &t_bv) -> Boxed_Value { + // not even attempting to call boxed_cast so that we don't get caught in + // some call recursion + return Carbon::Boxed_Value( + t_function(detail::Cast_Helper::cast(t_bv, nullptr))); + }; + + return Carbon::make_shared>( + user_type(), user_type(), func); +} + +template +Type_Conversion type_conversion() { + static_assert(std::is_convertible::value, + "Types are not automatically convertible"); + auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { + // not even attempting to call boxed_cast so that we don't get caught in + // some call recursion + return Carbon::Boxed_Value( + To(detail::Cast_Helper::cast(t_bv, nullptr))); + }; + + return Carbon::make_shared>( + user_type(), user_type(), func); +} + +template +Type_Conversion vector_conversion() { + auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { + const std::vector &from_vec = + detail::Cast_Helper &>::cast( + t_bv, nullptr); + + To vec; + vec.reserve(from_vec.size()); + for (const Boxed_Value &bv : from_vec) { + vec.push_back(detail::Cast_Helper::cast( + bv, nullptr)); + } + + return Boxed_Value(std::move(vec)); + }; + + return Carbon::make_shared>( + user_type>(), user_type(), func); +} + +template +Type_Conversion map_conversion() { + auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { + const std::map &from_map = + detail::Cast_Helper< + const std::map &>::cast(t_bv, + nullptr); + + To map; + for (const std::pair &p : from_map) { + map.insert(std::make_pair( + p.first, detail::Cast_Helper::cast( + p.second, nullptr))); + } + + return Boxed_Value(std::move(map)); + }; + + return Carbon::make_shared>( + user_type>(), user_type(), func); +} +} // namespace Carbon + +#endif diff --git a/src/carbon/defines.hpp b/src/carbon/defines.hpp new file mode 100644 index 00000000..bd192bb7 --- /dev/null +++ b/src/carbon/defines.hpp @@ -0,0 +1,273 @@ + +#ifndef CARBON_DEFINES_HPP +#define CARBON_DEFINES_HPP + +#ifdef _MSC_VER +#define CARBON_STRINGIZE(x) "" #x +#define CARBON_STRINGIZE_EXPANDED(x) CARBON_STRINGIZE(x) +#define CARBON_COMPILER_VERSION CARBON_STRINGIZE_EXPANDED(_MSC_FULL_VER) +#define CARBON_MSVC _MSC_VER +#define CARBON_HAS_DECLSPEC + +static_assert(_MSC_FULL_VER >= 190024210, + "Visual C++ 2015 Update 3 or later required"); + +#else +#define CARBON_COMPILER_VERSION __VERSION__ +#endif + +#include +#include + +#if defined(_LIBCPP_VERSION) +#define CARBON_LIBCPP +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +#define CARBON_WINDOWS +#endif + +#if defined(_WIN32) +#if defined(__llvm__) +#define CARBON_COMPILER_NAME "clang(windows)" +#elif defined(__GNUC__) +#define CARBON_COMPILER_NAME "gcc(mingw)" +#else +#define CARBON_COMPILER_NAME "msvc" +#endif +#else +#if defined(__llvm__) +#define CARBON_COMPILER_NAME "clang" +#elif defined(__GNUC__) +#define CARBON_COMPILER_NAME "gcc" +#else +#define CARBON_COMPILER_NAME "unknown" +#endif +#endif + +#if defined(__llvm__) +#define CARBON_CLANG +#endif + +#ifdef CARBON_HAS_DECLSPEC +#define CARBON_MODULE_EXPORT extern "C" __declspec(dllexport) +#else +#define CARBON_MODULE_EXPORT extern "C" +#endif + +#if defined(CARBON_MSVC) || (defined(__GNUC__) && __GNUC__ >= 5) || \ + defined(CARBON_CLANG) +#define CARBON_UTF16_UTF32 +#endif + +#ifdef _DEBUG +#define CARBON_DEBUG true +#else +#define CARBON_DEBUG false +#endif + +#include +#include +#include + +namespace Carbon { +constexpr static const int version_major = 7; +constexpr static const int version_minor = 0; +constexpr static const int version_patch = 0; + +constexpr static const char *compiler_version = CARBON_COMPILER_VERSION; +constexpr static const char *compiler_name = CARBON_COMPILER_NAME; +constexpr static const bool debug_build = CARBON_DEBUG; + +template +inline std::shared_ptr make_shared(Arg &&...arg) { +#ifdef CARBON_USE_STD_MAKE_SHARED + return std::make_shared(std::forward(arg)...); +#else + return std::shared_ptr( + static_cast(new D(std::forward(arg)...))); +#endif +} + +template +inline std::unique_ptr make_unique(Arg &&...arg) { +#ifdef CARBON_USE_STD_MAKE_SHARED + return std::make_unique(std::forward(arg)...); +#else + return std::unique_ptr( + static_cast(new D(std::forward(arg)...))); +#endif +} + +struct Build_Info { + [[nodiscard]] constexpr static int version_major() noexcept { + return Carbon::version_major; + } + + [[nodiscard]] constexpr static int version_minor() noexcept { + return Carbon::version_minor; + } + + [[nodiscard]] constexpr static int version_patch() noexcept { + return Carbon::version_patch; + } + + [[nodiscard]] static std::string version() { + return std::to_string(version_major()) + '.' + + std::to_string(version_minor()) + '.' + + std::to_string(version_patch()); + } + + [[nodiscard]] static std::string compiler_id() { + return compiler_name() + '-' + compiler_version(); + } + + [[nodiscard]] static std::string build_id() { + return compiler_id() + (debug_build() ? "-Debug" : "-Release"); + } + + [[nodiscard]] static std::string compiler_version() { + return Carbon::compiler_version; + } + + [[nodiscard]] static std::string compiler_name() { + return Carbon::compiler_name; + } + + [[nodiscard]] constexpr static bool debug_build() noexcept { + return Carbon::debug_build; + } +}; + +template +[[nodiscard]] constexpr auto parse_num(const std::string_view t_str) noexcept -> + typename std::enable_if::value, T>::type { + T t = 0; + for (const auto c : t_str) { + if (c < '0' || c > '9') { + return t; + } + t *= 10; + t += c - '0'; + } + return t; +} + +template +[[nodiscard]] auto parse_num(const std::string_view t_str) -> + typename std::enable_if::value, T>::type { + T t = 0; + T base{}; + T decimal_place = 0; + int exponent = 0; + + for (const auto c : t_str) { + switch (c) { + case '.': + decimal_place = 10; + break; + case 'e': + case 'E': + exponent = 1; + decimal_place = 0; + base = t; + t = 0; + break; + case '-': + exponent = -1; + break; + case '+': + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (decimal_place < 10) { + t *= 10; + t += static_cast(c - '0'); + } else { + t += static_cast(c - '0') / decimal_place; + decimal_place *= 10; + } + break; + default: + break; + } + } + return exponent ? base * std::pow(T(10), t * static_cast(exponent)) : t; +} + +struct str_equal { + [[nodiscard]] bool operator()(const std::string &t_lhs, + const std::string &t_rhs) const noexcept { + return t_lhs == t_rhs; + } + template + [[nodiscard]] constexpr bool operator()(const LHS &t_lhs, + const RHS &t_rhs) const noexcept { + return std::equal(t_lhs.begin(), t_lhs.end(), t_rhs.begin(), + t_rhs.end()); + } + struct is_transparent {}; +}; + +struct str_less { + [[nodiscard]] bool operator()(const std::string &t_lhs, + const std::string &t_rhs) const noexcept { + return t_lhs < t_rhs; + } + template + [[nodiscard]] constexpr bool operator()(const LHS &t_lhs, + const RHS &t_rhs) const noexcept { + return std::lexicographical_compare(t_lhs.begin(), t_lhs.end(), + t_rhs.begin(), t_rhs.end()); + } + struct is_transparent {}; +}; + +enum class Options { + No_Load_Modules, + Load_Modules, + No_External_Scripts, + External_Scripts +}; + +template +struct is_nothrow_forward_constructible + : std::bool_constant()})> {}; + +template +static inline constexpr bool is_nothrow_forward_constructible_v = + is_nothrow_forward_constructible::value; + +template +[[nodiscard]] constexpr auto make_container(T &&...t) { + Container c; + c.reserve(sizeof...(t)); + (c.push_back(std::forward(t)), ...); + return c; +} + +template +[[nodiscard]] auto make_vector(T &&...t) + -> std::vector...>> { + using container_type = std::vector...>>; + + return make_container(std::forward(t)...); +} + +[[nodiscard]] inline std::vector default_options() { +#ifdef CARBON_NO_DYNLOAD + return {Options::No_Load_Modules, Options::External_Scripts}; +#else + return {Options::Load_Modules, Options::External_Scripts}; +#endif +} +} // namespace Carbon +#endif diff --git a/src/carbon/extra/math.hpp b/src/carbon/extra/math.hpp new file mode 100644 index 00000000..8287bb8e --- /dev/null +++ b/src/carbon/extra/math.hpp @@ -0,0 +1,801 @@ +#ifndef CARBON_EXTRAS_MATH_HPP_ +#define CARBON_EXTRAS_MATH_HPP_ + +#include +#include + +#include "../carbon.hpp" + +namespace Carbon::extras::math { +// TRIG FUNCTIONS +template +ModulePtr cos(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::cos(p); }), "cos"); + return m; +} + +template +ModulePtr sin(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::sin(p); }), "sin"); + return m; +} + +template +ModulePtr tan(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::tan(p); }), "tan"); + return m; +} + +template +ModulePtr acos(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::acos(p); }), "acos"); + return m; +} + +template +ModulePtr asin(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::asin(p); }), "asin"); + return m; +} + +template +ModulePtr atan(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::atan(p); }), "atan"); + return m; +} + +template +ModulePtr atan2(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param1 p1, Param2 p2) { return std::atan2(p1, p2); }), + "atan2"); + return m; +} + +// HYPERBOLIC FUNCTIONS +template +ModulePtr cosh(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::cosh(p); }), "cosh"); + return m; +} + +template +ModulePtr sinh(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::sinh(p); }), "sinh"); + return m; +} + +template +ModulePtr tanh(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::tanh(p); }), "tanh"); + return m; +} + +#ifndef CARBON_EXTRAS_MATH_SKIP_ADVANCED +template +ModulePtr acosh(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::acosh(p); }), "acosh"); + return m; +} + +template +ModulePtr asinh(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::asinh(p); }), "asinh"); + return m; +} + +template +ModulePtr atanh(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::atanh(p); }), "atanh"); + return m; +} +#endif + +// EXPONENTIAL AND LOGARITHMIC FUNCTIONS +template +ModulePtr exp(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::exp(p); }), "exp"); + return m; +} + +template +ModulePtr frexp(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param1 p1, Param2 p2) { return std::frexp(p1, p2); }), + "frexp"); + return m; +} + +template +ModulePtr ldexp(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param1 p1, Param2 p2) { return std::ldexp(p1, p2); }), + "ldexp"); + return m; +} + +template +ModulePtr log(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::log(p); }), "log"); + return m; +} + +template +ModulePtr log10(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::log10(p); }), "log10"); + return m; +} + +template +ModulePtr modf(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param1 p1, Param2 p2) { return std::modf(p1, p2); }), + "modf"); + return m; +} + +#ifndef CARBON_EXTRAS_MATH_SKIP_ADVANCED +template +ModulePtr exp2(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::exp2(p); }), "exp2"); + return m; +} + +template +ModulePtr expm1(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::expm1(p); }), "expm1"); + return m; +} + +template +ModulePtr ilogb(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::ilogb(p); }), "ilogb"); + return m; +} + +template +ModulePtr log1p(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::log1p(p); }), "log1p"); + return m; +} + +template +ModulePtr log2(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::log2(p); }), "log2"); + return m; +} + +template +ModulePtr logb(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::logb(p); }), "logb"); + return m; +} + +template +ModulePtr scalbn(ModulePtr m = std::make_shared()) { + m->add( + Carbon::fun([](Param1 p1, Param2 p2) { return std::scalbn(p1, p2); }), + "scalbn"); + return m; +} + +template +ModulePtr scalbln(ModulePtr m = std::make_shared()) { + m->add( + Carbon::fun([](Param1 p1, Param2 p2) { return std::scalbln(p1, p2); }), + "scalbln"); + return m; +} +#endif + +// POWER FUNCTIONS +template +ModulePtr pow(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param1 p1, Param2 p2) { return std::pow(p1, p2); }), + "pow"); + return m; +} + +template +ModulePtr sqrt(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::sqrt(p); }), "sqrt"); + return m; +} + +#ifndef CARBON_EXTRAS_MATH_SKIP_ADVANCED +template +ModulePtr cbrt(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::cbrt(p); }), "cbrt"); + return m; +} + +template +ModulePtr hypot(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param1 p1, Param2 p2) { return std::hypot(p1, p2); }), + "hypot"); + return m; +} + +// ERROR AND GAMMA FUNCTIONS +template +ModulePtr erf(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::erf(p); }), "erf"); + return m; +} + +template +ModulePtr erfc(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::erfc(p); }), "erfc"); + return m; +} + +template +ModulePtr tgamma(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::tgamma(p); }), "tgamma"); + return m; +} + +template +ModulePtr lgamma(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::lgamma(p); }), "lgamma"); + return m; +} +#endif + +// ROUNDING AND REMAINDER FUNCTIONS +template +ModulePtr ceil(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::ceil(p); }), "ceil"); + return m; +} + +template +ModulePtr floor(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::floor(p); }), "floor"); + return m; +} + +template +ModulePtr fmod(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param1 p1, Param2 p2) { return std::fmod(p1, p2); }), + "fmod"); + return m; +} + +#ifndef CARBON_EXTRAS_MATH_SKIP_ADVANCED +template +ModulePtr trunc(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::trunc(p); }), "trunc"); + return m; +} + +template +ModulePtr round(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::round(p); }), "round"); + return m; +} + +template +ModulePtr lround(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::lround(p); }), "lround"); + return m; +} + +// long long ints do not work +template +ModulePtr llround(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::llround(p); }), "llround"); + return m; +} + +template +ModulePtr rint(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::rint(p); }), "rint"); + return m; +} + +template +ModulePtr lrint(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::lrint(p); }), "lrint"); + return m; +} + +// long long ints do not work +template +ModulePtr llrint(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::llrint(p); }), "llrint"); + return m; +} + +template +ModulePtr nearbyint(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::nearbyint(p); }), "nearbyint"); + return m; +} + +template +ModulePtr remainder(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun( + [](Param1 p1, Param2 p2) { return std::remainder(p1, p2); }), + "remainder"); + return m; +} + +template +ModulePtr remquo(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param1 p1, Param2 p2, Param3 p3) { + return std::remquo(p1, p2, p3); + }), + "remquo"); + return m; +} + +// FLOATING-POINT MANIPULATION FUNCTIONS +template +ModulePtr copysign(ModulePtr m = std::make_shared()) { + m->add( + Carbon::fun([](Param1 p1, Param2 p2) { return std::copysign(p1, p2); }), + "copysign"); + return m; +} + +template +ModulePtr nan(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::nan(p); }), "nan"); + return m; +} + +template +ModulePtr nextafter(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun( + [](Param1 p1, Param2 p2) { return std::nextafter(p1, p2); }), + "nextafter"); + return m; +} + +template +ModulePtr nexttoward(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun( + [](Param1 p1, Param2 p2) { return std::nexttoward(p1, p2); }), + "nexttoward"); + return m; +} + +// MINIMUM, MAXIMUM, DIFFERENCE FUNCTIONS +template +ModulePtr fdim(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param1 p1, Param2 p2) { return std::fdim(p1, p2); }), + "fdim"); + return m; +} + +template +ModulePtr fmax(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param1 p1, Param2 p2) { return std::fmax(p1, p2); }), + "fmax"); + return m; +} + +template +ModulePtr fmin(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param1 p1, Param2 p2) { return std::fmin(p1, p2); }), + "fmin"); + return m; +} + +// OTHER FUNCTIONS +template +ModulePtr fabs(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::fabs(p); }), "fabs"); + return m; +} +#endif + +template +ModulePtr abs(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::abs(p); }), "abs"); + return m; +} + +#ifndef CARBON_EXTRAS_MATH_SKIP_ADVANCED +template +ModulePtr fma(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param1 p1, Param2 p2, Param3 p3) { + return std::fma(p1, p2, p3); + }), + "fma"); + return m; +} + +// CLASSIFICATION FUNCTIONS +template +ModulePtr fpclassify(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::fpclassify(p); }), + "fpclassify"); + return m; +} +#endif + +template +ModulePtr isfinite(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::isfinite(p); }), "isfinite"); + return m; +} + +template +ModulePtr isinf(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::isinf(p); }), "isinf"); + return m; +} + +template +ModulePtr isnan(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::isnan(p); }), "isnan"); + return m; +} + +template +ModulePtr isnormal(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::isnormal(p); }), "isnormal"); + return m; +} + +template +ModulePtr signbit(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param p) { return std::signbit(p); }), "signbit"); + return m; +} + +// COMPARISON FUNCTIONS +template +ModulePtr isgreater(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun( + [](Param1 p1, Param2 p2) { return std::isgreater(p1, p2); }), + "isgreater"); + return m; +} + +template +ModulePtr isgreaterequal(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun([](Param1 p1, Param2 p2) { + return std::isgreaterequal(p1, p2); + }), + "isgreaterequal"); + return m; +} + +template +ModulePtr isless(ModulePtr m = std::make_shared()) { + m->add( + Carbon::fun([](Param1 p1, Param2 p2) { return std::isless(p1, p2); }), + "isless"); + return m; +} + +template +ModulePtr islessequal(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun( + [](Param1 p1, Param2 p2) { return std::islessequal(p1, p2); }), + "islessequal"); + return m; +} + +template +ModulePtr islessgreater(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun( + [](Param1 p1, Param2 p2) { return std::islessgreater(p1, p2); }), + "islessgreater"); + return m; +} + +template +ModulePtr isunordered(ModulePtr m = std::make_shared()) { + m->add(Carbon::fun( + [](Param1 p1, Param2 p2) { return std::isunordered(p1, p2); }), + "isunordered"); + return m; +} + +ModulePtr bootstrap(ModulePtr m = std::make_shared()) { + // TRIG FUNCTIONS + cos(m); + cos(m); + cos(m); + + sin(m); + sin(m); + sin(m); + + tan(m); + tan(m); + tan(m); + + acos(m); + acos(m); + acos(m); + + asin(m); + asin(m); + asin(m); + + atan(m); + atan(m); + atan(m); + + atan2(m); + atan2(m); + atan2(m); + + // HYPERBOLIC FUNCTIONS + cosh(m); + cosh(m); + cosh(m); + + sinh(m); + sinh(m); + sinh(m); + + tanh(m); + tanh(m); + tanh(m); + +#ifndef CARBON_EXTRAS_MATH_SKIP_ADVANCED + acosh(m); + acosh(m); + acosh(m); + + asinh(m); + asinh(m); + asinh(m); + + atanh(m); + atanh(m); + atanh(m); +#endif + + // EXPONENTIAL AND LOGARITHMIC FUNCTIONS + exp(m); + exp(m); + exp(m); + + frexp(m); + frexp(m); + frexp(m); + + ldexp(m); + ldexp(m); + ldexp(m); + + log(m); + log(m); + log(m); + + log10(m); + log10(m); + log10(m); + + modf(m); + modf(m); + modf(m); + +#ifndef CARBON_EXTRAS_MATH_SKIP_ADVANCED + exp2(m); + exp2(m); + exp2(m); + + expm1(m); + expm1(m); + expm1(m); + + ilogb(m); + ilogb(m); + ilogb(m); + + log1p(m); + log1p(m); + log1p(m); + + log2(m); + log2(m); + log2(m); + + logb(m); + logb(m); + logb(m); + + scalbn(m); + scalbn(m); + scalbn(m); + + scalbln(m); + scalbln(m); + scalbln(m); +#endif + + // POWER FUNCTIONS + pow(m); + pow(m); + pow(m); + + sqrt(m); + sqrt(m); + sqrt(m); + +#ifndef CARBON_EXTRAS_MATH_SKIP_ADVANCED + cbrt(m); + cbrt(m); + cbrt(m); + + hypot(m); + hypot(m); + hypot(m); + + // ERROR AND GAMMA FUNCTIONS + erf(m); + erf(m); + erf(m); + + erfc(m); + erfc(m); + erfc(m); + + tgamma(m); + tgamma(m); + tgamma(m); + + lgamma(m); + lgamma(m); + lgamma(m); +#endif + + // ROUNDING AND REMAINDER FUNCTIONS + ceil(m); + ceil(m); + ceil(m); + + floor(m); + floor(m); + floor(m); + + fmod(m); + fmod(m); + fmod(m); + +#ifndef CARBON_EXTRAS_MATH_SKIP_ADVANCED + trunc(m); + trunc(m); + trunc(m); + + round(m); + round(m); + round(m); + + lround(m); + lround(m); + lround(m); + + // long long ints do not work + llround(m); + llround(m); + llround(m); + + rint(m); + rint(m); + rint(m); + + lrint(m); + lrint(m); + lrint(m); + + // long long ints do not work + llrint(m); + llrint(m); + llrint(m); + + nearbyint(m); + nearbyint(m); + nearbyint(m); + + remainder(m); + remainder(m); + remainder(m); + + remquo(m); + remquo(m); + remquo(m); + + // FLOATING-POINT MANIPULATION FUNCTIONS + copysign(m); + copysign(m); + copysign(m); + + nan(m); + + nextafter(m); + nextafter(m); + nextafter(m); + + nexttoward(m); + nexttoward(m); + nexttoward(m); + + // MINIMUM, MAXIMUM, DIFFERENCE FUNCTIONS + fdim(m); + fdim(m); + fdim(m); + + fmax(m); + fmax(m); + fmax(m); + + fmin(m); + fmin(m); + fmin(m); + + // OTHER FUNCTIONS + fabs(m); + fabs(m); + fabs(m); +#endif + + abs(m); + abs(m); + abs(m); + +#ifndef CARBON_EXTRAS_MATH_SKIP_ADVANCED + fma(m); + fma(m); + fma(m); + + // CLASSIFICATION FUNCTIONS + fpclassify(m); + fpclassify(m); + fpclassify(m); +#endif + + isfinite(m); + isfinite(m); + isfinite(m); + + isinf(m); + isinf(m); + isinf(m); + + isnan(m); + isnan(m); + isnan(m); + + isnormal(m); + isnormal(m); + isnormal(m); + + signbit(m); + signbit(m); + signbit(m); + + // COMPARISON FUNCTIONS + isgreater(m); + isgreater(m); + isgreater(m); + + isgreaterequal(m); + isgreaterequal(m); + isgreaterequal(m); + + isless(m); + isless(m); + isless(m); + + islessequal(m); + islessequal(m); + islessequal(m); + + islessgreater(m); + islessgreater(m); + islessgreater(m); + + isunordered(m); + isunordered(m); + isunordered(m); + + return m; +} +} // namespace Carbon::extras::math + +#endif /* CARBON_EXTRAS_MATH_HPP_ */ \ No newline at end of file diff --git a/src/carbon/extra/stdlib.hpp b/src/carbon/extra/stdlib.hpp new file mode 100644 index 00000000..a592605e --- /dev/null +++ b/src/carbon/extra/stdlib.hpp @@ -0,0 +1,87 @@ +#ifndef CARBON_EXTRAS_STDLIB_HPP +#define CARBON_EXTRAS_STDLIB_HPP + +#include "../carbon.hpp" + +#include +#include +#include + +namespace Carbon::extras::stdlib { +template +ModulePtr optional(ModulePtr m = std::make_shared(), + const std::string &type = "") { + utility::add_class>( + *m, std::string("Optional") + type, + {constructor()>() + + }, + { + {fun(&std::optional::has_value), "has_value"}, + //{fun(&std::optional::value), "value"}, + //{fun(&std::optional::value_or), "value_or"}, + {fun(&std::optional::swap), "swap"}, + {fun(&std::optional::reset), "reset"}, + //{fun(&std::optional::emplace), "emplace"}, + //{fun(&std::optional::operator=), "operator="}, +#if __cplusplus >= 202110L + {fun(&std::optional::and_then), "and_then"}, + {fun(&std::optional::or_else), "or_else"}, + {fun(&std::optional::transform), "transform"}, +#endif + //{fun(&std::make_optional), std::string("make_Optional") + type} + }); + return m; +} + +ModulePtr timezone(ModulePtr m = std::make_shared()) { + utility::add_class( + *m, "Tzdb", {}, + {{fun(&std::chrono::tzdb::version), "version"}, + {fun(&std::chrono::tzdb::current_zone), "current_zone"}, + {fun(&std::chrono::tzdb::locate_zone), "locate_zone"}, + {fun(&std::chrono::tzdb::zones), "zones"}, + {fun(&std::chrono::tzdb::links), "links"}, + {fun(&std::chrono::tzdb::leap_seconds), "leap_seconds"}}); + + utility::add_class(*m, "TimezoneList", {}, {}); + + utility::add_class( + *m, "SysInfo", + { + + }, + {{fun(&std::chrono::sys_info::abbrev), "abbrev"}, + {fun(&std::chrono::sys_info::offset), "offset"}, + {fun(&std::chrono::sys_info::save), "save"}}); + + utility::add_class( + *m, "Timezone", {}, + { + //{fun&) + //const>(&std::chrono::time_zone::get_info), "get_info"} + }); + m->add(fun(&std::chrono::get_tzdb), "get_tzdb"); + m->add(fun(&std::chrono::get_tzdb_list), "get_tzdb_list"); + m->add(fun(&std::chrono::reload_tzdb), "reload_tzdb"); + m->add(fun(&std::chrono::remote_version), "remote_version"); + m->add(fun(&std::chrono::locate_zone), "locate_zone"); + m->add(fun(&std::chrono::current_zone), "current_zone"); + + return m; +} +/** + * Adds the String Methods to the given Carbon module. + */ +ModulePtr bootstrap(ModulePtr m = std::make_shared()) { + optional(m, "Int"); + optional(m, "Double"); + optional(m, "String"); + optional(m, "Bool"); + timezone(m); + return m; +} +} // namespace Carbon::extras::stdlib + +#endif /* CARBON_EXTRAS_STDLIB_HPP */ \ No newline at end of file diff --git a/src/carbon/extra/string.hpp b/src/carbon/extra/string.hpp new file mode 100644 index 00000000..a44dcf13 --- /dev/null +++ b/src/carbon/extra/string.hpp @@ -0,0 +1,161 @@ +#ifndef CARBON_EXTRAS_STRING_METHODS_HPP_ +#define CARBON_EXTRAS_STRING_METHODS_HPP_ + +#include +#include +#include +#include "../carbon.hpp" + +namespace Carbon::extras::string_methods { + +/** + * Replaces all occurances of a string within the given string. + * + * @code + * var hello = "Hello World" + * hello.replace("Hello", "Goodbye") + * // => "Goodbye World" + * @endcode + * + * @see replaceChar + */ +std::string replaceString(const std::string& subject, const std::string& search, + const std::string& replace) { + std::string result(subject); + size_t pos = 0; + while ((pos = result.find(search, pos)) != std::string::npos) { + result.replace(pos, search.length(), replace); + pos += replace.length(); + } + return result; +} + +/** + * Replaces all occurances of a character within the given character. + * + * @see replaceString + */ +std::string replaceChar(const std::string& subject, char search, char replace) { + std::string result(subject); + std::replace(result.begin(), result.end(), search, replace); + return result; +} + +/** + * Trims the given string. + */ +std::string trim(const std::string& subject) { + std::string result(subject); + std::string delimiters = "\t\n\v\f\r "; + result.erase(0, result.find_first_not_of(delimiters)); + result.erase(0, result.find_last_not_of(delimiters)); + return result; +} + +/** + * Trims the beginning of the given string. + */ +std::string trimStart(const std::string& subject) { + std::string result(subject); + std::string delimiters = "\t\n\v\f\r "; + result.erase(0, result.find_first_not_of(delimiters)); + return result; +} + +/** + * Trims the end of the given string. + */ +std::string trimEnd(const std::string& subject) { + std::string result(subject); + std::string delimiters = "\t\n\v\f\r "; + result.erase(result.find_last_not_of(delimiters) + 1); + return result; +} + +/** + * Splits the given string into a vector of strings. + * + * @code + * var input = "Hello|World|How|Are|You" + * var words = input.split("|") + * words[1] + * // => "World" + */ +std::vector split(const std::string& subject, + const std::string& token) { + std::string str(subject); + std::vector result; + while (str.size()) { + size_t index = str.find(token); + if (index != std::string::npos) { + result.push_back(str.substr(0, index)); + str = str.substr(index + token.size()); + if (str.size() == 0) { + result.push_back(str); + } + } else { + result.push_back(str); + str = ""; + } + } + return result; +} + +/** + * Convert the given string to lowercase letters. + */ +std::string toLowerCase(const std::string& subject) { + std::string result(subject); + std::transform(result.begin(), result.end(), result.begin(), + [](unsigned char c) { return std::tolower(c); }); + return result; +} + +/** + * Convert the given string to uppercase letters. + */ +std::string toUpperCase(const std::string& subject) { + std::string result(subject); + std::transform(result.begin(), result.end(), result.begin(), + [](unsigned char c) { return std::toupper(c); }); + return result; +} + +/** + * Checks if a string includes the given string. + * + * @see includesChar + */ +bool includes(const std::string& subject, const std::string& search) { + return subject.find(search) != std::string::npos; +} + +/** + * Checks if a string includes the given character. + * + * @see includes + */ +bool includesChar(const std::string& subject, char search) { + return subject.find(search) != std::string::npos; +} + +/** + * Adds the String Methods to the given Carbon module. + */ +ModulePtr bootstrap(ModulePtr m = std::make_shared()) { + m->add(fun(replaceString), "replace"); + m->add(fun(replaceChar), "replace"); + m->add(fun(trim), "trim"); + m->add(fun(split), "split"); + m->add(fun(toLowerCase), "toLowerCase"); + m->add(fun(toUpperCase), "toUpperCase"); + m->add(fun(includes), "includes"); + m->add(fun(includesChar), "includes"); + m->add(fun(trimStart), "trimStart"); + m->add(fun(trimEnd), "trimEnd"); + + return m; +} +} // namespace Carbon::extras::string_methods + +#endif /* CARBON_EXTRAS_STRING_METHODS_HPP_ */ \ No newline at end of file diff --git a/src/carbon/language/algebraic.hpp b/src/carbon/language/algebraic.hpp new file mode 100644 index 00000000..7c0d5d77 --- /dev/null +++ b/src/carbon/language/algebraic.hpp @@ -0,0 +1,164 @@ + + +#ifndef CARBON_ALGEBRAIC_HPP +#define CARBON_ALGEBRAIC_HPP + +#include "atom/algorithm/hash.hpp" + +#include + +namespace Carbon { +struct Operators { + enum class Opers { + equals, + less_than, + greater_than, + less_than_equal, + greater_than_equal, + not_equal, + assign, + pre_increment, + pre_decrement, + assign_product, + assign_sum, + assign_quotient, + assign_difference, + assign_bitwise_and, + assign_bitwise_or, + assign_shift_left, + assign_shift_right, + assign_remainder, + assign_bitwise_xor, + shift_left, + shift_right, + remainder, + bitwise_and, + bitwise_or, + bitwise_xor, + bitwise_complement, + sum, + quotient, + product, + difference, + unary_plus, + unary_minus, + invalid + }; + + constexpr static const char *to_string(Opers t_oper) noexcept { + constexpr const char *opers[] = { + "", "==", "<", ">", "<=", ">=", "!=", "", "=", "++", + "--", "*=", "+=", "/=", "-=", "", "&=", "|=", "<<=", ">>=", + "%=", "^=", "", "<<", ">>", "%", "&", "|", "^", "~", + "", "+", "/", "*", "-", "+", "-", ""}; + return opers[static_cast(t_oper)]; + } + + constexpr static Opers to_operator(std::string_view t_str, + bool t_is_unary = false) noexcept { +#ifdef CARBON_MSVC +#pragma warning(push) +#pragma warning(disable : 4307) +#endif + + const auto op_hash = Atom::Algorithm::fnv1a_hash(t_str); + switch (op_hash) { + case Atom::Algorithm::fnv1a_hash("=="): { + return Opers::equals; + } + case Atom::Algorithm::fnv1a_hash("<"): { + return Opers::less_than; + } + case Atom::Algorithm::fnv1a_hash(">"): { + return Opers::greater_than; + } + case Atom::Algorithm::fnv1a_hash("<="): { + return Opers::less_than_equal; + } + case Atom::Algorithm::fnv1a_hash(">="): { + return Opers::greater_than_equal; + } + case Atom::Algorithm::fnv1a_hash("!="): { + return Opers::not_equal; + } + case Atom::Algorithm::fnv1a_hash("="): { + return Opers::assign; + } + case Atom::Algorithm::fnv1a_hash("++"): { + return Opers::pre_increment; + } + case Atom::Algorithm::fnv1a_hash("--"): { + return Opers::pre_decrement; + } + case Atom::Algorithm::fnv1a_hash("*="): { + return Opers::assign_product; + } + case Atom::Algorithm::fnv1a_hash("+="): { + return Opers::assign_sum; + } + case Atom::Algorithm::fnv1a_hash("-="): { + return Opers::assign_difference; + } + case Atom::Algorithm::fnv1a_hash("&="): { + return Opers::assign_bitwise_and; + } + case Atom::Algorithm::fnv1a_hash("|="): { + return Opers::assign_bitwise_or; + } + case Atom::Algorithm::fnv1a_hash("<<="): { + return Opers::assign_shift_left; + } + case Atom::Algorithm::fnv1a_hash(">>="): { + return Opers::assign_shift_right; + } + case Atom::Algorithm::fnv1a_hash("%="): { + return Opers::assign_remainder; + } + case Atom::Algorithm::fnv1a_hash("^="): { + return Opers::assign_bitwise_xor; + } + case Atom::Algorithm::fnv1a_hash("<<"): { + return Opers::shift_left; + } + case Atom::Algorithm::fnv1a_hash(">>"): { + return Opers::shift_right; + } + case Atom::Algorithm::fnv1a_hash("%"): { + return Opers::remainder; + } + case Atom::Algorithm::fnv1a_hash("&"): { + return Opers::bitwise_and; + } + case Atom::Algorithm::fnv1a_hash("|"): { + return Opers::bitwise_or; + } + case Atom::Algorithm::fnv1a_hash("^"): { + return Opers::bitwise_xor; + } + case Atom::Algorithm::fnv1a_hash("~"): { + return Opers::bitwise_complement; + } + case Atom::Algorithm::fnv1a_hash("+"): { + return t_is_unary ? Opers::unary_plus : Opers::sum; + } + case Atom::Algorithm::fnv1a_hash("-"): { + return t_is_unary ? Opers::unary_minus : Opers::difference; + } + case Atom::Algorithm::fnv1a_hash("/"): { + return Opers::quotient; + } + case Atom::Algorithm::fnv1a_hash("*"): { + return Opers::product; + } + default: { + return Opers::invalid; + } + } +#ifdef CARBON_MSVC +#pragma warning(pop) +#endif + } +}; +} // namespace Carbon + +#endif /* _CARBON_ALGEBRAIC_HPP */ diff --git a/src/carbon/language/common.cpp b/src/carbon/language/common.cpp new file mode 100644 index 00000000..6d47ffdb --- /dev/null +++ b/src/carbon/language/common.cpp @@ -0,0 +1,424 @@ +#include "common.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include "../command/boxed_value.hpp" +#include "../command/dispatchkit.hpp" +#include "../command/proxy_functions.hpp" +#include "../defines.hpp" +#include "atom/experiment/type_info.hpp" + +namespace Carbon { +Parse_Location::Parse_Location(std::string t_fname, const int t_start_line, + const int t_start_col, const int t_end_line, + const int t_end_col) + : start(t_start_line, t_start_col), + end(t_end_line, t_end_col), + filename(std::make_shared(std::move(t_fname))) {} + +Parse_Location::Parse_Location(std::shared_ptr t_fname, + const int t_start_line, const int t_start_col, + const int t_end_line, const int t_end_col) + : start(t_start_line, t_start_col), + end(t_end_line, t_end_col), + filename(std::move(t_fname)) {} + +const std::string &AST_Node::filename() const noexcept { + return *location.filename; +} + +const File_Position &AST_Node::start() const noexcept { return location.start; } + +const File_Position &AST_Node::end() const noexcept { return location.end; } + +std::string AST_Node::pretty_print() const { + std::ostringstream oss; + + oss << text; + + for (auto &elem : get_children()) { + oss << elem.get().pretty_print() << ' '; + } + + return oss.str(); +} + +/// Prints the contents of an AST node, including its children, recursively +std::string AST_Node::to_string(const std::string &t_prepend) const { + std::ostringstream oss; + + oss << t_prepend << "(" << ast_node_type_to_string(this->identifier) << ") " + << this->text << " : " << this->location.start.line << ", " + << this->location.start.column << '\n'; + + for (auto &elem : get_children()) { + oss << elem.get().to_string(t_prepend + " "); + } + return oss.str(); +} + +AST_Node::AST_Node(std::string t_ast_node_text, AST_Node_Type t_id, + Parse_Location t_loc) + : identifier(t_id), + text(std::move(t_ast_node_text)), + location(std::move(t_loc)) {} + +const std::string &AST_Node_Trace::filename() const noexcept { + return *location.filename; +} + +const File_Position &AST_Node_Trace::start() const noexcept { + return location.start; +} + +const File_Position &AST_Node_Trace::end() const noexcept { + return location.end; +} + +std::string AST_Node_Trace::pretty_print() const { + std::ostringstream oss; + + oss << text; + + for (const auto &elem : children) { + oss << elem.pretty_print() << ' '; + } + + return oss.str(); +} + +std::vector AST_Node_Trace::get_children(const AST_Node &node) { + const auto node_children = node.get_children(); + return std::vector(node_children.begin(), + node_children.end()); +} + +AST_Node_Trace::AST_Node_Trace(const AST_Node &node) + : identifier(node.identifier), + text(node.text), + location(node.location), + children(get_children(node)) {} + +/// \brief Classes which may be thrown during error cases when ChaiScript is +/// executing. +namespace exception { +load_module_error::load_module_error(const std::string &t_reason) + : std::runtime_error(t_reason) {} + +load_module_error::load_module_error( + const std::string &t_name, const std::vector &t_errors) + : std::runtime_error(format_error(t_name, t_errors)) {} + +std::string load_module_error::format_error( + const std::string &t_name, const std::vector &t_errors) { + std::stringstream ss; + ss << "Error loading module '" << t_name << "'\n" + << " The following locations were searched:\n"; + + for (const auto &err : t_errors) { + ss << " " << err.what() << "\n"; + } + + return ss.str(); +} + +eval_error::eval_error( + const std::string &t_why, const File_Position &t_where, + const std::string &t_fname, const std::vector &t_parameters, + const std::vector &t_functions, + bool t_dot_notation, const Carbon::detail::Dispatch_Engine &t_ss) noexcept + : std::runtime_error( + format(t_why, t_where, t_fname, t_parameters, t_dot_notation, t_ss)), + reason(t_why), + start_position(t_where), + filename(t_fname), + detail(format_detail(t_functions, t_dot_notation, t_ss)) {} + +eval_error::eval_error( + const std::string &t_why, const std::vector &t_parameters, + const std::vector &t_functions, + bool t_dot_notation, const Carbon::detail::Dispatch_Engine &t_ss) noexcept + : std::runtime_error(format(t_why, t_parameters, t_dot_notation, t_ss)), + reason(t_why), + detail(format_detail(t_functions, t_dot_notation, t_ss)) {} + +eval_error::eval_error(const std::string &t_why, const File_Position &t_where, + const std::string &t_fname) noexcept + : std::runtime_error(format(t_why, t_where, t_fname)), + reason(t_why), + start_position(t_where), + filename(t_fname) {} + +eval_error::eval_error(const std::string &t_why) noexcept + : std::runtime_error("Error: \"" + t_why + "\" "), reason(t_why) {} + +std::string eval_error::pretty_print() const { + std::ostringstream ss; + + ss << what(); + if (!call_stack.empty()) { + ss << "during evaluation at (" << fname(call_stack[0]) << " " + << startpos(call_stack[0]) << ")\n"; + ss << '\n' << detail << '\n'; + ss << " " << fname(call_stack[0]) << " (" << startpos(call_stack[0]) + << ") '" << pretty(call_stack[0]) << "'"; + for (size_t j = 1; j < call_stack.size(); ++j) { + if (id(call_stack[j]) != Carbon::AST_Node_Type::Block && + id(call_stack[j]) != Carbon::AST_Node_Type::File) { + ss << '\n'; + ss << " from " << fname(call_stack[j]) << " (" + << startpos(call_stack[j]) << ") '" << pretty(call_stack[j]) + << "'"; + } + } + } + ss << '\n'; + return ss.str(); +} + +std::string eval_error::format_why(const std::string &t_why) { + return "Error: \"" + t_why + "\""; +} + +std::string eval_error::format_types( + const Const_Proxy_Function &t_func, bool t_dot_notation, + const Carbon::detail::Dispatch_Engine &t_ss) { + assert(t_func); + int arity = t_func->get_arity(); + std::vector types = t_func->get_param_types(); + + std::string retval; + if (arity == -1) { + retval = "(...)"; + if (t_dot_notation) { + retval = "(Object)." + retval; + } + } else if (types.size() <= 1) { + retval = "()"; + } else { + std::stringstream ss; + ss << "("; + + std::string paramstr; + + for (size_t index = 1; index != types.size(); ++index) { + paramstr += (types[index].is_const() ? "const " : ""); + paramstr += t_ss.get_type_name(types[index]); + + if (index == 1 && t_dot_notation) { + paramstr += ").("; + if (types.size() == 2) { + paramstr += ", "; + } + } else { + paramstr += ", "; + } + } + + ss << paramstr.substr(0, paramstr.size() - 2); + + ss << ")"; + retval = ss.str(); + } + + std::shared_ptr dynfun = + std::dynamic_pointer_cast( + t_func); + + if (dynfun && dynfun->has_parse_tree()) { + Proxy_Function f = dynfun->get_guard(); + + if (f) { + auto dynfunguard = std::dynamic_pointer_cast< + const dispatch::Dynamic_Proxy_Function>(f); + if (dynfunguard && dynfunguard->has_parse_tree()) { + retval += " : " + format_guard(dynfunguard->get_parse_tree()); + } + } + + retval += "\n Defined at " + + format_location(dynfun->get_parse_tree()); + } + + return retval; +} + +std::string eval_error::format_detail( + const std::vector &t_functions, + bool t_dot_notation, const Carbon::detail::Dispatch_Engine &t_ss) { + std::stringstream ss; + if (t_functions.size() == 1) { + assert(t_functions[0]); + ss << " Expected: " + << format_types(t_functions[0], t_dot_notation, t_ss) << '\n'; + } else { + ss << " " << t_functions.size() << " overloads available:\n"; + + for (const auto &t_function : t_functions) { + ss << " " << format_types((t_function), t_dot_notation, t_ss) + << '\n'; + } + } + + return ss.str(); +} + +std::string eval_error::format_parameters( + const std::vector &t_parameters, bool t_dot_notation, + const Carbon::detail::Dispatch_Engine &t_ss) { + std::stringstream ss; + ss << "("; + + if (!t_parameters.empty()) { + std::string paramstr; + + for (auto itr = t_parameters.begin(); itr != t_parameters.end(); + ++itr) { + paramstr += (itr->is_const() ? "const " : ""); + paramstr += t_ss.type_name(*itr); + + if (itr == t_parameters.begin() && t_dot_notation) { + paramstr += ").("; + if (t_parameters.size() == 1) { + paramstr += ", "; + } + } else { + paramstr += ", "; + } + } + + ss << paramstr.substr(0, paramstr.size() - 2); + } + ss << ")"; + + return ss.str(); +} + +std::string eval_error::format_filename(const std::string &t_fname) { + std::stringstream ss; + + if (t_fname != "__EVAL__") { + ss << "in '" << t_fname << "' "; + } else { + ss << "during evaluation "; + } + + return ss.str(); +} + +std::string eval_error::format_location(const File_Position &t_where) { + std::stringstream ss; + ss << "at (" << t_where.line << ", " << t_where.column << ")"; + return ss.str(); +} + +std::string eval_error::format(const std::string &t_why, + const File_Position &t_where, + const std::string &t_fname, + const std::vector &t_parameters, + bool t_dot_notation, + const Carbon::detail::Dispatch_Engine &t_ss) { + std::stringstream ss; + + ss << format_why(t_why); + ss << " "; + + ss << "With parameters: " + << format_parameters(t_parameters, t_dot_notation, t_ss); + ss << " "; + + ss << format_filename(t_fname); + ss << " "; + + ss << format_location(t_where); + + return ss.str(); +} + +std::string eval_error::format(const std::string &t_why, + const std::vector &t_parameters, + bool t_dot_notation, + const Carbon::detail::Dispatch_Engine &t_ss) { + std::stringstream ss; + + ss << format_why(t_why); + ss << " "; + + ss << "With parameters: " + << format_parameters(t_parameters, t_dot_notation, t_ss); + ss << " "; + + return ss.str(); +} + +std::string eval_error::format(const std::string &t_why, + const File_Position &t_where, + const std::string &t_fname) { + std::stringstream ss; + + ss << format_why(t_why); + ss << " "; + + ss << format_filename(t_fname); + ss << " "; + + ss << format_location(t_where); + + return ss.str(); +} + +file_not_found_error::file_not_found_error(const std::string &t_filename) + : std::runtime_error("File Not Found: " + t_filename), + filename(t_filename) {} + +} // namespace exception + +// static +bool AST_Node::get_bool_condition(const Boxed_Value &t_bv, + const Carbon::detail::Dispatch_State &t_ss) { + try { + return t_ss->boxed_cast(t_bv); + } catch (const exception::bad_boxed_cast &) { + throw exception::eval_error("Condition not boolean"); + } +} + +namespace eval { +namespace detail { + +Scope_Push_Pop::Scope_Push_Pop(const Carbon::detail::Dispatch_State &t_ds) + : m_ds(t_ds) { + m_ds->new_scope(m_ds.stack_holder()); +} + +Scope_Push_Pop::~Scope_Push_Pop() { m_ds->pop_scope(m_ds.stack_holder()); } + +Function_Push_Pop::Function_Push_Pop(const Carbon::detail::Dispatch_State &t_ds) + : m_ds(t_ds) { + m_ds->new_function_call(m_ds.stack_holder(), m_ds.conversion_saves()); +} + +Function_Push_Pop::~Function_Push_Pop() { + m_ds->pop_function_call(m_ds.stack_holder(), m_ds.conversion_saves()); +} + +void Function_Push_Pop::save_params(const Function_Params &t_params) { + m_ds->save_function_params(t_params); +} + +Stack_Push_Pop::Stack_Push_Pop(const Carbon::detail::Dispatch_State &t_ds) + : m_ds(t_ds) { + m_ds->new_stack(m_ds.stack_holder()); +} + +Stack_Push_Pop::~Stack_Push_Pop() { m_ds->pop_stack(m_ds.stack_holder()); } + +} // namespace detail +} // namespace eval +} // namespace Carbon diff --git a/src/carbon/language/common.hpp b/src/carbon/language/common.hpp new file mode 100644 index 00000000..ad424898 --- /dev/null +++ b/src/carbon/language/common.hpp @@ -0,0 +1,479 @@ + + +#ifndef CARBON_COMMON_HPP +#define CARBON_COMMON_HPP + +#include +#include +#include +#include +#include +#include + +#include +#include "../command/boxed_value.hpp" +#include "../command/dispatchkit.hpp" +#include "../command/proxy_functions.hpp" +#include "../defines.hpp" +#include "atom/experiment/type_info.hpp" + +namespace Carbon { +struct AST_Node; +struct AST_Node_Trace; +namespace exception { +struct eval_error; +} +} // namespace Carbon + +namespace Carbon { +struct Name_Validator { + template + static bool is_reserved_word(const T &s) noexcept { + const static std::unordered_set words{ + Atom::Algorithm::fnv1a_hash("def"), + Atom::Algorithm::fnv1a_hash("fun"), + Atom::Algorithm::fnv1a_hash("while"), + Atom::Algorithm::fnv1a_hash("for"), + Atom::Algorithm::fnv1a_hash("if"), + Atom::Algorithm::fnv1a_hash("else"), + Atom::Algorithm::fnv1a_hash("&&"), + Atom::Algorithm::fnv1a_hash("||"), + Atom::Algorithm::fnv1a_hash(","), + Atom::Algorithm::fnv1a_hash("auto"), + Atom::Algorithm::fnv1a_hash("return"), + Atom::Algorithm::fnv1a_hash("break"), + Atom::Algorithm::fnv1a_hash("true"), + Atom::Algorithm::fnv1a_hash("false"), + Atom::Algorithm::fnv1a_hash("class"), + Atom::Algorithm::fnv1a_hash("attr"), + Atom::Algorithm::fnv1a_hash("var"), + Atom::Algorithm::fnv1a_hash("global"), + Atom::Algorithm::fnv1a_hash("GLOBAL"), + Atom::Algorithm::fnv1a_hash("_"), + Atom::Algorithm::fnv1a_hash("__LINE__"), + Atom::Algorithm::fnv1a_hash("__FILE__"), + Atom::Algorithm::fnv1a_hash("__FUNC__"), + Atom::Algorithm::fnv1a_hash("__CLASS__")}; + + return words.count(Atom::Algorithm::fnv1a_hash(s)) == 1; + } + + template + static bool valid_object_name(const T &name) noexcept { + return name.find("::") == std::string::npos && !is_reserved_word(name); + } + + template + static void validate_object_name(const T &name) { + if (is_reserved_word(name)) { + throw exception::reserved_word_error(std::string(name)); + } + + if (name.find("::") != std::string::npos) { + throw exception::illegal_name_error(std::string(name)); + } + } +}; + +/// Signature of module entry point that all binary loadable modules must +/// implement. +using Create_Module_Func = ModulePtr (*)(); + +/// Types of AST nodes available to the parser and eval +enum class AST_Node_Type { + Id, + Fun_Call, + Unused_Return_Fun_Call, + Arg_List, + Equation, + Var_Decl, + Assign_Decl, + Array_Call, + Dot_Access, + Lambda, + Block, + Scopeless_Block, + Def, + While, + If, + For, + Ranged_For, + Inline_Array, + Inline_Map, + Return, + File, + Prefix, + Break, + Continue, + Map_Pair, + Value_Range, + Inline_Range, + Try, + Catch, + Finally, + Method, + Attr_Decl, + Logical_And, + Logical_Or, + Reference, + Switch, + Case, + Default, + Noop, + Class, + Binary, + Arg, + Global_Decl, + Constant, + Compiled +}; + +enum class Operator_Precedence { + Ternary_Cond, + Logical_Or, + Logical_And, + Bitwise_Or, + Bitwise_Xor, + Bitwise_And, + Equality, + Comparison, + Shift, + Addition, + Multiplication, + Prefix +}; + +namespace { +/// Helper lookup to get the name of each node type +constexpr const char *ast_node_type_to_string( + AST_Node_Type ast_node_type) noexcept { + constexpr const char *const ast_node_types[] = { + "Id", "Fun_Call", "Unused_Return_Fun_Call", + "Arg_List", "Equation", "Var_Decl", + "Assign_Decl", "Array_Call", "Dot_Access", + "Lambda", "Block", "Scopeless_Block", + "Def", "While", "If", + "For", "Ranged_For", "Inline_Array", + "Inline_Map", "Return", "File", + "Prefix", "Break", "Continue", + "Map_Pair", "Value_Range", "Inline_Range", + "Try", "Catch", "Finally", + "Method", "Attr_Decl", "Logical_And", + "Logical_Or", "Reference", "Switch", + "Case", "Default", "Noop", + "Class", "Binary", "Arg", + "Global_Decl", "Constant", "Compiled"}; + + return ast_node_types[static_cast(ast_node_type)]; +} +} // namespace + +/// \brief Convenience type for file positions +struct File_Position { + int line = 0; + int column = 0; + + constexpr File_Position(int t_file_line, int t_file_column) noexcept + : line(t_file_line), column(t_file_column) {} + + constexpr File_Position() noexcept = default; +}; + +struct Parse_Location { + Parse_Location(std::string t_fname = "", const int t_start_line = 0, + const int t_start_col = 0, const int t_end_line = 0, + const int t_end_col = 0); + + Parse_Location(std::shared_ptr t_fname, + const int t_start_line = 0, const int t_start_col = 0, + const int t_end_line = 0, const int t_end_col = 0); + + File_Position start; + File_Position end; + std::shared_ptr filename; +}; + +/// \brief Struct that doubles as both a parser ast_node and an AST node. +struct AST_Node { +public: + const AST_Node_Type identifier; + const std::string text; + Parse_Location location; + + const std::string &filename() const noexcept; + + const File_Position &start() const noexcept; + + const File_Position &end() const noexcept; + + std::string pretty_print() const; + + virtual std::vector> get_children() + const = 0; + virtual Boxed_Value eval( + const Carbon::detail::Dispatch_State &t_e) const = 0; + + /// Prints the contents of an AST node, including its children, recursively + std::string to_string(const std::string &t_prepend = "") const; + + static bool get_bool_condition( + const Boxed_Value &t_bv, const Carbon::detail::Dispatch_State &t_ss); + + virtual ~AST_Node() noexcept = default; + AST_Node(AST_Node &&) = default; + AST_Node &operator=(AST_Node &&) = delete; + AST_Node(const AST_Node &) = delete; + AST_Node &operator=(const AST_Node &) = delete; + +protected: + AST_Node(std::string t_ast_node_text, AST_Node_Type t_id, + Parse_Location t_loc); +}; + +/// \brief Typedef for pointers to AST_Node objects. Used in building of the +/// AST_Node tree +using AST_NodePtr = std::unique_ptr; +using AST_NodePtr_Const = std::unique_ptr; + +struct AST_Node_Trace { + const AST_Node_Type identifier; + const std::string text; + Parse_Location location; + + const std::string &filename() const noexcept; + + const File_Position &start() const noexcept; + + const File_Position &end() const noexcept; + + std::string pretty_print() const; + + std::vector get_children(const AST_Node &node); + + AST_Node_Trace(const AST_Node &node); + + std::vector children; +}; + +/// \brief Classes which may be thrown during error cases when ChaiScript is +/// executing. +namespace exception { +/// \brief Thrown if an error occurs while attempting to load a binary module +struct load_module_error : std::runtime_error { + explicit load_module_error(const std::string &t_reason); + + load_module_error(const std::string &t_name, + const std::vector &t_errors); + + load_module_error(const load_module_error &) = default; + ~load_module_error() noexcept override = default; + + static std::string format_error( + const std::string &t_name, + const std::vector &t_errors); +}; + +/// Errors generated during parsing or evaluation +struct eval_error : std::runtime_error { + std::string reason; + File_Position start_position; + std::string filename; + std::string detail; + std::vector call_stack; + + eval_error(const std::string &t_why, const File_Position &t_where, + const std::string &t_fname, + const std::vector &t_parameters, + const std::vector &t_functions, + bool t_dot_notation, + const Carbon::detail::Dispatch_Engine &t_ss) noexcept; + + eval_error(const std::string &t_why, + const std::vector &t_parameters, + const std::vector &t_functions, + bool t_dot_notation, + const Carbon::detail::Dispatch_Engine &t_ss) noexcept; + + eval_error(const std::string &t_why, const File_Position &t_where, + const std::string &t_fname) noexcept; + + explicit eval_error(const std::string &t_why) noexcept; + + eval_error(const eval_error &) = default; + + std::string pretty_print() const; + + ~eval_error() noexcept override = default; + +private: + template + static AST_Node_Type id(const T &t) noexcept { + return t.identifier; + } + + template + static std::string pretty(const T &t) { + return t.pretty_print(); + } + + template + static const std::string &fname(const T &t) noexcept { + return t.filename(); + } + + template + static std::string startpos(const T &t) { + std::ostringstream oss; + oss << t.start().line << ", " << t.start().column; + return oss.str(); + } + + static std::string format_why(const std::string &t_why); + + static std::string format_types( + const Const_Proxy_Function &t_func, bool t_dot_notation, + const Carbon::detail::Dispatch_Engine &t_ss); + + template + static std::string format_guard(const T &t) { + return t.pretty_print(); + } + + template + static std::string format_location(const T &t) { + std::ostringstream oss; + oss << "(" << t.filename() << " " << t.start().line << ", " + << t.start().column << ")"; + return oss.str(); + } + + static std::string format_detail( + const std::vector &t_functions, + bool t_dot_notation, const Carbon::detail::Dispatch_Engine &t_ss); + + static std::string format_parameters( + const std::vector &t_parameters, bool t_dot_notation, + const Carbon::detail::Dispatch_Engine &t_ss); + + static std::string format_filename(const std::string &t_fname); + + static std::string format_location(const File_Position &t_where); + + static std::string format(const std::string &t_why, + const File_Position &t_where, + const std::string &t_fname, + const std::vector &t_parameters, + bool t_dot_notation, + const Carbon::detail::Dispatch_Engine &t_ss); + + static std::string format(const std::string &t_why, + const std::vector &t_parameters, + bool t_dot_notation, + const Carbon::detail::Dispatch_Engine &t_ss); + + static std::string format(const std::string &t_why, + const File_Position &t_where, + const std::string &t_fname); +}; + +/// Errors generated when loading a file +struct file_not_found_error : std::runtime_error { + explicit file_not_found_error(const std::string &t_filename); + + file_not_found_error(const file_not_found_error &) = default; + ~file_not_found_error() noexcept override = default; + + std::string filename; +}; + +} // namespace exception + +namespace parser { +class Carbon_Parser_Base { +public: + virtual AST_NodePtr parse(const std::string &t_input, + const std::string &t_fname) = 0; + virtual void debug_print(const AST_Node &t, + std::string prepend = "") const = 0; + virtual void *get_tracer_ptr() = 0; + virtual ~Carbon_Parser_Base() = default; + Carbon_Parser_Base() = default; + Carbon_Parser_Base(Carbon_Parser_Base &&) = default; + Carbon_Parser_Base &operator=(Carbon_Parser_Base &&) = delete; + Carbon_Parser_Base &operator=(const Carbon_Parser_Base &&) = delete; + + template + T &get_tracer() noexcept { + // to do type check this somehow? + return *static_cast(get_tracer_ptr()); + } + +protected: + Carbon_Parser_Base(const Carbon_Parser_Base &) = default; +}; +} // namespace parser + +namespace eval { +namespace detail { +/// Special type for returned values +struct Return_Value { + Boxed_Value retval; +}; + +/// Special type indicating a call to 'break' +struct Break_Loop {}; + +/// Special type indicating a call to 'continue' +struct Continue_Loop {}; + +/// Creates a new scope then pops it on destruction +struct Scope_Push_Pop { + Scope_Push_Pop(Scope_Push_Pop &&) = default; + Scope_Push_Pop &operator=(Scope_Push_Pop &&) = delete; + Scope_Push_Pop(const Scope_Push_Pop &) = delete; + Scope_Push_Pop &operator=(const Scope_Push_Pop &) = delete; + + explicit Scope_Push_Pop(const Carbon::detail::Dispatch_State &t_ds); + + ~Scope_Push_Pop(); + +private: + const Carbon::detail::Dispatch_State &m_ds; +}; + +/// Creates a new function call and pops it on destruction +struct Function_Push_Pop { + Function_Push_Pop(Function_Push_Pop &&) = default; + Function_Push_Pop &operator=(Function_Push_Pop &&) = delete; + Function_Push_Pop(const Function_Push_Pop &) = delete; + Function_Push_Pop &operator=(const Function_Push_Pop &) = delete; + + explicit Function_Push_Pop(const Carbon::detail::Dispatch_State &t_ds); + + ~Function_Push_Pop(); + + void save_params(const Function_Params &t_params); + +private: + const Carbon::detail::Dispatch_State &m_ds; +}; + +/// Creates a new scope then pops it on destruction +struct Stack_Push_Pop { + Stack_Push_Pop(Stack_Push_Pop &&) = default; + Stack_Push_Pop &operator=(Stack_Push_Pop &&) = delete; + Stack_Push_Pop(const Stack_Push_Pop &) = delete; + Stack_Push_Pop &operator=(const Stack_Push_Pop &) = delete; + + explicit Stack_Push_Pop(const Carbon::detail::Dispatch_State &t_ds); + + ~Stack_Push_Pop(); + +private: + const Carbon::detail::Dispatch_State &m_ds; +}; +} // namespace detail +} // namespace eval +} // namespace Carbon + +#endif /* _CARBON_COMMON_HPP */ diff --git a/src/carbon/language/engine.cpp b/src/carbon/language/engine.cpp new file mode 100644 index 00000000..ca1ff89c --- /dev/null +++ b/src/carbon/language/engine.cpp @@ -0,0 +1,565 @@ +#include "engine.hpp" + +namespace Carbon { +Boxed_Value Carbon_Basic::do_eval(const std::string &t_input, + const std::string &t_filename = "__EVAL__", + bool /* t_internal*/ = false) { + try { + const auto p = m_parser->parse(t_input, t_filename); + return p->eval(Carbon::detail::Dispatch_State(m_engine)); + } catch (Carbon::eval::detail::Return_Value &rv) { + return rv.retval; + } +} + +Boxed_Value Carbon_Basic::internal_eval_file(const std::string &t_filename) { + for (const auto &path : m_use_paths) { + try { + const auto appendedpath = path + t_filename; + return do_eval(load_file(appendedpath), appendedpath, true); + } catch (const exception::file_not_found_error &) { + // failed to load, try the next path + } catch (const exception::eval_error &t_ee) { + throw Boxed_Value(t_ee); + } + } + + // failed to load by any name + throw exception::file_not_found_error(t_filename); +} + +Boxed_Value Carbon_Basic::internal_eval(const std::string &t_e) { + try { + return do_eval(t_e, "__EVAL__", true); + } catch (const exception::eval_error &t_ee) { + throw Boxed_Value(t_ee); + } +} + +Carbon::detail::Dispatch_Engine &Carbon_Basic::get_eval_engine() noexcept { + return m_engine; +} + +void Carbon_Basic::build_eval_system(const ModulePtr &t_lib, + const std::vector &t_opts) { + if (t_lib) { + add(t_lib); + } + + m_engine.add(fun([this]() { m_engine.dump_system(); }), "dump_system"); + m_engine.add( + fun([this](const Boxed_Value &t_bv) { m_engine.dump_object(t_bv); }), + "dump_object"); + m_engine.add( + fun([this](const Boxed_Value &t_bv, const std::string &t_type) { + return m_engine.is_type(t_bv, t_type); + }), + "is_type"); + m_engine.add(fun([this](const Boxed_Value &t_bv) { + return m_engine.type_name(t_bv); + }), + "type_name"); + m_engine.add(fun([this](const std::string &t_f) { + return m_engine.function_exists(t_f); + }), + "function_exists"); + m_engine.add(fun([this]() { return m_engine.get_function_objects(); }), + "get_functions"); + m_engine.add(fun([this]() { return m_engine.get_scripting_objects(); }), + "get_objects"); + + m_engine.add(dispatch::make_dynamic_proxy_function( + [this](const Function_Params &t_params) { + return m_engine.call_exists(t_params); + }), + "call_exists"); + + m_engine.add( + fun([this](const dispatch::Proxy_Function_Base &t_fun, + const std::vector &t_params) -> Boxed_Value { + Type_Conversions_State s( + this->m_engine.conversions(), + this->m_engine.conversions().conversion_saves()); + return t_fun(Function_Params{t_params}, s); + }), + "call"); + + m_engine.add(fun([this](const Type_Info &t_ti) { + return m_engine.get_type_name(t_ti); + }), + "name"); + + m_engine.add(fun([this](const std::string &t_type_name, bool t_throw) { + return m_engine.get_type(t_type_name, t_throw); + }), + "type"); + m_engine.add(fun([this](const std::string &t_type_name) { + return m_engine.get_type(t_type_name, true); + }), + "type"); + + m_engine.add( + fun([this]( + const Type_Info &t_from, const Type_Info &t_to, + const std::function &t_func) { + m_engine.add(Carbon::type_conversion(t_from, t_to, t_func)); + }), + "add_type_conversion"); + + if (std::find(t_opts.begin(), t_opts.end(), Options::No_Load_Modules) == + t_opts.end() && + std::find(t_opts.begin(), t_opts.end(), Options::Load_Modules) != + t_opts.end()) { + m_engine.add( + fun([this](const std::string &t_module, const std::string &t_file) { + load_module(t_module, t_file); + }), + "load_module"); + m_engine.add(fun([this](const std::string &t_module) { + return load_module(t_module); + }), + "load_module"); + } + + if (std::find(t_opts.begin(), t_opts.end(), Options::No_External_Scripts) == + t_opts.end() && + std::find(t_opts.begin(), t_opts.end(), Options::External_Scripts) != + t_opts.end()) { + m_engine.add( + fun([this](const std::string &t_file) { return use(t_file); }), + "use"); + m_engine.add(fun([this](const std::string &t_file) { + return internal_eval_file(t_file); + }), + "eval_file"); + } + + m_engine.add( + fun([this](const std::string &t_str) { return internal_eval(t_str); }), + "eval"); + m_engine.add(fun([this](const AST_Node &t_ast) { return eval(t_ast); }), + "eval"); + + m_engine.add(fun([this](const std::string &t_str, const bool t_dump) { + return parse(t_str, t_dump); + }), + "parse"); + m_engine.add(fun([this](const std::string &t_str) { return parse(t_str); }), + "parse"); + + m_engine.add( + fun([this](const Boxed_Value &t_bv, const std::string &t_name) { + add_global_const(t_bv, t_name); + }), + "add_global_const"); + m_engine.add( + fun([this](const Boxed_Value &t_bv, const std::string &t_name) { + add_global(t_bv, t_name); + }), + "add_global"); + m_engine.add( + fun([this](const Boxed_Value &t_bv, const std::string &t_name) { + set_global(t_bv, t_name); + }), + "set_global"); + + // why this unused parameter to Namespace? + m_engine.add(fun([this](const std::string &t_namespace_name) { + register_namespace([](Namespace & /*space*/) noexcept {}, + t_namespace_name); + import(t_namespace_name); + }), + "namespace"); + m_engine.add(fun([this](const std::string &t_namespace_name) { + import(t_namespace_name); + }), + "import"); +} + +bool Carbon_Basic::skip_bom(std::ifstream &infile) { + size_t bytes_needed = 3; + char buffer[3]; + + memset(buffer, '\0', bytes_needed); + + infile.read(buffer, static_cast(bytes_needed)); + + if ((buffer[0] == '\xef') && (buffer[1] == '\xbb') && + (buffer[2] == '\xbf')) { + infile.seekg(3); + return true; + } + + infile.seekg(0); + + return false; +} + +std::string Carbon_Basic::load_file(const std::string &t_filename) { + std::ifstream infile(t_filename.c_str(), + std::ios::in | std::ios::ate | std::ios::binary); + + if (!infile.is_open()) { + throw Carbon::exception::file_not_found_error(t_filename); + } + + auto size = infile.tellg(); + infile.seekg(0, std::ios::beg); + + assert(size >= 0); + + if (skip_bom(infile)) { + size -= 3; // decrement the BOM size from file size, otherwise + // we'll get parsing errors + assert(size >= 0); // and check if there's more text + } + + if (size == std::streampos(0)) { + return std::string(); + } else { + std::vector v(static_cast(size)); + infile.read(&v[0], static_cast(size)); + return std::string(v.begin(), v.end()); + } +} + +std::vector ensure_minimum_path_vec( + std::vector paths) { + if (paths.empty()) { + return {""}; + } else { + return paths; + } +} + +Carbon_Basic::Carbon_Basic( + const ModulePtr &t_lib, + std::unique_ptr &&parser, + std::vector t_module_paths = {}, + std::vector t_use_paths = {}, + const std::vector &t_opts = Carbon::default_options()) + : m_module_paths(ensure_minimum_path_vec(std::move(t_module_paths))), + m_use_paths(ensure_minimum_path_vec(std::move(t_use_paths))), + m_parser(std::move(parser)), + m_engine(*m_parser) { +#if !defined(CARBON_NO_DYNLOAD) && defined(_POSIX_VERSION) && \ + !defined(__CYGWIN__) + // If on Unix, add the path of the current executable to the module + // search path as windows would do + + union cast_union { + Boxed_Value (Carbon_Basic::*in_ptr)(const std::string &); + void *out_ptr; + }; + + Dl_info rInfo; + memset(&rInfo, 0, sizeof(rInfo)); + cast_union u; + u.in_ptr = &Carbon_Basic::use; + if ((dladdr(static_cast(u.out_ptr), &rInfo) != 0) && + (rInfo.dli_fname != nullptr)) { + std::string dllpath(rInfo.dli_fname); + const size_t lastslash = dllpath.rfind('/'); + if (lastslash != std::string::npos) { + dllpath.erase(lastslash); + } + + // Let's see if this is a link that we should expand + std::vector buf(2048); + const auto pathlen = + readlink(dllpath.c_str(), &buf.front(), buf.size()); + if (pathlen > 0 && static_cast(pathlen) < buf.size()) { + dllpath = std::string(&buf.front(), static_cast(pathlen)); + } + + m_module_paths.insert(m_module_paths.begin(), dllpath + "/"); + } +#endif + build_eval_system(t_lib, t_opts); +} + +#ifndef CARBON_NO_DYNLOAD +Carbon_Basic::Carbon_Basic( + std::unique_ptr &&parser, + std::vector t_module_paths = {}, + std::vector t_use_paths = {}, + const std::vector &t_opts = Carbon::default_options()) + : Carbon_Basic({}, std::move(parser), t_module_paths, t_use_paths, t_opts) { + try { + // attempt to load the stdlib + load_module("stdlib-" + Build_Info::version()); + } catch (const exception::load_module_error &t_err) { + std::cout + << "An error occurred while trying to load the chaiscript " + "standard library.\n" + "\n" + "You must either provide a standard library, or compile it " + "in.\n" + "For an example of compiling the standard library in,\n" + "see: https://gist.github.com/lefticus/9456197\n" + "Compiling the stdlib in is the recommended and MOST " + "SUPPORTED method.\n" + "\n\n" + << t_err.what(); + throw; + } +} +#else // CARBON_NO_DYNLOAD +Carbon_Basic::Carbon_Basic(std::unique_ptr &&parser, + std::vector t_module_paths = {}, + std::vector t_use_paths = {}, + const std::vector &t_opts = + Carbon::default_options()) = delete; +#endif + +parser::Carbon_Parser_Base &Carbon_Basic::get_parser() noexcept { + return *m_parser; +} + +const Boxed_Value Carbon_Basic::eval(const AST_Node &t_ast) { + try { + return t_ast.eval(Carbon::detail::Dispatch_State(m_engine)); + } catch (const exception::eval_error &t_ee) { + throw Boxed_Value(t_ee); + } +} + +AST_NodePtr Carbon_Basic::parse(const std::string &t_input, + const bool t_debug_print) { + auto ast = m_parser->parse(t_input, "PARSE"); + if (t_debug_print) { + m_parser->debug_print(*ast); + } + return ast; +} + +std::string Carbon_Basic::get_type_name(const Type_Info &ti) const { + return m_engine.get_type_name(ti); +} + +Boxed_Value Carbon_Basic::use(const std::string &t_filename) { + for (const auto &path : m_use_paths) { + const auto appendedpath = path + t_filename; + try { + Carbon::detail::threading::unique_lock< + Carbon::detail::threading::recursive_mutex> + l(m_use_mutex); + Carbon::detail::threading::unique_lock< + Carbon::detail::threading::shared_mutex> + l2(m_mutex); + + Boxed_Value retval; + + if (m_used_files.count(appendedpath) == 0) { + l2.unlock(); + retval = eval_file(appendedpath); + l2.lock(); + m_used_files.insert(appendedpath); + } + + return retval; // return, we loaded it, or it was already + // loaded + } catch (const exception::file_not_found_error &e) { + if (e.filename != appendedpath) { + // a nested file include failed + throw; + } + // failed to load, try the next path + } + } + + // failed to load by any name + throw exception::file_not_found_error(t_filename); +} + +Carbon_Basic &Carbon_Basic::add_global_const(const Boxed_Value &t_bv, + const std::string &t_name) { + Name_Validator::validate_object_name(t_name); + m_engine.add_global_const(t_bv, t_name); + return *this; +} + +Carbon_Basic &Carbon_Basic::add_global(const Boxed_Value &t_bv, + const std::string &t_name) { + Name_Validator::validate_object_name(t_name); + m_engine.add_global(t_bv, t_name); + return *this; +} + +Carbon_Basic &Carbon_Basic::set_global(const Boxed_Value &t_bv, + const std::string &t_name) { + Name_Validator::validate_object_name(t_name); + m_engine.set_global(t_bv, t_name); + return *this; +} + +Carbon_Basic::State Carbon_Basic::get_state() const { + Carbon::detail::threading::lock_guard< + Carbon::detail::threading::recursive_mutex> + l(m_use_mutex); + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l2(m_mutex); + + State s; + s.used_files = m_used_files; + s.engine_state = m_engine.get_state(); + s.active_loaded_modules = m_active_loaded_modules; + return s; +} + +void Carbon_Basic::set_state(const State &t_state) { + Carbon::detail::threading::lock_guard< + Carbon::detail::threading::recursive_mutex> + l(m_use_mutex); + Carbon::detail::threading::shared_lock< + Carbon::detail::threading::shared_mutex> + l2(m_mutex); + + m_used_files = t_state.used_files; + m_active_loaded_modules = t_state.active_loaded_modules; + m_engine.set_state(t_state.engine_state); +} + +std::map Carbon_Basic::get_locals() const { + return m_engine.get_locals(); +} + +void Carbon_Basic::set_locals( + const std::map &t_locals) { + m_engine.set_locals(t_locals); +} + +Carbon_Basic &Carbon_Basic::add(const Type_Conversion &d) { + m_engine.add(d); + return *this; +} + +Carbon_Basic &Carbon_Basic::add(const ModulePtr &t_p) { + t_p->apply(*this, this->get_eval_engine()); + return *this; +} + +std::string Carbon_Basic::load_module(const std::string &t_module_name) { +#ifdef CARBON_NO_DYNLOAD + throw Carbon::exception::load_module_error( + "Loadable module support was disabled (CARBON_NO_DYNLOAD)"); +#else + std::vector errors; + std::string version_stripped_name = t_module_name; + size_t version_pos = + version_stripped_name.find("-" + Build_Info::version()); + if (version_pos != std::string::npos) { + version_stripped_name.erase(version_pos); + } + + std::vector prefixes{"lib", "cyg", ""}; + + std::vector postfixes{".dll", ".so", ".bundle", ""}; + + for (auto &elem : m_module_paths) { + for (auto &prefix : prefixes) { + for (auto &postfix : postfixes) { + try { + const auto name = elem + prefix + t_module_name + postfix; + // std::cerr << "trying location: " << name << '\n'; + load_module(version_stripped_name, name); + return name; + } catch (const Carbon::exception::load_module_error &e) { + // std::cerr << "error: " << e.what() << '\n'; + errors.push_back(e); + // Try next set + } + } + } + } + + throw Carbon::exception::load_module_error(t_module_name, errors); +#endif +} + +void Carbon_Basic::load_module(const std::string &t_module_name, + const std::string &t_filename) { + Carbon::detail::threading::lock_guard< + Carbon::detail::threading::recursive_mutex> + l(m_use_mutex); + + if (m_loaded_modules.count(t_module_name) == 0) { + detail::Loadable_Module_Ptr lm( + new detail::Loadable_Module(t_module_name, t_filename)); + m_loaded_modules[t_module_name] = lm; + m_active_loaded_modules.insert(t_module_name); + add(lm->m_moduleptr); + } else if (m_active_loaded_modules.count(t_module_name) == 0) { + m_active_loaded_modules.insert(t_module_name); + add(m_loaded_modules[t_module_name]->m_moduleptr); + } +} + +Boxed_Value Carbon_Basic::operator()( + const std::string &t_script, + const Exception_Handler &t_handler = Exception_Handler()) { + return eval(t_script, t_handler); +} + +Boxed_Value Carbon_Basic::eval( + const std::string &t_input, + const Exception_Handler &t_handler, + const std::string &t_filename) { + try { + return do_eval(t_input, t_filename); + } catch (Boxed_Value &bv) { + if (t_handler) { + t_handler->handle(bv, m_engine); + } + throw; + } +} + +Boxed_Value Carbon_Basic::eval_file( + const std::string &t_filename, + const Exception_Handler &t_handler) { + return eval(load_file(t_filename), t_handler, t_filename); +} + +void Carbon_Basic::import(const std::string &t_namespace_name) { + Carbon::detail::threading::unique_lock< + Carbon::detail::threading::recursive_mutex> + l(m_use_mutex); + + if (m_engine.get_scripting_objects().count(t_namespace_name)) { + throw std::runtime_error("Namespace: " + t_namespace_name + + " was already defined"); + } else if (m_namespace_generators.count(t_namespace_name)) { + m_engine.add_global( + var(std::ref(m_namespace_generators[t_namespace_name]())), + t_namespace_name); + } else { + throw std::runtime_error("No registered namespace: " + + t_namespace_name); + } +} + +void Carbon_Basic::register_namespace( + const std::function &t_namespace_generator, + const std::string &t_namespace_name) { + Carbon::detail::threading::unique_lock< + Carbon::detail::threading::recursive_mutex> + l(m_use_mutex); + + if (!m_namespace_generators.count(t_namespace_name)) { + // contain the namespace object memory within the + // m_namespace_generators map + m_namespace_generators.emplace( + std::make_pair(t_namespace_name, + [=, space = Namespace()]() mutable -> Namespace & { + t_namespace_generator(space); + return space; + })); + } else { + throw std::runtime_error("Namespace: " + t_namespace_name + + " was already registered."); + } +} + +} // namespace Carbon diff --git a/src/carbon/language/engine.hpp b/src/carbon/language/engine.hpp new file mode 100644 index 00000000..c41cb420 --- /dev/null +++ b/src/carbon/language/engine.hpp @@ -0,0 +1,404 @@ + + +#ifndef CARBON_ENGINE_HPP +#define CARBON_ENGINE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../command/boxed_cast_helper.hpp" +#include "../command/boxed_value.hpp" +#include "../command/dispatchkit.hpp" +#include "../command/proxy_functions.hpp" +#include "../command/register_function.hpp" +#include "../command/type_conversions.hpp" +#include "../defines.hpp" +#include "../threading.hpp" +#include "common.hpp" + +#if defined(__linux__) || defined(__unix__) || defined(__APPLE__) || \ + defined(__HAIKU__) +#include +#endif + +#if !defined(CARBON_NO_DYNLOAD) && defined(_POSIX_VERSION) && \ + !defined(__CYGWIN__) +#include +#endif + +#if defined(CARBON_NO_DYNLOAD) +#include "unknown.hpp" +#elif defined(CARBON_WINDOWS) +#include "windows.hpp" +#elif _POSIX_VERSION +#include "posix.hpp" +#else +#include "unknown.hpp" +#endif + +#include "../command/exception_specification.hpp" + +namespace Carbon { +/// Namespace alias to provide cleaner and more explicit syntax to users. +using Namespace = dispatch::Dynamic_Object; + +namespace detail { +using Loadable_Module_Ptr = std::shared_ptr; +} + +/// \brief The main object that the ChaiScript user will use. +class Carbon_Basic { + mutable Carbon::detail::threading::shared_mutex m_mutex; + mutable Carbon::detail::threading::recursive_mutex m_use_mutex; + + std::set m_used_files; + std::map m_loaded_modules; + std::set m_active_loaded_modules; + + std::vector m_module_paths; + std::vector m_use_paths; + + std::unique_ptr m_parser; + + Carbon::detail::Dispatch_Engine m_engine; + + std::map> m_namespace_generators; + + /// Evaluates the given string in by parsing it and running the results + /// through the evaluator + Boxed_Value do_eval(const std::string &t_input, + const std::string &t_filename, bool /* t_internal*/); + + /// Evaluates the given file and looks in the 'use' paths + Boxed_Value internal_eval_file(const std::string &t_filename); + + /// Evaluates the given string, used during eval() inside of a script + Boxed_Value internal_eval(const std::string &t_e); + + /// Returns the current evaluation m_engine + Carbon::detail::Dispatch_Engine &get_eval_engine() noexcept; + + /// Builds all the requirements for ChaiScript, including its evaluator and + /// a run of its prelude. + void build_eval_system(const ModulePtr &t_lib, + const std::vector &t_opts); + + /// Skip BOM at the beginning of file + static bool skip_bom(std::ifstream &infile); + + /// Helper function for loading a file + static std::string load_file(const std::string &t_filename); + + std::vector ensure_minimum_path_vec( + std::vector paths); + +public: + /// \brief Virtual destructor for ChaiScript + virtual ~Carbon_Basic() = default; + + /// \brief Constructor for ChaiScript + /// \param[in] t_lib Standard library to apply to this ChaiScript instance + /// \param[in] t_modulepaths Vector of paths to search when attempting to + /// load a binary module \param[in] t_usepaths Vector of paths to search + /// when attempting to "use" an included ChaiScript file + Carbon_Basic(const ModulePtr &t_lib, + std::unique_ptr &&parser, + std::vector t_module_paths, + std::vector t_use_paths, + const std::vector &t_opts); + +#ifndef CARBON_NO_DYNLOAD + /// \brief Constructor for ChaiScript. + /// + /// This version of the ChaiScript constructor attempts to find the stdlib + /// module to load at runtime generates an error if it cannot be found. + /// + /// \param[in] t_modulepaths Vector of paths to search when attempting to + /// load a binary module \param[in] t_usepaths Vector of paths to search + /// when attempting to "use" an included ChaiScript file + explicit Carbon_Basic(std::unique_ptr &&parser, + std::vector t_module_paths, + std::vector t_use_paths, + const std::vector &t_opts); +#else // CARBON_NO_DYNLOAD + explicit Carbon_Basic(std::unique_ptr &&parser, + std::vector t_module_paths, + std::vector t_use_paths, + const std::vector &t_opts) = delete; +#endif + + parser::Carbon_Parser_Base &get_parser() noexcept; + + const Boxed_Value eval(const AST_Node &t_ast); + + AST_NodePtr parse(const std::string &t_input, const bool t_debug_print = false); + + std::string get_type_name(const Type_Info &ti) const; + + template + std::string get_type_name() const { + return get_type_name(user_type()); + } + + /// \brief Loads and parses a file. If the file is already open, it is not + /// reloaded. The use paths specified at ChaiScript construction time are + /// searched for the requested file. + /// + /// \param[in] t_filename Filename to load and evaluate + Boxed_Value use(const std::string &t_filename); + + /// \brief Adds a constant object that is available in all contexts and to + /// all threads \param[in] t_bv Boxed_Value to add as a global \param[in] + /// t_name Name of the value to add \throw + /// Carbon::exception::global_non_const If t_bv is not a constant object + /// \sa Boxed_Value::is_const + Carbon_Basic &add_global_const(const Boxed_Value &t_bv, + const std::string &t_name); + + /// \brief Adds a mutable object that is available in all contexts and to + /// all threads \param[in] t_bv Boxed_Value to add as a global \param[in] + /// t_name Name of the value to add \warning The user is responsible for + /// making sure the object is thread-safe if necessary + /// ChaiScript is thread-safe but provides no threading locking + /// mechanism to the script + Carbon_Basic &add_global(const Boxed_Value &t_bv, + const std::string &t_name); + + Carbon_Basic &set_global(const Boxed_Value &t_bv, + const std::string &t_name); + + /// \brief Represents the current state of the ChaiScript system. State and + /// be saved and restored \warning State object does not contain the user + /// defined type conversions of the engine. They + /// are left out due to performance considerations involved in + /// tracking the state + /// \sa ChaiScript::get_state + /// \sa ChaiScript::set_state + struct State { + std::set used_files; + Carbon::detail::Dispatch_Engine::State engine_state; + std::set active_loaded_modules; + }; + + /// \brief Returns a state object that represents the current state of the + /// global system + /// + /// The global system includes the reserved words, global const objects, + /// functions and types. local variables are thread specific and not + /// included. + /// + /// \return Current state of the global system + /// + /// \b Example: + /// + /// \code + /// Carbon::ChaiScript chai; + /// Carbon::ChaiScript::State s = chai.get_state(); // represents + /// bootstrapped initial state \endcode + State get_state() const; + + /// \brief Sets the state of the system + /// + /// The global system includes the reserved words, global objects, functions + /// and types. local variables are thread specific and not included. + /// + /// \param[in] t_state New state to set + /// + /// \b Example: + /// \code + /// Carbon::ChaiScript chai; + /// Carbon::ChaiScript::State s = chai.get_state(); // get initial state + /// chai.add(Carbon::fun(&somefunction), "somefunction"); + /// chai.set_state(s); // restore initial state, which does not have the + /// recently added "somefunction" \endcode + void set_state(const State &t_state); + + /// \returns All values in the local thread state, added through the add() + /// function + std::map get_locals() const; + + /// \brief Sets all of the locals for the current thread state. + /// + /// \param[in] t_locals The map set of variables to replace the + /// current state with + /// + /// Any existing locals are removed and the given set of variables is added + void set_locals(const std::map &t_locals); + + /// \brief Adds a type, function or object to ChaiScript. Objects are added + /// to the local thread state. \param[in] t_t Item to add \param[in] t_name + /// Name of item to add \returns Reference to current ChaiScript object + /// + /// \b Examples: + /// \code + /// Carbon::ChaiScript chai; + /// chai.add(user_type(), "MyClass"); // Add explicit + /// type info (not strictly necessary) + /// chai.add(Carbon::fun(&MyClass::function), "function"); // Add a + /// class method MyClass obj; chai.add(Carbon::var(&obj), "obj"); // Add + /// a pointer to a locally defined object \endcode + /// + /// \sa \ref adding_items + template + Carbon_Basic &add(const T &t_t, const std::string &t_name) { + Name_Validator::validate_object_name(t_name); + m_engine.add(t_t, t_name); + return *this; + } + + /// \brief Add a new conversion for upcasting to a base class + /// \sa Carbon::base_class + /// \param[in] d Base class / parent class + /// + /// \b Example: + /// \code + /// Carbon::ChaiScript chai; + /// chai.add(Carbon::base_class()); \endcode + Carbon_Basic &add(const Type_Conversion &d); + + /// \brief Adds all elements of a module to ChaiScript runtime + /// \param[in] t_p The module to add. + /// \sa Carbon::Module + Carbon_Basic &add(const ModulePtr &t_p); + + /// \brief Load a binary module from a dynamic library. Works on platforms + /// that support + /// dynamic libraries. + /// \param[in] t_module_name Name of the module to load + /// + /// The module is searched for in the registered module path folders + /// (Carbon::ChaiScript::ChaiScript) and with standard prefixes and + /// postfixes: ("lib"|"")\(".dll"|".so"|".bundle"|""). + /// + /// Once the file is located, the system looks for the symbol + /// "create_module_\". If no file can be found + /// matching the search criteria and containing the appropriate entry point + /// (the symbol mentioned above), an exception is thrown. + /// + /// \throw Carbon::exception::load_module_error In the event that no + /// matching module can be found. + std::string load_module(const std::string &t_module_name); + + /// \brief Load a binary module from a dynamic library. Works on platforms + /// that support + /// dynamic libraries. + /// + /// \param[in] t_module_name Module name to load + /// \param[in] t_filename Ignore normal filename search process and use + /// specific filename + /// + /// \sa ChaiScript::load_module(const std::string &t_module_name) + void load_module(const std::string &t_module_name, + const std::string &t_filename); + + /// \brief Evaluates a string. Equivalent to ChaiScript::eval. + /// + /// \param[in] t_script Script to execute + /// \param[in] t_handler Optional Exception_Handler used for automatic + /// unboxing of script thrown exceptions + /// + /// \return result of the script execution + /// + /// \throw Carbon::exception::eval_error In the case that evaluation + /// fails. + Boxed_Value operator()(const std::string &t_script, + const Exception_Handler &t_handler); + + /// \brief Evaluates a string and returns a typesafe result. + /// + /// \tparam T Type to extract from the result value of the script execution + /// \param[in] t_input Script to execute + /// \param[in] t_handler Optional Exception_Handler used for automatic + /// unboxing of script thrown exceptions \param[in] t_filename Optional + /// filename to report to the user for where the error occured. Useful + /// in special cases where you are loading a file + /// internally instead of using eval_file + /// + /// \return result of the script execution + /// + /// \throw Carbon::exception::eval_error In the case that evaluation + /// fails. \throw Carbon::exception::bad_boxed_cast In the case that + /// evaluation succeeds but the result value cannot be converted + /// to the requested type. + template + T eval(const std::string &t_input, + const Exception_Handler &t_handler = Exception_Handler(), + const std::string &t_filename = "__EVAL__") { + return m_engine.boxed_cast(eval(t_input, t_handler, t_filename)); + } + + /// \brief casts an object while applying any Dynamic_Conversion available + template + decltype(auto) boxed_cast(const Boxed_Value &bv) const { + return (m_engine.boxed_cast(bv)); + } + + /// \brief Evaluates a string. + /// + /// \param[in] t_input Script to execute + /// \param[in] t_handler Optional Exception_Handler used for automatic + /// unboxing of script thrown exceptions \param[in] t_filename Optional + /// filename to report to the user for where the error occurred. Useful + /// in special cases where you are loading a file + /// internally instead of using eval_file + /// + /// \return result of the script execution + /// + /// \throw exception::eval_error In the case that evaluation fails. + Boxed_Value eval(const std::string &t_input, + const Exception_Handler &t_handler = Exception_Handler(), + const std::string &t_filename = "__EVAL__"); + + /// \brief Loads the file specified by filename, evaluates it, and returns + /// the result. \param[in] t_filename File to load and parse. \param[in] + /// t_handler Optional Exception_Handler used for automatic unboxing of + /// script thrown exceptions \return result of the script execution \throw + /// Carbon::exception::eval_error In the case that evaluation fails. + Boxed_Value eval_file( + const std::string &t_filename, + const Exception_Handler &t_handler = Exception_Handler()); + + /// \brief Loads the file specified by filename, evaluates it, and returns + /// the type safe result. \tparam T Type to extract from the result value of + /// the script execution \param[in] t_filename File to load and parse. + /// \param[in] t_handler Optional Exception_Handler used for automatic + /// unboxing of script thrown exceptions \return result of the script + /// execution \throw Carbon::exception::eval_error In the case that + /// evaluation fails. \throw Carbon::exception::bad_boxed_cast In the + /// case that evaluation succeeds but the result value cannot be converted + /// to the requested type. + template + T eval_file(const std::string &t_filename, + const Exception_Handler &t_handler = Exception_Handler()) { + return m_engine.boxed_cast(eval_file(t_filename, t_handler)); + } + + /// \brief Imports a namespace object into the global scope of this + /// ChaiScript instance. \param[in] t_namespace_name Name of the namespace + /// to import. \throw std::runtime_error In the case that the namespace name + /// was never registered. + void import(const std::string &t_namespace_name); + + /// \brief Registers a namespace generator, which delays generation of the + /// namespace until it is imported, saving memory if it is never used. + /// \param[in] t_namespace_generator Namespace generator function. + /// \param[in] t_namespace_name Name of the Namespace function being + /// registered. \throw std::runtime_error In the case that the namespace + /// name was already registered. + void register_namespace( + const std::function &t_namespace_generator, + const std::string &t_namespace_name); +}; + +} // namespace Carbon +#endif /* CARBON_ENGINE_HPP */ diff --git a/src/carbon/language/eval.hpp b/src/carbon/language/eval.hpp new file mode 100644 index 00000000..17758a3c --- /dev/null +++ b/src/carbon/language/eval.hpp @@ -0,0 +1,1789 @@ + + +#ifndef CARBON_EVAL_HPP +#define CARBON_EVAL_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../defines.hpp" +#include "../command/boxed_cast.hpp" +#include "../command/boxed_number.hpp" +#include "../command/boxed_value.hpp" +#include "../command/dispatchkit.hpp" +#include "../command/dynamic_object_detail.hpp" +#include "../command/proxy_functions.hpp" +#include "../command/proxy_functions_detail.hpp" +#include "../command/register_function.hpp" +#include "atom/experiment/type_info.hpp" +#include "algebraic.hpp" +#include "common.hpp" + +namespace Carbon::exception { +class bad_boxed_cast; +} // namespace Carbon::exception + +namespace Carbon { +/// \brief Classes and functions that are part of the runtime eval system +namespace eval { +template +struct AST_Node_Impl; + +template +using AST_Node_Impl_Ptr = typename std::unique_ptr>; + +namespace detail { +/// Helper function that will set up the scope around a function call, including +/// handling the named function parameters +template +Boxed_Value eval_function( + Carbon::detail::Dispatch_Engine &t_ss, const AST_Node_Impl &t_node, + const std::vector &t_param_names, + const Function_Params &t_vals, + const std::map *t_locals = nullptr, + bool has_this_capture = false) { + Carbon::detail::Dispatch_State state(t_ss); + + const Boxed_Value *thisobj = [&]() -> const Boxed_Value * { + if (auto &stack = t_ss.get_stack_data(state.stack_holder()).back(); + !stack.empty() && stack.back().first == "__this") { + return &stack.back().second; + } else if (!t_vals.empty()) { + return &t_vals[0]; + } else { + return nullptr; + } + }(); + + Carbon::eval::detail::Stack_Push_Pop tpp(state); + if (thisobj && !has_this_capture) { + state.add_object("this", *thisobj); + } + + if (t_locals) { + for (const auto &[name, value] : *t_locals) { + state.add_object(name, value); + } + } + + for (size_t i = 0; i < t_param_names.size(); ++i) { + if (t_param_names[i] != "this") { + state.add_object(t_param_names[i], t_vals[i]); + } + } + + try { + return t_node.eval(state); + } catch (detail::Return_Value &rv) { + return std::move(rv.retval); + } +} + +inline Boxed_Value clone_if_necessary( + Boxed_Value incoming, std::atomic_uint_fast32_t &t_loc, + const Carbon::detail::Dispatch_State &t_ss) { + if (!incoming.is_return_value()) { + if (incoming.get_type_info().is_arithmetic()) { + return Boxed_Number::clone(incoming); + } else if (incoming.get_type_info().bare_equal_type_info( + typeid(bool))) { + return Boxed_Value( + *static_cast(incoming.get_const_ptr())); + } else if (incoming.get_type_info().bare_equal_type_info( + typeid(std::string))) { + return Boxed_Value( + *static_cast(incoming.get_const_ptr())); + } else { + std::array params{std::move(incoming)}; + return t_ss->call_function("clone", t_loc, Function_Params{params}, + t_ss.conversions()); + } + } else { + incoming.reset_return_value(); + return incoming; + } +} +} // namespace detail + +template +struct AST_Node_Impl : AST_Node { + AST_Node_Impl(std::string t_ast_node_text, AST_Node_Type t_id, + Parse_Location t_loc, + std::vector> t_children = + std::vector>()) + : AST_Node(std::move(t_ast_node_text), t_id, std::move(t_loc)), + children(std::move(t_children)) {} + + static bool get_scoped_bool_condition( + const AST_Node_Impl &node, + const Carbon::detail::Dispatch_State &t_ss) { + Carbon::eval::detail::Scope_Push_Pop spp(t_ss); + return get_bool_condition(node.eval(t_ss), t_ss); + } + + std::vector> get_children() const final { + std::vector> retval; + retval.reserve(children.size()); + for (auto &child : children) { + retval.emplace_back(*child); + } + + return retval; + } + + Boxed_Value eval( + const Carbon::detail::Dispatch_State &t_e) const final { + try { + T::trace(t_e, this); + return eval_internal(t_e); + } catch (exception::eval_error &ee) { + ee.call_stack.push_back(*this); + throw; + } + } + + std::vector> children; + +protected: + virtual Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &) const { + throw std::runtime_error("Undispatched ast_node (internal error)"); + } +}; + +template +struct Compiled_AST_Node : AST_Node_Impl { + Compiled_AST_Node(AST_Node_Impl_Ptr t_original_node, + std::vector> t_children, + std::function> &, + const Carbon::detail::Dispatch_State &t_ss)> + t_func) + : AST_Node_Impl(t_original_node->text, AST_Node_Type::Compiled, + t_original_node->location, std::move(t_children)), + m_func(std::move(t_func)), + m_original_node(std::move(t_original_node)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + return m_func(this->children, t_ss); + } + + std::function> &, + const Carbon::detail::Dispatch_State &t_ss)> + m_func; + AST_Node_Impl_Ptr m_original_node; +}; + +template +struct Fold_Right_Binary_Operator_AST_Node : AST_Node_Impl { + Fold_Right_Binary_Operator_AST_Node( + const std::string &t_oper, Parse_Location t_loc, + std::vector> t_children, Boxed_Value t_rhs) + : AST_Node_Impl(t_oper, AST_Node_Type::Binary, std::move(t_loc), + std::move(t_children)), + m_oper(Operators::to_operator(t_oper)), + m_rhs(std::move(t_rhs)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + return do_oper(t_ss, this->text, this->children[0]->eval(t_ss)); + } + +protected: + Boxed_Value do_oper(const Carbon::detail::Dispatch_State &t_ss, + const std::string &t_oper_string, + const Boxed_Value &t_lhs) const { + try { + if (t_lhs.get_type_info().is_arithmetic()) { + // If it's an arithmetic operation we want to short circuit + // dispatch + try { + return Boxed_Number::do_oper(m_oper, t_lhs, m_rhs); + } catch (const Carbon::exception::arithmetic_error &) { + throw; + } catch (...) { + throw exception::eval_error( + "Error with numeric operator calling: " + + t_oper_string); + } + } else { + Carbon::eval::detail::Function_Push_Pop fpp(t_ss); + std::array params{t_lhs, m_rhs}; + fpp.save_params(Function_Params{params}); + return t_ss->call_function(t_oper_string, m_loc, + Function_Params{params}, + t_ss.conversions()); + } + } catch (const exception::dispatch_error &e) { + throw exception::eval_error( + "Can not find appropriate '" + t_oper_string + "' operator.", + e.parameters, e.functions, false, *t_ss); + } + } + +private: + Operators::Opers m_oper; + Boxed_Value m_rhs; + mutable std::atomic_uint_fast32_t m_loc = {0}; +}; + +template +struct Binary_Operator_AST_Node : AST_Node_Impl { + Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(t_oper, AST_Node_Type::Binary, std::move(t_loc), + std::move(t_children)), + m_oper(Operators::to_operator(t_oper)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + auto lhs = this->children[0]->eval(t_ss); + auto rhs = this->children[1]->eval(t_ss); + return do_oper(t_ss, m_oper, this->text, lhs, rhs); + } + +protected: + Boxed_Value do_oper(const Carbon::detail::Dispatch_State &t_ss, + Operators::Opers t_oper, + const std::string &t_oper_string, + const Boxed_Value &t_lhs, + const Boxed_Value &t_rhs) const { + try { + if (t_oper != Operators::Opers::invalid && + t_lhs.get_type_info().is_arithmetic() && + t_rhs.get_type_info().is_arithmetic()) { + // If it's an arithmetic operation we want to short circuit + // dispatch + try { + return Boxed_Number::do_oper(t_oper, t_lhs, t_rhs); + } catch (const Carbon::exception::arithmetic_error &) { + throw; + } catch (...) { + throw exception::eval_error( + "Error with numeric operator calling: " + + t_oper_string); + } + } else { + Carbon::eval::detail::Function_Push_Pop fpp(t_ss); + std::array params{t_lhs, t_rhs}; + fpp.save_params(Function_Params(params)); + return t_ss->call_function(t_oper_string, m_loc, + Function_Params(params), + t_ss.conversions()); + } + } catch (const exception::dispatch_error &e) { + throw exception::eval_error( + "Can not find appropriate '" + t_oper_string + "' operator.", + e.parameters, e.functions, false, *t_ss); + } + } + +private: + Operators::Opers m_oper; + mutable std::atomic_uint_fast32_t m_loc = {0}; +}; + +template +struct Constant_AST_Node final : AST_Node_Impl { + Constant_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + Boxed_Value t_value) + : AST_Node_Impl(t_ast_node_text, AST_Node_Type::Constant, + std::move(t_loc)), + m_value(std::move(t_value)) {} + + explicit Constant_AST_Node(Boxed_Value t_value) + : AST_Node_Impl("", AST_Node_Type::Constant, Parse_Location()), + m_value(std::move(t_value)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &) const override { + return m_value; + } + + Boxed_Value m_value; +}; + +template +struct Id_AST_Node final : AST_Node_Impl { + Id_AST_Node(const std::string &t_ast_node_text, Parse_Location t_loc) + : AST_Node_Impl(t_ast_node_text, AST_Node_Type::Id, + std::move(t_loc)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + try { + return t_ss.get_object(this->text, m_loc); + } catch (std::exception &) { + throw exception::eval_error("Can not find object: " + this->text); + } + } + +private: + mutable std::atomic_uint_fast32_t m_loc = {0}; +}; + +template +struct Fun_Call_AST_Node : AST_Node_Impl { + Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, + std::move(t_loc), std::move(t_children)) { + assert(!this->children.empty()); + } + + template + Boxed_Value do_eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const { + Carbon::eval::detail::Function_Push_Pop fpp(t_ss); + + std::vector params; + + params.reserve(this->children[1]->children.size()); + for (const auto &child : this->children[1]->children) { + params.push_back(child->eval(t_ss)); + } + + if (Save_Params) { + fpp.save_params(Function_Params{params}); + } + + Boxed_Value fn(this->children[0]->eval(t_ss)); + + try { + return (*t_ss->boxed_cast( + fn))(Function_Params{params}, t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error( + std::string(e.what()) + " with function '" + + this->children[0]->text + "'", + e.parameters, e.functions, false, *t_ss); + } catch (const exception::bad_boxed_cast &) { + try { + using ConstFunctionTypeRef = const Const_Proxy_Function &; + Const_Proxy_Function f = + t_ss->boxed_cast(fn); + // handle the case where there is only 1 function to try to call + // and dispatch fails on it + throw exception::eval_error( + "Error calling function '" + this->children[0]->text + "'", + params, make_vector(f), false, *t_ss); + } catch (const exception::bad_boxed_cast &) { + throw exception::eval_error( + "'" + this->children[0]->pretty_print() + + "' does not evaluate to a function."); + } + } catch (const exception::arity_error &e) { + throw exception::eval_error(std::string(e.what()) + + " with function '" + + this->children[0]->text + "'"); + } catch (const exception::guard_error &e) { + throw exception::eval_error(std::string(e.what()) + + " with function '" + + this->children[0]->text + "'"); + } catch (detail::Return_Value &rv) { + return rv.retval; + } + } + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + return do_eval_internal(t_ss); + } +}; + +template +struct Unused_Return_Fun_Call_AST_Node final : Fun_Call_AST_Node { + Unused_Return_Fun_Call_AST_Node( + std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : Fun_Call_AST_Node(std::move(t_ast_node_text), std::move(t_loc), + std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + return this->template do_eval_internal(t_ss); + } +}; + +template +struct Arg_AST_Node final : AST_Node_Impl { + Arg_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Arg_List, + std::move(t_loc), std::move(t_children)) {} +}; + +template +struct Arg_List_AST_Node final : AST_Node_Impl { + Arg_List_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Arg_List, + std::move(t_loc), std::move(t_children)) {} + + static std::string get_arg_name(const AST_Node_Impl &t_node) { + if (t_node.children.empty()) { + return t_node.text; + } else if (t_node.children.size() == 1) { + return t_node.children[0]->text; + } else { + return t_node.children[1]->text; + } + } + + static std::vector get_arg_names( + const AST_Node_Impl &t_node) { + std::vector retval; + + for (const auto &node : t_node.children) { + retval.push_back(get_arg_name(*node)); + } + + return retval; + } + + static std::pair get_arg_type( + const AST_Node_Impl &t_node, + const Carbon::detail::Dispatch_State &t_ss) { + if (t_node.children.size() < 2) { + return {}; + } else { + return {t_node.children[0]->text, + t_ss->get_type(t_node.children[0]->text, false)}; + } + } + + static dispatch::Param_Types get_arg_types( + const AST_Node_Impl &t_node, + const Carbon::detail::Dispatch_State &t_ss) { + std::vector> retval; + + for (const auto &child : t_node.children) { + retval.push_back(get_arg_type(*child, t_ss)); + } + + return dispatch::Param_Types(std::move(retval)); + } +}; + +template +struct Equation_AST_Node final : AST_Node_Impl { + Equation_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Equation, + std::move(t_loc), std::move(t_children)), + m_oper(Operators::to_operator(this->text)) { + assert(this->children.size() == 2); + } + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + Carbon::eval::detail::Function_Push_Pop fpp(t_ss); + + auto params = [&]() { + // The RHS *must* be evaluated before the LHS + // consider `var range = range(x)` + // if we declare the variable in scope first, then the name lookup + // fails for the RHS + auto rhs = this->children[1]->eval(t_ss); + auto lhs = this->children[0]->eval(t_ss); + std::array p{std::move(lhs), std::move(rhs)}; + return p; + }(); + + if (params[0].is_return_value()) { + throw exception::eval_error( + "Error, cannot assign to temporary value."); + } else if (params[0].is_const()) { + throw exception::eval_error( + "Error, cannot assign to constant value."); + } + + if (m_oper != Operators::Opers::invalid && + params[0].get_type_info().is_arithmetic() && + params[1].get_type_info().is_arithmetic()) { + try { + return Boxed_Number::do_oper(m_oper, params[0], params[1]); + } catch (const std::exception &) { + throw exception::eval_error( + "Error with unsupported arithmetic assignment operation."); + } + } else if (m_oper == Operators::Opers::assign) { + try { + if (params[0].is_undef()) { + if (!this->children.empty() && + ((this->children[0]->identifier == + AST_Node_Type::Reference) || + (!this->children[0]->children.empty() && + this->children[0]->children[0]->identifier == + AST_Node_Type::Reference))) + + { + /// \todo This does not handle the case of an unassigned + /// reference variable + /// being assigned outside of its declaration + params[0].assign(params[1]); + params[0].reset_return_value(); + return params[1]; + } else { + params[1] = detail::clone_if_necessary( + std::move(params[1]), m_clone_loc, t_ss); + } + } + + try { + return t_ss->call_function(this->text, m_loc, + Function_Params{params}, + t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error("Unable to find appropriate'" + + this->text + "' operator.", + e.parameters, e.functions, + false, *t_ss); + } + } catch (const exception::dispatch_error &e) { + throw exception::eval_error( + "Missing clone or copy constructor for right hand side of " + "equation", + e.parameters, e.functions, false, *t_ss); + } + } else if (this->text == ":=") { + if (params[0].is_undef() || + Boxed_Value::type_match(params[0], params[1])) { + params[0].assign(params[1]); + params[0].reset_return_value(); + } else { + throw exception::eval_error("Mismatched types in equation"); + } + } else { + try { + return t_ss->call_function(this->text, m_loc, + Function_Params{params}, + t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error( + "Unable to find appropriate'" + this->text + "' operator.", + e.parameters, e.functions, false, *t_ss); + } + } + + return params[1]; + } + +private: + Operators::Opers m_oper; + mutable std::atomic_uint_fast32_t m_loc = {0}; + mutable std::atomic_uint_fast32_t m_clone_loc = {0}; +}; + +template +struct Global_Decl_AST_Node final : AST_Node_Impl { + Global_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Global_Decl, std::move(t_loc), + std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + const std::string &idname = [&]() -> const std::string & { + if (this->children[0]->identifier == AST_Node_Type::Reference) { + return this->children[0]->children[0]->text; + } else { + return this->children[0]->text; + } + }(); + + return t_ss->add_global_no_throw(Boxed_Value(), idname); + } +}; + +template +struct Var_Decl_AST_Node final : AST_Node_Impl { + Var_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Var_Decl, + std::move(t_loc), std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + const std::string &idname = this->children[0]->text; + + try { + Boxed_Value bv; + t_ss.add_object(idname, bv); + return bv; + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Variable redefined '" + e.name() + + "'"); + } + } +}; + +template +struct Assign_Decl_AST_Node final : AST_Node_Impl { + Assign_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Assign_Decl, std::move(t_loc), + std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + const std::string &idname = this->children[0]->text; + + try { + Boxed_Value bv(detail::clone_if_necessary( + this->children[1]->eval(t_ss), m_loc, t_ss)); + bv.reset_return_value(); + t_ss.add_object(idname, bv); + return bv; + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Variable redefined '" + e.name() + + "'"); + } + } + +private: + mutable std::atomic_uint_fast32_t m_loc = {0}; +}; + +template +struct Array_Call_AST_Node final : AST_Node_Impl { + Array_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Array_Call, std::move(t_loc), + std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + Carbon::eval::detail::Function_Push_Pop fpp(t_ss); + + std::array params{this->children[0]->eval(t_ss), + this->children[1]->eval(t_ss)}; + + try { + fpp.save_params(Function_Params{params}); + return t_ss->call_function("[]", m_loc, Function_Params{params}, + t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error( + "Can not find appropriate array lookup operator '[]'.", + e.parameters, e.functions, false, *t_ss); + } + } + +private: + mutable std::atomic_uint_fast32_t m_loc = {0}; +}; + +template +struct Dot_Access_AST_Node final : AST_Node_Impl { + Dot_Access_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Dot_Access, std::move(t_loc), + std::move(t_children)), + m_fun_name( + ((this->children[1]->identifier == AST_Node_Type::Fun_Call) || + (this->children[1]->identifier == AST_Node_Type::Array_Call)) + ? this->children[1]->children[0]->text + : this->children[1]->text) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + Carbon::eval::detail::Function_Push_Pop fpp(t_ss); + + Boxed_Value retval = this->children[0]->eval(t_ss); + auto params = make_vector(retval); + + bool has_function_params = false; + if (this->children[1]->children.size() > 1) { + has_function_params = true; + for (const auto &child : this->children[1]->children[1]->children) { + params.push_back(child->eval(t_ss)); + } + } + + fpp.save_params(Function_Params{params}); + + try { + retval = + t_ss->call_member(m_fun_name, m_loc, Function_Params{params}, + has_function_params, t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + if (e.functions.empty()) { + throw exception::eval_error("'" + m_fun_name + + "' is not a function."); + } else { + throw exception::eval_error( + std::string(e.what()) + " for function '" + m_fun_name + + "'", + e.parameters, e.functions, true, *t_ss); + } + } catch (detail::Return_Value &rv) { + retval = std::move(rv.retval); + } + + if (this->children[1]->identifier == AST_Node_Type::Array_Call) { + try { + std::array p{ + retval, this->children[1]->children[1]->eval(t_ss)}; + retval = t_ss->call_function( + "[]", m_array_loc, Function_Params{p}, t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error( + "Can not find appropriate array lookup operator '[]'.", + e.parameters, e.functions, true, *t_ss); + } + } + + return retval; + } + +private: + mutable std::atomic_uint_fast32_t m_loc = {0}; + mutable std::atomic_uint_fast32_t m_array_loc = {0}; + const std::string m_fun_name; +}; + +template +struct Lambda_AST_Node final : AST_Node_Impl { + Lambda_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl( + t_ast_node_text, AST_Node_Type::Lambda, std::move(t_loc), + std::vector>( + std::make_move_iterator(t_children.begin()), + std::make_move_iterator(std::prev(t_children.end())))), + m_param_names( + Arg_List_AST_Node::get_arg_names(*this->children[1])), + m_this_capture(has_this_capture(this->children[0]->children)), + m_lambda_node(std::move(t_children.back())) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + const auto captures = [&]() -> std::map { + std::map named_captures; + for (const auto &capture : this->children[0]->children) { + named_captures.insert( + std::make_pair(capture->children[0]->text, + capture->children[0]->eval(t_ss))); + } + return named_captures; + }(); + + const auto numparams = this->children[1]->children.size(); + const auto param_types = + Arg_List_AST_Node::get_arg_types(*this->children[1], t_ss); + + std::reference_wrapper engine( + *t_ss); + + return Boxed_Value(dispatch::make_dynamic_proxy_function( + [engine, lambda_node = this->m_lambda_node, + param_names = this->m_param_names, captures, + this_capture = + this->m_this_capture](const Function_Params &t_params) { + return detail::eval_function(engine, *lambda_node, param_names, + t_params, &captures, this_capture); + }, + static_cast(numparams), m_lambda_node, param_types)); + } + + static bool has_this_capture( + const std::vector> &t_children) noexcept { + return std::any_of(std::begin(t_children), std::end(t_children), + [](const auto &child) { + return child->children[0]->text == "this"; + }); + } + +private: + const std::vector m_param_names; + const bool m_this_capture = false; + const std::shared_ptr> m_lambda_node; +}; + +template +struct Scopeless_Block_AST_Node final : AST_Node_Impl { + Scopeless_Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Scopeless_Block, std::move(t_loc), + std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + const auto num_children = this->children.size(); + for (size_t i = 0; i < num_children - 1; ++i) { + this->children[i]->eval(t_ss); + } + return this->children.back()->eval(t_ss); + } +}; + +template +struct Block_AST_Node final : AST_Node_Impl { + Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Block, + std::move(t_loc), std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + Carbon::eval::detail::Scope_Push_Pop spp(t_ss); + + const auto num_children = this->children.size(); + for (size_t i = 0; i < num_children - 1; ++i) { + this->children[i]->eval(t_ss); + } + return this->children.back()->eval(t_ss); + } +}; + +template +struct Def_AST_Node final : AST_Node_Impl { + std::shared_ptr> m_body_node; + std::shared_ptr> m_guard_node; + + Def_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl( + std::move(t_ast_node_text), AST_Node_Type::Def, std::move(t_loc), + std::vector>( + std::make_move_iterator(t_children.begin()), + std::make_move_iterator(std::prev( + t_children.end(), has_guard(t_children, 1) ? 2 : 1)))), + // This apparent use after move is safe because we are only moving out + // the specific elements we need on each operation. + m_body_node(get_body_node(std::move(t_children))), + m_guard_node( + get_guard_node(std::move(t_children), + t_children.size() - this->children.size() == 2)) + + {} + + static std::shared_ptr> get_guard_node( + std::vector> &&vec, bool has_guard) { + if (has_guard) { + return std::move(*std::prev(vec.end(), 2)); + } else { + return {}; + } + } + + static std::shared_ptr> get_body_node( + std::vector> &&vec) { + return std::move(vec.back()); + } + + static bool has_guard(const std::vector> &t_children, + const std::size_t offset) noexcept { + if ((t_children.size() > 2 + offset) && + (t_children[1 + offset]->identifier == AST_Node_Type::Arg_List)) { + if (t_children.size() > 3 + offset) { + return true; + } + } else { + if (t_children.size() > 2 + offset) { + return true; + } + } + return false; + } + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + std::vector t_param_names; + size_t numparams = 0; + + dispatch::Param_Types param_types; + + if ((this->children.size() > 1) && + (this->children[1]->identifier == AST_Node_Type::Arg_List)) { + numparams = this->children[1]->children.size(); + t_param_names = + Arg_List_AST_Node::get_arg_names(*this->children[1]); + param_types = + Arg_List_AST_Node::get_arg_types(*this->children[1], t_ss); + } + + std::reference_wrapper engine( + *t_ss); + std::shared_ptr guard; + if (m_guard_node) { + guard = dispatch::make_dynamic_proxy_function( + [engine, guardnode = m_guard_node, + t_param_names](const Function_Params &t_params) { + return detail::eval_function(engine, *guardnode, + t_param_names, t_params); + }, + static_cast(numparams), m_guard_node); + } + + try { + const std::string &l_function_name = this->children[0]->text; + t_ss->add(dispatch::make_dynamic_proxy_function( + [engine, func_node = m_body_node, + t_param_names](const Function_Params &t_params) { + return detail::eval_function( + engine, *func_node, t_param_names, t_params); + }, + static_cast(numparams), m_body_node, param_types, + guard), + l_function_name); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Function redefined '" + e.name() + + "'"); + } + return void_var(); + } +}; + +template +struct While_AST_Node final : AST_Node_Impl { + While_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::While, + std::move(t_loc), std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + Carbon::eval::detail::Scope_Push_Pop spp(t_ss); + + try { + while (this->get_scoped_bool_condition(*this->children[0], t_ss)) { + try { + this->children[1]->eval(t_ss); + } catch (detail::Continue_Loop &) { + // we got a continue exception, which means all of the + // remaining loop implementation is skipped and we just need + // to continue to the next condition test + } + } + } catch (detail::Break_Loop &) { + // loop was broken intentionally + } + + return void_var(); + } +}; + +template +struct Class_AST_Node final : AST_Node_Impl { + Class_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Class, + std::move(t_loc), std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + Carbon::eval::detail::Scope_Push_Pop spp(t_ss); + + /// \todo do this better + // put class name in current scope so it can be looked up by the attrs + // and methods + t_ss.add_object("_current_class_name", + const_var(this->children[0]->text)); + + this->children[1]->eval(t_ss); + + return void_var(); + } +}; + +template +struct If_AST_Node final : AST_Node_Impl { + If_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::If, + std::move(t_loc), std::move(t_children)) { + assert(this->children.size() == 3); + } + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + if (this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)) { + return this->children[1]->eval(t_ss); + } else { + return this->children[2]->eval(t_ss); + } + } +}; + +template +struct Ranged_For_AST_Node final : AST_Node_Impl { + Ranged_For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Ranged_For, std::move(t_loc), + std::move(t_children)) { + assert(this->children.size() == 3); + } + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + const auto get_function = [&t_ss](const std::string &t_name, + auto &t_hint) { + uint_fast32_t hint = t_hint; + auto [funs_loc, funs] = t_ss->get_function(t_name, hint); + if (funs_loc != hint) { + t_hint = uint_fast32_t(funs_loc); + } + return std::move(funs); + }; + + const auto call_function = [&t_ss](const auto &t_funcs, + const Boxed_Value &t_param) { + return dispatch::dispatch(*t_funcs, Function_Params{t_param}, + t_ss.conversions()); + }; + + const std::string &loop_var_name = this->children[0]->text; + Boxed_Value range_expression_result = this->children[1]->eval(t_ss); + + const auto do_loop = [&loop_var_name, &t_ss, + this](const auto &ranged_thing) { + try { + for (auto &&loop_var : ranged_thing) { + // This scope push and pop might not be the best thing for + // perf but we know it's 100% correct + Carbon::eval::detail::Scope_Push_Pop spp(t_ss); + /// to-do make this if-constexpr with C++17 branch + if (!std::is_same, + Boxed_Value>::value) { + t_ss.add_get_object(loop_var_name, + Boxed_Value(std::ref(loop_var))); + } else { + t_ss.add_get_object(loop_var_name, + Boxed_Value(loop_var)); + } + try { + this->children[2]->eval(t_ss); + } catch (detail::Continue_Loop &) { + } + } + } catch (detail::Break_Loop &) { + // loop broken + } + return void_var(); + }; + + if (range_expression_result.get_type_info().bare_equal_type_info( + typeid(std::vector))) { + return do_loop(boxed_cast &>( + range_expression_result)); + } else if (range_expression_result.get_type_info().bare_equal_type_info( + typeid(std::map))) { + return do_loop( + boxed_cast &>( + range_expression_result)); + } else { + const auto range_funcs = get_function("range", m_range_loc); + const auto empty_funcs = get_function("empty", m_empty_loc); + const auto front_funcs = get_function("front", m_front_loc); + const auto pop_front_funcs = + get_function("pop_front", m_pop_front_loc); + + try { + const auto range_obj = + call_function(range_funcs, range_expression_result); + while ( + !boxed_cast(call_function(empty_funcs, range_obj))) { + Carbon::eval::detail::Scope_Push_Pop spp(t_ss); + t_ss.add_get_object(loop_var_name, + call_function(front_funcs, range_obj)); + try { + this->children[2]->eval(t_ss); + } catch (detail::Continue_Loop &) { + // continue statement hit + } + call_function(pop_front_funcs, range_obj); + } + } catch (detail::Break_Loop &) { + // loop broken + } + return void_var(); + } + } + +private: + mutable std::atomic_uint_fast32_t m_range_loc = {0}; + mutable std::atomic_uint_fast32_t m_empty_loc = {0}; + mutable std::atomic_uint_fast32_t m_front_loc = {0}; + mutable std::atomic_uint_fast32_t m_pop_front_loc = {0}; +}; + +template +struct For_AST_Node final : AST_Node_Impl { + For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::For, + std::move(t_loc), std::move(t_children)) { + assert(this->children.size() == 4); + } + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + Carbon::eval::detail::Scope_Push_Pop spp(t_ss); + + try { + for (this->children[0]->eval(t_ss); + this->get_scoped_bool_condition(*this->children[1], t_ss); + this->children[2]->eval(t_ss)) { + try { + // Body of Loop + this->children[3]->eval(t_ss); + } catch (detail::Continue_Loop &) { + // we got a continue exception, which means all of the + // remaining loop implementation is skipped and we just need + // to continue to the next iteration step + } + } + } catch (detail::Break_Loop &) { + // loop broken + } + + return void_var(); + } +}; + +template +struct Switch_AST_Node final : AST_Node_Impl { + Switch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Switch, + std::move(t_loc), std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + bool breaking = false; + size_t currentCase = 1; + bool hasMatched = false; + + Carbon::eval::detail::Scope_Push_Pop spp(t_ss); + + Boxed_Value match_value(this->children[0]->eval(t_ss)); + + while (!breaking && (currentCase < this->children.size())) { + try { + if (this->children[currentCase]->identifier == + AST_Node_Type::Case) { + // This is a little odd, but because want to see both the + // switch and the case simultaneously, I do a downcast here. + try { + std::array p{ + match_value, + this->children[currentCase]->children[0]->eval( + t_ss)}; + if (hasMatched || boxed_cast(t_ss->call_function( + "==", m_loc, Function_Params{p}, + t_ss.conversions()))) { + this->children[currentCase]->eval(t_ss); + hasMatched = true; + } + } catch (const exception::bad_boxed_cast &) { + throw exception::eval_error( + "Internal error: case guard evaluation not " + "boolean"); + } + } else if (this->children[currentCase]->identifier == + AST_Node_Type::Default) { + this->children[currentCase]->eval(t_ss); + hasMatched = true; + } + } catch (detail::Break_Loop &) { + breaking = true; + } + ++currentCase; + } + return void_var(); + } + + mutable std::atomic_uint_fast32_t m_loc = {0}; +}; + +template +struct Case_AST_Node final : AST_Node_Impl { + Case_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Case, + std::move(t_loc), std::move(t_children)) { + assert(this->children.size() == + 2); /* how many children does it have? */ + } + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + Carbon::eval::detail::Scope_Push_Pop spp(t_ss); + + this->children[1]->eval(t_ss); + + return void_var(); + } +}; + +template +struct Default_AST_Node final : AST_Node_Impl { + Default_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Default, + std::move(t_loc), std::move(t_children)) { + assert(this->children.size() == 1); + } + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + Carbon::eval::detail::Scope_Push_Pop spp(t_ss); + + this->children[0]->eval(t_ss); + + return void_var(); + } +}; + +template +struct Inline_Array_AST_Node final : AST_Node_Impl { + Inline_Array_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Inline_Array, std::move(t_loc), + std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + try { + std::vector vec; + if (!this->children.empty()) { + vec.reserve(this->children[0]->children.size()); + for (const auto &child : this->children[0]->children) { + vec.push_back(detail::clone_if_necessary(child->eval(t_ss), + m_loc, t_ss)); + } + } + return const_var(std::move(vec)); + } catch (const exception::dispatch_error &) { + throw exception::eval_error( + "Can not find appropriate 'clone' or copy constructor for " + "vector elements"); + } + } + +private: + mutable std::atomic_uint_fast32_t m_loc = {0}; +}; + +template +struct Inline_Map_AST_Node final : AST_Node_Impl { + Inline_Map_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Inline_Map, std::move(t_loc), + std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + try { + std::map retval; + + for (const auto &child : this->children[0]->children) { + retval.insert(std::make_pair( + t_ss->boxed_cast( + child->children[0]->eval(t_ss)), + detail::clone_if_necessary(child->children[1]->eval(t_ss), + m_loc, t_ss))); + } + + return const_var(std::move(retval)); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error( + "Can not find appropriate copy constructor or 'clone' while " + "inserting into Map.", + e.parameters, e.functions, false, *t_ss); + } + } + +private: + mutable std::atomic_uint_fast32_t m_loc = {0}; +}; + +template +struct Return_AST_Node final : AST_Node_Impl { + Return_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Return, + std::move(t_loc), std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + if (!this->children.empty()) { + throw detail::Return_Value{this->children[0]->eval(t_ss)}; + } else { + throw detail::Return_Value{void_var()}; + } + } +}; + +template +struct File_AST_Node final : AST_Node_Impl { + File_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::File, + std::move(t_loc), std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + try { + const auto num_children = this->children.size(); + + if (num_children > 0) { + for (size_t i = 0; i < num_children - 1; ++i) { + this->children[i]->eval(t_ss); + } + return this->children.back()->eval(t_ss); + } else { + return void_var(); + } + } catch (const detail::Continue_Loop &) { + throw exception::eval_error( + "Unexpected `continue` statement outside of a loop"); + } catch (const detail::Break_Loop &) { + throw exception::eval_error( + "Unexpected `break` statement outside of a loop"); + } + } +}; + +template +struct Reference_AST_Node final : AST_Node_Impl { + Reference_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Reference, + std::move(t_loc), std::move(t_children)) { + assert(this->children.size() == 1); + } + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + Boxed_Value bv; + t_ss.add_object(this->children[0]->text, bv); + return bv; + } +}; + +template +struct Prefix_AST_Node final : AST_Node_Impl { + Prefix_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Prefix, + std::move(t_loc), std::move(t_children)), + m_oper(Operators::to_operator(this->text, true)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + Boxed_Value bv(this->children[0]->eval(t_ss)); + + try { + // short circuit arithmetic operations + if (m_oper != Operators::Opers::invalid && + m_oper != Operators::Opers::bitwise_and && + bv.get_type_info().is_arithmetic()) { + if ((m_oper == Operators::Opers::pre_increment || + m_oper == Operators::Opers::pre_decrement) && + bv.is_const()) { + throw exception::eval_error( + "Error with prefix operator evaluation: cannot modify " + "constant value."); + } + return Boxed_Number::do_oper(m_oper, bv); + } else { + Carbon::eval::detail::Function_Push_Pop fpp(t_ss); + fpp.save_params(Function_Params{bv}); + return t_ss->call_function( + this->text, m_loc, Function_Params{bv}, t_ss.conversions()); + } + } catch (const exception::dispatch_error &e) { + throw exception::eval_error( + "Error with prefix operator evaluation: '" + this->text + "'", + e.parameters, e.functions, false, *t_ss); + } + } + +private: + Operators::Opers m_oper = Operators::Opers::invalid; + mutable std::atomic_uint_fast32_t m_loc = {0}; +}; + +template +struct Break_AST_Node final : AST_Node_Impl { + Break_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Break, + std::move(t_loc), std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &) const override { + throw detail::Break_Loop(); + } +}; + +template +struct Continue_AST_Node final : AST_Node_Impl { + Continue_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Continue, + std::move(t_loc), std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &) const override { + throw detail::Continue_Loop(); + } +}; + +template +struct Noop_AST_Node final : AST_Node_Impl { + Noop_AST_Node() + : AST_Node_Impl("", AST_Node_Type::Noop, Parse_Location()) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &) const override { + // It's a no-op, that evaluates to "void" + return val; + } + + Boxed_Value val = void_var(); +}; + +template +struct Map_Pair_AST_Node final : AST_Node_Impl { + Map_Pair_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Map_Pair, + std::move(t_loc), std::move(t_children)) {} +}; + +template +struct Value_Range_AST_Node final : AST_Node_Impl { + Value_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Value_Range, std::move(t_loc), + std::move(t_children)) {} +}; + +template +struct Inline_Range_AST_Node final : AST_Node_Impl { + Inline_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Inline_Range, std::move(t_loc), + std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + try { + std::array params{ + this->children[0]->children[0]->children[0]->eval(t_ss), + this->children[0]->children[0]->children[1]->eval(t_ss)}; + + return t_ss->call_function("generate_range", m_loc, + Function_Params{params}, + t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error( + "Unable to generate range vector, while calling " + "'generate_range'", + e.parameters, e.functions, false, *t_ss); + } + } + +private: + mutable std::atomic_uint_fast32_t m_loc = {0}; +}; + +template +struct Try_AST_Node final : AST_Node_Impl { + Try_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Try, + std::move(t_loc), std::move(t_children)) {} + + Boxed_Value handle_exception(const Carbon::detail::Dispatch_State &t_ss, + const Boxed_Value &t_except) const { + Boxed_Value retval; + + size_t end_point = this->children.size(); + if (this->children.back()->identifier == AST_Node_Type::Finally) { + assert(end_point > 0); + end_point = this->children.size() - 1; + } + for (size_t i = 1; i < end_point; ++i) { + Carbon::eval::detail::Scope_Push_Pop catch_scope(t_ss); + auto &catch_block = *this->children[i]; + + if (catch_block.children.size() == 1) { + // No variable capture + retval = catch_block.children[0]->eval(t_ss); + break; + } else if (catch_block.children.size() == 2 || + catch_block.children.size() == 3) { + const auto name = Arg_List_AST_Node::get_arg_name( + *catch_block.children[0]); + + if (dispatch::Param_Types( + std::vector>{ + Arg_List_AST_Node::get_arg_type( + *catch_block.children[0], t_ss)}) + .match(Function_Params{t_except}, t_ss.conversions()) + .first) { + t_ss.add_object(name, t_except); + + if (catch_block.children.size() == 2) { + // Variable capture + retval = catch_block.children[1]->eval(t_ss); + break; + } + } + } else { + if (this->children.back()->identifier == + AST_Node_Type::Finally) { + this->children.back()->children[0]->eval(t_ss); + } + throw exception::eval_error( + "Internal error: catch block size unrecognized"); + } + } + + return retval; + } + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + Boxed_Value retval; + + Carbon::eval::detail::Scope_Push_Pop spp(t_ss); + + try { + retval = this->children[0]->eval(t_ss); + } catch (const exception::eval_error &e) { + retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); + } catch (const std::runtime_error &e) { + retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); + } catch (const std::out_of_range &e) { + retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); + } catch (const std::exception &e) { + retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); + } catch (Boxed_Value &e) { + retval = handle_exception(t_ss, e); + } catch (...) { + if (this->children.back()->identifier == AST_Node_Type::Finally) { + this->children.back()->children[0]->eval(t_ss); + } + throw; + } + + if (this->children.back()->identifier == AST_Node_Type::Finally) { + retval = this->children.back()->children[0]->eval(t_ss); + } + + return retval; + } +}; + +template +struct Catch_AST_Node final : AST_Node_Impl { + Catch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Catch, + std::move(t_loc), std::move(t_children)) {} +}; + +template +struct Finally_AST_Node final : AST_Node_Impl { + Finally_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Finally, + std::move(t_loc), std::move(t_children)) {} +}; + +template +struct Method_AST_Node final : AST_Node_Impl { + std::shared_ptr> m_body_node; + std::shared_ptr> m_guard_node; + + Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl( + std::move(t_ast_node_text), AST_Node_Type::Method, + std::move(t_loc), + std::vector>( + std::make_move_iterator(t_children.begin()), + std::make_move_iterator(std::prev( + t_children.end(), + Def_AST_Node::has_guard(t_children, 1) ? 2 : 1)))), + m_body_node(Def_AST_Node::get_body_node(std::move(t_children))), + m_guard_node(Def_AST_Node::get_guard_node( + std::move(t_children), + t_children.size() - this->children.size() == 2)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + AST_Node_Impl_Ptr guardnode; + + const std::string &class_name = this->children[0]->text; + + // The first param of a method is always the implied this ptr. + std::vector t_param_names{"this"}; + dispatch::Param_Types param_types; + + if ((this->children.size() > 2) && + (this->children[2]->identifier == AST_Node_Type::Arg_List)) { + auto args = Arg_List_AST_Node::get_arg_names(*this->children[2]); + t_param_names.insert(t_param_names.end(), args.begin(), args.end()); + param_types = + Arg_List_AST_Node::get_arg_types(*this->children[2], t_ss); + } + + const size_t numparams = t_param_names.size(); + + std::shared_ptr guard; + std::reference_wrapper engine( + *t_ss); + if (m_guard_node) { + guard = dispatch::make_dynamic_proxy_function( + [engine, t_param_names, + guardnode = m_guard_node](const Function_Params &t_params) { + return Carbon::eval::detail::eval_function( + engine, *guardnode, t_param_names, t_params); + }, + static_cast(numparams), m_guard_node); + } + + try { + const std::string &function_name = this->children[1]->text; + + if (function_name == class_name) { + param_types.push_front(class_name, Type_Info()); + + t_ss->add( + std::make_shared< + dispatch::detail::Dynamic_Object_Constructor>( + class_name, + dispatch::make_dynamic_proxy_function( + [engine, t_param_names, node = m_body_node]( + const Function_Params &t_params) { + return Carbon::eval::detail::eval_function( + engine, *node, t_param_names, t_params); + }, + static_cast(numparams), m_body_node, + param_types, guard)), + function_name); + + } else { + // if the type is unknown, then this generates a function that + // looks up the type at runtime. Defining the type first before + // this is called is better + auto type = t_ss->get_type(class_name, false); + param_types.push_front(class_name, type); + + t_ss->add( + std::make_shared( + class_name, + dispatch::make_dynamic_proxy_function( + [engine, t_param_names, node = m_body_node]( + const Function_Params &t_params) { + return Carbon::eval::detail::eval_function( + engine, *node, t_param_names, t_params); + }, + static_cast(numparams), m_body_node, + param_types, guard), + type), + function_name); + } + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Method redefined '" + e.name() + "'"); + } + return void_var(); + } +}; + +template +struct Attr_Decl_AST_Node final : AST_Node_Impl { + Attr_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Attr_Decl, + std::move(t_loc), std::move(t_children)) {} + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + std::string class_name = this->children[0]->text; + + try { + std::string attr_name = this->children[1]->text; + + t_ss->add( + std::make_shared( + std::move(class_name), + fun([attr_name](dispatch::Dynamic_Object &t_obj) { + return t_obj.get_attr(attr_name); + }), + true + + ), + this->children[1]->text); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Attribute redefined '" + e.name() + + "'"); + } + return void_var(); + } +}; + +template +struct Logical_And_AST_Node final : AST_Node_Impl { + Logical_And_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Logical_And, std::move(t_loc), + std::move(t_children)) { + assert(this->children.size() == 2); + } + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + return const_var( + this->get_bool_condition(this->children[0]->eval(t_ss), t_ss) && + this->get_bool_condition(this->children[1]->eval(t_ss), t_ss)); + } +}; + +template +struct Logical_Or_AST_Node final : AST_Node_Impl { + Logical_Or_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, + std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Logical_Or, std::move(t_loc), + std::move(t_children)) { + assert(this->children.size() == 2); + } + + Boxed_Value eval_internal( + const Carbon::detail::Dispatch_State &t_ss) const override { + return const_var( + this->get_bool_condition(this->children[0]->eval(t_ss), t_ss) || + this->get_bool_condition(this->children[1]->eval(t_ss), t_ss)); + } +}; +} // namespace eval + +} // namespace Carbon +#endif /* CARBON_EVAL_HPP */ diff --git a/src/carbon/language/optimizer.hpp b/src/carbon/language/optimizer.hpp new file mode 100644 index 00000000..9d4bcc78 --- /dev/null +++ b/src/carbon/language/optimizer.hpp @@ -0,0 +1,521 @@ + + +#ifndef CARBON_OPTIMIZER_HPP +#define CARBON_OPTIMIZER_HPP + +#include "eval.hpp" + +namespace Carbon { +namespace optimizer { +template +struct Optimizer : T... { + Optimizer() = default; + explicit Optimizer(T... t) : T(std::move(t))... {} + + template + auto optimize(eval::AST_Node_Impl_Ptr p) { + ((p = static_cast(*this).optimize(std::move(p))), ...); + return p; + } +}; + +template +eval::AST_Node_Impl &child_at(eval::AST_Node_Impl &node, + const size_t offset) noexcept { + if (node.children[offset]->identifier == AST_Node_Type::Compiled) { + return *( + dynamic_cast &>(*node.children[offset]) + .m_original_node); + } else { + return *node.children[offset]; + } +} + +template +const eval::AST_Node_Impl &child_at(const eval::AST_Node_Impl &node, + const size_t offset) noexcept { + if (node.children[offset]->identifier == AST_Node_Type::Compiled) { + return *(dynamic_cast &>( + *node.children[offset]) + .m_original_node); + } else { + return *node.children[offset]; + } + + /* + if (node->identifier == AST_Node_Type::Compiled) { + return dynamic_cast&>(*node).m_original_node->children[offset]; } else + { return node->children[offset]; + } + */ +} + +template +auto child_count(const eval::AST_Node_Impl &node) noexcept { + if (node.identifier == AST_Node_Type::Compiled) { + return dynamic_cast &>(node) + .m_original_node->children.size(); + } else { + return node.children.size(); + } +} + +template +auto make_compiled_node(eval::AST_Node_Impl_Ptr original_node, + std::vector> children, + Callable callable) { + return Carbon::make_unique, + eval::Compiled_AST_Node>( + std::move(original_node), std::move(children), std::move(callable)); +} + +struct Return { + template + auto optimize(eval::AST_Node_Impl_Ptr p) { + if ((p->identifier == AST_Node_Type::Def || + p->identifier == AST_Node_Type::Lambda) && + !p->children.empty()) { + auto &last_child = p->children.back(); + if (last_child->identifier == AST_Node_Type::Block) { + auto &block_last_child = last_child->children.back(); + if (block_last_child->identifier == AST_Node_Type::Return) { + if (block_last_child->children.size() == 1) { + last_child->children.back() = + std::move(block_last_child->children[0]); + } + } + } + } + + return p; + } +}; + +template +bool contains_var_decl_in_scope(const eval::AST_Node_Impl &node) noexcept { + if (node.identifier == AST_Node_Type::Var_Decl || + node.identifier == AST_Node_Type::Assign_Decl || + node.identifier == AST_Node_Type::Reference) { + return true; + } + + const auto num = child_count(node); + + for (size_t i = 0; i < num; ++i) { + const auto &child = child_at(node, i); + if (child.identifier != AST_Node_Type::Block && + child.identifier != AST_Node_Type::For && + child.identifier != AST_Node_Type::Ranged_For && + contains_var_decl_in_scope(child)) { + return true; + } + } + + return false; +} + +struct Block { + template + auto optimize(eval::AST_Node_Impl_Ptr node) { + if (node->identifier == AST_Node_Type::Block) { + if (!contains_var_decl_in_scope(*node)) { + if (node->children.size() == 1) { + return std::move(node->children[0]); + } else { + return Carbon::make_unique< + eval::AST_Node_Impl, + eval::Scopeless_Block_AST_Node>( + node->text, node->location, std::move(node->children)); + } + } + } + + return node; + } +}; + +struct Dead_Code { + template + auto optimize(eval::AST_Node_Impl_Ptr node) { + if (node->identifier == AST_Node_Type::Block) { + std::vector keepers; + const auto num_children = node->children.size(); + keepers.reserve(num_children); + + for (size_t i = 0; i < num_children; ++i) { + const auto &child = *node->children[i]; + if ((child.identifier != AST_Node_Type::Id && + child.identifier != AST_Node_Type::Constant && + child.identifier != AST_Node_Type::Noop) || + i == num_children - 1) { + keepers.push_back(i); + } + } + + if (keepers.size() == num_children) { + return node; + } else { + const auto new_children = [&]() { + std::vector> retval; + for (const auto x : keepers) { + retval.push_back(std::move(node->children[x])); + } + return retval; + }; + + return Carbon::make_unique, + eval::Block_AST_Node>( + node->text, node->location, new_children()); + } + } else { + return node; + } + } +}; + +struct Unused_Return { + template + auto optimize(eval::AST_Node_Impl_Ptr node) { + if ((node->identifier == AST_Node_Type::Block || + node->identifier == AST_Node_Type::Scopeless_Block) && + !node->children.empty()) { + for (size_t i = 0; i < node->children.size() - 1; ++i) { + auto child = node->children[i].get(); + if (child->identifier == AST_Node_Type::Fun_Call) { + node->children[i] = Carbon::make_unique< + eval::AST_Node_Impl, + eval::Unused_Return_Fun_Call_AST_Node>( + child->text, child->location, + std::move(child->children)); + } + } + } else if ((node->identifier == AST_Node_Type::For || + node->identifier == AST_Node_Type::While) && + child_count(*node) > 0) { + auto &child = child_at(*node, child_count(*node) - 1); + if (child.identifier == AST_Node_Type::Block || + child.identifier == AST_Node_Type::Scopeless_Block) { + auto num_sub_children = child_count(child); + for (size_t i = 0; i < num_sub_children; ++i) { + auto &sub_child = child_at(child, i); + if (sub_child.identifier == AST_Node_Type::Fun_Call) { + child.children[i] = Carbon::make_unique< + eval::AST_Node_Impl, + eval::Unused_Return_Fun_Call_AST_Node>( + sub_child.text, sub_child.location, + std::move(sub_child.children)); + } + } + } + } + return node; + } +}; + +struct Assign_Decl { + template + auto optimize(eval::AST_Node_Impl_Ptr node) { + if ((node->identifier == AST_Node_Type::Equation) && + node->text == "=" && node->children.size() == 2 && + node->children[0]->identifier == AST_Node_Type::Var_Decl) { + std::vector> new_children; + new_children.push_back(std::move(node->children[0]->children[0])); + new_children.push_back(std::move(node->children[1])); + return Carbon::make_unique, + eval::Assign_Decl_AST_Node>( + node->text, node->location, std::move(new_children)); + } + + return node; + } +}; + +struct If { + template + auto optimize(eval::AST_Node_Impl_Ptr node) { + if ((node->identifier == AST_Node_Type::If) && + node->children.size() >= 2 && + node->children[0]->identifier == AST_Node_Type::Constant) { + const auto condition = dynamic_cast *>( + node->children[0].get()) + ->m_value; + if (condition.get_type_info().bare_equal_type_info(typeid(bool))) { + if (boxed_cast(condition)) { + return std::move(node->children[1]); + } else if (node->children.size() == 3) { + return std::move(node->children[2]); + } + } + } + + return node; + } +}; + +struct Partial_Fold { + template + auto optimize(eval::AST_Node_Impl_Ptr node) { + // Fold right side + if (node->identifier == AST_Node_Type::Binary && + node->children.size() == 2 && + node->children[0]->identifier != AST_Node_Type::Constant && + node->children[1]->identifier == AST_Node_Type::Constant) { + try { + const auto &oper = node->text; + const auto parsed = Operators::to_operator(oper); + if (parsed != Operators::Opers::invalid) { + const auto rhs = dynamic_cast *>( + node->children[1].get()) + ->m_value; + if (rhs.get_type_info().is_arithmetic()) { + return Carbon::make_unique< + eval::AST_Node_Impl, + eval::Fold_Right_Binary_Operator_AST_Node>( + node->text, node->location, + std::move(node->children), rhs); + } + } + } catch (const std::exception &) { + // failure to fold, that's OK + } + } + + return node; + } +}; + +struct Constant_Fold { + template + auto optimize(eval::AST_Node_Impl_Ptr node) { + if (node->identifier == AST_Node_Type::Prefix && + node->children.size() == 1 && + node->children[0]->identifier == AST_Node_Type::Constant) { + try { + const auto &oper = node->text; + const auto parsed = Operators::to_operator(oper, true); + const auto lhs = + dynamic_cast *>( + node->children[0].get()) + ->m_value; + const auto match = oper + node->children[0]->text; + + if (parsed != Operators::Opers::invalid && + parsed != Operators::Opers::bitwise_and && + lhs.get_type_info().is_arithmetic()) { + const auto val = Boxed_Number::do_oper(parsed, lhs); + return Carbon::make_unique, + eval::Constant_AST_Node>( + std::move(match), node->location, std::move(val)); + } else if (lhs.get_type_info().bare_equal_type_info( + typeid(bool)) && + oper == "!") { + return Carbon::make_unique, + eval::Constant_AST_Node>( + std::move(match), node->location, + Boxed_Value(!boxed_cast(lhs))); + } + } catch (const std::exception &) { + // failure to fold, that's OK + } + } else if ((node->identifier == AST_Node_Type::Logical_And || + node->identifier == AST_Node_Type::Logical_Or) && + node->children.size() == 2 && + node->children[0]->identifier == AST_Node_Type::Constant && + node->children[1]->identifier == AST_Node_Type::Constant) { + try { + const auto lhs = + dynamic_cast &>( + *node->children[0]) + .m_value; + const auto rhs = + dynamic_cast &>( + *node->children[1]) + .m_value; + if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && + rhs.get_type_info().bare_equal_type_info(typeid(bool))) { + const auto match = node->children[0]->text + " " + + node->text + " " + + node->children[1]->text; + const auto val = [lhs_val = boxed_cast(lhs), + rhs_val = boxed_cast(rhs), + id = node->identifier] { + if (id == AST_Node_Type::Logical_And) { + return Boxed_Value(lhs_val && rhs_val); + } else { + return Boxed_Value(lhs_val || rhs_val); + } + }(); + + return Carbon::make_unique, + eval::Constant_AST_Node>( + std::move(match), node->location, std::move(val)); + } + } catch (const std::exception &) { + // failure to fold, that's OK + } + } else if (node->identifier == AST_Node_Type::Binary && + node->children.size() == 2 && + node->children[0]->identifier == AST_Node_Type::Constant && + node->children[1]->identifier == AST_Node_Type::Constant) { + try { + const auto &oper = node->text; + const auto parsed = Operators::to_operator(oper); + if (parsed != Operators::Opers::invalid) { + const auto lhs = + dynamic_cast &>( + *node->children[0]) + .m_value; + const auto rhs = + dynamic_cast &>( + *node->children[1]) + .m_value; + if (lhs.get_type_info().is_arithmetic() && + rhs.get_type_info().is_arithmetic()) { + const auto val = + Boxed_Number::do_oper(parsed, lhs, rhs); + const auto match = node->children[0]->text + " " + + oper + " " + node->children[1]->text; + return Carbon::make_unique< + eval::AST_Node_Impl, eval::Constant_AST_Node>( + std::move(match), node->location, std::move(val)); + } + } + } catch (const std::exception &) { + // failure to fold, that's OK + } + } else if (node->identifier == AST_Node_Type::Fun_Call && + node->children.size() == 2 && + node->children[0]->identifier == AST_Node_Type::Id && + node->children[1]->identifier == AST_Node_Type::Arg_List && + node->children[1]->children.size() == 1 && + node->children[1]->children[0]->identifier == + AST_Node_Type::Constant) { + const auto arg = dynamic_cast &>( + *node->children[1]->children[0]) + .m_value; + if (arg.get_type_info().is_arithmetic()) { + const auto &fun_name = node->children[0]->text; + + const auto make_constant = [&node, &fun_name](auto val) { + const auto match = fun_name + "(" + + node->children[1]->children[0]->text + + ")"; + return Carbon::make_unique, + eval::Constant_AST_Node>( + std::move(match), node->location, const_var(val)); + }; + + if (fun_name == "double") { + return make_constant(Boxed_Number(arg).get_as()); + } else if (fun_name == "int") { + return make_constant(Boxed_Number(arg).get_as()); + } else if (fun_name == "float") { + return make_constant(Boxed_Number(arg).get_as()); + } else if (fun_name == "long") { + return make_constant(Boxed_Number(arg).get_as()); + } else if (fun_name == "size_t") { + return make_constant(Boxed_Number(arg).get_as()); + } + } + } + + return node; + } +}; + +struct For_Loop { + template + auto optimize(eval::AST_Node_Impl_Ptr for_node) { + if (for_node->identifier != AST_Node_Type::For) { + return for_node; + } + + const auto &eq_node = child_at(*for_node, 0); + const auto &binary_node = child_at(*for_node, 1); + const auto &prefix_node = child_at(*for_node, 2); + + if (child_count(*for_node) == 4 && + eq_node.identifier == AST_Node_Type::Assign_Decl && + child_count(eq_node) == 2 && + child_at(eq_node, 0).identifier == AST_Node_Type::Id && + child_at(eq_node, 1).identifier == AST_Node_Type::Constant && + binary_node.identifier == AST_Node_Type::Binary && + binary_node.text == "<" && child_count(binary_node) == 2 && + child_at(binary_node, 0).identifier == AST_Node_Type::Id && + child_at(binary_node, 0).text == child_at(eq_node, 0).text && + child_at(binary_node, 1).identifier == AST_Node_Type::Constant && + prefix_node.identifier == AST_Node_Type::Prefix && + prefix_node.text == "++" && child_count(prefix_node) == 1 && + child_at(prefix_node, 0).identifier == AST_Node_Type::Id && + child_at(prefix_node, 0).text == child_at(eq_node, 0).text) { + const Boxed_Value &begin = + dynamic_cast &>( + child_at(eq_node, 1)) + .m_value; + const Boxed_Value &end = + dynamic_cast &>( + child_at(binary_node, 1)) + .m_value; + const std::string &id = child_at(prefix_node, 0).text; + + if (begin.get_type_info().bare_equal(user_type()) && + end.get_type_info().bare_equal(user_type())) { + const auto start_int = boxed_cast(begin); + const auto end_int = boxed_cast(end); + + // note that we are moving the last element out, then popping + // the empty shared_ptr from the vector + std::vector> body_vector; + auto body_child = std::move(for_node->children[3]); + for_node->children.pop_back(); + body_vector.emplace_back(std::move(body_child)); + + return make_compiled_node( + std::move(for_node), std::move(body_vector), + [id, start_int, end_int]( + const std::vector> &children, + const Carbon::detail::Dispatch_State &t_ss) { + assert(children.size() == 1); + Carbon::eval::detail::Scope_Push_Pop spp(t_ss); + + int i = start_int; + t_ss.add_object(id, var(&i)); + + try { + for (; i < end_int; ++i) { + try { + // Body of Loop + children[0]->eval(t_ss); + } catch (eval::detail::Continue_Loop &) { + // we got a continue exception, which means + // all of the remaining loop implementation + // is skipped and we just need to continue + // to the next iteration step + } + } + } catch (eval::detail::Break_Loop &) { + // loop broken + } + + return void_var(); + }); + } else { + return for_node; + } + } else { + return for_node; + } + } +}; + +using Optimizer_Default = + Optimizer; + +} // namespace optimizer +} // namespace Carbon + +#endif diff --git a/src/carbon/language/parser.hpp b/src/carbon/language/parser.hpp new file mode 100644 index 00000000..aaf6e6ed --- /dev/null +++ b/src/carbon/language/parser.hpp @@ -0,0 +1,3172 @@ + + +#ifndef CARBON_PARSER_HPP +#define CARBON_PARSER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../command/boxed_value.hpp" +#include "atom/algorithm/hash.hpp" +#include "atom/experiment/sstring.hpp" +#include "common.hpp" +#include "optimizer.hpp" +#include "tracer.hpp" + +#if defined(CARBON_UTF16_UTF32) +#include +#include +#endif + +#if defined(CARBON_MSVC) && defined(max) && defined(min) +#define CARBON_PUSHED_MIN_MAX +#pragma push_macro("max") // Why Microsoft? why? This is worse than bad +#undef max +#pragma push_macro("min") +#undef min +#endif + +namespace Carbon { +/// \brief Classes and functions used during the parsing process. +namespace parser { +/// \brief Classes and functions internal to the parsing process. Not supported +/// for the end user. +namespace detail { +enum Alphabet { + symbol_alphabet = 0, + keyword_alphabet, + int_alphabet, + float_alphabet, + x_alphabet, + hex_alphabet, + b_alphabet, + bin_alphabet, + id_alphabet, + white_alphabet, + int_suffix_alphabet, + float_suffix_alphabet, + max_alphabet, + lengthof_alphabet = 256 +}; + +// Generic for u16, u32 and wchar +template +struct Char_Parser_Helper { + // common for all implementations + static std::string u8str_from_ll(long long val) { + using char_type = std::string::value_type; + + if (val <= 0xFF) { + return std::string(1, static_cast(val)); + } + + return std::string{static_cast(val >> 8), + static_cast(val)}; + } + + static string_type str_from_ll(long long val) { + using target_char_type = typename string_type::value_type; +#if defined(CARBON_UTF16_UTF32) + // convert + return std::wstring_convert, + target_char_type>{} + .from_bytes(u8str_from_ll(val)); +#else + // no conversion available, just put value as character + return string_type(1, static_cast(val)); +#endif + } +}; + +// Specialization for char AKA UTF-8 +template <> +struct Char_Parser_Helper { + static std::string str_from_ll(long long val) { + // little SFINAE trick to avoid base class + return Char_Parser_Helper::u8str_from_ll(val); + } +}; +} // namespace detail + +template +class Carbon_Parser final : public Carbon_Parser_Base { + void *get_tracer_ptr() noexcept override { return &m_tracer; } + + std::size_t m_current_parse_depth = 0; + + struct Depth_Counter { + static const auto max_depth = Parse_Depth; + Depth_Counter(Carbon_Parser *t_parser) : parser(t_parser) { + ++parser->m_current_parse_depth; + if (parser->m_current_parse_depth > max_depth) { + throw exception::eval_error( + "Maximum parse depth exceeded", + File_Position(parser->m_position.line, + parser->m_position.col), + *(parser->m_filename)); + } + } + + ~Depth_Counter() noexcept { --parser->m_current_parse_depth; } + + Carbon_Parser *parser; + }; + + template + constexpr static void set_alphabet(Array2D &array, const First first, + const Second second) noexcept { + auto *first_ptr = &std::get<0>(array) + static_cast(first); + auto *second_ptr = + &std::get<0>(*first_ptr) + static_cast(second); + *second_ptr = true; + } + + constexpr static std::array, + detail::max_alphabet> + build_alphabet() noexcept { + std::array, + detail::max_alphabet> + alphabet{}; + + set_alphabet(alphabet, detail::symbol_alphabet, '?'); + + set_alphabet(alphabet, detail::symbol_alphabet, '?'); + set_alphabet(alphabet, detail::symbol_alphabet, '+'); + set_alphabet(alphabet, detail::symbol_alphabet, '-'); + set_alphabet(alphabet, detail::symbol_alphabet, '*'); + set_alphabet(alphabet, detail::symbol_alphabet, '/'); + set_alphabet(alphabet, detail::symbol_alphabet, '|'); + set_alphabet(alphabet, detail::symbol_alphabet, '&'); + set_alphabet(alphabet, detail::symbol_alphabet, '^'); + set_alphabet(alphabet, detail::symbol_alphabet, '='); + set_alphabet(alphabet, detail::symbol_alphabet, '.'); + set_alphabet(alphabet, detail::symbol_alphabet, '<'); + set_alphabet(alphabet, detail::symbol_alphabet, '>'); + + for (size_t c = 'a'; c <= 'z'; ++c) { + set_alphabet(alphabet, detail::keyword_alphabet, c); + } + for (size_t c = 'A'; c <= 'Z'; ++c) { + set_alphabet(alphabet, detail::keyword_alphabet, c); + } + for (size_t c = '0'; c <= '9'; ++c) { + set_alphabet(alphabet, detail::keyword_alphabet, c); + } + set_alphabet(alphabet, detail::keyword_alphabet, '_'); + + for (size_t c = '0'; c <= '9'; ++c) { + set_alphabet(alphabet, detail::int_alphabet, c); + } + for (size_t c = '0'; c <= '9'; ++c) { + set_alphabet(alphabet, detail::float_alphabet, c); + } + set_alphabet(alphabet, detail::float_alphabet, '.'); + + for (size_t c = '0'; c <= '9'; ++c) { + set_alphabet(alphabet, detail::hex_alphabet, c); + } + for (size_t c = 'a'; c <= 'f'; ++c) { + set_alphabet(alphabet, detail::hex_alphabet, c); + } + for (size_t c = 'A'; c <= 'F'; ++c) { + set_alphabet(alphabet, detail::hex_alphabet, c); + } + + set_alphabet(alphabet, detail::x_alphabet, 'x'); + set_alphabet(alphabet, detail::x_alphabet, 'X'); + + for (size_t c = '0'; c <= '1'; ++c) { + set_alphabet(alphabet, detail::bin_alphabet, c); + } + set_alphabet(alphabet, detail::b_alphabet, 'b'); + set_alphabet(alphabet, detail::b_alphabet, 'B'); + + for (size_t c = 'a'; c <= 'z'; ++c) { + set_alphabet(alphabet, detail::id_alphabet, c); + } + for (size_t c = 'A'; c <= 'Z'; ++c) { + set_alphabet(alphabet, detail::id_alphabet, c); + } + set_alphabet(alphabet, detail::id_alphabet, '_'); + + set_alphabet(alphabet, detail::white_alphabet, ' '); + set_alphabet(alphabet, detail::white_alphabet, '\t'); + + set_alphabet(alphabet, detail::int_suffix_alphabet, 'l'); + set_alphabet(alphabet, detail::int_suffix_alphabet, 'L'); + set_alphabet(alphabet, detail::int_suffix_alphabet, 'u'); + set_alphabet(alphabet, detail::int_suffix_alphabet, 'U'); + + set_alphabet(alphabet, detail::float_suffix_alphabet, 'l'); + set_alphabet(alphabet, detail::float_suffix_alphabet, 'L'); + set_alphabet(alphabet, detail::float_suffix_alphabet, 'f'); + set_alphabet(alphabet, detail::float_suffix_alphabet, 'F'); + + return alphabet; + } + + struct Operator_Matches { + using SS = Static_String; + + std::array m_0{{SS("?")}}; + std::array m_1{{SS("||")}}; + std::array m_2{{SS("&&")}}; + std::array m_3{{SS("|")}}; + std::array m_4{{SS("^")}}; + std::array m_5{{SS("&")}}; + std::array m_6{{SS("=="), SS("!=")}}; + std::array m_7{ + {SS("<"), SS("<="), SS(">"), SS(">=")}}; + std::array m_8{{SS("<<"), SS(">>")}}; + // We share precedence here but then separate them later + std::array m_9{{SS("+"), SS("-")}}; + std::array m_10{{SS("*"), SS("/"), SS("%")}}; + std::array m_11{ + {SS("++"), SS("--"), SS("-"), SS("+"), SS("!"), SS("~")}}; + + bool is_match(std::string_view t_str) const noexcept { + constexpr std::array groups{ + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}; + return std::any_of(groups.begin(), groups.end(), + [&t_str, this](const std::size_t group) { + return is_match(group, t_str); + }); + } + + template + bool any_of(const std::size_t t_group, Predicate &&predicate) const { + auto match = [&predicate](const auto &array) { + return std::any_of(array.begin(), array.end(), predicate); + }; + + switch (t_group) { + case 0: + return match(m_0); + case 1: + return match(m_1); + case 2: + return match(m_2); + case 3: + return match(m_3); + case 4: + return match(m_4); + case 5: + return match(m_5); + case 6: + return match(m_6); + case 7: + return match(m_7); + case 8: + return match(m_8); + case 9: + return match(m_9); + case 10: + return match(m_10); + case 11: + return match(m_11); + default: + return false; + } + } + + constexpr bool is_match(const std::size_t t_group, + std::string_view t_str) const noexcept { + auto match = [&t_str](const auto &array) { + return std::any_of( + array.begin(), array.end(), + [&t_str](const auto &v) { return v == t_str; }); + }; + + switch (t_group) { + case 0: + return match(m_0); + case 1: + return match(m_1); + case 2: + return match(m_2); + case 3: + return match(m_3); + case 4: + return match(m_4); + case 5: + return match(m_5); + case 6: + return match(m_6); + case 7: + return match(m_7); + case 8: + return match(m_8); + case 9: + return match(m_9); + case 10: + return match(m_10); + case 11: + return match(m_11); + default: + return false; + } + } + }; + + constexpr static std::array + create_operators() noexcept { + std::array operators = { + {Operator_Precedence::Ternary_Cond, Operator_Precedence::Logical_Or, + Operator_Precedence::Logical_And, Operator_Precedence::Bitwise_Or, + Operator_Precedence::Bitwise_Xor, Operator_Precedence::Bitwise_And, + Operator_Precedence::Equality, Operator_Precedence::Comparison, + Operator_Precedence::Shift, Operator_Precedence::Addition, + Operator_Precedence::Multiplication, Operator_Precedence::Prefix}}; + return operators; + } + + constexpr static Static_String m_multiline_comment_end{"*/"}; + constexpr static Static_String m_multiline_comment_begin{"/*"}; + constexpr static Static_String m_singleline_comment{"//"}; + constexpr static Static_String m_annotation{"#"}; + constexpr static Static_String m_cr_lf{"\r\n"}; + constexpr static auto m_operators = create_operators(); + + std::shared_ptr m_filename; + std::vector> m_match_stack; + + struct Position { + constexpr Position() = default; + + constexpr Position(const char *t_pos, const char *t_end) noexcept + : line(1), col(1), m_pos(t_pos), m_end(t_end), m_last_col(1) {} + + static std::string_view str(const Position &t_begin, + const Position &t_end) noexcept { + if (t_begin.m_pos != nullptr && t_end.m_pos != nullptr) { + return std::string_view( + t_begin.m_pos, + std::size_t(std::distance(t_begin.m_pos, t_end.m_pos))); + } else { + return {}; + } + } + + constexpr Position &operator++() noexcept { + if (m_pos != m_end) { + if (*m_pos == '\n') { + ++line; + m_last_col = col; + col = 1; + } else { + ++col; + } + + ++m_pos; + } + return *this; + } + + constexpr Position &operator--() noexcept { + --m_pos; + if (*m_pos == '\n') { + --line; + col = m_last_col; + } else { + --col; + } + return *this; + } + + constexpr Position &operator+=(size_t t_distance) noexcept { + *this = (*this) + t_distance; + return *this; + } + + constexpr Position operator+(size_t t_distance) const noexcept { + Position ret(*this); + for (size_t i = 0; i < t_distance; ++i) { + ++ret; + } + return ret; + } + + constexpr Position &operator-=(size_t t_distance) noexcept { + *this = (*this) - t_distance; + return *this; + } + + constexpr Position operator-(size_t t_distance) const noexcept { + Position ret(*this); + for (size_t i = 0; i < t_distance; ++i) { + --ret; + } + return ret; + } + + constexpr bool operator==(const Position &t_rhs) const noexcept { + return m_pos == t_rhs.m_pos; + } + + constexpr bool operator!=(const Position &t_rhs) const noexcept { + return m_pos != t_rhs.m_pos; + } + + constexpr bool has_more() const noexcept { return m_pos != m_end; } + + constexpr size_t remaining() const noexcept { + return static_cast(m_end - m_pos); + } + + constexpr const char &operator*() const noexcept { + if (m_pos == m_end) { + return ""[0]; + } else { + return *m_pos; + } + } + + int line = -1; + int col = -1; + + private: + const char *m_pos = nullptr; + const char *m_end = nullptr; + int m_last_col = -1; + }; + + Position m_position; + + Tracer m_tracer; + Optimizer m_optimizer; + + void validate_object_name(std::string_view name) const { + if (!Name_Validator::valid_object_name(name)) { + throw exception::eval_error( + "Invalid Object Name: " + std::string(name), + File_Position(m_position.line, m_position.col), *m_filename); + } + } + +public: + explicit Carbon_Parser(Tracer tracer = Tracer(), + Optimizer optimizer = Optimizer()) + : m_tracer(std::move(tracer)), m_optimizer(std::move(optimizer)) { + m_match_stack.reserve(2); + } + + Tracer &get_tracer() noexcept { return m_tracer; } + + Optimizer &get_optimizer() noexcept { return m_optimizer; } + + Carbon_Parser(const Carbon_Parser &) = delete; + Carbon_Parser &operator=(const Carbon_Parser &) = delete; + Carbon_Parser(Carbon_Parser &&) = default; + Carbon_Parser &operator=(Carbon_Parser &&) = delete; + + constexpr static auto m_alphabet = build_alphabet(); + constexpr static Operator_Matches m_operator_matches{}; + + /// test a char in an m_alphabet + constexpr bool char_in_alphabet(char c, detail::Alphabet a) const noexcept { + return m_alphabet[a][static_cast(c)]; + } + + /// Prints the parsed ast_nodes as a tree + void debug_print(const AST_Node &t, + std::string prepend = "") const override { + std::cout << prepend << "(" << ast_node_type_to_string(t.identifier) + << ") " << t.text << " : " << t.start().line << ", " + << t.start().column << '\n'; + for (const auto &node : t.get_children()) { + debug_print(node.get(), prepend + " "); + } + } + + /// Helper function that collects ast_nodes from a starting position to the + /// top of the stack into a new AST node + template + void build_match(size_t t_match_start, std::string t_text = "") { + bool is_deep = false; + + Parse_Location filepos = [&]() -> Parse_Location { + // so we want to take everything to the right of this and make them + // children + if (t_match_start != m_match_stack.size()) { + is_deep = true; + return Parse_Location( + m_filename, + m_match_stack[t_match_start]->location.start.line, + m_match_stack[t_match_start]->location.start.column, + m_position.line, m_position.col); + } else { + return Parse_Location(m_filename, m_position.line, + m_position.col, m_position.line, + m_position.col); + } + }(); + + std::vector> new_children; + + if (is_deep) { + new_children.assign( + std::make_move_iterator(m_match_stack.begin() + + static_cast(t_match_start)), + std::make_move_iterator(m_match_stack.end())); + m_match_stack.erase( + m_match_stack.begin() + static_cast(t_match_start), + m_match_stack.end()); + } + + /// \todo fix the fact that a successful match that captured no + /// ast_nodes doesn't have any real start position + m_match_stack.push_back(m_optimizer.optimize( + Carbon::make_unique, NodeType>( + std::move(t_text), std::move(filepos), + std::move(new_children)))); + } + + /// Reads a symbol group from input if it matches the parameter, without + /// skipping initial whitespace + inline auto Symbol_(const Static_String &sym) noexcept { + const auto len = sym.size(); + if (m_position.remaining() >= len) { + const char *file_pos = &(*m_position); + for (size_t pos = 0; pos < len; ++pos) { + if (sym.c_str()[pos] != file_pos[pos]) { + return false; + } + } + m_position += len; + return true; + } + return false; + } + + /// Skips any multi-line or single-line comment + bool SkipComment() { + if (Symbol_(m_multiline_comment_begin)) { + while (m_position.has_more()) { + if (Symbol_(m_multiline_comment_end)) { + break; + } else if (!Eol_()) { + ++m_position; + } + } + return true; + } else if (Symbol_(m_singleline_comment)) { + while (m_position.has_more()) { + if (Symbol_(m_cr_lf)) { + m_position -= 2; + break; + } else if (Char_('\n')) { + --m_position; + break; + } else { + ++m_position; + } + } + return true; + } else if (Symbol_(m_annotation)) { + while (m_position.has_more()) { + if (Symbol_(m_cr_lf)) { + m_position -= 2; + break; + } else if (Char_('\n')) { + --m_position; + break; + } else { + ++m_position; + } + } + return true; + } + return false; + } + + /// Skips ChaiScript whitespace, which means space and tab, but not cr/lf + /// jespada: Modified SkipWS to skip optionally CR ('\n') and/or LF+CR + /// ("\r\n") AlekMosingiewicz: Added exception when illegal character + /// detected + bool SkipWS(bool skip_cr = false) { + bool retval = false; + + while (m_position.has_more()) { + if (static_cast(*m_position) > 0x7e) { + throw exception::eval_error( + "Illegal character", + File_Position(m_position.line, m_position.col), + *m_filename); + } + auto end_line = + (*m_position != 0) && + ((*m_position == '\n') || + (*m_position == '\r' && *(m_position + 1) == '\n')); + + if (char_in_alphabet(*m_position, detail::white_alphabet) || + (skip_cr && end_line)) { + if (end_line) { + if (*m_position == '\r') { + // discards lf + ++m_position; + } + } + + ++m_position; + + retval = true; + } else if (SkipComment()) { + retval = true; + } else { + break; + } + } + return retval; + } + + /// Reads the optional exponent (scientific notation) and suffix for a Float + bool read_exponent_and_suffix() noexcept { + // Support a form of scientific notation: 1e-5, 35.5E+8, 0.01e19 + if (m_position.has_more() && (std::tolower(*m_position) == 'e')) { + ++m_position; + if (m_position.has_more() && + ((*m_position == '-') || (*m_position == '+'))) { + ++m_position; + } + auto exponent_pos = m_position; + while (m_position.has_more() && + char_in_alphabet(*m_position, detail::int_alphabet)) { + ++m_position; + } + if (m_position == exponent_pos) { + // Require at least one digit after the exponent + return false; + } + } + + // Parse optional float suffix + while (m_position.has_more() && + char_in_alphabet(*m_position, detail::float_suffix_alphabet)) { + ++m_position; + } + + return true; + } + + /// Reads a floating point value from input, without skipping initial + /// whitespace + bool Float_() noexcept { + if (m_position.has_more() && + char_in_alphabet(*m_position, detail::float_alphabet)) { + while (m_position.has_more() && + char_in_alphabet(*m_position, detail::int_alphabet)) { + ++m_position; + } + + if (m_position.has_more() && (std::tolower(*m_position) == 'e')) { + // The exponent is valid even without any decimal in the Float + // (1e8, 3e-15) + return read_exponent_and_suffix(); + } else if (m_position.has_more() && (*m_position == '.')) { + ++m_position; + if (m_position.has_more() && + char_in_alphabet(*m_position, detail::int_alphabet)) { + while ( + m_position.has_more() && + char_in_alphabet(*m_position, detail::int_alphabet)) { + ++m_position; + } + + // After any decimal digits, support an optional exponent + // (3.7e3) + return read_exponent_and_suffix(); + } else { + --m_position; + } + } + } + return false; + } + + /// Reads a hex value from input, without skipping initial whitespace + bool Hex_() noexcept { + if (m_position.has_more() && (*m_position == '0')) { + ++m_position; + + if (m_position.has_more() && + char_in_alphabet(*m_position, detail::x_alphabet)) { + ++m_position; + if (m_position.has_more() && + char_in_alphabet(*m_position, detail::hex_alphabet)) { + while ( + m_position.has_more() && + char_in_alphabet(*m_position, detail::hex_alphabet)) { + ++m_position; + } + while (m_position.has_more() && + char_in_alphabet(*m_position, + detail::int_suffix_alphabet)) { + ++m_position; + } + + return true; + } else { + --m_position; + } + } else { + --m_position; + } + } + + return false; + } + + /// Reads an integer suffix + void IntSuffix_() { + while (m_position.has_more() && + char_in_alphabet(*m_position, detail::int_suffix_alphabet)) { + ++m_position; + } + } + + /// Reads a binary value from input, without skipping initial whitespace + bool Binary_() { + if (m_position.has_more() && (*m_position == '0')) { + ++m_position; + + if (m_position.has_more() && + char_in_alphabet(*m_position, detail::b_alphabet)) { + ++m_position; + if (m_position.has_more() && + char_in_alphabet(*m_position, detail::bin_alphabet)) { + while ( + m_position.has_more() && + char_in_alphabet(*m_position, detail::bin_alphabet)) { + ++m_position; + } + return true; + } else { + --m_position; + } + } else { + --m_position; + } + } + + return false; + } + + /// Parses a floating point value and returns a Boxed_Value representation + /// of it + static Boxed_Value buildFloat(std::string_view t_val) { + bool float_ = false; + bool long_ = false; + + auto i = t_val.size(); + + for (; i > 0; --i) { + char val = t_val[i - 1]; + + if (val == 'f' || val == 'F') { + float_ = true; + } else if (val == 'l' || val == 'L') { + long_ = true; + } else { + break; + } + } + + if (float_) { + return const_var(parse_num(t_val.substr(0, i))); + } else if (long_) { + return const_var(parse_num(t_val.substr(0, i))); + } else { + return const_var(parse_num(t_val.substr(0, i))); + } + } + + static Boxed_Value buildInt(const int base, std::string_view t_val, + const bool prefixed) { + bool unsigned_ = false; + bool long_ = false; + bool longlong_ = false; + + auto i = t_val.size(); + + for (; i > 0; --i) { + const char val = t_val[i - 1]; + + if (val == 'u' || val == 'U') { + unsigned_ = true; + } else if (val == 'l' || val == 'L') { + if (long_) { + longlong_ = true; + } + + long_ = true; + } else { + break; + } + } + + if (prefixed) { + t_val.remove_prefix(2); + } + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-compare" + +#ifdef CARBON_CLANG +#pragma GCC diagnostic ignored "-Wtautological-compare" +#pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + +#endif + + try { + /// TODO fix this to use from_chars + auto u = std::stoll(std::string(t_val), nullptr, base); + + if (!unsigned_ && !long_ && u >= std::numeric_limits::min() && + u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if ((unsigned_ || base != 10) && !long_ && + u >= std::numeric_limits::min() && + u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if (!unsigned_ && !longlong_ && + u >= std::numeric_limits::min() && + u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if ((unsigned_ || base != 10) && !longlong_ && + u >= std::numeric_limits::min() && + u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else if (!unsigned_ && + u >= std::numeric_limits::min() && + u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else { + return const_var(static_cast(u)); + } + + } catch (const std::out_of_range &) { + // too big to be signed + try { + /// TODO fix this to use from_chars + auto u = std::stoull(std::string(t_val), nullptr, base); + + if (!longlong_ && + u >= std::numeric_limits::min() && + u <= std::numeric_limits::max()) { + return const_var(static_cast(u)); + } else { + return const_var(static_cast(u)); + } + } catch (const std::out_of_range &) { + // it's just simply too big + return const_var(std::numeric_limits::max()); + } + } + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + template + std::unique_ptr> make_node( + std::string_view t_match, const int t_prev_line, const int t_prev_col, + Param &&...param) { + return Carbon::make_unique, T>( + std::string(t_match), + Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, + m_position.col), + std::forward(param)...); + } + + /// Reads a number from the input, detecting if it's an integer or floating + /// point + bool Num() { + SkipWS(); + + const auto start = m_position; + if (m_position.has_more() && + char_in_alphabet(*m_position, detail::float_alphabet)) { + try { + if (Hex_()) { + auto match = Position::str(start, m_position); + auto bv = buildInt(16, match, true); + m_match_stack.emplace_back( + make_node>( + match, start.line, start.col, std::move(bv))); + return true; + } + + if (Binary_()) { + auto match = Position::str(start, m_position); + auto bv = buildInt(2, match, true); + m_match_stack.push_back( + make_node>( + match, start.line, start.col, std::move(bv))); + return true; + } + if (Float_()) { + auto match = Position::str(start, m_position); + auto bv = buildFloat(match); + m_match_stack.push_back( + make_node>( + match, start.line, start.col, std::move(bv))); + return true; + } else { + IntSuffix_(); + auto match = Position::str(start, m_position); + if (!match.empty() && (match[0] == '0')) { + auto bv = buildInt(8, match, false); + m_match_stack.push_back( + make_node>( + match, start.line, start.col, std::move(bv))); + } else if (!match.empty()) { + auto bv = buildInt(10, match, false); + m_match_stack.push_back( + make_node>( + match, start.line, start.col, std::move(bv))); + } else { + return false; + } + return true; + } + } catch (const std::invalid_argument &) { + // error parsing number passed in to buildFloat/buildInt + return false; + } + } else { + return false; + } + } + + /// Reads an identifier from input which conforms to C's identifier naming + /// conventions, without skipping initial whitespace + bool Id_() { + if (m_position.has_more() && + char_in_alphabet(*m_position, detail::id_alphabet)) { + while (m_position.has_more() && + char_in_alphabet(*m_position, detail::keyword_alphabet)) { + ++m_position; + } + + return true; + } else if (m_position.has_more() && (*m_position == '`')) { + ++m_position; + const auto start = m_position; + + while (m_position.has_more() && (*m_position != '`')) { + if (Eol()) { + throw exception::eval_error( + "Carriage return in identifier literal", + File_Position(m_position.line, m_position.col), + *m_filename); + } else { + ++m_position; + } + } + + if (start == m_position) { + throw exception::eval_error( + "Missing contents of identifier literal", + File_Position(m_position.line, m_position.col), + *m_filename); + } else if (!m_position.has_more()) { + throw exception::eval_error( + "Incomplete identifier literal", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + ++m_position; + + return true; + } + return false; + } + + /// Reads (and potentially captures) an identifier from input + bool Id(const bool validate) { + SkipWS(); + + const auto start = m_position; + if (Id_()) { + auto text = Position::str(start, m_position); + const auto text_hash = Atom::Algorithm::fnv1a_hash(text); + + if (validate) { + validate_object_name(text); + } + +#ifdef CARBON_MSVC +#pragma warning(push) +#pragma warning(disable : 4307) +#endif + + switch (text_hash) { + case Atom::Algorithm::fnv1a_hash("true"): { + m_match_stack.push_back( + make_node>( + text, start.line, start.col, const_var(true))); + } break; + case Atom::Algorithm::fnv1a_hash("false"): { + m_match_stack.push_back( + make_node>( + text, start.line, start.col, const_var(false))); + } break; + case Atom::Algorithm::fnv1a_hash("Infinity"): { + m_match_stack.push_back( + make_node>( + text, start.line, start.col, + const_var( + std::numeric_limits::infinity()))); + } break; + case Atom::Algorithm::fnv1a_hash("NaN"): { + m_match_stack.push_back( + make_node>( + text, start.line, start.col, + const_var( + std::numeric_limits::quiet_NaN()))); + } break; + case Atom::Algorithm::fnv1a_hash("__LINE__"): { + m_match_stack.push_back( + make_node>( + text, start.line, start.col, + const_var(start.line))); + } break; + case Atom::Algorithm::fnv1a_hash("__FILE__"): { + m_match_stack.push_back( + make_node>( + text, start.line, start.col, + const_var(m_filename))); + } break; + case Atom::Algorithm::fnv1a_hash("__FUNC__"): { + std::string fun_name = "NOT_IN_FUNCTION"; + for (size_t idx = m_match_stack.empty() + ? 0 + : m_match_stack.size() - 1; + idx > 0; --idx) { + if (m_match_stack[idx - 1]->identifier == + AST_Node_Type::Id && + m_match_stack[idx - 0]->identifier == + AST_Node_Type::Arg_List) { + fun_name = m_match_stack[idx - 1]->text; + } + } + + m_match_stack.push_back( + make_node>( + text, start.line, start.col, const_var(fun_name))); + } break; + case Atom::Algorithm::fnv1a_hash("__CLASS__"): { + std::string fun_name = "NOT_IN_CLASS"; + for (size_t idx = m_match_stack.empty() + ? 0 + : m_match_stack.size() - 1; + idx > 1; --idx) { + if (m_match_stack[idx - 2]->identifier == + AST_Node_Type::Id && + m_match_stack[idx - 1]->identifier == + AST_Node_Type::Id && + m_match_stack[idx - 0]->identifier == + AST_Node_Type::Arg_List) { + fun_name = m_match_stack[idx - 2]->text; + } + } + + m_match_stack.push_back( + make_node>( + std::move(text), start.line, start.col, + const_var(fun_name))); + } break; + case Atom::Algorithm::fnv1a_hash("_"): { + m_match_stack.push_back( + make_node>( + std::move(text), start.line, start.col, + Boxed_Value(std::make_shared< + dispatch::Placeholder_Object>()))); + } break; + default: { + auto val = text; + if (*start == '`') { + // 'escaped' literal, like an operator name + val = Position::str(start + 1, m_position - 1); + // val.remove_prefix(1); val.remove_suffix(1); + } + m_match_stack.push_back( + make_node>(val, start.line, + start.col)); + } break; + } + +#ifdef CARBON_MSVC +#pragma warning(pop) +#endif + + return true; + } else { + return false; + } + } + + /// Reads an argument from input + bool Arg(const bool t_type_allowed = true) { + const auto prev_stack_top = m_match_stack.size(); + SkipWS(); + + if (!Id(true)) { + return false; + } + + SkipWS(); + + if (t_type_allowed) { + Id(true); + } + + build_match>(prev_stack_top); + + return true; + } + + /// Reads a quoted string from input, without skipping initial whitespace + bool Quoted_String_() { + if (m_position.has_more() && (*m_position == '\"')) { + char prev_char = *m_position; + ++m_position; + + int in_interpolation = 0; + bool in_quote = false; + + while (m_position.has_more() && + ((*m_position != '\"') || (in_interpolation > 0) || + (prev_char == '\\'))) { + if (!Eol_()) { + if (prev_char == '$' && *m_position == '{') { + ++in_interpolation; + } else if (prev_char != '\\' && *m_position == '"') { + in_quote = !in_quote; + } else if (*m_position == '}' && !in_quote) { + --in_interpolation; + } + + if (prev_char == '\\') { + prev_char = 0; + } else { + prev_char = *m_position; + } + ++m_position; + } + } + + if (m_position.has_more()) { + ++m_position; + } else { + throw exception::eval_error( + "Unclosed quoted string", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + return true; + } + return false; + } + + template + struct Char_Parser { + string_type &match; + using char_type = typename string_type::value_type; + bool is_escaped = false; + bool is_interpolated = false; + bool saw_interpolation_marker = false; + bool is_octal = false; + bool is_hex = false; + std::size_t unicode_size = 0; + const bool interpolation_allowed; + + string_type octal_matches; + string_type hex_matches; + + Char_Parser(string_type &t_match, const bool t_interpolation_allowed) + : match(t_match), interpolation_allowed(t_interpolation_allowed) {} + + Char_Parser &operator=(const Char_Parser &) = delete; + + ~Char_Parser() { + try { + if (is_octal) { + process_octal(); + } + + if (is_hex) { + process_hex(); + } + + if (unicode_size > 0) { + process_unicode(); + } + } catch (const std::invalid_argument &) { + } catch (const exception::eval_error &) { + // Something happened with parsing, we'll catch it later? + } + } + + void process_hex() { + if (!hex_matches.empty()) { + auto val = stoll(hex_matches, nullptr, 16); + match.push_back(char_type(val)); + } + hex_matches.clear(); + is_escaped = false; + is_hex = false; + } + + void process_octal() { + if (!octal_matches.empty()) { + auto val = stoll(octal_matches, nullptr, 8); + match.push_back(char_type(val)); + } + octal_matches.clear(); + is_escaped = false; + is_octal = false; + } + + void process_unicode() { + const auto ch = + static_cast(std::stoi(hex_matches, nullptr, 16)); + const auto match_size = hex_matches.size(); + hex_matches.clear(); + is_escaped = false; + const auto u_size = unicode_size; + unicode_size = 0; + + char buf[4]; + if (u_size != match_size) { + throw exception::eval_error( + "Incomplete unicode escape sequence"); + } + if (u_size == 4 && ch >= 0xD800 && ch <= 0xDFFF) { + throw exception::eval_error( + "Invalid 16 bit universal character"); + } + + if (ch < 0x80) { + match += static_cast(ch); + } else if (ch < 0x800) { + buf[0] = static_cast(0xC0 | (ch >> 6)); + buf[1] = static_cast(0x80 | (ch & 0x3F)); + match.append(buf, 2); + } else if (ch < 0x10000) { + buf[0] = static_cast(0xE0 | (ch >> 12)); + buf[1] = static_cast(0x80 | ((ch >> 6) & 0x3F)); + buf[2] = static_cast(0x80 | (ch & 0x3F)); + match.append(buf, 3); + } else if (ch < 0x200000) { + buf[0] = static_cast(0xF0 | (ch >> 18)); + buf[1] = static_cast(0x80 | ((ch >> 12) & 0x3F)); + buf[2] = static_cast(0x80 | ((ch >> 6) & 0x3F)); + buf[3] = static_cast(0x80 | (ch & 0x3F)); + match.append(buf, 4); + } else { + // this must be an invalid escape sequence? + throw exception::eval_error( + "Invalid 32 bit universal character"); + } + } + + void parse(const char_type t_char, const int line, const int col, + const std::string &filename) { + const bool is_octal_char = t_char >= '0' && t_char <= '7'; + + const bool is_hex_char = (t_char >= '0' && t_char <= '9') || + (t_char >= 'a' && t_char <= 'f') || + (t_char >= 'A' && t_char <= 'F'); + + if (is_octal) { + if (is_octal_char) { + octal_matches.push_back(t_char); + + if (octal_matches.size() == 3) { + process_octal(); + } + return; + } else { + process_octal(); + } + } else if (is_hex) { + if (is_hex_char) { + hex_matches.push_back(t_char); + + if (hex_matches.size() == 2 * sizeof(char_type)) { + // This rule differs from the C/C++ standard, but + // ChaiScript does not offer the same workaround + // options, and having hexadecimal sequences longer than + // can fit into the char type is undefined behavior + // anyway. + process_hex(); + } + return; + } else { + process_hex(); + } + } else if (unicode_size > 0) { + if (is_hex_char) { + hex_matches.push_back(t_char); + + if (hex_matches.size() == unicode_size) { + // Format is specified to be 'slash'uABCD + // on collecting from A to D do parsing + process_unicode(); + } + return; + } else { + // Not a unicode anymore, try parsing any way + // May be someone used 'slash'uAA only + process_unicode(); + } + } + + if (t_char == '\\') { + if (is_escaped) { + match.push_back('\\'); + is_escaped = false; + } else { + is_escaped = true; + } + } else { + if (is_escaped) { + if (is_octal_char) { + is_octal = true; + octal_matches.push_back(t_char); + } else if (t_char == 'x') { + is_hex = true; + } else if (t_char == 'u') { + unicode_size = 4; + } else if (t_char == 'U') { + unicode_size = 8; + } else { + switch (t_char) { + case ('\''): + match.push_back('\''); + break; + case ('\"'): + match.push_back('\"'); + break; + case ('?'): + match.push_back('?'); + break; + case ('a'): + match.push_back('\a'); + break; + case ('b'): + match.push_back('\b'); + break; + case ('f'): + match.push_back('\f'); + break; + case ('n'): + match.push_back('\n'); + break; + case ('r'): + match.push_back('\r'); + break; + case ('t'): + match.push_back('\t'); + break; + case ('v'): + match.push_back('\v'); + break; + case ('$'): + match.push_back('$'); + break; + default: + throw exception::eval_error( + "Unknown escaped sequence in string", + File_Position(line, col), filename); + } + is_escaped = false; + } + } else if (interpolation_allowed && t_char == '$') { + saw_interpolation_marker = true; + } else { + match.push_back(t_char); + } + } + } + }; + + /// Reads (and potentially captures) a quoted string from input. Translates + /// escaped sequences. + bool Quoted_String() { + Depth_Counter dc{this}; + SkipWS(); + + const auto start = m_position; + + if (Quoted_String_()) { + std::string match; + const auto prev_stack_top = m_match_stack.size(); + + bool is_interpolated = [&]() -> bool { + Char_Parser cparser(match, true); + + auto s = start + 1, end = m_position - 1; + + while (s != end) { + if (cparser.saw_interpolation_marker) { + if (*s == '{') { + // We've found an interpolation point + + m_match_stack.push_back( + make_node>( + match, start.line, start.col, + const_var(match))); + + if (cparser.is_interpolated) { + // If we've seen previous interpolation, add on + // instead of making a new one + build_match< + eval::Binary_Operator_AST_Node>( + prev_stack_top, "+"); + } + + // We've finished with the part of the string up to + // this point, so clear it + match.clear(); + + std::string eval_match; + + ++s; + while ((s != end) && (*s != '}')) { + eval_match.push_back(*s); + ++s; + } + + if (*s == '}') { + cparser.is_interpolated = true; + ++s; + + const auto tostr_stack_top = + m_match_stack.size(); + + m_match_stack.push_back( + make_node>( + "to_string", start.line, start.col)); + + const auto ev_stack_top = m_match_stack.size(); + + try { + m_match_stack.push_back( + parse_instr_eval(eval_match)); + } catch (const exception::eval_error &e) { + throw exception::eval_error( + e.what(), + File_Position(start.line, start.col), + *m_filename); + } + + build_match>( + ev_stack_top); + build_match>( + tostr_stack_top); + build_match< + eval::Binary_Operator_AST_Node>( + prev_stack_top, "+"); + } else { + throw exception::eval_error( + "Unclosed in-string eval", + File_Position(start.line, start.col), + *m_filename); + } + } else { + match.push_back('$'); + } + cparser.saw_interpolation_marker = false; + } else { + cparser.parse(*s, start.line, start.col, *m_filename); + + ++s; + } + } + + if (cparser.saw_interpolation_marker) { + match.push_back('$'); + } + + return cparser.is_interpolated; + }(); + + m_match_stack.push_back(make_node>( + match, start.line, start.col, const_var(match))); + + if (is_interpolated) { + build_match>( + prev_stack_top, "+"); + } + + return true; + } else { + return false; + } + } + + /// Reads a character group from input, without skipping initial whitespace + bool Single_Quoted_String_() { + bool retval = false; + if (m_position.has_more() && (*m_position == '\'')) { + retval = true; + char prev_char = *m_position; + ++m_position; + + while (m_position.has_more() && + ((*m_position != '\'') || (prev_char == '\\'))) { + if (!Eol_()) { + if (prev_char == '\\') { + prev_char = 0; + } else { + prev_char = *m_position; + } + ++m_position; + } + } + + if (m_position.has_more()) { + ++m_position; + } else { + throw exception::eval_error( + "Unclosed single-quoted string", + File_Position(m_position.line, m_position.col), + *m_filename); + } + } + return retval; + } + + /// Reads (and potentially captures) a char group from input. Translates + /// escaped sequences. + bool Single_Quoted_String() { + Depth_Counter dc{this}; + SkipWS(); + + const auto start = m_position; + if (Single_Quoted_String_()) { + std::string match; + + { + // scope for cparser destructor + Char_Parser cparser(match, false); + + for (auto s = start + 1, end = m_position - 1; s != end; ++s) { + cparser.parse(*s, start.line, start.col, *m_filename); + } + } + + if (match.size() != 1) { + throw exception::eval_error( + "Single-quoted strings must be 1 character long", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + m_match_stack.push_back(make_node>( + match, start.line, start.col, const_var(char(match.at(0))))); + return true; + } else { + return false; + } + } + + /// Reads a char from input if it matches the parameter, without skipping + /// initial whitespace + bool Char_(const char c) { + if (m_position.has_more() && (*m_position == c)) { + ++m_position; + return true; + } else { + return false; + } + } + + /// Reads (and potentially captures) a char from input if it matches the + /// parameter + bool Char(const char t_c) { + Depth_Counter dc{this}; + SkipWS(); + return Char_(t_c); + } + + /// Reads a string from input if it matches the parameter, without skipping + /// initial whitespace + bool Keyword_(const Static_String &t_s) { + const auto len = t_s.size(); + if (m_position.remaining() >= len) { + auto tmp = m_position; + for (size_t i = 0; tmp.has_more() && i < len; ++i) { + if (*tmp != t_s.c_str()[i]) { + return false; + } + ++tmp; + } + m_position = tmp; + return true; + } + + return false; + } + + /// Reads (and potentially captures) a string from input if it matches the + /// parameter + bool Keyword(const Static_String &t_s) { + Depth_Counter dc{this}; + SkipWS(); + const auto start = m_position; + bool retval = Keyword_(t_s); + // ignore substring matches + if (retval && m_position.has_more() && + char_in_alphabet(*m_position, detail::keyword_alphabet)) { + m_position = start; + retval = false; + } + + return retval; + } + + bool is_operator(std::string_view t_s) const noexcept { + return m_operator_matches.is_match(t_s); + } + + /// Reads (and potentially captures) a symbol group from input if it matches + /// the parameter + bool Symbol(const Static_String &t_s, + const bool t_disallow_prevention = false) { + Depth_Counter dc{this}; + SkipWS(); + const auto start = m_position; + bool retval = Symbol_(t_s); + + // ignore substring matches + if (retval && m_position.has_more() && + (t_disallow_prevention == false) && + char_in_alphabet(*m_position, detail::symbol_alphabet)) { + if (*m_position != '=' && + is_operator(Position::str(start, m_position)) && + !is_operator(Position::str(start, m_position + 1))) { + // don't throw this away, it's a good match and the next is not + } else { + m_position = start; + retval = false; + } + } + + return retval; + } + + /// Reads an end-of-line group from input, without skipping initial + /// whitespace + bool Eol_(const bool t_eos = false) { + bool retval = false; + + if (m_position.has_more() && (Symbol_(m_cr_lf) || Char_('\n'))) { + retval = true; + //++m_position.line; + m_position.col = 1; + } else if (m_position.has_more() && !t_eos && Char_(';')) { + retval = true; + } + + return retval; + } + + /// Reads until the end of the current statement + bool Eos() { + Depth_Counter dc{this}; + SkipWS(); + + return Eol_(true); + } + + /// Reads (and potentially captures) an end-of-line group from input + bool Eol() { + Depth_Counter dc{this}; + SkipWS(); + + return Eol_(); + } + + /// Reads a comma-separated list of values from input. Id's only, no types + /// allowed + bool Id_Arg_List() { + Depth_Counter dc{this}; + SkipWS(true); + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Arg(false)) { + retval = true; + while (Eol()) { + } + + while (Char(',')) { + while (Eol()) { + } + if (!Arg(false)) { + throw exception::eval_error( + "Unexpected value in parameter list", + File_Position(m_position.line, m_position.col), + *m_filename); + } + } + } + build_match>(prev_stack_top); + + SkipWS(true); + + return retval; + } + + /// Reads a comma-separated list of values from input, for function + /// declarations + bool Decl_Arg_List() { + Depth_Counter dc{this}; + SkipWS(true); + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Arg()) { + retval = true; + while (Eol()) { + } + + while (Char(',')) { + while (Eol()) { + } + if (!Arg()) { + throw exception::eval_error( + "Unexpected value in parameter list", + File_Position(m_position.line, m_position.col), + *m_filename); + } + } + } + build_match>(prev_stack_top); + + SkipWS(true); + + return retval; + } + + /// Reads a comma-separated list of values from input + bool Arg_List() { + Depth_Counter dc{this}; + SkipWS(true); + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Equation()) { + retval = true; + while (Eol()) { + } + while (Char(',')) { + while (Eol()) { + } + if (!Equation()) { + throw exception::eval_error( + "Unexpected value in parameter list", + File_Position(m_position.line, m_position.col), + *m_filename); + } + } + } + + build_match>(prev_stack_top); + + SkipWS(true); + + return retval; + } + + /// Reads possible special container values, including ranges and map_pairs + bool Container_Arg_List() { + Depth_Counter dc{this}; + bool retval = false; + SkipWS(true); + + const auto prev_stack_top = m_match_stack.size(); + + if (Value_Range()) { + retval = true; + build_match>(prev_stack_top); + } else if (Map_Pair()) { + retval = true; + while (Eol()) { + } + while (Char(',')) { + while (Eol()) { + } + if (!Map_Pair()) { + throw exception::eval_error( + "Unexpected value in container", + File_Position(m_position.line, m_position.col), + *m_filename); + } + } + build_match>(prev_stack_top); + } else if (Operator()) { + retval = true; + while (Eol()) { + } + while (Char(',')) { + while (Eol()) { + } + if (!Operator()) { + throw exception::eval_error( + "Unexpected value in container", + File_Position(m_position.line, m_position.col), + *m_filename); + } + } + build_match>(prev_stack_top); + } + + SkipWS(true); + + return retval; + } + + /// Reads a lambda (anonymous function) from input + bool Lambda() { + Depth_Counter dc{this}; + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("fun")) { + retval = true; + + if (Char('[')) { + Id_Arg_List(); + if (!Char(']')) { + throw exception::eval_error( + "Incomplete anonymous function bind", + File_Position(m_position.line, m_position.col), + *m_filename); + } + } else { + // make sure we always have the same number of nodes + build_match>(prev_stack_top); + } + + if (Char('(')) { + Decl_Arg_List(); + if (!Char(')')) { + throw exception::eval_error( + "Incomplete anonymous function", + File_Position(m_position.line, m_position.col), + *m_filename); + } + } else { + throw exception::eval_error( + "Incomplete anonymous function", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + while (Eol()) { + } + + if (!Block()) { + throw exception::eval_error( + "Incomplete anonymous function", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top); + } + + return retval; + } + + /// Reads a function definition from input + bool Def(const bool t_class_context = false, + const std::string &t_class_name = "") { + Depth_Counter dc{this}; + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("def")) { + retval = true; + + if (t_class_context) { + m_match_stack.push_back(make_node>( + t_class_name, m_position.line, m_position.col)); + } + + if (!Id(true)) { + throw exception::eval_error( + "Missing function name in definition", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + bool is_method = false; + + if (Symbol("::")) { + // We're now a method + is_method = true; + + if (!Id(true)) { + throw exception::eval_error( + "Missing method name in definition", + File_Position(m_position.line, m_position.col), + *m_filename); + } + } + + if (Char('(')) { + Decl_Arg_List(); + if (!Char(')')) { + throw exception::eval_error( + "Incomplete function definition", + File_Position(m_position.line, m_position.col), + *m_filename); + } + } + + while (Eos()) { + } + + if (Char(':')) { + if (!Operator()) { + throw exception::eval_error( + "Missing guard expression for function", + File_Position(m_position.line, m_position.col), + *m_filename); + } + } + + while (Eol()) { + } + if (!Block()) { + throw exception::eval_error( + "Incomplete function definition", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + if (is_method || t_class_context) { + build_match>(prev_stack_top); + } else { + build_match>(prev_stack_top); + } + } + + return retval; + } + + /// Reads a function definition from input + bool Try() { + Depth_Counter dc{this}; + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("try")) { + retval = true; + + while (Eol()) { + } + + if (!Block()) { + throw exception::eval_error( + "Incomplete 'try' block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + bool has_matches = true; + while (has_matches) { + while (Eol()) { + } + has_matches = false; + if (Keyword("catch")) { + const auto catch_stack_top = m_match_stack.size(); + if (Char('(')) { + if (!(Arg() && Char(')'))) { + throw exception::eval_error( + "Incomplete 'catch' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + } + + while (Eol()) { + } + + if (!Block()) { + throw exception::eval_error( + "Incomplete 'catch' block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + build_match>(catch_stack_top); + has_matches = true; + } + } + while (Eol()) { + } + if (Keyword("finally")) { + const auto finally_stack_top = m_match_stack.size(); + + while (Eol()) { + } + + if (!Block()) { + throw exception::eval_error( + "Incomplete 'finally' block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + build_match>(finally_stack_top); + } + + build_match>(prev_stack_top); + } + + return retval; + } + + /// Reads an if/else if/else block from input + bool If() { + Depth_Counter dc{this}; + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("if")) { + retval = true; + + if (!Char('(')) { + throw exception::eval_error( + "Incomplete 'if' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + if (!Equation()) { + throw exception::eval_error( + "Incomplete 'if' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + const bool is_if_init = Eol() && Equation(); + + if (!Char(')')) { + throw exception::eval_error( + "Incomplete 'if' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + while (Eol()) { + } + + if (!Block()) { + throw exception::eval_error( + "Incomplete 'if' block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + bool has_matches = true; + while (has_matches) { + while (Eol()) { + } + has_matches = false; + if (Keyword("else")) { + if (If()) { + has_matches = true; + } else { + while (Eol()) { + } + + if (!Block()) { + throw exception::eval_error( + "Incomplete 'else' block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + has_matches = true; + } + } + } + + const auto num_children = m_match_stack.size() - prev_stack_top; + + if ((is_if_init && num_children == 3) || + (!is_if_init && num_children == 2)) { + m_match_stack.push_back( + Carbon::make_unique, + eval::Noop_AST_Node>()); + } + + if (!is_if_init) { + build_match>(prev_stack_top); + } else { + build_match>(prev_stack_top + 1); + build_match>(prev_stack_top); + } + } + + return retval; + } + + /// Reads a class block from input + bool Class(const bool t_class_allowed) { + Depth_Counter dc{this}; + bool retval = false; + + size_t prev_stack_top = m_match_stack.size(); + + if (Keyword("class")) { + if (!t_class_allowed) { + throw exception::eval_error( + "Class definitions only allowed at top scope", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + retval = true; + + if (!Id(true)) { + throw exception::eval_error( + "Missing class name in definition", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + const auto class_name = m_match_stack.back()->text; + + while (Eol()) { + } + + if (!Class_Block(class_name)) { + throw exception::eval_error( + "Incomplete 'class' block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top); + } + + return retval; + } + + /// Reads a while block from input + bool While() { + Depth_Counter dc{this}; + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("while")) { + retval = true; + + if (!Char('(')) { + throw exception::eval_error( + "Incomplete 'while' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + if (!(Operator() && Char(')'))) { + throw exception::eval_error( + "Incomplete 'while' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + while (Eol()) { + } + + if (!Block()) { + throw exception::eval_error( + "Incomplete 'while' block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top); + } + + return retval; + } + + /// Reads the ranged `for` conditions from input + bool Range_Expression() { + Depth_Counter dc{this}; + // the first element will have already been captured by the For_Guards() + // call that preceeds it + return Char(':') && Equation(); + } + + /// Reads the C-style `for` conditions from input + bool For_Guards() { + Depth_Counter dc{this}; + if (!(Equation() && Eol())) { + if (!Eol()) { + return false; + } else { + m_match_stack.push_back( + Carbon::make_unique, + eval::Noop_AST_Node>()); + } + } + + if (!(Equation() && Eol())) { + if (!Eol()) { + return false; + } else { + m_match_stack.push_back( + Carbon::make_unique, + eval::Constant_AST_Node>( + Boxed_Value(true))); + } + } + + if (!Equation()) { + m_match_stack.push_back( + Carbon::make_unique, + eval::Noop_AST_Node>()); + } + + return true; + } + + /// Reads a for block from input + bool For() { + Depth_Counter dc{this}; + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("for")) { + retval = true; + + if (!Char('(')) { + throw exception::eval_error( + "Incomplete 'for' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + const bool classic_for = For_Guards() && Char(')'); + if (!classic_for && !(Range_Expression() && Char(')'))) { + throw exception::eval_error( + "Incomplete 'for' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + while (Eol()) { + } + + if (!Block()) { + throw exception::eval_error( + "Incomplete 'for' block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + const auto num_children = m_match_stack.size() - prev_stack_top; + + if (classic_for) { + if (num_children != 4) { + throw exception::eval_error( + "Incomplete 'for' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + build_match>(prev_stack_top); + } else { + if (num_children != 3) { + throw exception::eval_error( + "Incomplete ranged-for expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + build_match>(prev_stack_top); + } + } + + return retval; + } + + /// Reads a case block from input + bool Case() { + Depth_Counter dc{this}; + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("case")) { + retval = true; + + if (!Char('(')) { + throw exception::eval_error( + "Incomplete 'case' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + if (!(Operator() && Char(')'))) { + throw exception::eval_error( + "Incomplete 'case' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + while (Eol()) { + } + + if (!Block()) { + throw exception::eval_error( + "Incomplete 'case' block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top); + } else if (Keyword("default")) { + retval = true; + + while (Eol()) { + } + + if (!Block()) { + throw exception::eval_error( + "Incomplete 'default' block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top); + } + + return retval; + } + + /// Reads a switch statement from input + bool Switch() { + Depth_Counter dc{this}; + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("switch")) { + if (!Char('(')) { + throw exception::eval_error( + "Incomplete 'switch' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + if (!(Operator() && Char(')'))) { + throw exception::eval_error( + "Incomplete 'switch' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + while (Eol()) { + } + + if (Char('{')) { + while (Eol()) { + } + + while (Case()) { + while (Eol()) { + } // eat + } + + while (Eol()) { + } // eat + + if (!Char('}')) { + throw exception::eval_error( + "Incomplete block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + } else { + throw exception::eval_error( + "Incomplete block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top); + return true; + + } else { + return false; + } + } + + /// Reads a curly-brace C-style class block from input + bool Class_Block(const std::string &t_class_name) { + Depth_Counter dc{this}; + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Char('{')) { + retval = true; + + Class_Statements(t_class_name); + if (!Char('}')) { + throw exception::eval_error( + "Incomplete class block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + if (m_match_stack.size() == prev_stack_top) { + m_match_stack.push_back( + Carbon::make_unique, + eval::Noop_AST_Node>()); + } + + build_match>(prev_stack_top); + } + + return retval; + } + + /// Reads a curly-brace C-style block from input + bool Block() { + Depth_Counter dc{this}; + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (Char('{')) { + retval = true; + + Statements(); + if (!Char('}')) { + throw exception::eval_error( + "Incomplete block", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + if (m_match_stack.size() == prev_stack_top) { + m_match_stack.push_back( + Carbon::make_unique, + eval::Noop_AST_Node>()); + } + + build_match>(prev_stack_top); + } + + return retval; + } + + /// Reads a return statement from input + bool Return() { + Depth_Counter dc{this}; + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("return")) { + Operator(); + build_match>(prev_stack_top); + return true; + } else { + return false; + } + } + + /// Reads a break statement from input + bool Break() { + Depth_Counter dc{this}; + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("break")) { + build_match>(prev_stack_top); + return true; + } else { + return false; + } + } + + /// Reads a continue statement from input + bool Continue() { + Depth_Counter dc{this}; + const auto prev_stack_top = m_match_stack.size(); + + if (Keyword("continue")) { + build_match>(prev_stack_top); + return true; + } else { + return false; + } + } + + /// Reads a dot expression(member access), then proceeds to check if it's a + /// function or array call + bool Dot_Fun_Array() { + Depth_Counter dc{this}; + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + if (Lambda() || Num() || Quoted_String() || Single_Quoted_String() || + Paren_Expression() || Inline_Container() || Id(false)) { + retval = true; + bool has_more = true; + + while (has_more) { + has_more = false; + + if (Char('(')) { + has_more = true; + + Arg_List(); + if (!Char(')')) { + throw exception::eval_error( + "Incomplete function call", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>( + prev_stack_top); + /// \todo Work around for method calls until we have a + /// better solution + if (!m_match_stack.back()->children.empty()) { + if (m_match_stack.back()->children[0]->identifier == + AST_Node_Type::Dot_Access) { + if (m_match_stack.empty()) { + throw exception::eval_error( + "Incomplete dot access fun call", + File_Position(m_position.line, + m_position.col), + *m_filename); + } + if (m_match_stack.back()->children.empty()) { + throw exception::eval_error( + "Incomplete dot access fun call", + File_Position(m_position.line, + m_position.col), + *m_filename); + } + auto dot_access = + std::move(m_match_stack.back()->children[0]); + auto func_call = std::move(m_match_stack.back()); + m_match_stack.pop_back(); + func_call->children.erase( + func_call->children.begin()); + if (dot_access->children.empty()) { + throw exception::eval_error( + "Incomplete dot access fun call", + File_Position(m_position.line, + m_position.col), + *m_filename); + } + func_call->children.insert( + func_call->children.begin(), + std::move(dot_access->children.back())); + dot_access->children.pop_back(); + dot_access->children.push_back( + std::move(func_call)); + if (dot_access->children.size() != 2) { + throw exception::eval_error( + "Incomplete dot access fun call", + File_Position(m_position.line, + m_position.col), + *m_filename); + } + m_match_stack.push_back(std::move(dot_access)); + } + } + } else if (Char('[')) { + has_more = true; + + if (!(Operator() && Char(']'))) { + throw exception::eval_error( + "Incomplete array access", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>( + prev_stack_top); + } else if (Symbol(".")) { + has_more = true; + if (!(Id(true))) { + throw exception::eval_error( + "Incomplete dot access fun call", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + if (std::distance(m_match_stack.begin() + + static_cast(prev_stack_top), + m_match_stack.end()) != 2) { + throw exception::eval_error( + "Incomplete dot access fun call", + File_Position(m_position.line, m_position.col), + *m_filename); + } + build_match>( + prev_stack_top); + } else if (Eol()) { + auto start = --m_position; + while (Eol()) { + } + if (Symbol(".")) { + has_more = true; + --m_position; + } else { + m_position = start; + } + } + } + } + + return retval; + } + + /// Reads a variable declaration from input + bool Var_Decl(const bool t_class_context = false, + const std::string &t_class_name = "") { + Depth_Counter dc{this}; + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + + if (t_class_context && + (Keyword("attr") || Keyword("auto") || Keyword("var"))) { + retval = true; + + m_match_stack.push_back(make_node>( + t_class_name, m_position.line, m_position.col)); + + if (!Id(true)) { + throw exception::eval_error( + "Incomplete attribute declaration", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top); + } else if (Keyword("auto") || Keyword("var")) { + retval = true; + + if (Reference()) { + // we built a reference node - continue + } else if (Id(true)) { + build_match>(prev_stack_top); + } else { + throw exception::eval_error( + "Incomplete variable declaration", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + } else if (Keyword("global")) { + retval = true; + + if (!(Reference() || Id(true))) { + throw exception::eval_error( + "Incomplete global declaration", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top); + } else if (Keyword("attr")) { + retval = true; + + if (!Id(true)) { + throw exception::eval_error( + "Incomplete attribute declaration", + File_Position(m_position.line, m_position.col), + *m_filename); + } + if (!Symbol("::")) { + throw exception::eval_error( + "Incomplete attribute declaration", + File_Position(m_position.line, m_position.col), + *m_filename); + } + if (!Id(true)) { + throw exception::eval_error( + "Missing attribute name in definition", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top); + } + + return retval; + } + + /// Reads an expression surrounded by parentheses from input + bool Paren_Expression() { + Depth_Counter dc{this}; + if (Char('(')) { + if (!Operator()) { + throw exception::eval_error( + "Incomplete expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + if (!Char(')')) { + throw exception::eval_error( + "Missing closing parenthesis ')'", + File_Position(m_position.line, m_position.col), + *m_filename); + } + return true; + } else { + return false; + } + } + + /// Reads, and identifies, a short-form container initialization from input + bool Inline_Container() { + Depth_Counter dc{this}; + const auto prev_stack_top = m_match_stack.size(); + + if (Char('[')) { + Container_Arg_List(); + + if (!Char(']')) { + throw exception::eval_error( + "Missing closing square bracket ']' in container " + "initializer", + File_Position(m_position.line, m_position.col), + *m_filename); + } + if ((prev_stack_top != m_match_stack.size()) && + (!m_match_stack.back()->children.empty())) { + if (m_match_stack.back()->children[0]->identifier == + AST_Node_Type::Value_Range) { + build_match>( + prev_stack_top); + } else if (m_match_stack.back()->children[0]->identifier == + AST_Node_Type::Map_Pair) { + build_match>( + prev_stack_top); + } else { + build_match>( + prev_stack_top); + } + } else { + build_match>( + prev_stack_top); + } + + return true; + } else { + return false; + } + } + + /// Parses a variable specified with a & aka reference + bool Reference() { + Depth_Counter dc{this}; + const auto prev_stack_top = m_match_stack.size(); + + if (Symbol("&")) { + if (!Id(true)) { + throw exception::eval_error( + "Incomplete '&' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top); + return true; + } else { + return false; + } + } + + /// Reads a unary prefixed expression from input + bool Prefix() { + Depth_Counter dc{this}; + const auto prev_stack_top = m_match_stack.size(); + using SS = Static_String; + const std::array prefix_opers{ + {SS{"++"}, SS{"--"}, SS{"-"}, SS{"+"}, SS{"!"}, SS{"~"}}}; + + for (const auto &oper : prefix_opers) { + const bool is_char = oper.size() == 1; + if ((is_char && Char(oper.c_str()[0])) || + (!is_char && Symbol(oper))) { + if (!Operator(m_operators.size() - 1)) { + throw exception::eval_error( + "Incomplete prefix '" + std::string(oper.c_str()) + + "' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top, + oper.c_str()); + return true; + } + } + + return false; + } + + /// Parses any of a group of 'value' style ast_node groups from input + bool Value() { + Depth_Counter dc{this}; + return Var_Decl() || Dot_Fun_Array() || Prefix(); + } + + bool Operator_Helper(const size_t t_precedence, std::string &oper) { + return m_operator_matches.any_of(t_precedence, + [&oper, this](const auto &elem) { + if (Symbol(elem)) { + oper = elem.c_str(); + return true; + } else { + return false; + } + }); + } + + bool Operator(const size_t t_precedence = 0) { + Depth_Counter dc{this}; + bool retval = false; + const auto prev_stack_top = m_match_stack.size(); + + if (m_operators[t_precedence] != Operator_Precedence::Prefix) { + if (Operator(t_precedence + 1)) { + retval = true; + std::string oper; + while (Operator_Helper(t_precedence, oper)) { + while (Eol()) { + } + if (!Operator(t_precedence + 1)) { + throw exception::eval_error( + "Incomplete '" + oper + "' expression", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + switch (m_operators[t_precedence]) { + case (Operator_Precedence::Ternary_Cond): + if (Symbol(":")) { + if (!Operator(t_precedence + 1)) { + throw exception::eval_error( + "Incomplete '" + oper + "' expression", + File_Position(m_position.line, + m_position.col), + *m_filename); + } + build_match>( + prev_stack_top); + } else { + throw exception::eval_error( + "Incomplete '" + oper + "' expression", + File_Position(m_position.line, + m_position.col), + *m_filename); + } + break; + + case (Operator_Precedence::Addition): + case (Operator_Precedence::Multiplication): + case (Operator_Precedence::Shift): + case (Operator_Precedence::Equality): + case (Operator_Precedence::Bitwise_And): + case (Operator_Precedence::Bitwise_Xor): + case (Operator_Precedence::Bitwise_Or): + case (Operator_Precedence::Comparison): + build_match>( + prev_stack_top, oper); + break; + + case (Operator_Precedence::Logical_And): + build_match>( + prev_stack_top, oper); + break; + case (Operator_Precedence::Logical_Or): + build_match>( + prev_stack_top, oper); + break; + case (Operator_Precedence::Prefix): + assert(false); // cannot reach here because of if() + // statement at the top + break; + + // default: + // throw + // exception::eval_error("Internal + // error: unhandled ast_node", + // File_Position(m_position.line, + // m_position.col), *m_filename); + } + } + } + } else { + return Value(); + } + + return retval; + } + + /// Reads a pair of values used to create a map initialization from input + bool Map_Pair() { + Depth_Counter dc{this}; + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + const auto prev_pos = m_position; + + if (Operator()) { + if (Symbol(":")) { + retval = true; + if (!Operator()) { + throw exception::eval_error( + "Incomplete map pair", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top); + } else { + m_position = prev_pos; + while (prev_stack_top != m_match_stack.size()) { + m_match_stack.pop_back(); + } + } + } + + return retval; + } + + /// Reads a pair of values used to create a range initialization from input + bool Value_Range() { + Depth_Counter dc{this}; + bool retval = false; + + const auto prev_stack_top = m_match_stack.size(); + const auto prev_pos = m_position; + + if (Operator()) { + if (Symbol("..")) { + retval = true; + if (!Operator()) { + throw exception::eval_error( + "Incomplete value range", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top); + } else { + m_position = prev_pos; + while (prev_stack_top != m_match_stack.size()) { + m_match_stack.pop_back(); + } + } + } + + return retval; + } + + /// Parses a string of binary equation operators + bool Equation() { + Depth_Counter dc{this}; + const auto prev_stack_top = m_match_stack.size(); + + using SS = Static_String; + + if (Operator()) { + for (const auto &sym : {SS{"="}, SS{":="}, SS{"+="}, SS{"-="}, + SS{"*="}, SS{"/="}, SS{"%="}, SS{"<<="}, + SS{">>="}, SS{"&="}, SS{"^="}, SS{"|="}}) { + if (Symbol(sym, true)) { + SkipWS(true); + if (!Equation()) { + throw exception::eval_error( + "Incomplete equation", + File_Position(m_position.line, m_position.col), + *m_filename); + } + + build_match>(prev_stack_top, + sym.c_str()); + return true; + } + } + return true; + } + + return false; + } + + /// Parses statements allowed inside of a class block + bool Class_Statements(const std::string &t_class_name) { + Depth_Counter dc{this}; + bool retval = false; + + bool has_more = true; + bool saw_eol = true; + + while (has_more) { + const auto start = m_position; + if (Def(true, t_class_name) || Var_Decl(true, t_class_name)) { + if (!saw_eol) { + throw exception::eval_error( + "Two function definitions missing line separator", + File_Position(start.line, start.col), *m_filename); + } + has_more = true; + retval = true; + saw_eol = true; + } else if (Eol()) { + has_more = true; + retval = true; + saw_eol = true; + } else { + has_more = false; + } + } + + return retval; + } + + /// Top level parser, starts parsing of all known parses + bool Statements(const bool t_class_allowed = false) { + Depth_Counter dc{this}; + bool retval = false; + + bool has_more = true; + bool saw_eol = true; + + while (has_more) { + const auto start = m_position; + if (Def() || Try() || If() || While() || Class(t_class_allowed) || + For() || Switch()) { + if (!saw_eol) { + throw exception::eval_error( + "Two function definitions missing line separator", + File_Position(start.line, start.col), *m_filename); + } + has_more = true; + retval = true; + saw_eol = true; + } else if (Return() || Break() || Continue() || Equation()) { + if (!saw_eol) { + throw exception::eval_error( + "Two expressions missing line separator", + File_Position(start.line, start.col), *m_filename); + } + has_more = true; + retval = true; + saw_eol = false; + } else if (Block() || Eol()) { + has_more = true; + retval = true; + saw_eol = true; + } else { + has_more = false; + } + } + + return retval; + } + + AST_NodePtr parse(const std::string &t_input, + const std::string &t_fname) override { + Carbon_Parser parser(m_tracer, m_optimizer); + return parser.parse_internal(t_input, t_fname); + } + + eval::AST_Node_Impl_Ptr parse_instr_eval( + const std::string &t_input) { + auto last_position = m_position; + auto last_filename = m_filename; + auto last_match_stack = + std::exchange(m_match_stack, decltype(m_match_stack){}); + + auto retval = parse_internal(t_input, "instr eval"); + + m_position = std::move(last_position); + m_filename = std::move(last_filename); + m_match_stack = std::move(last_match_stack); + + return eval::AST_Node_Impl_Ptr( + dynamic_cast *>(retval.release())); + } + + /// Parses the given input string, tagging parsed ast_nodes with the given + /// m_filename. + AST_NodePtr parse_internal(const std::string &t_input, + std::string t_fname) { + const auto begin = t_input.empty() ? nullptr : &t_input.front(); + const auto end = begin == nullptr ? nullptr : begin + t_input.size(); + m_position = Position(begin, end); + m_filename = std::make_shared(std::move(t_fname)); + + if ((t_input.size() > 1) && (t_input[0] == '#') && + (t_input[1] == '!')) { + while (m_position.has_more() && (!Eol())) { + ++m_position; + } + } + + if (Statements(true)) { + if (m_position.has_more()) { + throw exception::eval_error( + "Unparsed input", + File_Position(m_position.line, m_position.col), + *m_filename); + } else { + build_match>(0); + } + } else { + m_match_stack.push_back( + Carbon::make_unique, + eval::Noop_AST_Node>()); + } + + AST_NodePtr retval(std::move(m_match_stack.front())); + m_match_stack.clear(); + return retval; + } +}; +} // namespace parser +} // namespace Carbon + +#if defined(CARBON_MSVC) && defined(CARBON_PUSHED_MIN_MAX) +#undef CARBON_PUSHED_MIN_MAX +#pragma pop_macro("min") +#pragma pop_macro("max") +#endif + +#endif /* CARBON_PARSER_HPP */ diff --git a/src/carbon/language/posix.hpp b/src/carbon/language/posix.hpp new file mode 100644 index 00000000..abfeb171 --- /dev/null +++ b/src/carbon/language/posix.hpp @@ -0,0 +1,53 @@ + + +#ifndef CARBON_POSIX_HPP +#define CARBON_POSIX_HPP + +#include +#include + +namespace Carbon::detail { +struct Loadable_Module { + struct DLModule { + explicit DLModule(const std::string &t_filename) + : m_data(dlopen(t_filename.c_str(), RTLD_NOW)) { + if (m_data == nullptr) { + throw Carbon::exception::load_module_error(dlerror()); + } + } + + DLModule(DLModule &&) = default; + DLModule &operator=(DLModule &&) = default; + DLModule(const DLModule &) = delete; + DLModule &operator=(const DLModule &) = delete; + + ~DLModule() { dlclose(m_data); } + + void *m_data; + }; + + template + struct DLSym { + DLSym(DLModule &t_mod, const std::string &t_symbol) + : m_symbol( + reinterpret_cast(dlsym(t_mod.m_data, t_symbol.c_str()))) { + if (!m_symbol) { + throw Carbon::exception::load_module_error(dlerror()); + } + } + + T m_symbol; + }; + + Loadable_Module(const std::string &t_module_name, + const std::string &t_filename) + : m_dlmodule(t_filename), + m_func(m_dlmodule, "create_module_" + t_module_name), + m_moduleptr(m_func.m_symbol()) {} + + DLModule m_dlmodule; + DLSym m_func; + ModulePtr m_moduleptr; +}; +} // namespace Carbon::detail +#endif diff --git a/src/carbon/language/prelude.cpp b/src/carbon/language/prelude.cpp new file mode 100644 index 00000000..acaac4f2 --- /dev/null +++ b/src/carbon/language/prelude.cpp @@ -0,0 +1,551 @@ +#include "prelude.hpp" + +namespace Carbon { +std::string Carbon_Prelude::prelude() { + return R"chaiscript( + +def lt(l, r) { + if (call_exists(`<`, l, r)) { + l < r + } else { + type_name(l) < type_name(r) + } +} + + +def gt(l, r) { + if (call_exists(`>`, l, r)) { + l > r + } else { + type_name(l) > type_name(r) + } +} + +def eq(l, r) { + if (call_exists(`==`, l, r)) { + l == r + } else { + false + } +} + +def new(x) { + eval(type_name(x))(); +} + +def clone(double x) { + double(x).clone_var_attrs(x) +} + +def clone(string x) { + string(x).clone_var_attrs(x) +} + +def clone(vector x) { + vector(x).clone_var_attrs(x) +} + + +def clone(int x) { + int(x).clone_var_attrs(x) +} + +def clone(x) : function_exists(type_name(x)) && call_exists(eval(type_name(x)), x) +{ + eval(type_name(x))(x).clone_var_attrs(x); +} + + +# to_string for Pair() +def to_string(x) : call_exists(first, x) && call_exists(second, x) { + "<" + x.first.to_string() + ", " + x.second.to_string() + ">"; +} + +# to_string for containers +def to_string(x) : call_exists(range, x) && !x.is_type("string"){ + "[" + x.join(", ") + "]"; +} + +# Prints to console with no carriage return +def puts(x) { + print_string(x.to_string()); +} + +# Prints to console with carriage return +def print(x) { + println_string(x.to_string()); +} + +# Returns the maximum value of two numbers +def max(a, b) { + if (a>b) { + a + } else { + b + } +} + +# Returns the minimum value of two numbers +def min(a, b) +{ + if (a 0) && (!r.empty())) { + inserter(r.front()); + r.pop_front(); + --i; + } +} + + +# Returns a new container with the given number of elements taken from the container +def take(container, num) { + auto retval := new(container); + take(container, num, back_inserter(retval)); + retval; +} + + +def take_while(container, f, inserter) : call_exists(range, container) { + auto r := range(container); + while ((!r.empty()) && f(r.front())) { + inserter(r.front()); + r.pop_front(); + } +} + + +# Returns a new container with the given elements match the second value function +def take_while(container, f) { + auto retval := new(container); + take_while(container, f, back_inserter(retval)); + retval; +} + + +def drop(container, num, inserter) : call_exists(range, container) { + auto r := range(container); + auto i = num; + while ((i > 0) && (!r.empty())) { + r.pop_front(); + --i; + } + while (!r.empty()) { + inserter(r.front()); + r.pop_front(); + } +} + + +# Returns a new container with the given number of elements dropped from the given container +def drop(container, num) { + auto retval := new(container); + drop(container, num, back_inserter(retval)); + retval; +} + + +def drop_while(container, f, inserter) : call_exists(range, container) { + auto r := range(container); + while ((!r.empty())&& f(r.front())) { + r.pop_front(); + } + while (!r.empty()) { + inserter(r.front()); + r.pop_front(); + } +} + + +# Returns a new container with the given elements dropped that match the second value function +def drop_while(container, f) { + auto retval := new(container); + drop_while(container, f, back_inserter(retval)); + retval; +} + + +# Applies the second value function to the container. Starts with the first two elements. Expects at least 2 elements. +def reduce(container, func) : container.size() >= 2 && call_exists(range, container) { + auto r := range(container); + auto retval = r.front(); + r.pop_front(); + retval = func(retval, r.front()); + r.pop_front(); + while (!r.empty()) { + retval = func(retval, r.front()); + r.pop_front(); + } + retval; +} + + +# Returns a string of the elements in container delimited by the second value string +def join(container, delim) { + auto retval = ""; + auto range := range(container); + if (!range.empty()) { + retval += to_string(range.front()); + range.pop_front(); + while (!range.empty()) { + retval += delim; + retval += to_string(range.front()); + range.pop_front(); + } + } + retval; +} + + +def filter(container, f, inserter) : call_exists(range, container) { + auto r := range(container); + while (!r.empty()) { + if (f(r.front())) { + inserter(r.front()); + } + r.pop_front(); + } +} + + +# Returns a new Vector which match the second value function +def filter(container, f) { + auto retval := new(container); + filter(container, f, back_inserter(retval)); + retval; +} + + +def generate_range(x, y, inserter) { + auto i = x; + while (i <= y) { + inserter(i); + ++i; + } +} + + +# Returns a new Vector which represents the range from the first value to the second value +def generate_range(x, y) { + auto retval := Vector(); + generate_range(x,y,back_inserter(retval)); + retval; +} + + +# Returns a new Vector with the first value to the second value as its elements +def collate(x, y) { + return [x, y]; +} + + +def zip_with(f, x, y, inserter) : call_exists(range, x) && call_exists(range, y) { + auto r_x := range(x); + auto r_y := range(y); + while (!r_x.empty() && !r_y.empty()) { + inserter(f(r_x.front(), r_y.front())); + r_x.pop_front(); + r_y.pop_front(); + } +} + + +# Returns a new Vector which joins matching elements of the second and third value with the first value function +def zip_with(f, x, y) { + auto retval := Vector(); + zip_with(f,x,y,back_inserter(retval)); + retval; +} + + +# Returns a new Vector which joins matching elements of the first and second +def zip(x, y) { + zip_with(collate, x, y); +} + + +# Returns the position of the second value string in the first value string +def string::find(string substr) { + find(this, substr, size_t(0)); +} + + +# Returns the position of last match of the second value string in the first value string +def string::rfind(string substr) { + rfind(this, substr, size_t(-1)); +} + + +# Returns the position of the first match of elements in the second value string in the first value string +def string::find_first_of(string list) { + find_first_of(this, list, size_t(0)); +} + + +# Returns the position of the last match of elements in the second value string in the first value string +def string::find_last_of(string list) { + find_last_of(this, list, size_t(-1)); +} + + +# Returns the position of the first non-matching element in the second value string in the first value string +def string::find_first_not_of(string list) { + find_first_not_of(this, list, size_t(0)); +} + + +# Returns the position of the last non-matching element in the second value string in the first value string +def string::find_last_not_of(string list) { + find_last_not_of(this, list, size_t(-1)); +} + + +def string::ltrim() { + drop_while(this, fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'}); +} + + +def string::rtrim() { + reverse(drop_while(reverse(this), fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'})); +} + + +def string::trim() { + ltrim(rtrim(this)); +} + + +def find(container, value, Function compare_func) : call_exists(range, container) { + auto range := range(container); + while (!range.empty()) { + if (compare_func(range.front(), value)) { + return range; + } else { + range.pop_front(); + } + } + range; +} + + +def find(container, value) { + find(container, value, eq) +} + + +)chaiscript"; +} +} // namespace Carbon diff --git a/src/carbon/language/prelude.hpp b/src/carbon/language/prelude.hpp new file mode 100644 index 00000000..2b49394e --- /dev/null +++ b/src/carbon/language/prelude.hpp @@ -0,0 +1,14 @@ + + +#ifndef CARBON_PRELUDE_HPP +#define CARBON_PRELUDE_HPP + +#include + +namespace Carbon { +struct Carbon_Prelude { + static std::string prelude(); +}; +} // namespace Carbon + +#endif /* CARBON_PRELUDE_HPP */ diff --git a/src/carbon/language/prelude_docs.hpp b/src/carbon/language/prelude_docs.hpp new file mode 100644 index 00000000..19e2a44f --- /dev/null +++ b/src/carbon/language/prelude_docs.hpp @@ -0,0 +1,832 @@ +/// This file is not technically part of the ChaiScript API. It is used solely +/// for generating Doxygen docs regarding the ChaiScript standard runtime +/// library. + +/// \brief Items in this namespace exist in the ChaiScript language runtime. +/// They are not part of the C++ API +namespace Carbon_Language { +/// \page LangStandardLibraryRef ChaiScript Language Standard Library Reference +/// +/// ChaiScript, at its core, has some very functional programming-inspired +/// habits. Few places show this off as clearly as the prelude, itself a name +/// taken as a nod to the popular functional language Haskell. This prelude is +/// available to all standard ChaiScript applications, and provides a simple +/// foundation for using numbers, strings, and ranges (the general category of +/// Range cs and their iteration). +/// + +/// \brief Generic concept of a value in ChaiScript. +/// +/// The Object type exists merely as a concept. All objects in ChaiScript +/// support this concept and have the following methods available to them. All +/// objects are stored internally as Carbon::Boxed_Value types. +/// +/// \sa Carbon::Boxed_Value +class Object { +public: + /// \brief Returns the Type_Info value for this Object + Type_Info get_type_info() const; + + /// \brief Returns true if the Object is of the named type + bool is_type(string) const; + + /// \brief Returns true if the Object is of the Type_Info passed in + bool is_type(Type_Info) const; + + /// \brief Returns true if the Object is immutable + bool is_var_const() const; + + /// \brief Returns true if the Object is a pointer and the pointer is null + bool is_var_null() const; + + /// \brief Returns true if the Object is stored as a pointer + bool is_var_pointer() const; + + /// \brief Returns true if the Object is stored as a reference + bool is_var_reference() const; + + /// \brief Returns true if the Object does not contain a value is is + /// undefined. + bool is_var_undef() const; + + /// \brief Returns the registered name of the type of the object. + /// + /// \sa Type_Info::name(); + string type_name() const; +}; + +/// \brief Item returned from a Range object from a Map +class Map_Pair { +public: + /// \brief Returns the key of the Map entry + const string first(); + + /// \brief Returns the value Object of the Map entry + Object second(); +}; + +/// \brief Maps strings to Objects +/// +/// ChaiScript has a built in shortcut for generating Map objects: +/// +/// Example: +/// \code +/// eval> var m = ["a":1, "b":2]; +/// [, ] +/// eval> m.count("a"); +/// 1 +/// eval> m.count("c"); +/// 0 +/// eval> m.size(); +/// 2 +/// \endcode +/// +/// Implemented as std::map +/// +/// \sa Map_Pair +/// \sa Carbon::bootstrap::standard_library::map_type +class Map { +public: + /// \brief Returns an object that implements the Range concept for the + /// Map_Pair's in this Map + Range range(); + + /// \brief Returns an object that implements the Const_Range concept for the + /// Map_Pair's in this Map + Const_Range range() const; + + /// \brief Returns the number of elements in the Map + int size() const; + + /// \brief Returns the item at the given key, creating an undefined Object + /// if the key does not yet exist in the map + Object operator[](string); + + /// \brief Clears the map of all items + void clear(); + + /// \brief Returns the number of items in the Map with the given key. + /// Returns 0 or 1 since this is not an std::multimap. + int count(string) const; + + /// \brief Returns true if the map contains no items + bool empty() const; +}; + +/// \brief A concept implemented by string, Vector and Map. It is convertible to +/// Range, default constructable and back_insertable +class Container { +public: + void push_back(Object); + Range range(); + Const_Range range() const; +}; + +/// \brief Converts o into a string. +/// +/// \code +/// eval> to_string(3).is_type("string")
+/// true
+/// \endcode +string to_string(Object o); + +/// \brief Prints o to the terminal, without a trailing carriage return. Applies +/// conversions to string automatically. \code eval> puts("hi, "); puts("there") +/// hi, thereeval> +/// \endcode +/// \sa to_string +/// \sa print +void puts(Object o); + +/// \brief Prints o to the terminal, with a trailing carriage return. Applies +/// conversions to string automatically \code eval> print("hello") hello eval> +/// \endcode +/// \sa to_string +/// \sa puts +void print(Object o); + +/// \brief ChaiScript representation of std::string. It is an std::string but +/// only some member are exposed to ChaiScript. +/// +/// Because the ChaiScript string object is an std::string, it is directly +/// convertible to and from std::string using the Carbon::boxed_cast and +/// Carbon::var functions. +/// +/// With the exception of string::trim, string::rtrim, string::ltrim, all +/// members are direct pass-throughs to the std::string of the same name. +/// +/// \note Object and function notations are equivalent in ChaiScript. This means +/// that +/// \c "bob".find("b") and \c find("bob", "b") are exactly the same. Most +/// examples below follow the second formation of the function calls. +/// \sa \ref keyworddef for extending existing C++ classes in ChaiScript +/// \sa Carbon::bootstrap::standard_library::string_type +class string { +public: + /// \brief Finds the first instance of substr. + /// \code + /// eval> find("abab", "ab") + /// 0 + /// \endcode + int find(string s) const; + + /// \brief Finds the last instance of substr. + /// \code + /// eval> rfind("abab", "ab") + /// 2 + /// \endcode + int rfind(string s) const; + + /// \brief Finds the first of characters in list in the string. + /// + /// \code + /// eval> find_first_of("abab", "bec") + /// 1 + /// \endcode + int find_first_of(string list) const; + + /// \brief Finds the last of characters in list in the string. + /// + /// \code + /// eval> find_last_of("abab", "bec") + /// 3 + /// \endcode + int find_last_of(string list) const; + + /// \brief Finds the first non-matching character to list in the str string. + /// + /// \code + /// eval> find_first_not_of("abcd", "fec") + /// 0 + /// \endcode + int find_first_not_of(string list) const; + + /// \brief Finds the last non-matching character to list in the list string. + /// + /// \code + /// eval> find_last_not_of("abcd", "fec") + /// 3 + /// \endcode + int find_last_not_of(string list) const; + + /// \brief Removes whitespace from the front of the string, returning a new + /// string + /// + /// \note This function is implemented as a ChaiScript function using the + /// def member function notation. + /// + /// \code + /// eval> ltrim(" bob") + /// bob + /// \endcode + /// + /// \sa \ref keyworddef + string lstrim() const; + + /// \brief Removes whitespace from the back of the string, returning a new + /// string + /// + /// \note This function is implemented as a ChaiScript function using the + /// def member function notation. + /// + /// \code + /// eval> rtrim("bob ") + "|" + /// bob| + /// \endcode + /// + /// \sa \ref keyworddef + string rtrim() const; + + /// \brief Removes whitespace from the front and back of the string, + /// returning a new string + /// + /// \note This function is implemented as a ChaiScript function using the + /// def member function notation. + /// + /// \code + /// eval> trim(" bob ") + "|" + /// bob| + /// \endcode + /// + /// Equivalent to rtrim(ltrim(" bob ")); + /// + /// \sa \ref keyworddef + string trim() const; + + /// \brief Returns the character at the given index in the string, const + /// version + const char &operator[](int t_index) const; + + /// \brief Returns the character at the given index in the string + char &operator[](int t_index); + + /// \brief Returns underlying const char * for C api compatibility + const char *c_str() const; + + /// \brief Returns a pointer to the raw data in the string + const char *data() const; + + /// \brief Resets the string to empty + void clear(); + + /// \brief Returns true if the string is empty + bool empty() const; + + /// \brief Returns the size of the string in bytes. + /// + /// This function normally returns size_t in C++. In ChaiScript the return + /// value is cast to int for ease of use. + int size() const; + + /// \brief Returns an object that implements the Range concept for the + /// characters of this string + Range range(); + + /// \brief Returns an object that implements the Const_Range concept for the + /// characters of this string + Const_Range range() const; +}; + +/// \brief A concept in ChaiScript that is implemented by \ref string, Vector +/// and Map. It provides +/// easy iteration over the elements in a container. +/// +/// Implemented by the template +/// Carbon::bootstrap::standard_library::Bidir_Range +/// +/// \sa Const_Range +class Range { +public: + /// \brief Returns the last item of the range + Object back(); + + /// \brief Returns true if the front and back pointers have passed each + /// other, if no items + /// are left in the Range + bool empty() const; + + /// \brief Returns the first item of the range + Object front(); + + /// \brief Moves the back pointer back one. + /// + /// \post back() returns the element at back() - 1; + void pop_back(); + + /// \brief Moves the front pointer forward one + /// + /// \post front() returns the element at front() + 1; + void pop_front(); +}; + +/// \brief A concept in ChaiScript that is implemented by \ref string, Vector +/// and Map. It provides +/// easy iteration over the elements in a container. Contained values are +/// const. +/// +/// Implemented by the template +/// Carbon::bootstrap::standard_library::Const_Bidir_Range +/// +/// \sa Range +class Const_Range { +public: + /// \brief Returns the last item of the range + const Object back(); + + /// \brief Returns true if the front and back pointers have passed each + /// other, if no items + /// are left in the Range + bool empty() const; + + /// \brief Returns the first item of the range + const Object front(); + + /// \brief Moves the back pointer back one. + /// + /// \post back() returns the element at back() - 1; + void pop_back(); + + /// \brief Moves the front pointer forward one + /// + /// \post front() returns the element at front() + 1; + void pop_front(); +}; + +/// \brief A vector of Objects +/// +/// ChaiScript includes a shortcut for creating a Vector of Objects +/// +/// Example: +/// \code +/// eval> var v = [1,2,3,4] +/// [1, 2, 3, 4] +/// eval> v[0]; +/// 1 +/// eval> v.size(); +/// 4 +/// \endcode +/// +/// Implemented with std::vector +/// +/// \sa Carbon::bootstrap::standard_library::vector_type +class Vector { +public: + /// \brief returns the Object at the given index. Throws an exception if the + /// index does not exist + Object operator[](int t_index); + + /// \brief returns a const Object at the given index. Throws an exception if + /// the index does not exist. + const Object operator[](int t_index) const; + + /// \brief returns the last item in the Vector + Object back(); + + /// \brief Clears the Vector of all items + void clear(); + + /// \brief Returns true if the Vector is contains 0 items + bool empty(); + + /// \brief Erases the element at the given index + void erase_at(int t_index); + + /// \brief Returns the first item in the Vector + Object front(); + + /// \brief Inserts a new item in the Vector at the given index. The item is + /// not cloned on insert + /// + /// \sa insert_ref + void insert_ref_at(int, Object); + + /// \brief Inserts a new item in the Vector at the given index. The item is + /// cloned on insert + /// + /// \sa insert_ref + void insert_at(int, Object); + + /// \brief Removes the last item from the Vector + void pop_back(); + + /// \brief Adds an item to the end of the Vector. The item is not cloned. + /// + /// \sa push_back + void push_back_ref(Object); + + /// \brief Adds an item to the end of the Vector. The item is cloned. + /// + /// \sa push_back_ref + void push_back(Object); + + /// \brief Returns a Range object for the entire vector + Range range(); + + /// \brief Returns a Const_Range object for the entire vector + Const_Range range() const; + + /// \brief Returns the number of elements in the Vector + int size() const; +}; + +class Type_Info { +public: + /// \brief Compares this Type_Info object with another one and returns true + /// if the two types are the same + /// after const, pointer, reference are removed. + bool bare_equal(Type_Info t_ti) const; + + /// \brief Returns the mangled C++ name for the type given by the compiler + /// after const, pointer, reference is removed. + string cpp_bare_name() const; + + /// \brief Returns the mangled C++ name for the type given by the compiler. + string cpp_name() const; + + /// \brief Returns true if the type is const + bool is_type_const() const; + + /// \brief Returns true if the type is a pointer + bool is_type_pointer() const; + + /// \brief Returns true if the type is a reference + bool is_type_reference() const; + + /// \brief Returns true if the type is undefined + bool is_type_undef() const; + + /// \brief Returns true if the type is "void" + bool is_type_void() const; + + /// \brief Returns the ChaiScript registered name for the type if one + /// exists. + string name() const; +}; + +/// \brief Represents a function object in ChaiScript +/// +/// A function object may be one function, such as: +/// \code +/// var f = fun(x) { return x; } +/// \endcode +/// +/// Or it may represent multiple functions +/// \code +/// var f2 = `-`; // represents the unary - as well as the set of binary - +/// operators \endcode +/// +/// Guarded function example +/// \code +/// def f3(x) : x > 2 { +/// return x; +/// } +/// \endcode +/// +/// Examples in the function definitions below will reference these examples +class Function { +public: + /// \brief Returns the annotation description of the function + string get_annotation() const; + + /// \brief Returns the arity of the function, -1 if the function takes a + /// variable number of parameters + /// + /// Example: + /// \code + /// eval> f.get_arity() + /// 1 + /// eval> f2.get_arity() + /// -1 + /// \endcode + int get_arity() const; + + /// \brief Returns a vector of the contained functions + /// + /// Example: + /// \code + /// eval> f.get_contained_functions().size() + /// 0 + /// eval> f2.get_contained_functions().size() + /// 11 + /// eval> var v = f2.get_contained_functions(); + /// v[0].get_arity() + /// 2 + /// \endcode + Vector get_contained_functions() const; + + /// \brief Returns a function guard as function + /// + /// Example: + /// \code + /// eval> f.get_guard() // Throws exception + /// Function does not have a guard + /// eval> f3.get_guard().get_arity() + /// 1 + /// \endcode + Function get_guard() const; + + /// \brief Returns a vector of Type_Info objects that represent the param + /// types for this function. + /// The first value in the list is the return type. + /// + /// If this function is a conglomerate of several functions + /// (get_contained_values().size() > 0) then the function returns as many + /// Type_Info objects as it can. If the functions contained all have the + /// same arity, then it represents the arity. If they have different + /// arities, it returns only one value - the return type. + /// + /// For each parameter that is the same type, the type is returned. If the + /// types are different then a Type_Info for Object is returned. + /// + /// Example: + /// \code + /// eval> f2.get_param_types().size(); // Returns a Type_Info for Object for + /// the return type + /// 1 + /// \endcode + Vector get_param_types() const; + + /// \brief Returns true if the function has a guard to it. Always returns + /// false for a conglomerate function + bool has_guard() const; + + /// \brief Calls the function with the given set of parameters and returns + /// the value; + /// + /// Example: + /// \code + /// eval> `-`.call([2,1]); + /// 1 + /// \endcode + Object call(Vector t_params) const; +} + +/// \brief Returns the max of a or b. Requires that operator>(a, b) exists +/// Equivalent to +/// \code +/// return a>b?a:b; +/// \endcode +/// +/// Example: +/// \code +/// eval> max(4, 10) +/// 10 +/// \endcode +Object +max(Object a, Object b); + +/// \brief Returns the min of a or b. Requires that operator<(a, b) exists +/// +/// Equivalent to +/// \code +/// return a min(4, 10) +/// 4 +/// \endcode +Object min(Object a, Object b); + +/// \brief Returns true if x is an even integer. +/// +/// Will also work on any non-integer type for which an operator%(x, int) exists +/// +/// Example: +/// \code +/// eval> even(4) +/// true +/// \endcode +bool even(Object x); + +/// \brief Returns true if x is an odd integer. +/// +/// Will also work on any non-integer type for which an operator%(x, int) exists +/// +/// Example: +/// \code +/// eval> odd(4) +/// false +/// \endcode +bool even(Object x); + +/// \brief Applies the function f over each element in the Range c. +/// +/// Example: +/// \code +/// eval> for_each([1, 2, 3], print) +/// 1 +/// 2 +/// 3 +/// \endcode +void for_each(Range c, Function f); + +/// \brief Applies f over each element in the Range c, joining all the results. +/// +/// Example: +/// \code +/// eval> map([1, 2, 3], odd) +/// [true, false, true] +/// \endcode +Object map(Range c, Function f); + +/// \brief Starts with the initial value and applies the function f to it and +/// the first element of the Range c. +/// The result is then applied to the second element, and so on until the +/// elements are exhausted. +/// +/// Example: +/// \code +/// eval> foldl([1, 2, 3, 4], `+`, 0) +/// 10 +/// \endcode +Object foldl(Range c, Function f, Object initial); + +/// \brief Returns the sum total of the values in the Range c. +/// +/// Example: +/// \code +/// eval> sum([1, 2, 3, 4]) +/// 10 +/// \endcode +/// +/// Equivalent to: +/// \code +/// foldl(c, `+`, 0.0); +/// \endcode +Numeric sum(Range c); + +/// \brief Returns the product of the value in the Range c. +/// +/// Example: +/// \code +/// eval> product([1, 2, 3, 4]) +/// 24 +/// \endcode +/// +/// Equivalent to: +/// \code +/// foldl(c, `*`, 1.0); +/// \endcode +Numeric product(Range c); + +/// \brief Takes num elements from the Range c, returning them. +/// +/// Example: +/// \code +/// eval> take([1, 2, 3, 4], 2) +/// [1, 2] +/// \endcode +/// +/// \returns A container of the same type that was passed in +Object take(Range c, int num); + +/// \brief Takes elements from the Range c that match function f, stopping at +/// the first non-match, returning them as a new Vector. +/// +/// Example: +/// \code +/// eval> take_while([1, 2, 3], odd) +/// [1] +/// \endcode +/// +/// \returns A container of the same type that was passed in +Object take_while(Range c, Function f); + +/// \brief Drops num elements from the Range c, returning the remainder. +/// +/// Example: +/// \code +/// eval> drop([1, 2, 3, 4], 2) +/// [3, 4] +/// \endcode +/// +/// \returns A container of the same type that was passed in +Object drop(Range c, int num); + +/// \brief Drops elements from the Range c that match f, stopping at the first +/// non-match, returning the remainder. +/// +/// Example: +/// \code +/// eval> drop_while([1, 2, 3], odd) +/// [2, 3] +/// \endcode +Object drop_while(Range c, Function f); + +/// \brief Similar to foldl, this takes the first two elements as its starting +/// values for f. This assumes Range c has at least 2 elements. +/// +/// Example: +/// \code +/// eval> reduce([1, 2, 3, 4], `+`) +/// 10 +/// \endcode +Object reduce(Range c, Function f); + +/// \brief Takes elements from Container c that match function f, return them. +/// +/// Example: +/// \code +/// eval> filter([1, 2, 3, 4], odd) +/// [1, 3] +/// \endcode +Object filter(Container c, Function f); + +/// \brief Joins the elements of the Range c into a string, delimiting each with +/// the delim string. +/// +/// Example: +/// \code +/// eval> join([1, 2, 3], "*") +/// 1*2*3 +/// \endcode +string join(Range c, string delim); + +/// \brief Returns the contents of the Container c in reversed order. +/// +/// Example: +/// \code +/// eval> reverse([1, 2, 3, 4, 5, 6, 7]) +/// [7, 6, 5, 4, 3, 2, 1] +/// \endcode +Container reverse(Container c); + +/// \brief Generates a new Vector filled with values starting at x and ending +/// with y. +/// +/// Works on types supporting operator<=(x, y) and operator++(x) +/// +/// Example: +/// \code +/// eval> generate_range(1, 10) +/// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +/// \endcode +Vector generate_range(Object x, Object y); + +/// \brief Returns a new Range with x and y concatenated. +/// +/// Example: +/// \code +/// eval> concat([1, 2, 3], [4, 5, 6]) +/// [1, 2, 3, 4, 5, 6] +/// \endcode +Object concat(Range x, Range y); + +/// \brief Returns a new Vector with x and y as its values. +/// +/// Example: +/// \code +/// eval> collate(1, 2) +/// [1, 2] +/// \endcode +Vector collate(Object x, Object y); + +/// \brief Applies f to elements of x and y, returning a new Vector with the +/// result of each application. +/// +/// Example: +/// \code +/// eval> zip_with(`+`, [1, 2, 3], [4, 5, 6]) +/// [5, 7, 9] +/// \endcode +Vector zip_with(Function f, Range x, Range y); + +/// \brief Collates elements of x and y, returning a new Vector with the result. +/// +/// Example: +/// \code +/// eval> zip([1, 2, 3], [4, 5, 6]) +/// [[1, 4], [2, 5], [3, 6]] +/// \endcode +Vector zip(Range x, Range y); + +/// \brief returns true if there exists a call to the Function f that takes the +/// given parameters +/// +/// Example: +/// \code +/// eval> call_exists(`+`, 1, 2) +/// true +/// \endcode +bool call_exists(Function f, ...); + +/// \brief Reverses a Range object so that the elements are accessed in reverse +Range retro(Range); + +/// \brief Reverses a Const_Range object so that the elements are accessed in +/// reverse +Const_Range retro(Const_Range); + +/// \brief Raises the given object as an exception. Any type of object can be +/// thrown. +/// +/// Example: +/// \code +/// eval> try { throw(1); } catch (e) { print("Exception caught: " + +/// to_string(e)); } Exception caught: 1 \endcode +/// +/// \sa \ref keywordtry +void throw(Object); +} // namespace Carbon_Language diff --git a/src/carbon/language/tracer.hpp b/src/carbon/language/tracer.hpp new file mode 100644 index 00000000..96737eb0 --- /dev/null +++ b/src/carbon/language/tracer.hpp @@ -0,0 +1,34 @@ +#include "eval.hpp" + + +#ifndef CARBON_TRACER_HPP +#define CARBON_TRACER_HPP + +namespace Carbon::eval { +struct Noop_Tracer_Detail { + template + constexpr void trace(const Carbon::detail::Dispatch_State &, + const AST_Node_Impl *) noexcept {} +}; + +template +struct Tracer : T... { + Tracer() = default; + constexpr explicit Tracer(T... t) : T(std::move(t))... {} + + void do_trace(const Carbon::detail::Dispatch_State &ds, + const AST_Node_Impl> *node) { + (static_cast(*this).trace(ds, node), ...); + } + + static void trace(const Carbon::detail::Dispatch_State &ds, + const AST_Node_Impl> *node) { + ds->get_parser().get_tracer>().do_trace(ds, node); + } +}; + +using Noop_Tracer = Tracer; + +} // namespace Carbon::eval + +#endif diff --git a/src/carbon/language/unknown.hpp b/src/carbon/language/unknown.hpp new file mode 100644 index 00000000..bf46e876 --- /dev/null +++ b/src/carbon/language/unknown.hpp @@ -0,0 +1,23 @@ + + +#ifndef CARBON_UNKNOWN_HPP +#define CARBON_UNKNOWN_HPP + +namespace Carbon { +namespace detail { +struct Loadable_Module { + Loadable_Module(const std::string &, const std::string &) { +#ifdef CARBON_NO_DYNLOAD + throw Carbon::exception::load_module_error( + "Loadable module support was disabled (CARBON_NO_DYNLOAD)"); +#else + throw Carbon::exception::load_module_error( + "Loadable module support not available for your platform"); +#endif + } + + ModulePtr m_moduleptr; +}; +} // namespace detail +} // namespace Carbon +#endif diff --git a/src/carbon/language/windows.hpp b/src/carbon/language/windows.hpp new file mode 100644 index 00000000..7e6d52e5 --- /dev/null +++ b/src/carbon/language/windows.hpp @@ -0,0 +1,139 @@ + + +#ifndef CARBON_WINDOWS_HPP +#define CARBON_WINDOWS_HPP + +#include + +#ifdef CARBON_WINDOWS +#define VC_EXTRA_LEAN +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif + +namespace Carbon { +namespace detail { +struct Loadable_Module { + template + static std::wstring to_wstring(const T &t_str) { + return std::wstring(t_str.begin(), t_str.end()); + } + + template + static std::string to_string(const T &t_str) { + return std::string(t_str.begin(), t_str.end()); + } + +#if defined(_UNICODE) || defined(UNICODE) + template + static std::wstring to_proper_string(const T &t_str) { + return to_wstring(t_str); + } +#else + template + static std::string to_proper_string(const T &t_str) { + return to_string(t_str); + } +#endif + + static std::string get_error_message(DWORD t_err) { + using StringType = LPTSTR; + +#if defined(_UNICODE) || defined(UNICODE) + std::wstring retval = L"Unknown Error"; +#else + std::string retval = "Unknown Error"; +#endif + StringType lpMsgBuf = nullptr; + + if (FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, t_err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&lpMsgBuf), 0, nullptr) != 0 && + lpMsgBuf) { + retval = lpMsgBuf; + LocalFree(lpMsgBuf); + } + + return to_string(retval); + } + + struct DLModule { + explicit DLModule(const std::string &t_filename) + : m_data(LoadLibrary(to_proper_string(t_filename).c_str())) { + if (!m_data) { + throw Carbon::exception::load_module_error( + get_error_message(GetLastError())); + } + } + + DLModule(DLModule &&) = default; + DLModule &operator=(DLModule &&) = default; + DLModule(const DLModule &) = delete; + DLModule &operator=(const DLModule &) = delete; + + ~DLModule() { FreeLibrary(m_data); } + + HMODULE m_data; + }; + + /* + TODO: Fix -Wcast-function-type here + template + struct DLSym { + DLSym(DLModule &t_mod, const std::string &t_symbol) + : m_symbol(reinterpret_cast( + GetProcAddress(t_mod.m_data, t_symbol.c_str()))) { + if (!m_symbol) { + throw Carbon::exception::load_module_error( + get_error_message(GetLastError())); + } + } + + T m_symbol; + }; + */ + template + struct DLSym { + using FunctionPtr = T; + + DLSym(DLModule &t_mod, const std::string &t_symbol) + : m_symbol(get_function_pointer(t_mod, t_symbol)) { + if (!m_symbol) { + throw Carbon::exception::load_module_error( + get_error_message(GetLastError())); + } + } + + FunctionPtr m_symbol; + + private: + // Helper function to safely retrieve the function pointer + static FunctionPtr get_function_pointer(DLModule &t_mod, + const std::string &t_symbol) { + FARPROC proc = GetProcAddress(t_mod.m_data, t_symbol.c_str()); + if (!proc) { + throw std::runtime_error( + "Failed to retrieve function pointer for symbol: " + + t_symbol); + } + return reinterpret_cast(proc); + } + }; + + Loadable_Module(const std::string &t_module_name, + const std::string &t_filename) + : m_dlmodule(t_filename), + m_func(m_dlmodule, "create_module_" + t_module_name), + m_moduleptr(m_func.m_symbol()) {} + + DLModule m_dlmodule; + DLSym m_func; + ModulePtr m_moduleptr; +}; +} // namespace detail +} // namespace Carbon +#endif diff --git a/src/carbon/stdlib.hpp b/src/carbon/stdlib.hpp new file mode 100644 index 00000000..124e300a --- /dev/null +++ b/src/carbon/stdlib.hpp @@ -0,0 +1,72 @@ + + +#ifndef CARBON_STDLIB_HPP +#define CARBON_STDLIB_HPP + +#include +#include +#include +#include +#include + +#include "defines.hpp" +#include "language/common.hpp" + +#include "command/function_call.hpp" + +// #include "command/dispatchkit.hpp" +#include "command/bootstrap.hpp" +#include "command/bootstrap_stl.hpp" +#include "command/operators.hpp" +// #include "command/boxed_value.hpp" +#include "command/register_function.hpp" +#include "language/prelude.hpp" +#include "utils/json_wrap.hpp" + +#ifndef CARBON_NO_THREADS +#include +#endif\ + +/// @file +/// +/// This file generates the standard library that normal ChaiScript usage +/// requires. + +namespace Carbon { +class Std_Lib { +public: + [[nodiscard]] static ModulePtr library() { + auto lib = std::make_shared(); + bootstrap::Bootstrap::bootstrap(*lib); + + bootstrap::standard_library::vector_type>( + "Vector", *lib); + bootstrap::standard_library::string_type("string", *lib); + bootstrap::standard_library::map_type< + std::map>("Map", *lib); + //bootstrap::standard_library::map_type< + // std::unordered_map>("HashMap", *lib); + bootstrap::standard_library::pair_type< + std::pair>("Pair", *lib); + +#ifndef CARBON_NO_THREADS + bootstrap::standard_library::future_type< + std::future>("future", *lib); + lib->add( + Carbon::fun( + [](const std::function &t_func) { + return std::async(std::launch::async, t_func); + }), + "async"); +#endif + + json_wrap::library(*lib); + + lib->eval(Carbon_Prelude::prelude() /*, "standard prelude"*/); + + return lib; + } +}; +} // namespace Carbon + +#endif diff --git a/src/carbon/threading.hpp b/src/carbon/threading.hpp new file mode 100644 index 00000000..e3ab5053 --- /dev/null +++ b/src/carbon/threading.hpp @@ -0,0 +1,133 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#ifndef CARBON_THREADING_HPP +#define CARBON_THREADING_HPP + +#include + +#ifndef CARBON_NO_THREADS +#include +#include +#include +#else +#ifndef CARBON_NO_THREADS_WARNING +#pragma message("ChaiScript is compiling without thread safety.") +#endif +#endif + +#include "defines.hpp" + +/// \file +/// +/// This file contains code necessary for thread support in ChaiScript. +/// If the compiler definition CARBON_NO_THREADS is defined then thread safety +/// is disabled in ChaiScript. This has the result that some code is faster, because mutex locks are not required. +/// It also has the side effect that the Carbon::ChaiScript object may not be accessed from more than +/// one thread simultaneously. + +/// If threading is enabled, then this namespace contains std thread classes. +/// If threading is not enabled, then stubbed in wrappers that do nothing are provided. +/// This allows us to avoid \#ifdef code in the sections that need thread safety. +namespace Carbon::detail::threading { +#ifndef CARBON_NO_THREADS + + template + using unique_lock = std::unique_lock; + + template + using shared_lock = std::shared_lock; + + template + using lock_guard = std::lock_guard; + + using std::shared_mutex; + + using std::mutex; + + using std::recursive_mutex; + + /// Typesafe thread specific storage. If threading is enabled, this class uses a mutex protected map. If + /// threading is not enabled, the class always returns the same data, regardless of which thread it is called from. + template + class Thread_Storage { + public: + Thread_Storage() = default; + Thread_Storage(const Thread_Storage &) = delete; + Thread_Storage(Thread_Storage &&) = delete; + Thread_Storage &operator=(const Thread_Storage &) = delete; + Thread_Storage &operator=(Thread_Storage &&) = delete; + + ~Thread_Storage() { t().erase(this); } + + inline const T *operator->() const noexcept { return &(t()[this]); } + + inline const T &operator*() const noexcept { return t()[this]; } + + inline T *operator->() noexcept { return &(t()[this]); } + + inline T &operator*() noexcept { return t()[this]; } + + void *m_key; + + private: + /// todo: is it valid to make this noexcept? The allocation could fail, but if it + /// does there is no possible way to recover + static std::unordered_map &t() noexcept { + static thread_local std::unordered_map my_t; + return my_t; + } + }; + +#else // threading disabled + template + class unique_lock { + public: + constexpr explicit unique_lock(T &) noexcept {} + constexpr void lock() noexcept {} + constexpr void unlock() noexcept {} + }; + + template + class shared_lock { + public: + constexpr explicit shared_lock(T &) noexcept {} + constexpr void lock() noexcept {} + constexpr void unlock() noexcept {} + }; + + template + class lock_guard { + public: + constexpr explicit lock_guard(T &) noexcept {} + }; + + class shared_mutex { + }; + + class recursive_mutex { + }; + + template + class Thread_Storage { + public: + constexpr explicit Thread_Storage() noexcept {} + + constexpr inline T *operator->() const noexcept { return &obj; } + + constexpr inline T &operator*() const noexcept { return obj; } + + private: + mutable T obj; + }; + +#endif +} // namespace Carbon::detail::threading + +#endif diff --git a/src/carbon/utils/json.hpp b/src/carbon/utils/json.hpp new file mode 100644 index 00000000..711fb1d4 --- /dev/null +++ b/src/carbon/utils/json.hpp @@ -0,0 +1,663 @@ + + +#ifndef SIMPLEJSON_HPP +#define SIMPLEJSON_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../defines.hpp" +#include "atom/experiment/flatmap.hpp" + +namespace Carbon::json { +using std::enable_if; +using std::initializer_list; +using std::is_convertible; +using std::is_floating_point; +using std::is_integral; +using std::is_same; + +class JSON { +public: + enum class Class { + Null = 0, + Object, + Array, + String, + Floating, + Integral, + Boolean + }; + +private: + using Data = std::variant, + std::vector, std::string, double, + std::int64_t, bool>; + + struct Internal { + Internal(std::nullptr_t) : d(nullptr) {} + Internal() : d(nullptr) {} + Internal(Class c) : d(make_type(c)) {} + template + Internal(T t) : d(std::move(t)) {} + + static Data make_type(Class c) { + switch (c) { + case Class::Null: + return nullptr; + case Class::Object: + return QuickFlatMap{}; + case Class::Array: + return std::vector{}; + case Class::String: + return std::string{}; + case Class::Floating: + return double{}; + case Class::Integral: + return std::int64_t{}; + case Class::Boolean: + return bool{}; + } + throw std::runtime_error("unknown type"); + } + + void set_type(Class c) { + if (type() != c) { + d = make_type(c); + } + } + + Class type() const noexcept { return Class(d.index()); } + + template + decltype(auto) visit_or(Visitor &&visitor, Or &&other) const { + if (type() == Class(ClassValue)) { + return visitor( + std::get(ClassValue)>(d)); + } else { + return other(); + } + } + + template + auto &get_set_type() { + set_type(ClassValue); + return (std::get(ClassValue)>(d)); + } + + auto &Map() { return get_set_type(); } + auto &Vector() { return get_set_type(); } + auto &String() { return get_set_type(); } + auto &Int() { return get_set_type(); } + auto &Float() { return get_set_type(); } + auto &Bool() { return get_set_type(); } + + auto Map() const noexcept { + return std::get_if(Class::Object)>(&d); + } + auto Vector() const noexcept { + return std::get_if(Class::Array)>(&d); + } + auto String() const noexcept { + return std::get_if(Class::String)>(&d); + } + auto Int() const noexcept { + return std::get_if(Class::Integral)>(&d); + } + auto Float() const noexcept { + return std::get_if(Class::Floating)>(&d); + } + auto Bool() const noexcept { + return std::get_if(Class::Boolean)>(&d); + } + + Data d; + }; + + Internal internal; + +public: + template + class JSONWrapper { + Container *object = nullptr; + + public: + JSONWrapper(Container *val) : object(val) {} + JSONWrapper(std::nullptr_t) {} + + typename Container::iterator begin() { + return object ? object->begin() : typename Container::iterator(); + } + typename Container::iterator end() { + return object ? object->end() : typename Container::iterator(); + } + typename Container::const_iterator begin() const { + return object ? object->begin() : typename Container::iterator(); + } + typename Container::const_iterator end() const { + return object ? object->end() : typename Container::iterator(); + } + }; + + template + class JSONConstWrapper { + const Container *object = nullptr; + + public: + JSONConstWrapper(const Container *val) : object(val) {} + JSONConstWrapper(std::nullptr_t) {} + + typename Container::const_iterator begin() const noexcept { + return object ? object->begin() + : typename Container::const_iterator(); + } + typename Container::const_iterator end() const noexcept { + return object ? object->end() + : typename Container::const_iterator(); + } + }; + + JSON() = default; + JSON(std::nullptr_t) {} + + explicit JSON(Class type) : internal(type) {} + + JSON(initializer_list list) : internal(Class::Object) { + for (auto i = list.begin(), e = list.end(); i != e; ++i, ++i) { + operator[](i->to_string()) = *std::next(i); + } + } + + template + explicit JSON(T b, typename enable_if::value>::type * = + nullptr) noexcept + : internal(static_cast(b)) {} + + template + explicit JSON(T i, typename enable_if::value && + !is_same::value>::type * = + nullptr) noexcept + : internal(static_cast(i)) {} + + template + explicit JSON(T f, typename enable_if::value>::type * = + nullptr) noexcept + : internal(static_cast(f)) {} + + template + explicit JSON( + T s, typename enable_if::value>::type * = + nullptr) + : internal(static_cast(s)) {} + + static JSON Load(const std::string &); + + JSON &operator[](const std::string &key) { + return internal.Map().operator[](key); + } + + JSON &operator[](const size_t index) { + auto &vec = internal.Vector(); + if (index >= vec.size()) { + vec.resize(index + 1); + } + + return vec.operator[](index); + } + + JSON &at(const std::string &key) { return operator[](key); } + + const JSON &at(const std::string &key) const { + return internal.visit_or( + [&](const auto &m) -> const JSON & { return m.at(key); }, + []() -> const JSON & { + throw std::range_error("Not an object, no keys"); + }); + } + + JSON &at(size_t index) { return operator[](index); } + + const JSON &at(size_t index) const { + return internal.visit_or( + [&](const auto &m) -> const JSON & { return m.at(index); }, + []() -> const JSON & { + throw std::range_error("Not an array, no indexes"); + }); + } + + auto length() const noexcept { + return internal.visit_or( + [&](const auto &m) { return static_cast(m.size()); }, + []() { return -1; }); + } + + bool has_key(const std::string &key) const noexcept { + return internal.visit_or( + [&](const auto &m) { return m.count(key) != 0; }, + []() { return false; }); + } + + int size() const noexcept { + if (auto m = internal.Map(); m != nullptr) { + return static_cast(m->size()); + } + if (auto v = internal.Vector(); v != nullptr) { + return static_cast(v->size()); + } else { + return -1; + } + } + + Class JSONType() const noexcept { return internal.type(); } + + /// Functions for getting primitives from the JSON object. + bool is_null() const noexcept { return internal.type() == Class::Null; } + + std::string to_string() const noexcept { + return internal.visit_or([](const auto &o) { return o; }, + []() { return std::string{}; }); + } + double to_float() const noexcept { + return internal.visit_or( + [](const auto &o) { return o; }, []() { return double{}; }); + } + std::int64_t to_int() const noexcept { + return internal.visit_or( + [](const auto &o) { return o; }, []() { return std::int64_t{}; }); + } + bool to_bool() const noexcept { + return internal.visit_or( + [](const auto &o) { return o; }, []() { return false; }); + } + + JSONWrapper> object_range() { + return std::get_if(Class::Object)>( + &internal.d); + } + + JSONWrapper> array_range() { + return std::get_if(Class::Array)>(&internal.d); + } + + JSONConstWrapper> object_range() const { + return std::get_if(Class::Object)>( + &internal.d); + } + + JSONConstWrapper> array_range() const { + return std::get_if(Class::Array)>(&internal.d); + } + + std::string dump(long depth = 1, std::string tab = " ") const { + switch (internal.type()) { + case Class::Null: + return "null"; + case Class::Object: { + std::string pad = ""; + for (long i = 0; i < depth; ++i, pad += tab) { + } + + std::string s = "{\n"; + bool skip = true; + for (auto &p : *internal.Map()) { + if (!skip) { + s += ",\n"; + } + s += (pad + "\"" + json_escape(p.first) + + "\" : " + p.second.dump(depth + 1, tab)); + skip = false; + } + s += ("\n" + pad.erase(0, 2) + "}"); + return s; + } + case Class::Array: { + std::string s = "["; + bool skip = true; + for (auto &p : *internal.Vector()) { + if (!skip) { + s += ", "; + } + s += p.dump(depth + 1, tab); + skip = false; + } + s += "]"; + return s; + } + case Class::String: + return "\"" + json_escape(*internal.String()) + "\""; + case Class::Floating: + return std::to_string(*internal.Float()); + case Class::Integral: + return std::to_string(*internal.Int()); + case Class::Boolean: + return *internal.Bool() ? "true" : "false"; + } + + throw std::runtime_error("Unhandled JSON type"); + } + +private: + static std::string json_escape(const std::string &str) { + std::string output; + for (char i : str) { + switch (i) { + case '\"': + output += "\\\""; + break; + case '\\': + output += "\\\\"; + break; + case '\b': + output += "\\b"; + break; + case '\f': + output += "\\f"; + break; + case '\n': + output += "\\n"; + break; + case '\r': + output += "\\r"; + break; + case '\t': + output += "\\t"; + break; + default: + output += i; + break; + } + } + return output; + } + +private: +}; + +struct JSONParser { + static bool isspace(const char c) noexcept { +#ifdef CARBON_MSVC +// MSVC warns on these line in some circumstances +#pragma warning(push) +#pragma warning(disable : 6330) +#endif + return ::isspace(c) != 0; +#ifdef CARBON_MSVC +#pragma warning(pop) +#endif + } + + static void consume_ws(const std::string &str, size_t &offset) { + while (isspace(str.at(offset)) && offset <= str.size()) { + ++offset; + } + } + + static JSON parse_object(const std::string &str, size_t &offset) { + JSON Object(JSON::Class::Object); + + ++offset; + consume_ws(str, offset); + if (str.at(offset) == '}') { + ++offset; + return Object; + } + + for (; offset < str.size();) { + JSON Key = parse_next(str, offset); + consume_ws(str, offset); + if (str.at(offset) != ':') { + throw std::runtime_error( + std::string("JSON ERROR: Object: Expected colon, found '") + + str.at(offset) + "'\n"); + } + consume_ws(str, ++offset); + JSON Value = parse_next(str, offset); + Object[Key.to_string()] = Value; + + consume_ws(str, offset); + if (str.at(offset) == ',') { + ++offset; + continue; + } else if (str.at(offset) == '}') { + ++offset; + break; + } else { + throw std::runtime_error( + std::string("JSON ERROR: Object: Expected comma, found '") + + str.at(offset) + "'\n"); + } + } + + return Object; + } + + static JSON parse_array(const std::string &str, size_t &offset) { + JSON Array(JSON::Class::Array); + size_t index = 0; + + ++offset; + consume_ws(str, offset); + if (str.at(offset) == ']') { + ++offset; + return Array; + } + + for (; offset < str.size();) { + Array[index++] = parse_next(str, offset); + consume_ws(str, offset); + + if (str.at(offset) == ',') { + ++offset; + continue; + } else if (str.at(offset) == ']') { + ++offset; + break; + } else { + throw std::runtime_error( + std::string( + "JSON ERROR: Array: Expected ',' or ']', found '") + + str.at(offset) + "'\n"); + } + } + + return Array; + } + + static JSON parse_string(const std::string &str, size_t &offset) { + std::string val; + for (char c = str.at(++offset); c != '\"'; c = str.at(++offset)) { + if (c == '\\') { + switch (str.at(++offset)) { + case '\"': + val += '\"'; + break; + case '\\': + val += '\\'; + break; + case '/': + val += '/'; + break; + case 'b': + val += '\b'; + break; + case 'f': + val += '\f'; + break; + case 'n': + val += '\n'; + break; + case 'r': + val += '\r'; + break; + case 't': + val += '\t'; + break; + case 'u': { + val += "\\u"; + for (size_t i = 1; i <= 4; ++i) { + c = str.at(offset + i); + if ((c >= '0' && c <= '9') || + (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F')) { + val += c; + } else { + throw std::runtime_error( + std::string("JSON ERROR: String: Expected " + "hex character in unicode " + "escape, found '") + + c + "'"); + } + } + offset += 4; + } break; + default: + val += '\\'; + break; + } + } else { + val += c; + } + } + ++offset; + return JSON(val); + } + + static JSON parse_number(const std::string &str, size_t &offset) { + std::string val, exp_str; + char c = '\0'; + bool isDouble = false; + bool isNegative = false; + std::int64_t exp = 0; + bool isExpNegative = false; + if (offset < str.size() && str.at(offset) == '-') { + isNegative = true; + ++offset; + } + for (; offset < str.size();) { + c = str.at(offset++); + if (c >= '0' && c <= '9') { + val += c; + } else if (c == '.' && !isDouble) { + val += c; + isDouble = true; + } else { + break; + } + } + if (offset < str.size() && (c == 'E' || c == 'e')) { + c = str.at(offset++); + if (c == '-') { + isExpNegative = true; + } else if (c == '+') { + // do nothing + } else { + --offset; + } + + for (; offset < str.size();) { + c = str.at(offset++); + if (c >= '0' && c <= '9') { + exp_str += c; + } else if (!isspace(c) && c != ',' && c != ']' && c != '}') { + throw std::runtime_error( + std::string("JSON ERROR: Number: Expected a number for " + "exponent, found '") + + c + "'"); + } else { + break; + } + } + exp = Carbon::parse_num(exp_str) * + (isExpNegative ? -1 : 1); + } else if (offset < str.size() && + (!isspace(c) && c != ',' && c != ']' && c != '}')) { + throw std::runtime_error( + std::string("JSON ERROR: Number: unexpected character '") + c + + "'"); + } + --offset; + + if (isDouble) { + return JSON((isNegative ? -1 : 1) * Carbon::parse_num(val) * + std::pow(10, exp)); + } else { + if (!exp_str.empty()) { + return JSON( + (isNegative ? -1 : 1) * + static_cast(Carbon::parse_num(val)) * + std::pow(10, exp)); + } else { + return JSON((isNegative ? -1 : 1) * + Carbon::parse_num(val)); + } + } + } + + static JSON parse_bool(const std::string &str, size_t &offset) { + if (str.substr(offset, 4) == "true") { + offset += 4; + return JSON(true); + } else if (str.substr(offset, 5) == "false") { + offset += 5; + return JSON(false); + } else { + throw std::runtime_error( + std::string( + "JSON ERROR: Bool: Expected 'true' or 'false', found '") + + str.substr(offset, 5) + "'"); + } + } + + static JSON parse_null(const std::string &str, size_t &offset) { + if (str.substr(offset, 4) != "null") { + throw std::runtime_error( + std::string("JSON ERROR: Null: Expected 'null', found '") + + str.substr(offset, 4) + "'"); + } + offset += 4; + return JSON(); + } + + static JSON parse_next(const std::string &str, size_t &offset) { + char value; + consume_ws(str, offset); + value = str.at(offset); + switch (value) { + case '[': + return parse_array(str, offset); + case '{': + return parse_object(str, offset); + case '\"': + return parse_string(str, offset); + case 't': + case 'f': + return parse_bool(str, offset); + case 'n': + return parse_null(str, offset); + default: + if ((value <= '9' && value >= '0') || value == '-') { + return parse_number(str, offset); + } + } + throw std::runtime_error( + std::string("JSON ERROR: Parse: Unexpected starting character '") + + value + "'"); + } +}; + +inline JSON JSON::Load(const std::string &str) { + size_t offset = 0; + return JSONParser::parse_next(str, offset); +} + +} // namespace Carbon::json + +#endif diff --git a/src/carbon/utils/json_wrap.hpp b/src/carbon/utils/json_wrap.hpp new file mode 100644 index 00000000..6f44b04a --- /dev/null +++ b/src/carbon/utils/json_wrap.hpp @@ -0,0 +1,138 @@ +#ifndef CARBON_SIMPLEJSON_WRAP_HPP +#define CARBON_SIMPLEJSON_WRAP_HPP + +#include "json.hpp" + +namespace Carbon { +class json_wrap { +public: + static Module &library(Module &m) { + m.add(Carbon::fun( + [](const std::string &t_str) { return from_json(t_str); }), + "from_json"); + m.add(Carbon::fun(&json_wrap::to_json), "to_json"); + + return m; + } + +private: + static Boxed_Value from_json(const json::JSON &t_json) { + switch (t_json.JSONType()) { + case json::JSON::Class::Null: + return Boxed_Value(); + case json::JSON::Class::Object: { + std::map m; + + for (const auto &p : t_json.object_range()) { + m.insert(std::make_pair(p.first, from_json(p.second))); + } + + return Boxed_Value(m); + } + case json::JSON::Class::Array: { + std::vector vec; + + for (const auto &p : t_json.array_range()) { + vec.emplace_back(from_json(p)); + } + + return Boxed_Value(vec); + } + case json::JSON::Class::String: + return Boxed_Value(t_json.to_string()); + case json::JSON::Class::Floating: + return Boxed_Value(t_json.to_float()); + case json::JSON::Class::Integral: + return Boxed_Value(t_json.to_int()); + case json::JSON::Class::Boolean: + return Boxed_Value(t_json.to_bool()); + } + + throw std::runtime_error("Unknown JSON type"); + } + + static Boxed_Value from_json(const std::string &t_json) { + try { + return from_json(json::JSON::Load(t_json)); + } catch (const std::out_of_range &) { + throw std::runtime_error("Unparsed JSON input"); + } + } + + static std::string to_json(const Boxed_Value &t_bv) { + return to_json_object(t_bv).dump(); + } + + static json::JSON to_json_object(const Boxed_Value &t_bv) { + try { + const std::map m = Carbon::boxed_cast< + const std::map &>(t_bv); + + json::JSON obj(json::JSON::Class::Object); + for (const auto &o : m) { + obj[o.first] = to_json_object(o.second); + } + return obj; + } catch (const Carbon::exception::bad_boxed_cast &) { + // not a map + } + + try { + const std::vector v = + Carbon::boxed_cast &>(t_bv); + + json::JSON obj(json::JSON::Class::Array); + for (size_t i = 0; i < v.size(); ++i) { + obj[i] = to_json_object(v[i]); + } + return obj; + } catch (const Carbon::exception::bad_boxed_cast &) { + // not a vector + } + + try { + Boxed_Number bn(t_bv); + if (Boxed_Number::is_floating_point(t_bv)) { + return json::JSON(bn.get_as()); + } else { + return json::JSON(bn.get_as()); + } + } catch (const Carbon::detail::exception::bad_any_cast &) { + // not a number + } + + try { + return json::JSON(boxed_cast(t_bv)); + } catch (const Carbon::exception::bad_boxed_cast &) { + // not a bool + } + + try { + return json::JSON(boxed_cast(t_bv)); + } catch (const Carbon::exception::bad_boxed_cast &) { + // not a string + } + + try { + const Carbon::dispatch::Dynamic_Object &o = + boxed_cast(t_bv); + + json::JSON obj(json::JSON::Class::Object); + for (const auto &attr : o.get_attrs()) { + obj[attr.first] = to_json_object(attr.second); + } + return obj; + } catch (const Carbon::exception::bad_boxed_cast &) { + // not a dynamic object + } + + if (t_bv.is_null()) + return json::JSON(); // a null value + + throw std::runtime_error("Unknown object type to convert to JSON"); + } +}; + +} // namespace Carbon + +#endif diff --git a/src/carbon/utils/utility.hpp b/src/carbon/utils/utility.hpp new file mode 100644 index 00000000..3352e3fe --- /dev/null +++ b/src/carbon/utils/utility.hpp @@ -0,0 +1,105 @@ + + +#ifndef CARBON_UTILITY_UTILITY_HPP_ +#define CARBON_UTILITY_UTILITY_HPP_ + +#include +#include +#include +#include + +#include "../command/operators.hpp" +#include "../command/register_function.hpp" +#include "../language/common.hpp" + +namespace Carbon::utility { +/// Single step command for registering a class with ChaiScript +/// +/// \param[in,out] t_module Model to add class to +/// \param[in] t_class_name Name of the class being registered +/// \param[in] t_constructors Vector of constructors to add +/// \param[in] t_funcs Vector of methods to add +/// +/// \example Adding a basic class to ChaiScript in one step +/// +/// \code +/// Carbon::utility::add_class(*m, +/// "test", +/// { constructor(), +/// constructor() }, +/// { {fun(&test::function), "function"}, +/// {fun(&test::function2), "function2"}, +/// {fun(&test::function3), "function3"}, +/// {fun(static_cast(&test::function_overload)), +/// "function_overload" }, +/// {fun(static_cast(&test::function_overload)), +/// "function_overload" }, {fun(static_cast(&test::operator=)), "=" } +/// } +/// ); +/// +template +void add_class(ModuleType &t_module, const std::string &t_class_name, + const std::vector &t_constructors, + const std::vector> + &t_funcs) { + t_module.add(user_type(), t_class_name); + + for (const Carbon::Proxy_Function &ctor : t_constructors) { + t_module.add(ctor, t_class_name); + } + + for (const auto &fun : t_funcs) { + t_module.add(fun.first, fun.second); + } +} + +template +typename std::enable_if::value, void>::type add_class( + ModuleType &t_module, const std::string &t_class_name, + const std::vector::type, + std::string>> &t_constants) { + t_module.add(user_type(), t_class_name); + + t_module.add(Carbon::constructor(), t_class_name); + t_module.add(Carbon::constructor(), t_class_name); + + using namespace Carbon::bootstrap::operators; + equal(t_module); + not_equal(t_module); + assign(t_module); + + t_module.add( + Carbon::fun([](const Enum &e, const int &i) { return e == i; }), "=="); + t_module.add( + Carbon::fun([](const int &i, const Enum &e) { return i == e; }), "=="); + + for (const auto &constant : t_constants) { + t_module.add_global_const(Carbon::const_var(Enum(constant.first)), + constant.second); + } +} + +template +typename std::enable_if::value, void>::type add_class( + ModuleType &t_module, const std::string &t_class_name, + const std::vector> &t_constants) { + t_module.add(user_type(), t_class_name); + + t_module.add(Carbon::constructor(), t_class_name); + t_module.add(Carbon::constructor(), + t_class_name); + + using namespace Carbon::bootstrap::operators; + equal(t_module); + not_equal(t_module); + assign(t_module); + + for (const auto &constant : t_constants) { + t_module.add_global_const(Carbon::const_var(EnumClass(constant.first)), + constant.second); + } +} +} // namespace Carbon::utility + +#endif diff --git a/src/config/CMakeLists.txt b/src/config/CMakeLists.txt new file mode 100644 index 00000000..2076c950 --- /dev/null +++ b/src/config/CMakeLists.txt @@ -0,0 +1,58 @@ +# This project is licensed under the terms of the GPL3 license. +# +# Project Name: Lithium-Config +# Description: the official config module for lithium server +# Author: Max Qian +# License: GPL3 + +cmake_minimum_required(VERSION 3.20) +project(lithium-config C CXX) + +# Sources +set(${PROJECT_NAME}_SOURCES + _main.cpp + _component.cpp + configor.cpp +) + +# Headers +set(${PROJECT_NAME}_HEADERS + _component.hpp + configor.hpp +) + +set(${PROJECT_NAME}_LIBS + loguru + atom-component +) + +# Build Object Library +add_library(${PROJECT_NAME}_OBJECT OBJECT) +set_property(TARGET ${PROJECT_NAME}_OBJECT PROPERTY POSITION_INDEPENDENT_CODE 1) + +target_sources(${PROJECT_NAME}_OBJECT + PUBLIC + ${${PROJECT_NAME}_HEADERS} + PRIVATE + ${${PROJECT_NAME}_SOURCES} +) + +target_link_libraries(${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) + +add_library(${PROJECT_NAME} SHARED) + +target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_OBJECT ${${PROJECT_NAME}_LIBS}) +target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT}) +target_include_directories(${PROJECT_NAME} PUBLIC .) + +set_target_properties(${PROJECT_NAME} PROPERTIES + VERSION ${CMAKE_HYDROGEN_VERSION_STRING} + SOVERSION ${HYDROGEN_SOVERSION} + OUTPUT_NAME ${PROJECT_NAME} +) + +install(TARGETS ${PROJECT_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + + diff --git a/src/config/_component.cpp b/src/config/_component.cpp index 34c2ae92..96a02226 100644 --- a/src/config/_component.cpp +++ b/src/config/_component.cpp @@ -1,69 +1,191 @@ +/* + * _component.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-13 + +Description: Config Component for Atom Addon + +**************************************************/ + #include "_component.hpp" #include "configor.hpp" #include "atom/log/loguru.hpp" +#define CONFIG_MANAGER_CHECK \ + if (!m_configManager) { \ + LOG_F(ERROR, "ConfigComponent::{}: configManager is nullptr", \ + __func__); \ + return createErrorResponse(__func__, {"error", "key not found"}, \ + "key not found"); \ + } + +#define LITHIUM_CONFIG_KEY_CHECK(key) \ + if (!m_params.contains(key) || !m_params[key].is_string()) { \ + LOG_F(ERROR, "ConfigComponent::{}: {} not found", __func__, key); \ + return createErrorResponse(__func__, \ + {"error", std::string("missing ") + key}, \ + std::string("missing ") + key); \ + } + ConfigComponent::ConfigComponent(const std::string& name) - : SharedComponent(name), m_configManager(Lithium::ConfigManager::createShared()) {} + : SharedComponent(name), + m_configManager(Lithium::ConfigManager::createUnique()) { + DLOG_F(INFO, "ConfigComponent::Constructor"); -ConfigComponent::~ConfigComponent() {} + DLOG_F(INFO, "Injecting commands"); + registerFunc("getConfig", &ConfigComponent::getConfig, this); + registerFunc("setConfig", &ConfigComponent::setConfig, this); + registerFunc("deleteConfig", &ConfigComponent::deleteConfig, this); + registerFunc("loadConfig", &ConfigComponent::loadConfig, this); + registerFunc("saveConfig", &ConfigComponent::saveConfig, this); + registerFunc("hasConfig", &ConfigComponent::hasConfig, this); + registerFunc("loadConfigs", &ConfigComponent::loadConfigs, this); +} -bool ConfigComponent::initialize() { return true; } +ConfigComponent::~ConfigComponent() { + DLOG_F(INFO, "ConfigComponent::Destructor"); +} -bool ConfigComponent::destroy() { return true; } +bool ConfigComponent::initialize() { + DLOG_F(INFO, "ConfigComponent::initialize"); + return true; +} + +bool ConfigComponent::destroy() { + DLOG_F(INFO, "ConfigComponent::destroy"); + return true; +} json ConfigComponent::getConfig(const json& m_params) { - if (!m_configManager) { - LOG_F(ERROR, "ConfigComponent::getConfig m_configManager is nullptr"); - return createErrorResponse("getConfig", {"error", "key not found"}, - "key not found"); - } - if (!m_params.contains("key") || !m_params["key"].is_string()) { - LOG_F(ERROR, "ConfigComponent::getConfig: key not found"); - return createErrorResponse("getConfig", {"error", "missing key"}, - "missing key"); - } + CONFIG_MANAGER_CHECK; + LITHIUM_CONFIG_KEY_CHECK("key"); std::string key = m_params["key"].get(); - if (!m_configManager->hasValue(key)) - { + if (!m_configManager->hasValue(key)) { LOG_F(ERROR, "ConfigComponent::getConfig: key not found"); return createErrorResponse("getConfig", {"error", "key not found"}, "key not found"); } - if (auto value = m_configManager->getValue(key); value) - { - return createSuccessResponse("getConfig", {"value",value}); + if (auto value = m_configManager->getValue(key); value.has_value()) { + return createSuccessResponse("getConfig", {"value", value.value()}); } - return createErrorResponse("getConfig", {"error", {"error", "failed to get config by key"}}, - "failed to get config by key"); + return createErrorResponse( + "getConfig", {"error", {"error", "failed to get config by key"}}, + "failed to get config by key"); } -json ConfigComponent::setConfig(const json &m_params) -{ - if (!m_configManager) - { - LOG_F(ERROR,"ConfigComponent::setConfig: m_configManager is nullptr"); - return createErrorResponse("setConfig", {"error", {"error", "config manager not set"}}, - "config manager not set"); +json ConfigComponent::setConfig(const json& m_params) { + CONFIG_MANAGER_CHECK; + if (!m_params.contains("key") || !m_params.contains("value")) { + LOG_F(ERROR, + "ConfigComponent::setConfig: m_params does not contain key"); + return createErrorResponse("setConfig", {"error", "key not set"}, + "key not set"); } - if (!m_params.contains("key") || !m_params.contains("value")) - { - LOG_F(ERROR,"ConfigComponent::setConfig: m_params does not contain key"); - return createErrorResponse("setConfig", {"error", {"error", "key not set"}}, + if (!m_params["key"].is_string() || !m_params["value"].is_primitive()) { + LOG_F(ERROR, + "ConfigComponent::setConfig: m_params is not a string or " + "primitive"); + return createErrorResponse("setConfig", {"error", "key not set"}, "key not set"); } - if (!m_params["key"].is_string() || !m_params["value"].is_primitive()) - { - LOG_F(ERROR,"ConfigComponent::setConfig: m_params is not a string or primitive"); - return createErrorResponse("setConfig", {"error", {"error", "key not set"}}, + if (!setVariable(m_params["key"].get(), m_params["value"])) { + LOG_F(ERROR, "ConfigComponent::setConfig: setVariable failed"); + return createErrorResponse( + "setConfig", {"error", {"error", "key not set"}}, "key not set"); + } + return createSuccessResponse("setConfig", {}); +} + +json ConfigComponent::deleteConfig(const json& m_params) { + CONFIG_MANAGER_CHECK; + LITHIUM_CONFIG_KEY_CHECK("key"); + try { + if (!m_configManager->deleteValue(m_params["key"].get())) { + LOG_F(ERROR, "ConfigComponent::deleteConfig: deleteValue failed"); + return createErrorResponse(__func__, {"error", "key not set"}, + "key not set"); + } + } catch (const json::exception& e) { + LOG_F(ERROR, "ConfigComponent::deleteConfig: json exception: {}", + e.what()); + return createErrorResponse(__func__, {"error", e.what()}, "key not set"); } - if (!setVariable(m_params["key"].get(), m_params["value"])) - { - LOG_F(ERROR,"ConfigComponent::setConfig: setVariable failed"); - return createErrorResponse("setConfig", {"error", {"error", "key not set"}}, + return createSuccessResponse(__func__, {}); +} + +json ConfigComponent::hasConfig(const json& m_params) { + CONFIG_MANAGER_CHECK; + LITHIUM_CONFIG_KEY_CHECK("key"); + try { + if (!m_configManager->hasValue(m_params["key"].get())) { + LOG_F(ERROR, "ConfigComponent::hasConfig: hasValue failed"); + return createErrorResponse(__func__, {"error", "key not set"}, + "key not set"); + } + } catch (const json::exception& e) { + LOG_F(ERROR, "ConfigComponent::hasConfig: json exception: {}", + e.what()); + return createErrorResponse(__func__, {"error", e.what()}, "key not set"); } - return createSuccessResponse("setConfig", {}); + return createSuccessResponse(__func__, {}); +} + +json ConfigComponent::loadConfig(const json& m_params) { + CONFIG_MANAGER_CHECK; + if (!m_params.contains("path") || !m_params["path"].is_string()) { + LOG_F(ERROR, "ConfigComponent::loadConfig: path not set"); + return createErrorResponse("loadConfig", {"error", "path not set"}, + "path not set"); + } + if (!m_configManager->loadFromFile(m_params["path"].get())) { + LOG_F(ERROR, "ConfigComponent::loadConfig: loadFromFile failed"); + return createErrorResponse("loadConfig", {"error", "path not set"}, + "path not set"); + } + return createSuccessResponse("loadConfig", {}); +} + +json ConfigComponent::saveConfig(const json& m_params) { + CONFIG_MANAGER_CHECK; + if (!m_params.contains("path") || !m_params["path"].is_string()) { + LOG_F(ERROR, "ConfigComponent::saveConfig: path not set"); + return createErrorResponse("saveConfig", {"error", "path not set"}, + "path not set"); + } + if (!m_configManager->saveToFile(m_params["path"].get())) { + LOG_F(ERROR, "ConfigComponent::saveConfig: saveToFile failed"); + return createErrorResponse(__func__, {"error", "path not set"}, + "path not set"); + } + return createSuccessResponse(__func__, {}); +} + +json ConfigComponent::loadConfigs(const json& m_params) { + CONFIG_MANAGER_CHECK; + if (!m_params.contains("path") || !m_params["path"].is_string()) { + LOG_F(ERROR, "ConfigComponent::loadConfigs: path not set"); + return createErrorResponse("loadConfigs", {"error", "path not set"}, + "path not set"); + } + if (!m_configManager->loadFromFile(m_params["path"].get())) { + LOG_F(ERROR, "ConfigComponent::loadConfigs: loadFromFile failed"); + return createErrorResponse(__func__, {"error", "path not set"}, + "path not set"); + } + return createSuccessResponse(__func__, {}); +} + +json ConfigComponent::tidyConfig(const json& m_params) { + CONFIG_MANAGER_CHECK; + m_configManager->tidyConfig(); + return createSuccessResponse(__func__, {}); } \ No newline at end of file diff --git a/src/config/_component.hpp b/src/config/_component.hpp index 8e6dd022..e7242c83 100644 --- a/src/config/_component.hpp +++ b/src/config/_component.hpp @@ -1,15 +1,27 @@ +/* + * _component.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-13 + +Description: Config Component for Atom Addon + +**************************************************/ + #ifndef LITHIUM_CONFIG_COMPONENT_HPP #define LITHIUM_CONFIG_COMPONENT_HPP #include "atom/components/templates/shared_component.hpp" -namespace Lithium -{ - class ConfigManager; +namespace Lithium { +class ConfigManager; } -class ConfigComponent : public SharedComponent -{ +class ConfigComponent : public SharedComponent { public: explicit ConfigComponent(const std::string &name); ~ConfigComponent(); @@ -33,6 +45,7 @@ class ConfigComponent : public SharedComponent json loadConfig(const json &m_params); json loadConfigs(const json &m_params); json saveConfig(const json &m_params); + json tidyConfig(const json &m_params); private: std::unique_ptr m_configManager; diff --git a/src/config/_main.cpp b/src/config/_main.cpp new file mode 100644 index 00000000..1c5ca8c1 --- /dev/null +++ b/src/config/_main.cpp @@ -0,0 +1,28 @@ +/* + * _main.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-13 + +Description: Main Entry + +**************************************************/ + +#include "_component.hpp" + +extern "C" { + +std::shared_ptr getInstance( + [[maybe_unused]] const json ¶ms) { + if (params.contains("name") && params["name"].is_string()) { + return std::make_shared( + params["name"].get()); + } + return std::make_shared("lithium.config"); +} + +} \ No newline at end of file diff --git a/src/config/_script.hpp b/src/config/_script.hpp new file mode 100644 index 00000000..788c4fd8 --- /dev/null +++ b/src/config/_script.hpp @@ -0,0 +1,190 @@ +/* + * _script.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-13 + +Description: Carbon Binding of ConfigManager + +**************************************************/ + +#ifndef LITHIUM_CONFIG_SCRIPT_HPP +#define LITHIUM_CONFIG_SCRIPT_HPP + +#include "carbon/carbon.hpp" + +#include "configor.hpp" + +#include "atom/log/loguru.hpp" +#include "atom/server/global_ptr.hpp" + +namespace Lithium::_Script::Config { +/** + * Adds the String Methods to the given Carbon m. + */ +Carbon::ModulePtr bootstrap( + Carbon::ModulePtr m = std::make_shared()) { + try { + auto m_configManager = GetWeakPtr("lithium.config"); + if (m_configManager.expired()) { + LOG_F(ERROR, "config manager pointer is expired!"); + return nullptr; + } + + auto get_config_func = [](std::weak_ptr manager, + const std::string &sv) -> json { + if (!sv.empty() && !manager.expired()) { + LOG_F(ERROR, "no key path found"); + return {}; + } + if (auto value = manager.lock()->getValue(sv); value.has_value()) { + return value.value(); + } + return {}; + }; + + m->add(Carbon::fun([m_configManager, + get_config_func](const std::string &key) -> double { + if (!key.empty()) { + try { + return get_config_func(m_configManager, key) + .get(); + } catch (const json::exception &e) { + } + } + return -1; + }), + "get_number_config"); + + m->add(Carbon::fun([m_configManager, get_config_func]( + const std::string &key) -> std::string { + if (!key.empty()) { + try { + return get_config_func(m_configManager, key) + .get(); + } catch (const json::exception &e) { + } + } + return ""; + }), + "get_string_config"); + + m->add(Carbon::fun([m_configManager, + get_config_func](const std::string &key) -> bool { + if (!key.empty()) { + try { + return get_config_func(m_configManager, key) + .get(); + } catch (const json::exception &e) { + } + } + return false; + }), + "get_boolean_config"); + + m->add(Carbon::fun([m_configManager](const std::string &key, + double number) { + if (!key.empty() && !m_configManager.expired()) [[likely]] { + if (m_configManager.lock()->setValue(key, number)) { + return true; + } + } + return false; + }), + "set_number_config"); + + m->add(Carbon::fun([m_configManager](const std::string &key, + const std::string &text) { + if (!key.empty() && !m_configManager.expired()) [[likely]] { + if (m_configManager.lock()->setValue(key, text)) { + return true; + } + } + return false; + }), + "set_string_config"); + + m->add( + Carbon::fun([m_configManager](const std::string &key, bool value) { + if (!key.empty() && !m_configManager.expired()) [[likely]] { + if (m_configManager.lock()->setValue(key, value)) { + return true; + } + } + return false; + }), + "set_bool_config"); + + m->add(Carbon::fun([m_configManager](const std::string &key) { + if (!key.empty() && !m_configManager.expired()) [[likely]] { + if (m_configManager.lock()->hasValue(key)) { + return true; + } + } + return false; + }), + "has_config"); + + m->add(Carbon::fun([m_configManager](const std::string &key) { + if (!key.empty() && !m_configManager.expired()) [[likely]] { + if (m_configManager.lock()->deleteValue(key)) { + return true; + } + } + return false; + }), + "delete_config"); + + m->add(Carbon::fun([m_configManager](const std::string &file_path) { + if (!file_path.empty() && !m_configManager.expired()) + [[likely]] { + if (m_configManager.lock()->loadFromFile(file_path)) { + return true; + } + } + return false; + }), + "load_config"); + + m->add(Carbon::fun([m_configManager](const std::string &folder_path) { + if (!folder_path.empty() && !m_configManager.expired()) + [[likely]] { + if (m_configManager.lock()->loadFromDir(folder_path)) { + return true; + } + } + return false; + }), + "load_configs"); + + m->add(Carbon::fun([m_configManager](const std::string &file_path) { + if (!file_path.empty() && !m_configManager.expired()) + [[likely]] { + if (m_configManager.lock()->saveToFile(file_path)) { + return true; + } + } + return false; + }), + "save_config"); + + m->add(Carbon::fun([m_configManager]() { + if (!m_configManager.expired()) [[likely]] { + m_configManager.lock()->tidyConfig(); + } + }), + "tidy_config"); + + return m; + } catch (const std::bad_any_cast &e) { + LOG_F(ERROR, "Failed to load config manager"); + return nullptr; + } +} +} // namespace Lithium::_Script::Config + +#endif diff --git a/src/config/configor.cpp b/src/config/configor.cpp index cfe5a952..d2ca6a15 100644 --- a/src/config/configor.cpp +++ b/src/config/configor.cpp @@ -29,171 +29,181 @@ Description: Configor namespace fs = std::filesystem; -namespace Lithium { -ConfigManager::ConfigManager() { +class ConfigManagerImpl { +public: + mutable std::shared_mutex rw_mutex_; + json config_; +}; + +ConfigManager::ConfigManager() : m_impl(std::make_unique()) { if (loadFromFile("config.json")) { - DLOG_F(INFO, "current config: {}", config_.dump(4)); + DLOG_F(INFO, "Config loaded successfully."); } } ConfigManager::~ConfigManager() { saveToFile("config.json"); } std::shared_ptr ConfigManager::createShared() { - return std::make_shared(); + static std::shared_ptr instance = + std::make_shared(); + return instance; +} + +std::unique_ptr ConfigManager::createUnique() { + return std::make_unique(); } -bool ConfigManager::loadFromFile(const std::string &path) { - std::lock_guard lock(mutex_); +bool ConfigManager::loadFromFile(const fs::path& path) { + std::shared_lock lock(m_impl->rw_mutex_); try { std::ifstream ifs(path); - if (!ifs.is_open()) { - LOG_F(ERROR, "Failed to open file: {}", path); + if (!ifs || ifs.peek() == std::ifstream::traits_type::eof()) { + LOG_F(ERROR, "Failed to open file: {}", path.string()); return false; } json j = json::parse(ifs); - const std::string basename = fs::path(path).stem().string(); - config_[basename] = j["config"]; - DLOG_F(INFO, "Loaded config file {} successfully", path); + if (j.empty()) { + return false; + } + + mergeConfig(j); return true; - } catch (const json::exception &e) { - LOG_F(ERROR, "Failed to parse file: {}, error message: {}", path, + } catch (const json::exception& e) { + LOG_F(ERROR, "Failed to parse file: {}, error message: {}", path.string(), e.what()); - } catch (const std::exception &e) { - LOG_F(ERROR, "Failed to load config file: {}, error message: {}", path, + } catch (const std::exception& e) { + LOG_F(ERROR, "Failed to load config file: {}, error message: {}", path.string(), e.what()); } return false; } -bool ConfigManager::loadFromDir(const std::string &dir_path, bool recursive) { - std::lock_guard lock(rw_mutex_); - for (const auto &entry : fs::directory_iterator(dir_path)) { - if (entry.is_regular_file() && entry.path().extension() == ".json") { - loadFromFile(entry.path().string()); - } else if (recursive && entry.is_directory()) { - const std::string subdir_path = entry.path().string(); - const std::string config_file_path = subdir_path + "/config.json"; - if (fs::exists(config_file_path)) { - try { - json j = json::parse(std::ifstream(config_file_path)); - auto merged_j = json::object(); - merged_j[dir_path][entry.path().filename().string()] = j; - mergeConfig(merged_j); - } catch (const json::exception &e) { - LOG_F(ERROR, "Failed to parse file: {}, error message: {}", - config_file_path, e.what()); - } catch (const std::exception &e) { - LOG_F(ERROR, "Failed to open file {}", config_file_path); - } +bool ConfigManager::loadFromDir(const fs::path& dir_path, bool recursive) { + std::shared_lock lock(m_impl->rw_mutex_); + try { + for (const auto& entry : fs::directory_iterator(dir_path)) { + if (entry.is_regular_file() && + entry.path().extension() == ".json") { + loadFromFile(entry.path()); + } else if (recursive && entry.is_directory()) { + loadFromDir(entry.path(), true); } - loadFromDir(subdir_path, true); } + } catch (const std::exception& e) { + LOG_F(ERROR, "Failed to load config file from: {}, error message: {}", + dir_path.string(), e.what()); + return false; } return true; } -json ConfigManager::getValue(const std::string &key_path) const { - // std::lock_guard lock(rw_mutex_); - try { - const json *p = &config_; - for (const auto &key : Atom::Utils::splitString(key_path, '/')) { - if (p->is_object() && p->contains(key)) { - p = &(*p)[key]; - } else { - LOG_F(ERROR, "Key not found: {}", key_path); - return nullptr; - } +std::optional ConfigManager::getValue(const std::string& key_path) const { + std::shared_lock lock(m_impl->rw_mutex_); + const json* p = &m_impl->config_; + for (const auto& key : key_path | std::views::split('/')) { + if (p->is_object() && + p->contains(std::string(key.begin(), key.end()))) { + p = &(*p)[std::string(key.begin(), key.end())]; + } else { + return std::nullopt; } - return *p; - } catch (const std::exception &e) { - LOG_F(ERROR, "Failed to get value: {} {}", key_path, e.what()); - return nullptr; } + return *p; } -bool ConfigManager::setValue(const std::string &key_path, const json &value) { - // std::lock_guard lock(rw_mutex_); - try { - json *p = &config_; - for (const auto &key : Atom::Utils::splitString(key_path, '/')) { - DLOG_F(INFO, "Key: {}" , key); - if (!p->is_object()) { - LOG_F(ERROR, "Invalid key path: {}", key_path); - return false; - } - p = &(*p)[key]; - } - *p = value; - } catch (const std::exception &e) { - LOG_F(ERROR, "Failed to set key: {} with {}", key_path, e.what()); - return false; +bool ConfigManager::setValue(const std::string& key_path, const json& value) { + std::unique_lock lock(m_impl->rw_mutex_); + json* p = &m_impl->config_; + std::vector keys; + for (auto sub_range : key_path | std::views::split('/')) { + keys.emplace_back(sub_range.data(), sub_range.size()); } - return true; -} -bool ConfigManager::deleteValue(const std::string &key_path) { - std::lock_guard lock(rw_mutex_); - std::vector keys = Atom::Utils::splitString(key_path, '/'); - json *p = &config_; - for (const auto &key : keys) { + for (auto it = keys.begin(); it != keys.end(); ++it) { + if (it + 1 == keys.end()) { + (*p)[*it] = value; + return true; + } + if (!p->is_object()) { return false; } - auto it = p->find(key); - if (it == p->end()) { - return false; + + if (!p->contains(*it)) { + (*p)[*it] = json::object(); } - p = &(*p)[key]; + + p = &(*p)[*it]; } - p->clear(); - return true; -} -bool ConfigManager::hasValue(const std::string &key_path) const { - return getValue(key_path) != nullptr; + return true; } -void ConfigManager::tidyConfig() { - std::lock_guard lock(mutex_); - - json updated_config; - - for (const auto &[key, value] : config_.items()) { - std::vector keys = Atom::Utils::splitString(key, '/'); - json *p = &updated_config; - - for (const auto &sub_key : keys) { - if (!p->contains(sub_key)) { - (*p)[sub_key] = json::object(); +bool ConfigManager::deleteValue(const std::string& key_path) { + std::unique_lock lock(m_impl->rw_mutex_); + std::vector keys; + for (auto sub_range : key_path | std::views::split('/')) { + keys.emplace_back(sub_range.data(), sub_range.size()); + } + json* p = &m_impl->config_; + for (auto it = keys.begin(); it != keys.end(); ++it) { + if (it + 1 == keys.end()) { // Last key + if (p->contains(*it)) { + p->erase(*it); + return true; } - - p = &(*p)[sub_key]; + return false; } - - *p = value; + if (!p->contains(*it) || !(*p)[*it].is_object()) { + return false; + } + p = &(*p)[*it]; } - - config_ = std::move(updated_config); + return false; } -void ConfigManager::mergeConfig(const json &j) { config_.merge_patch(j); } +bool ConfigManager::hasValue(const std::string& key_path) const { + return getValue(key_path).has_value(); +} -bool ConfigManager::saveToFile(const std::string &file_path) const { +bool ConfigManager::saveToFile(const fs::path& file_path) const { + std::unique_lock lock(m_impl->rw_mutex_); std::ofstream ofs(file_path); - if (!ofs.is_open()) { - LOG_F(ERROR, "Failed to open file: {}", file_path); + if (!ofs) { + LOG_F(ERROR, "Failed to open file: {}", file_path.string()); return false; } try { - ofs << config_.dump(4); - } catch (const std::exception &e) { - LOG_F(ERROR, "Failed to save config to file: {}, error message: {}", - file_path, e.what()); + ofs << m_impl->config_.dump(4); ofs.close(); + return true; + } catch (const std::exception& e) { + LOG_F(ERROR, "Failed to save config to file: {}, error message: {}", + file_path.string(), e.what()); return false; } - ofs.close(); - DLOG_F(INFO, "Save config to file: {}", file_path); - return true; } -} // namespace Lithium + +void ConfigManager::tidyConfig() { + std::unique_lock lock(m_impl->rw_mutex_); + json updated_config; + for (const auto& [key, value] : m_impl->config_.items()) { + json* p = &updated_config; + for (auto sub_key : key | std::views::split('/')) { + if (!p->contains(std::string(sub_key.begin(), sub_key.end()))) { + (*p)[std::string(sub_key.begin(), sub_key.end())] = + json::object(); + } + p = &(*p)[std::string(sub_key.begin(), sub_key.end())]; + } + *p = value; + } + m_impl->config_ = std::move(updated_config); +} + +void ConfigManager::mergeConfig(const json& src) { + m_impl->config_.merge_patch(src); +} + +void ConfigManager::clearConfig() { m_impl->config_.clear(); } + diff --git a/src/config/configor.hpp b/src/config/configor.hpp index 5f94b42a..2d008e64 100644 --- a/src/config/configor.hpp +++ b/src/config/configor.hpp @@ -12,38 +12,56 @@ Description: Configor **************************************************/ -#pragma once +#ifndef LITHIUM_CONFIG_CONFIGOR_HPP +#define LITHIUM_CONFIG_CONFIGOR_HPP -#include +#include #include #include +namespace fs = std::filesystem; #include "error/error_code.hpp" #include "atom/type/json.hpp" using json = nlohmann::json; -#define GetIntConfig(path) \ - GetPtr("lithium.config")->getValue(path).get() +#define GetIntConfig(path) \ + GetPtr("lithium.config") \ + .value() \ + ->getValue(path) \ + .value() \ + .get() + +#define GetFloatConfig(path) \ + GetPtr("lithium.config") \ + .value() \ + ->getValue(path) \ + .value() \ + .get() + +#define GetBoolConfig(path) \ + GetPtr("lithium.config") \ + .value() \ + ->getValue(path) \ + .value() \ + .get() + +#define GetDoubleConfig(path) \ + GetPtr("lithium.config") \ + .value() \ + ->getValue(path) \ + .value() \ + .get() + +#define GetStringConfig(path) \ + GetPtr("lithium.config") \ + .value() \ + ->getValue(path) \ + .value() \ + .get() + +class ConfigManagerImpl; -#define GetFloatConfig(path) \ - GetPtr("lithium.config")->getValue(path).get() - -#define GetBoolConfig(path) \ - GetPtr("lithium.config")->getValue(path).get() - -#define GetDoubleConfig(path) \ - GetPtr("lithium.config")->getValue(path).get() - -#define GetStringConfig(path) \ - GetPtr("lithium.config")->getValue(path).get() - -namespace Lithium { -/** - * @brief 配置管理器 - * - * Config manager. - */ class ConfigManager { public: /** @@ -60,31 +78,22 @@ class ConfigManager { */ ~ConfigManager(); + // ------------------------------------------------------------------- + // Common methods + // ------------------------------------------------------------------- + /** - * @brief 创建一个全局唯一的ConfigManager实例 + * @brief 创建ConfigManager的共享指针,但是全局唯一 * - * Create a globally unique ConfigManager instance. + * Create a shared pointer of ConfigManager. But global only */ static std::shared_ptr createShared(); - /** - * @brief 从指定文件中加载JSON配置,并与原有配置进行合并 - * - * Load JSON configuration from the specified file and merge with the - * existing configuration. - * - * @param path 配置文件路径 - */ - bool loadFromFile(const std::string &path); + static std::unique_ptr createUnique(); - /** - * @brief 加载指定目录下的所有JSON配置文件 - * - * Load all JSON configuration files in the specified directory. - * - * @param dir_path 配置文件所在目录的路径 - */ - bool loadFromDir(const std::string &dir_path, bool recursive); + // ------------------------------------------------------------------- + // Config methods + // ------------------------------------------------------------------- /** * @brief 获取一个配置项的值 @@ -95,7 +104,8 @@ class ConfigManager { * "database/username" * @return json 配置项对应的值,如果键不存在则返回 nullptr */ - json getValue(const std::string &key_path) const; + [[nodiscard("config value should not be ignored!")]] std::optional + getValue(const std::string& key_path) const; /** * @brief 添加或更新一个配置项 @@ -107,8 +117,7 @@ class ConfigManager { * @param value 配置项的值,使用 JSON 格式进行表示 * @return bool 成功返回 true,失败返回 false */ - bool setValue(const std::string &key_path, const json &value); - + bool setValue(const std::string& key_path, const json& value); /** * @brief 删除一个配置项 * @@ -117,7 +126,8 @@ class ConfigManager { * @param key_path 配置项的键路径,使用斜杠 / 进行分隔,如 * "database/username" */ - bool deleteValue(const std::string &key_path); + + bool deleteValue(const std::string& key_path); /** * @brief 判断一个配置项是否存在 @@ -128,7 +138,27 @@ class ConfigManager { * "database/username" * @return bool 存在返回 true,不存在返回 false */ - bool hasValue(const std::string &key_path) const; + [[nodiscard("status of the value should not be ignored")]] bool hasValue( + const std::string& key_path) const; + + /** + * @brief 从指定文件中加载JSON配置,并与原有配置进行合并 + * + * Load JSON configuration from the specified file and merge with the + * existing configuration. + * + * @param path 配置文件路径 + */ + bool loadFromFile(const fs::path& path); + + /** + * @brief 加载指定目录下的所有JSON配置文件 + * + * Load all JSON configuration files in the specified directory. + * + * @param dir_path 配置文件所在目录的路径 + */ + bool loadFromDir(const fs::path& dir_path, bool recursive = false); /** * @brief 将当前配置保存到指定文件 @@ -137,7 +167,7 @@ class ConfigManager { * * @param file_path 目标文件路径 */ - bool saveToFile(const std::string &file_path) const; + bool saveToFile(const fs::path& file_path) const; /** * @brief 清理配置项 @@ -146,11 +176,15 @@ class ConfigManager { */ void tidyConfig(); + /** + * @brief 清除所有配置(测试用) + * + * Clear all of the configurations, only used for test + */ + void clearConfig(); + private: - // JSON配置项 - json config_; - std::mutex mutex_; - mutable std::shared_mutex rw_mutex_; + std::unique_ptr m_impl; /** * @brief 将 JSON 配置合并到当前配置中 @@ -159,6 +193,7 @@ class ConfigManager { * * @param j JSON 配置 */ - void mergeConfig(const json &j); + void mergeConfig(const json& j); }; -} // namespace Lithium + +#endif diff --git a/src/config/package.json b/src/config/package.json new file mode 100644 index 00000000..22441f2f --- /dev/null +++ b/src/config/package.json @@ -0,0 +1,34 @@ +{ + "name": "lithium.config", + "version": "1.0.0", + "type": "shared", + "description": "Lithium server builtin config module", + "license": "GPL-3.0-or-later", + "author": "Max Qian", + "repository": { + "type": "git", + "url": "https://github.com/ElementAstro/Lithium" + }, + "bugs": { + "type": "git", + "url": "https://github.com/ElementAstro/Lithium/issues" + }, + "homepage": { + "type": "git", + "url": "https://github.com/ElementAstro/Lithium" + }, + "keywords": [ + "lithium", + "config" + ], + "scripts": { + "build": "cmake --build-type=Release -- -j 4", + "lint": "clang-format -i src/*.cpp src/*.h" + }, + "modules": [ + { + "name": "config", + "entry": "getInstance" + } + ] +} \ No newline at end of file diff --git a/src/controller/AuthController.hpp b/src/controller/AuthController.hpp deleted file mode 100644 index cf373f75..00000000 --- a/src/controller/AuthController.hpp +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef EXAMPLE_JWT_AUTHCONTROLLER_HPP -#define EXAMPLE_JWT_AUTHCONTROLLER_HPP - -#include "service/AuthService.hpp" - -#include "oatpp/core/macro/codegen.hpp" -#include "oatpp/parser/json/mapping/ObjectMapper.hpp" -#include "oatpp/web/server/api/ApiController.hpp" - -#include OATPP_CODEGEN_BEGIN(ApiController) //<- Begin Codegen - -/** - * User REST controller. - */ -class AuthController : public oatpp::web::server::api::ApiController { -public: - AuthController(const std::shared_ptr& objectMapper) - : oatpp::web::server::api::ApiController(objectMapper) {} - -private: - AuthService m_userService; // Create user service. -public: - static std::shared_ptr createShared( - OATPP_COMPONENT(std::shared_ptr, - objectMapper) // Inject objectMapper component here as - // default parameter - ) { - return std::make_shared(objectMapper); - } - - ENDPOINT_INFO(signUp) { - info->summary = "Sign up"; - - info->addConsumes>("application/json"); - - info->addResponse>(Status::CODE_200, - "application/json"); - info->addResponse>(Status::CODE_500, - "application/json"); - } - ENDPOINT("POST", "users/signup", signUp, BODY_DTO(Object, dto)) { - return createDtoResponse(Status::CODE_200, m_userService.signUp(dto)); - } - - ENDPOINT_INFO(signIn) { - info->summary = "Sign in"; - - info->addConsumes>("application/json"); - - info->addResponse>(Status::CODE_200, - "application/json"); - info->addResponse>(Status::CODE_500, - "application/json"); - } - ENDPOINT("POST", "users/signin", signIn, BODY_DTO(Object, dto)) { - return createDtoResponse(Status::CODE_200, m_userService.signIn(dto)); - } - - ENDPOINT_INFO(deleteUser) { - info->summary = "Delete User"; - - info->addResponse>(Status::CODE_200, - "application/json"); - info->addResponse>(Status::CODE_500, - "application/json"); - } - ENDPOINT("DELETE", "users", deleteUser, BUNDLE(String, userId)) { - return createDtoResponse(Status::CODE_200, - m_userService.deleteUserById(userId)); - } -}; - -#include OATPP_CODEGEN_BEGIN(ApiController) //<- End Codegen - -#endif // EXAMPLE_JWT_AUTHCONTROLLER_HPP diff --git a/src/controller/StaticController.hpp b/src/controller/StaticController.hpp deleted file mode 100644 index 2e2d442f..00000000 --- a/src/controller/StaticController.hpp +++ /dev/null @@ -1,45 +0,0 @@ - -#ifndef EXAMPLE_JWT_STATICCONTROLLER_HPP -#define EXAMPLE_JWT_STATICCONTROLLER_HPP - -#include "oatpp/core/macro/codegen.hpp" -#include "oatpp/core/macro/component.hpp" -#include "oatpp/parser/json/mapping/ObjectMapper.hpp" -#include "oatpp/web/server/api/ApiController.hpp" - -#include OATPP_CODEGEN_BEGIN(ApiController) //<- Begin Codegen - -class StaticController : public oatpp::web::server::api::ApiController { -public: - StaticController(const std::shared_ptr& objectMapper) - : oatpp::web::server::api::ApiController(objectMapper) {} - -public: - static std::shared_ptr createShared( - OATPP_COMPONENT(std::shared_ptr, - objectMapper) // Inject objectMapper component here as - // default parameter - ) { - return std::make_shared(objectMapper); - } - - ENDPOINT("GET", "/", root) { - const char* html = - "" - " " - " " - " " - " " - "

Hello CRUD example project!

" - " Checkout Swagger-UI page" - " " - ""; - auto response = createResponse(Status::CODE_200, html); - response->putHeader(Header::CONTENT_TYPE, "text/html"); - return response; - } -}; - -#include OATPP_CODEGEN_BEGIN(ApiController) //<- End Codegen - -#endif // EXAMPLE_JWT_STATICCONTROLLER_HPP diff --git a/src/controller/StoryController.hpp b/src/controller/StoryController.hpp deleted file mode 100644 index f0b8c155..00000000 --- a/src/controller/StoryController.hpp +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef EXAMPLE_JWT_STORYCONTROLLER_HPP -#define EXAMPLE_JWT_STORYCONTROLLER_HPP - -#include "service/StoryService.hpp" - -#include "oatpp/core/macro/codegen.hpp" -#include "oatpp/parser/json/mapping/ObjectMapper.hpp" -#include "oatpp/web/server/api/ApiController.hpp" - -#include OATPP_CODEGEN_BEGIN(ApiController) //<- Begin Codegen - -/** - * Story REST controller. - */ -class StoryController : public oatpp::web::server::api::ApiController { -public: - StoryController(const std::shared_ptr& objectMapper) - : oatpp::web::server::api::ApiController(objectMapper) {} - -private: - StoryService m_storyService; // Create story service. -public: - static std::shared_ptr createShared( - OATPP_COMPONENT(std::shared_ptr, - objectMapper) // Inject objectMapper component here as - // default parameter - ) { - return std::make_shared(objectMapper); - } - - ENDPOINT_INFO(createStory) { - info->summary = "Create new Story"; - - info->addConsumes>("application/json"); - - info->addResponse>(Status::CODE_200, - "application/json"); - info->addResponse>(Status::CODE_500, - "application/json"); - } - ENDPOINT("POST", "stories", createStory, BUNDLE(String, userId), - BODY_DTO(Object, storyDto)) { - storyDto->id = nullptr; - return createDtoResponse(Status::CODE_200, - m_storyService.createStory(userId, storyDto)); - } - - ENDPOINT_INFO(putStory) { - info->summary = "Update Story by storyId"; - - info->addConsumes>("application/json"); - - info->addResponse>(Status::CODE_200, - "application/json"); - info->addResponse>(Status::CODE_404, - "application/json"); - info->addResponse>(Status::CODE_500, - "application/json"); - - info->pathParams["storyId"].description = "Story Identifier"; - } - ENDPOINT("PUT", "stories/{storyId}", putStory, BUNDLE(String, userId), - PATH(String, storyId), BODY_DTO(Object, storyDto)) { - storyDto->id = storyId; - return createDtoResponse(Status::CODE_200, - m_storyService.updateStory(userId, storyDto)); - } - - ENDPOINT_INFO(getStoryById) { - info->summary = "Get one Story by storyId"; - - info->addResponse>(Status::CODE_200, - "application/json"); - info->addResponse>(Status::CODE_404, - "application/json"); - info->addResponse>(Status::CODE_500, - "application/json"); - - info->pathParams["storyId"].description = "Story Identifier"; - } - ENDPOINT("GET", "stories/{storyId}", getStoryById, BUNDLE(String, userId), - PATH(String, storyId)) { - return createDtoResponse( - Status::CODE_200, - m_storyService.getStoryByUserIdAndId(userId, storyId)); - } - - ENDPOINT_INFO(getStories) { - info->summary = "Get All user stories"; - - info->addResponse>(Status::CODE_200, - "application/json"); - info->addResponse>(Status::CODE_500, - "application/json"); - } - ENDPOINT("GET", "stories/offset/{offset}/limit/{limit}", getStories, - BUNDLE(String, userId), PATH(UInt32, offset), - PATH(UInt32, limit)) { - return createDtoResponse( - Status::CODE_200, - m_storyService.getAllUserStories(userId, offset, limit)); - } - - ENDPOINT_INFO(deleteStory) { - info->summary = "Delete story by storyId"; - - info->addResponse>(Status::CODE_200, - "application/json"); - info->addResponse>(Status::CODE_500, - "application/json"); - - info->pathParams["storyId"].description = "Story Identifier"; - } - ENDPOINT("DELETE", "stories/{userId}", deleteStory, BUNDLE(String, userId), - PATH(String, storyId)) { - return createDtoResponse( - Status::CODE_200, - m_storyService.deleteStoryByUserIdAndId(userId, storyId)); - } -}; - -#include OATPP_CODEGEN_BEGIN(ApiController) //<- End Codegen - -#endif /* EXAMPLE_JWT_STORYCONTROLLER_HPP */ diff --git a/src/database/StoryDb.hpp b/src/database/StoryDb.hpp deleted file mode 100644 index d416c133..00000000 --- a/src/database/StoryDb.hpp +++ /dev/null @@ -1,65 +0,0 @@ - -#ifndef EXAMPLE_JWT_STORYDB_HPP -#define EXAMPLE_JWT_STORYDB_HPP - -#include "model/StoryModel.hpp" -#include "oatpp-sqlite/orm.hpp" - -#include OATPP_CODEGEN_BEGIN(DbClient) //<- Begin Codegen - -/** - * StoryDb client definitions. - */ -class StoryDb : public oatpp::orm::DbClient { -public: - StoryDb(const std::shared_ptr& executor) - : oatpp::orm::DbClient(executor) { - oatpp::orm::SchemaMigration migration(executor); - migration.addFile(1 /* start from version 1 */, "./sql/story.sql"); - // TODO - Add more migrations here. - migration - .migrate(); // <-- run migrations. This guy will throw on error. - - auto version = executor->getSchemaVersion(); - OATPP_LOGD("UserDb", "Migration - OK. Version=%lld.", version); - } - - QUERY(createStory, - "INSERT INTO Stories" - "(id, userid, content) VALUES " - "(uuid_generate_v4(), :story.userid, :story.content) " - "RETURNING *;", - PREPARE(true), // prepared statement! - PARAM(oatpp::Object, story)) - - QUERY(updateStory, - "UPDATE Stories " - "SET " - " content=:story.content, " - "WHERE " - " id=:story.id AND userid=:story.userid " - "RETURNING *;", - PREPARE(true), // prepared statement! - PARAM(oatpp::Object, story)) - - QUERY(getStoryByUserIdAndId, - "SELECT * FROM Stories WHERE id=:id AND userid=:userId;", - PREPARE(true), // prepared statement! - PARAM(oatpp::String, userId), PARAM(oatpp::String, id)) - - QUERY(getAllUserStories, - "SELECT * FROM Stories WHERE userid=:userId LIMIT :limit OFFSET " - ":offset;", - PREPARE(true), // prepared statement! - PARAM(oatpp::String, userId), PARAM(oatpp::UInt32, offset), - PARAM(oatpp::UInt32, limit)) - - QUERY(deleteStoryByUserIdAndId, - "DELETE FROM Stories WHERE id=:id AND userid=:userId;", - PREPARE(true), // prepared statement! - PARAM(oatpp::String, userId), PARAM(oatpp::String, id)) -}; - -#include OATPP_CODEGEN_END(DbClient) //<- End Codegen - -#endif // EXAMPLE_JWT_STORYDB_HPP diff --git a/src/database/UserDb.hpp b/src/database/UserDb.hpp deleted file mode 100644 index 9bf68c35..00000000 --- a/src/database/UserDb.hpp +++ /dev/null @@ -1,73 +0,0 @@ - -#ifndef EXAMPLE_JWT_USERDB_HPP -#define EXAMPLE_JWT_USERDB_HPP - -#include "data/UserDto.hpp" -#include "model/UserModel.hpp" -#include "oatpp-sqlite/orm.hpp" - -#include OATPP_CODEGEN_BEGIN(DbClient) //<- Begin Codegen - -/** - * UserDb client definitions. - */ -class UserDb : public oatpp::orm::DbClient { -public: - UserDb(const std::shared_ptr& executor) - : oatpp::orm::DbClient(executor) { - oatpp::orm::SchemaMigration migration(executor); - migration.addFile(1 /* start from version 1 */, "./sql/user.sql"); - // TODO - Add more migrations here. - migration - .migrate(); // <-- run migrations. This guy will throw on error. - - auto version = executor->getSchemaVersion(); - OATPP_LOGD("UserDb", "Migration - OK. Version=%lld.", version); - } - - QUERY(createUser, - "INSERT INTO AppUser" - "(username, email, password, role) VALUES " - "(:user.username, :user.email, :user.password, :user.role);", - PARAM(oatpp::Object, user)) - - QUERY(updateUser, - "UPDATE AppUser " - "SET " - " username=:user.username, " - " email=:user.email, " - " password=:user.password, " - " role=:user.role " - "WHERE " - " id=:user.id;", - PARAM(oatpp::Object, user)) - - QUERY(getUserById, "SELECT * FROM AppUser WHERE id=:id;", - PARAM(oatpp::Int32, id)) - - QUERY(getAllUsers, "SELECT * FROM AppUser LIMIT :limit OFFSET :offset;", - PARAM(oatpp::UInt32, offset), PARAM(oatpp::UInt32, limit)) - - QUERY(deleteUserById, "DELETE FROM AppUser WHERE id=:id;", - PARAM(oatpp::Int32, id)) - - QUERY(changeUserPassword, - "UPDATE users " - "SET " - " pswhash=crypt(:newPassword, gen_salt('bf', 8)) " - "WHERE " - " id=:id AND pswhash=crypt(:oldPassword, pswhash);", - PREPARE(true), // prepared statement! - PARAM(oatpp::String, userId, "id"), PARAM(oatpp::String, oldPassword), - PARAM(oatpp::String, newPassword)) - - QUERY(authenticateUser, - "SELECT id FROM users WHERE username=:username AND " - "pswhash=crypt(:password, pswhash);", - PREPARE(true), // prepared statement! - PARAM(oatpp::String, username), PARAM(oatpp::String, password)) -}; - -#include OATPP_CODEGEN_END(DbClient) //<- End Codegen - -#endif // EXAMPLE_JWT_USERDB_HPP diff --git a/src/database/model/StoryModel.hpp b/src/database/model/StoryModel.hpp deleted file mode 100644 index 75b57a4e..00000000 --- a/src/database/model/StoryModel.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef EXAMPLE_JWT_STORYMODEL_HPP -#define EXAMPLE_JWT_STORYMODEL_HPP - -#include "oatpp/core/macro/codegen.hpp" -#include "oatpp/core/Types.hpp" - -#include OATPP_CODEGEN_BEGIN(DTO) - -class StoryModel : public oatpp::DTO { - - DTO_INIT(StoryModel, DTO) - - DTO_FIELD(String, id); - DTO_FIELD(String, userId, "userid"); - DTO_FIELD(String, content, "content"); - -}; - -#include OATPP_CODEGEN_END(DTO) - - -#endif //EXAMPLE_JWT_STORYMODEL_HPP diff --git a/src/database/model/UserModel.hpp b/src/database/model/UserModel.hpp deleted file mode 100644 index 013e9a94..00000000 --- a/src/database/model/UserModel.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef EXAMPLE_JWT_DB_MODEL_USERMODEL_HPP -#define EXAMPLE_JWT_DB_MODEL_USERMODEL_HPP - -#include "oatpp/core/macro/codegen.hpp" -#include "oatpp/core/Types.hpp" - -#include OATPP_CODEGEN_BEGIN(DTO) - -class UserModel : public oatpp::DTO { - - DTO_INIT(UserModel, DTO) - - DTO_FIELD(String, id); - DTO_FIELD(String, userName, "username"); - DTO_FIELD(String, email, "email"); - DTO_FIELD(String, password, "password"); - -}; - -#include OATPP_CODEGEN_END(DTO) - -#endif /* EXAMPLE_JWT_DB_MODEL_USERMODEL_HPP */ diff --git a/src/debug/terminal.cpp b/src/debug/terminal.cpp index 7498b479..ff1d06a2 100644 --- a/src/debug/terminal.cpp +++ b/src/debug/terminal.cpp @@ -14,359 +14,156 @@ Description: Terminal #include "terminal.hpp" +#include #include -#include -#ifdef _WIN32 -#include -#include -#include -const std::string SHELL_COMMAND = "powershell.exe -Command "; -#else -#include -#include -const std::string SHELL_COMMAND = "sh -c "; -#endif +namespace fs = std::filesystem; namespace Lithium::Terminal { -CommandManager::CommandManager() : hist_iter_(command_history_.begin()) {} - -void CommandManager::registerCommand( - const std::string &cmd, - std::function func) { - std::lock_guard lock(mutex_); - commands_.emplace(cmd, func); -} -const std::string &CommandManager::runCommand(const std::string &cmd, - const std::string &arg) { - std::lock_guard lock(mutex_); - auto it = commands_.find(cmd); - if (it != commands_.end()) { - const std::string command_str = cmd + " " + arg; - command_history_.emplace_back(command_str); - history_index_ = command_history_.size(); - static std::string result = it->second(arg); - return result; - } - static const std::string unknown_cmd = - "\033[31mUnknown command: " + cmd + "\033[0m\n"; - return unknown_cmd; +ConsoleTerminal::ConsoleTerminal() { +#ifdef _WIN32 + hConsole = GetStdHandle(STD_OUTPUT_HANDLE); +#else + tcgetattr(STDIN_FILENO, &orig_termios); +#endif + registerMemberCommand("help", this, helpCommand); + registerMemberCommand("pwd", this, pwdCommand); + registerMemberCommand("help", this, helpCommand); + registerMemberCommand("echo", this, echoCommand); + registerMemberCommand("create", this, createFile); + registerMemberCommand("delete", this, deleteFile); } -std::future CommandManager::runCommandAsync( - const std::string &cmd, const std::string &arg) { - std::lock_guard lock(mutex_); - auto it = commands_.find(cmd); - if (it != commands_.end()) { - std::future future = - std::async(std::launch::async, [this, it, arg]() { - const std::string command_str = it->first + " " + arg; - command_history_.emplace_back(command_str); - history_index_ = command_history_.size(); - return std::string(it->second(arg)); - }); - addFuture(std::move(future)); - return future; - } - return std::async(std::launch::async, []() { - return std::string("\033[31mUnknown command\033[0m"); - }); +ConsoleTerminal::~ConsoleTerminal() { +#ifdef _WIN32 + // Windows-specific cleanup can be added here if needed +#else + tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); +#endif } -void CommandManager::addFuture(std::future &&future) { - std::lock_guard lock(mutex_); - futures_.emplace_back(std::move(future)); +void ConsoleTerminal::registerCommand(const std::string& name, + CommandFunction func) { + commandMap.emplace(name, std::move(func)); } -void CommandManager::join() { - for (auto it = futures_.begin(); it != futures_.end();) { - if (it->valid()) { - try { - it->wait(); - const std::string result = it->get(); - if (!result.empty()) { - std::cout << result << std::endl; - } - it = futures_.erase(it); - } catch (const std::exception &e) { - std::cerr << "Exception in task: " << e.what() << std::endl; - it = futures_.erase(it); - } - } else { - it = futures_.erase(it); - } - } -} - -const std::vector &CommandManager::getRegisteredCommands() const { +std::vector ConsoleTerminal::getRegisteredCommands() const { std::vector commands; - for (const auto &cmd : commands_) { - commands.emplace_back(cmd.first); + for (const auto& [name, _] : commandMap) { + commands.push_back(name); } - static const std::vector command_list = std::move(commands); - return command_list; + return commands; } -const std::string &CommandManager::getPrevCommand() { - if (hist_iter_ != command_history_.begin()) { - --hist_iter_; - history_index_ = hist_iter_ - command_history_.begin(); - } - static const std::string empty_str; - return (history_index_ >= 0 && history_index_ < command_history_.size()) - ? *(hist_iter_) - : empty_str; -} +void ConsoleTerminal::callCommand(const std::string& name, + const std::vector& args) { + if (auto it = commandMap.find(name); it != commandMap.end()) { + try { + it->second(args); + } catch (const std::exception& e) { + std::cout << "Error: " << e.what() << '\n'; + } -const std::string &CommandManager::getNextCommand() { - if (hist_iter_ != command_history_.end() - 1) { - ++hist_iter_; - history_index_ = (hist_iter_ - command_history_.begin()); + } else { + std::cout << "Command '" << name << "' not found.\n"; } - static const std::string empty_str; - return (history_index_ >= 0 && history_index_ < command_history_.size()) - ? *(hist_iter_) - : empty_str; -} - -void CommandManager::addCommandHistory(const std::string &cmd) { - command_history_.emplace_back(cmd); - history_index_ = command_history_.size(); - hist_iter_ = command_history_.end(); } -bool CommandManager::hasNextCommand() const { - return hist_iter_ != command_history_.end() - 1; -} +void ConsoleTerminal::run() { + std::string input; + std::deque history; + int historyIndex = 0; -bool CommandManager::hasPrevCommand() const { - return hist_iter_ != command_history_.begin(); -} + printHeader(); -std::string getCursorLocation() { - std::cout << "\033[6n" << std::flush; + while (true) { + std::cout << "> "; + std::getline(std::cin, input); - char c = '\0'; - std::string result = ""; - while (c != 'R') { - if (read(STDIN_FILENO, &c, 1) == -1) { + history.push_front(input); + if (history.size() > MAX_HISTORY_SIZE) { + history.pop_back(); + } + historyIndex = 0; + + std::istringstream iss(input); + std::string command; + iss >> command; + std::vector args(std::istream_iterator{iss}, + std::istream_iterator{}); + + if (command == "exit") { + break; + } else if (command == "clear") { + clearConsole(); + continue; } - result += c; - } - return result; + callCommand(command, args); + } } -bool isColorSupported() { -#ifdef _WIN32 - HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); - if (hOut == INVALID_HANDLE_VALUE) - return false; - - DWORD dwMode; - if (!GetConsoleMode(hOut, &dwMode)) - return false; - - dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - if (!SetConsoleMode(hOut, dwMode)) - return false; - - // 输出绿色字符 - std::cout << "\x1B[32m" << std::flush; - - int c = getchar(); // 读取用户的输入 - bool result = (c == '\033'); // 判断是否为控制字符 - - // 恢复默认颜色 - std::cout << "\x1B[0m" << std::flush; - - // 还原控制台模式 - dwMode &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING; - SetConsoleMode(hOut, dwMode); - - return result; -#else - if (!isatty(fileno(stdout))) - return false; - - struct termios saved_termios, modified_termios; - tcgetattr(STDIN_FILENO, &saved_termios); - - modified_termios = saved_termios; - modified_termios.c_lflag &= ~(ECHO | ICANON); - tcsetattr(STDIN_FILENO, TCSAFLUSH, &modified_termios); - - // 输出绿色字符 - std::cout << "\033[32m" << std::flush; - - int c = getchar(); // 读取用户的输入 - bool result = (c == '\033'); // 判断是否为控制字符 - - // 恢复默认颜色 - std::cout << "\033[0m" << std::flush; - - tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_termios); - - return result; -#endif +void ConsoleTerminal::helpCommand(const std::vector& args) { + std::cout << "Available commands:\n"; + std::cout << " print [args...] - Print arguments to console\n"; + std::cout << " ls [dir] - List files in directory\n"; + std::cout << " rm - Delete file\n"; + std::cout << " help - Show available commands\n"; + std::cout << " exit - Exit the terminal\n"; } -std::string getTerminalInput(CommandManager &manager) { - // 保存当前终端属性 -#ifdef _WIN32 - HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); - DWORD mode; - GetConsoleMode(hStdin, &mode); - DWORD saved_mode = mode; -#else - struct termios saved_termios, modified_termios; - tcgetattr(STDIN_FILENO, &saved_termios); -#endif - - // 修改终端属性 -#ifdef _WIN32 - mode &= ~ENABLE_ECHO_INPUT; - mode &= ~ENABLE_LINE_INPUT; - SetConsoleMode(hStdin, mode); -#else - modified_termios = saved_termios; - modified_termios.c_lflag &= ~(ECHO | ICANON); - modified_termios.c_cc[VTIME] = 0; // 设置非阻塞模式 - modified_termios.c_cc[VMIN] = 1; // 设置最少读取的字符数 - tcsetattr(STDIN_FILENO, TCSAFLUSH, &modified_termios); -#endif - - std::string input = ""; - int c = 0; - - std::cout << "\033[94m>>>\033[0m " << std::flush; // 改变提示符颜色 - - while (true) { - // 读取字符 -#ifdef _WIN32 - c = _getch(); -#else - c = getchar(); -#endif - - if (c == '\n' || c == EOF) { // 输入结束,退出循环 - std::cout << std::endl; // 输出一个换行符 - - if (!input.empty()) { - // 将输入的命令添加到历史记录中 - manager.addCommandHistory(input); - - std::string output = manager.runCommand(input, ""); - std::cout << output; - } - - std::cout << "\033[94m>>>\033[0m " << std::flush; // 重新输出提示符 - - input.clear(); // 清空输入缓存 - } else if (c == 127 || c == 8) { // 处理退格键 - if (!input.empty()) { - input.pop_back(); - -#ifdef _WIN32 - std::cout - << "\b \b" - << std:: - flush; // 输出退格符、空格和退格符,相当于清空当前字符 -#else - std::cout - << "\b \b" - << std:: - flush; // 输出退格符、空格和退格符,相当于清空当前字符 -#endif - } - } else if (c == '\033') { // 处理特殊字符,如向上键 -#ifdef _WIN32 - int nextC = _getch(); -#else - int nextC = getchar(); -#endif - if (nextC == '[') { -#ifdef _WIN32 - switch (_getch()) -#else - switch (getchar()) -#endif - { - case 'B': // 向上键 - if (manager.hasNextCommand()) { - input = manager.getNextCommand(); - std::cout << "\r\033[K" << "\033[94m>>>\033[0m " - << input - << std::flush; // 输出命令和提示符 - } - break; - - case 'A': // 向下键 - if (manager.hasPrevCommand()) { - input = manager.getPrevCommand(); - std::cout << "\r\033[K" << "\033[94m>>>\033[0m " - << input - << std::flush; // 输出命令和提示符 - } - break; - - default: - break; - } - } - } else { // 处理普通字符 - input += c; - std::cout << static_cast(c) - << std::flush; // 直接输出字符即可 - } +void ConsoleTerminal::echoCommand(const std::vector& args) { + for (const auto& arg : args) { + std::cout << arg << ' '; } + std::cout << '\n'; +} - // 恢复终端属性 -#ifdef _WIN32 - SetConsoleMode(hStdin, saved_mode); -#else - tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_termios); -#endif +void ConsoleTerminal::pwdCommand(const std::vector& args) { + std::cout << "Current directory: " << fs::current_path() << '\n'; +} - return input; +void ConsoleTerminal::cdCommand(const std::vector& args) { + if (args.empty()) { + std::cout << "Usage: cd \n"; + return; + } + fs::current_path(args[0]); } -void clearTerminal() { -#ifdef _WIN32 - // Windows 平台下使用 System 函数 - std::system("cls"); -#else - // Linux 平台下使用 system 函数 - std::system("clear"); -#endif +void ConsoleTerminal::listDirectory(const std::vector& args) { + std::string path = "."; + if (!args.empty()) + path = args[0]; + for (const auto& entry : fs::directory_iterator(path)) + std::cout << entry.path() << '\n'; } -// 实现 ls 指令,用于显示当前目录下的文件和文件夹 -std::string lsCommand(const std::string &arg) { - std::string cmd = "ls -al "; - if (!arg.empty()) { - cmd += arg; - } else { - cmd += "."; +void ConsoleTerminal::createFile(const std::vector& args) { + if (args.empty()) { + std::cout << "No file name provided.\n"; + return; } - - FILE *pipe = popen(cmd.c_str(), "r"); - if (!pipe) { - return "Failed to execute command: " + cmd; + std::ofstream file(args[0]); + if (file.is_open()) { + std::cout << "File created: " << args[0] << '\n'; + file.close(); + } else { + std::cout << "Failed to create file: " << args[0] << '\n'; } +} - char buffer[128]; - std::string result = ""; - while (fgets(buffer, 128, pipe) != nullptr) { - result += buffer; +void ConsoleTerminal::deleteFile(const std::vector& args) { + if (args.empty()) { + std::cout << "No file name provided.\n"; + return; } - pclose(pipe); - - return result; + fs::remove(args[0]); + std::cout << "File deleted: " << args[0] << '\n'; } -void printHeader() { +void ConsoleTerminal::printHeader() { std::cout << "Welcome to Lithium Command Line Tool v1.0" << std::endl; std::cout << "A debugging tool for Lithium Engine" << std::endl; std::cout << "--------------------------------------------------" @@ -377,62 +174,18 @@ void printHeader() { << std::endl; } -// 实现 pwd 指令,用于显示当前工作目录路径名 -std::string pwdCommand(const std::string &arg) { - return "Current working directory: " + std::string(getcwd(nullptr, 0)) + - "\n"; -} - -// 实现 mkdir 指令,用于创建目录 -std::string mkdirCommand(const std::string &arg) { - std::string cmd = "mkdir " + arg; - - FILE *pipe = popen(cmd.c_str(), "r"); - if (!pipe) { - return "Failed to execute command: " + cmd; - } - - pclose(pipe); - - return "Directory created: " + arg; -} - -// 实现 cp 指令,用于复制文件或目录 -std::string cpCommand(const std::string &arg) { - std::string cmd = "cp -r " + arg; - - FILE *pipe = popen(cmd.c_str(), "r"); - if (!pipe) { - return "Failed to execute command: " + cmd; - } - - pclose(pipe); - - return "File or directory copied: " + arg; -} - -// 实现 help 指令,列出所有可用的命令 -std::string helpCommand(CommandManager &manager, const std::string &arg) { - std::vector commands = manager.getRegisteredCommands(); - - // 构造输出字符串 - std::stringstream ss; - ss << "Available commands:" << std::endl; - for (const auto &cmd : commands) { - ss << " - " << cmd << std::endl; - } - return ss.str(); +void ConsoleTerminal::clearConsole() { +#ifdef _WIN32 + COORD topLeft = {0, 0}; + CONSOLE_SCREEN_BUFFER_INFO screen; + DWORD written; + GetConsoleScreenBufferInfo(hConsole, &screen); + FillConsoleOutputCharacter(hConsole, ' ', screen.dwSize.X * screen.dwSize.Y, + topLeft, &written); + SetConsoleCursorPosition(hConsole, topLeft); +#else + std::cout << "\x1B[2J\x1B[H"; +#endif } -std::string systemCommand(const std::string &arg) { - std::string command = SHELL_COMMAND + "'" + arg + "' 2>&1"; - std::string output; - int result = std::system(command.c_str()); - if (result == 0) { - output = "\033[32mCommand executed successfully.\033[0m\n"; - } else { - output = "\033[31mCommand failed to execute.\033[0m\n"; - } - return output; -} } // namespace Lithium::Terminal diff --git a/src/debug/terminal.hpp b/src/debug/terminal.hpp index 1f8f92d1..ce7e6cbb 100644 --- a/src/debug/terminal.hpp +++ b/src/debug/terminal.hpp @@ -1,200 +1,83 @@ -/* - * terminal.hpp - * - * Copyright (C) 2023-2024 Max Qian - */ - -/************************************************* - -Date: 2023-6-30 - -Description: Terminal - -**************************************************/ - -#pragma once - -#define HAS_LITHIUM_TERMINAL 1 +#ifndef LITHIUM_DEBUG_TERMINAL_HPP +#define LITHIUM_DEBUG_TERMINAL_HPP +#include +#include #include -#include -#include -#include +#include +#include +#include #include +#include #include -#if ENABLE_FASTHASH -#include "emhash/emhash8_table.hpp" +#ifdef _WIN32 +#include #else -#include +#include +#include #endif namespace Lithium::Terminal { -/** - * @brief 命令管理器类,用于注册、运行指令函数以及管理历史记录等功能。 - */ -class CommandManager { +class ConsoleTerminal { public: - /** - * @brief 构造函数,初始化命令管理器。 - */ - CommandManager(); - - /** - * @brief 注册指令函数。 - * @param cmd 指令名称。 - * @param func 指令函数。 - */ - void registerCommand(const std::string &cmd, - std::function func); - - /** - * @brief 运行指令函数,同步调用。 - * @param cmd 指令名称。 - * @param arg 指令参数。 - * @return 指令函数的返回值。 - */ - const std::string &runCommand(const std::string &cmd, - const std::string &arg); - - /** - * @brief 运行指令函数,异步调用。 - * @param cmd 指令名称。 - * @param arg 指令参数。 - * @return std::future 对象,可用于获取异步调用的返回值。 - */ - std::future runCommandAsync(const std::string &cmd, - const std::string &arg); - - /** - * @brief 添加 std::future 对象到 futures_ 中。 - * @param future std::future 对象。 - */ - void addFuture(std::future &&future); - - /** - * @brief 等待所有异步调用结束。 - */ - void join(); - - /** - * @brief 获取注册的指令函数名称列表。 - * @return 指令函数名称列表。 - */ - const std::vector &getRegisteredCommands() const; - - /** - * @brief 获取历史记录中上一个命令。 - * @return 上一个命令。 - */ - const std::string &getPrevCommand(); - - /** - * @brief 获取历史记录中下一个命令。 - * @return 下一个命令。 - */ - const std::string &getNextCommand(); - - /** - * @brief 添加命令到历史记录中。 - * @param cmd 命令。 - */ - void addCommandHistory(const std::string &cmd); - - /** - * @brief 检查是否存在下一个历史命令。 - * @return 若存在下一个历史命令则返回 true,否则返回 false。 - */ - bool hasNextCommand() const; - - /** - * @brief 检查是否存在上一个历史命令。 - * @return 若存在上一个历史命令则返回 true,否则返回 false。 - */ - bool hasPrevCommand() const; + using CommandFunction = + std::function&)>; + + ConsoleTerminal(); + + ~ConsoleTerminal(); + + void registerCommand(const std::string& name, CommandFunction func); + + template + void registerMemberCommand( + const std::string& name, Class* instance, + void (Class::*memFunc)(const std::vector&)) { + CommandFunction func = [instance, + memFunc](const std::vector& args) { + (instance->*memFunc)(args); + }; + registerCommand(name, func); + } + + std::vector getRegisteredCommands() const; + + void callCommand(const std::string& name, + const std::vector& args); + + void run(); + +protected: + void helpCommand(const std::vector& args); + + void echoCommand(const std::vector& args); + + void pwdCommand(const std::vector& args); + + void cdCommand(const std::vector& args); + + void listDirectory(const std::vector& args); + + void createFile(const std::vector& args); + + void deleteFile(const std::vector& args); private: - std::unordered_map> - commands_; // 注册的指令函数 - std::mutex mutex_; // 互斥锁,用于保护 commands_ 和 futures_ - std::vector> - futures_; // 异步调用的 std::future 对象 - - std::vector command_history_; // 历史命令 - int history_index_ = 0; // 当前历史命令的索引位置 - std::vector::const_iterator hist_iter_; // 历史命令遍历迭代器 -}; + void printHeader(); -/** - * @brief 获取光标位置。 - * @return 光标位置字符串。 - */ -std::string getCursorLocation(); - -/** - * @brief 检查终端是否支持彩色输出。 - * @return 若支持彩色输出则返回 true,否则返回 false。 - */ -bool isColorSupported(); - -/** - * @brief 清除终端。 - */ -void clearTerminal(); - -/** - * @brief 获取终端输入。 - * @param manager 命令管理器。 - * @return 终端输入字符串。 - */ -std::string getTerminalInput(CommandManager &manager); - -/** - * @brief ls 指令的实现,用于显示当前目录下的文件和文件夹。 - * @param arg 指令参数。 - * @return 执行结果字符串。 - */ -std::string lsCommand(const std::string &arg); - -/** - * @brief pwd 指令的实现,用于显示当前工作目录路径名。 - * @param arg 指令参数。 - * @return 执行结果字符串。 - */ -std::string pwdCommand(const std::string &arg); - -/** - * @brief 打印终端头部信息。 - */ -void printHeader(); - -/** - * @brief mkdir 指令的实现,用于创建目录。 - * @param arg 指令参数。 - * @return 执行结果字符串。 - */ -std::string mkdirCommand(const std::string &arg); - -/** - * @brief cp 指令的实现,用于复制文件或目录。 - * @param arg 指令参数。 - * @return 执行结果字符串。 - */ -std::string cpCommand(const std::string &arg); - -/** - * @brief help 指令的实现,列出所有可用的命令。 - * @param manager 命令管理器。 - * @param arg 指令参数。 - * @return 执行结果字符串。 - */ -std::string helpCommand(CommandManager &manager, const std::string &arg); - -/** - * @brief system 指令的实现,用于执行系统命令。 - * @param arg 指令参数。 - * @return 执行结果字符串。 - */ -std::string systemCommand(const std::string &arg); +private: + std::unordered_map commandMap; + static const int MAX_HISTORY_SIZE = 100; + +#ifdef _WIN32 + HANDLE hConsole; +#else + struct termios orig_termios; +#endif + + void clearConsole(); +}; } // namespace Lithium::Terminal + +#endif diff --git a/src/device/manager.cpp b/src/device/manager.cpp index 9fbf3424..b93fe20c 100644 --- a/src/device/manager.cpp +++ b/src/device/manager.cpp @@ -29,11 +29,7 @@ Description: Device Manager #include "atom/utils/random.hpp" #include "utils/utils.hpp" -#ifdef __cpp_lib_format -#include -#else #include -#endif #include #include "config.h" diff --git a/src/device/server/hydrogen.cpp b/src/device/server/hydrogen.cpp index a6895557..75368f94 100644 --- a/src/device/server/hydrogen.cpp +++ b/src/device/server/hydrogen.cpp @@ -215,11 +215,11 @@ bool HydrogenManager::isRunning() { bool HydrogenManager::isInstalled() { #ifdef _WIN32 - if (!Atom::System::CheckSoftwareInstalled("hydrogenserver.exe")) { + if (!Atom::System::checkSoftwareInstalled("hydrogenserver.exe")) { return false; } #else - if (!Atom::System::CheckSoftwareInstalled("hydrogenserver")) { + if (!Atom::System::checkSoftwareInstalled("hydrogenserver")) { return false; } #endif diff --git a/src/interceptor/AuthInterceptor.cpp b/src/interceptor/AuthInterceptor.cpp deleted file mode 100644 index 30259671..00000000 --- a/src/interceptor/AuthInterceptor.cpp +++ /dev/null @@ -1,33 +0,0 @@ - -#include "AuthInterceptor.hpp" - -AuthInterceptor::AuthInterceptor(const std::shared_ptr& jwt) - : m_authHandler(jwt) { - authEndpoints.route("POST", "users/signup", false); - authEndpoints.route("POST", "users/signin", false); - - authEndpoints.route("GET", "swagger/*", false); - authEndpoints.route("GET", "api-docs/oas-3.0.0.json", false); -} - -std::shared_ptr AuthInterceptor::intercept( - const std::shared_ptr& request) { - auto r = authEndpoints.getRoute(request->getStartingLine().method, - request->getStartingLine().path); - if (r && !r.getEndpoint()) { - return nullptr; // Continue without Authorization - } - - auto authHeader = - request->getHeader(oatpp::web::protocol::http::Header::AUTHORIZATION); - - auto authObject = std::static_pointer_cast( - m_authHandler.handleAuthorization(authHeader)); - if (authObject) { - request->putBundleData("userId", authObject->userId); - return nullptr; // Continue - token is valid. - } - - throw oatpp::web::protocol::http::HttpError( - oatpp::web::protocol::http::Status::CODE_401, "Unauthorized", {}); -} \ No newline at end of file diff --git a/src/interceptor/AuthInterceptor.hpp b/src/interceptor/AuthInterceptor.hpp deleted file mode 100644 index a14c2c09..00000000 --- a/src/interceptor/AuthInterceptor.hpp +++ /dev/null @@ -1,25 +0,0 @@ - -#ifndef EXAMPLE_JWT_AUTHINTERCEPTOR_HPP -#define EXAMPLE_JWT_AUTHINTERCEPTOR_HPP - -#include "auth/AuthHandler.hpp" - -#include "oatpp/web/server/HttpConnectionHandler.hpp" -#include "oatpp/web/server/HttpRouter.hpp" -#include "oatpp/web/server/handler/AuthorizationHandler.hpp" -#include "oatpp/web/server/interceptor/RequestInterceptor.hpp" - -class AuthInterceptor - : public oatpp::web::server::interceptor::RequestInterceptor { -private: - AuthHandler m_authHandler; - oatpp::web::server::HttpRouterTemplate authEndpoints; - -public: - AuthInterceptor(const std::shared_ptr& jwt); - - std::shared_ptr intercept( - const std::shared_ptr& request) override; -}; - -#endif // EXAMPLE_JWT_AUTHINTERCEPTOR_HPP diff --git a/src/launcher/faq.cpp b/src/launcher/faq.cpp deleted file mode 100644 index fdd83372..00000000 --- a/src/launcher/faq.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "json.hpp" - - -using json = nlohmann::json; - -struct FAQ { - std::string question; - std::string description; - std::string category; - std::vector solutions; - int difficulty; - std::vector links; -}; - -public: -void addFAQ(const FAQ &faq) { - std::lock_guard lock(mutex); - faqs.push_back(faq); -} - -void deleteFAQ(const std::string &question) { - std::lock_guard lock(mutex); - faqs.erase(std::remove_if( - faqs.begin(), faqs.end(), - [&](const FAQ &faq) { return faq.question == question; }), - faqs.end()); -} - -std::vector searchFAQs(const std::string &keyword) { - std::lock_guard lock(mutex); - - // 检查缓存中是否存在搜索结果 - if (cache.find(keyword) != cache.end()) { - return cache[keyword]; - } - - std::vector results; - for (const auto &faq : faqs) { - if (faq.question.find(keyword) != std::string::npos || - faq.description.find(keyword) != std::string::npos || - faq.category.find(keyword) != std::string::npos) { - results.push_back(faq); - } - } - - // 将搜索结果存入缓存 - cache[keyword] = results; - - return results; -} - -std::vector getFAQs() const { - std::lock_guard lock(mutex); - return faqs; -} - -std::vector getCategorizedFAQs(const std::string &category) { - std::lock_guard lock(mutex); - - // 检查缓存中是否存在分类结果 - if (categoryCache.find(category) != categoryCache.end()) { - return categoryCache[category]; - } - - std::vector results; - for (const auto &faq : faqs) { - if (faq.category == category) { - results.push_back(faq); - } - } - - // 将分类结果存入缓存 - categoryCache[category] = results; - - return results; -} - -void saveToFile(const std::string &filename) { - std::lock_guard lock(mutex); - json jsonData; - - for (const auto &faq : faqs) { - json jsonFAQ; - jsonFAQ["question"] = faq.question; - jsonFAQ["description"] = faq.description; - jsonFAQ["category"] = faq.category; - jsonFAQ["solutions"] = faq.solutions; - jsonFAQ["difficulty"] = faq.difficulty; - jsonFAQ["links"] = faq.links; - - jsonData.push_back(jsonFAQ); - } - - std::ofstream file(filename); - file << jsonData.dump(4); // 4-space indentation for pretty printing -} - -void loadFromFile(const std::string &filename) { - std::lock_guard lock(mutex); - std::ifstream file(filename); - json jsonData; - file >> jsonData; - - faqs.clear(); - cache.clear(); - categoryCache.clear(); - - for (const auto &jsonFAQ : jsonData) { - FAQ faq; - faq.question = jsonFAQ["question"]; - faq.description = jsonFAQ["description"]; - faq.category = jsonFAQ["category"]; - faq.solutions = jsonFAQ["solutions"]; - faq.difficulty = jsonFAQ["difficulty"]; - faq.links = jsonFAQ["links"]; - - faqs.push_back(faq); - } -} - -void printFAQs() const { - std::lock_guard lock(mutex); - json jsonData; - - for (const auto &faq : faqs) { - json jsonFAQ; - jsonFAQ["question"] = faq.question; - jsonFAQ["description"] = faq.description; - jsonFAQ["category"] = faq.category; - jsonFAQ["solutions"] = faq.solutions; - jsonFAQ["difficulty"] = faq.difficulty; - jsonFAQ["links"] = faq.links; - - jsonData.push_back(jsonFAQ); - } - - std::cout << jsonData.dump(4) << std::endl; -} \ No newline at end of file diff --git a/src/launcher/faq.hpp b/src/launcher/faq.hpp deleted file mode 100644 index ae52915a..00000000 --- a/src/launcher/faq.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include -#include - - -#include "atom/type/json.hpp" -using json = nlohmann::json; - -struct FAQ { - std::string question; - std::string description; - std::string category; - std::vector solutions; - int difficulty; - std::vector links; -}; - -class FAQManager { -public: - void addFAQ(const FAQ &faq); - - void deleteFAQ(const std::string &question); - - std::vector searchFAQs(const std::string &keyword); - - std::vector getCategorizedFAQs(const std::string &category); - - void saveToFile(const std::string &filename); - - void loadFromFile(const std::string &filename); - - void printFAQs() const; - -private: - std::vector faqs; - std::unordered_map> cache; - std::unordered_map> categoryCache; - mutable std::mutex mutex; -}; \ No newline at end of file diff --git a/src/preload.cpp b/src/preload.cpp new file mode 100644 index 00000000..52150710 --- /dev/null +++ b/src/preload.cpp @@ -0,0 +1,89 @@ +#include "preload.hpp" + +#include + +#include "atom/async/pool.hpp" +#include "atom/io/io.hpp" +#include "atom/log/loguru.hpp" +#include "atom/utils/aes.hpp" +#include "atom/utils/string.hpp" +#include "atom/web/httpclient.hpp" + +#include "utils/resource.hpp" + +bool checkResources() { + for (auto &[key, value] : resource::LITHIUM_RESOURCES) { + if (!Atom::IO::isFileExists(key.data())) { + LOG_F(ERROR, "Resource file '{}' is missing.", key); + return false; + } + auto sha256_val = Atom::Utils::calculateSha256(key); + if (!sha256_val.empty()) { + LOG_F(ERROR, "Failed to calculate SHA256 value of '{}'.", key); + value.second = true; + continue; + } + auto expected_sha256 = value.first; + if (sha256_val != expected_sha256) { + LOG_F(ERROR, "SHA256 check failed for '{}'", key); + return false; + } + value.second = true; + } + + DLOG_F(INFO, "All resource files are found."); + return true; +} + +void downloadResources() { + DLOG_F(INFO, "Downloading missing resources..."); + + // 创建线程池 + Atom::Async::ThreadPool pool(std::thread::hardware_concurrency()); + + // 创建任务列表 + std::vector> tasks; + + for (auto &[key, value] : resource::LITHIUM_RESOURCES) { + // 发送 HTTP GET 请求下载文件 + const auto url = Atom::Utils::joinStrings( + {resource::LITHIUM_RESOURCE_SERVER, key}, "/"); + + // 添加下载任务到线程池 + tasks.emplace_back(pool.enqueue([url] { + try { + auto client = Atom::Web::HttpClient( + resource::LITHIUM_RESOURCE_SERVER, 443, true); + json res_body; + std::string err; + auto res = client.sendGetRequest(url, {}, res_body, err); + + if (!res) { + LOG_F(ERROR, "Failed to download resource: {}", url); + return false; + } + + // 将下载的数据写入文件 + std::ofstream outfile( + std::string(Atom::Utils::splitString(url, '/').back())); + outfile.write(res_body.dump().c_str(), res_body.dump().size()); + + DLOG_F(INFO, "Resource file '{}' downloaded.", url); + return true; + } catch (const std::exception &e) { + LOG_F(ERROR, "Error occurred when downloading resource '{}: {}", + url, e.what()); + return false; + } + })); + } + for (auto &&task : tasks) { + task.wait(); + } + for (auto &&task : tasks) { + if (!task.get()) { + LOG_F(ERROR, "Failed to download some resources."); + } + } + DLOG_F(INFO, "Downloading finished."); +} \ No newline at end of file diff --git a/src/preload.hpp b/src/preload.hpp new file mode 100644 index 00000000..b734dd58 --- /dev/null +++ b/src/preload.hpp @@ -0,0 +1,7 @@ +#ifndef LITHIUM_PRELOAD_HPP +#define LITHIUM_PRELOAD_HPP + +bool checkResources(); +void downloadResources(); + +#endif diff --git a/src/script/carbon.cpp b/src/script/carbon.cpp new file mode 100644 index 00000000..1a3f1bc7 --- /dev/null +++ b/src/script/carbon.cpp @@ -0,0 +1,183 @@ +#include "carbon.hpp" + +#include "carbon/carbon.hpp" +#include "carbon/extra/math.hpp" +#include "carbon/extra/stdlib.hpp" +#include "carbon/extra/string.hpp" + +#include "atom/algorithm/_script.hpp" +#include "atom/error/_script.hpp" +#include "atom/io/_script.hpp" +#include "atom/system/_script.hpp" +#include "atom/type/_script.hpp" + +#include "config/_script.hpp" + +#include "atom/async/pool.hpp" +#include "atom/experiment/noncopyable.hpp" +#include "atom/log/loguru.hpp" + +namespace Lithium { +// Pimpl implementation +class CarbonScriptImpl : public NonCopyable { +public: + explicit CarbonScriptImpl(const fs::path &script_dir = "./script", + int cache_expiry_seconds = 60) + : m_carbon(std::make_unique()) { + if (!Atom::IO::isFolderExists(script_dir)) { + throw std::runtime_error("Script directory does not exist: " + + script_dir_.string()); + } + cache_cleanup_thread_ = + std::thread(&CarbonScriptImpl::CacheCleanupLoop, this); + LOG_F(INFO, "CarbonScript initializing ..."); + InitMyApp(); + LOG_F(INFO, "CarbonScript initialized"); + } + + void InitSubModules() { + m_carbon->add(Carbon::extras::math::bootstrap()); + m_carbon->add(Carbon::extras::string_methods::bootstrap()); + m_carbon->add(Carbon::extras::stdlib::bootstrap()); + + m_carbon->add(Atom::_Script::Algorithm::bootstrap()); + m_carbon->add(Atom::_Script::Error::bootstrap()); + m_carbon->add(Atom::_Script::IO::bootstrap()); + m_carbon->add(Atom::_Script::System::bootstrap()); + m_carbon->add(Atom::_Script::Type::bootstrap()); + + m_carbon->add(Lithium::_Script::Config::bootstrap()); + + // Add additional sub-modules if needed + } + + void InitMyApp() { + LOG_F(INFO, "CarbonScriptr initializing ..."); + InitSubModules(); + LOG_F(INFO, "CarbonScript initialized"); + // m_carbon->add_global(Carbon::var(MyApp), "app"); + } + + bool LoadScriptFile(const std::string &filename) { + std::ifstream file(filename); + if (file) { + std::string script((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + file.close(); + m_carbon->eval(script); + } else { + LOG_F(ERROR, "Failed to open script file: {}", filename.c_str()); + return false; + } + return true; + } + + bool RunCommand(const std::string &command) { + try { + m_carbon->eval(command); + } catch (Carbon::exception::eval_error &e) { + LOG_F(ERROR, "Failed to eval {} : {}", e.filename.c_str(), + e.what()); + return false; + }; + return true; + } + + bool RunScript(const std::string &filename) { + try { + m_carbon->eval_file(filename); + } catch (Carbon::exception::eval_error &e) { + LOG_F(ERROR, "Failed to run {} : {}", e.filename.c_str(), e.what()); + return false; + } + return true; + } + + bool RunMultiCommand(const std::vector &commands) { + for (auto command : commands) { + try { + m_carbon->eval(command); + } catch (Carbon::exception::eval_error &e) { + LOG_F(ERROR, "Failed to run: {}", e.what()); + return false; + } + } + return true; + } + + void cacheScript(const std::string &script_name) { + auto script_path = script_dir_ / (script_name + ".li"); + + if (!Atom::IO::isFileExists(script_path)) { + LOG_F(ERROR, "Script not found: {}", script_name); + return; + } + std::lock_guard lock(cache_mutex_); + auto it = script_cache_.find(script_path); + if (it == script_cache_.end()) { + std::ifstream script_file(script_path); + std::string script_content( + (std::istreambuf_iterator(script_file)), + std::istreambuf_iterator()); + m_carbon->eval(script_content); + script_cache_[script_path] = std::chrono::steady_clock::now(); + } else { + script_cache_[script_path] = std::chrono::steady_clock::now(); + } + } + +private: + void CacheCleanupLoop() { + while (!stop_cleanup_thread_) { + std::this_thread::sleep_for( + std::chrono::seconds(cache_expiry_seconds_)); + std::lock_guard lock(cache_mutex_); + auto now = std::chrono::steady_clock::now(); + for (auto it = script_cache_.begin(); it != script_cache_.end();) { + if (now - it->second > + std::chrono::seconds(cache_expiry_seconds_)) { + it = script_cache_.erase(it); + } else { + ++it; + } + } + } + } + +private: + std::unique_ptr m_carbon; + + fs::path script_dir_; + std::unordered_map + script_cache_; + std::mutex cache_mutex_; + std::thread cache_cleanup_thread_; + bool stop_cleanup_thread_ = false; + int cache_expiry_seconds_; +}; + +CarbonScript::CarbonScript() : impl_(std::make_unique()) {} + +std::shared_ptr CarbonScript::createShared() { + return std::make_shared(); +} + +void CarbonScript::Init() { impl_->InitMyApp(); } + +bool CarbonScript::LoadScriptFile(const std::string &filename) { + return impl_->LoadScriptFile(filename); +} + +bool CarbonScript::RunCommand(const std::string &command) { + return impl_->RunCommand(command); +} + +bool CarbonScript::RunMultiCommand(const std::vector &commands) { + return impl_->RunMultiCommand(commands); +} + +bool CarbonScript::RunScript(const std::string &filename) { + return impl_->RunScript(filename); +} + +} // namespace Lithium diff --git a/src/script/carbon.hpp b/src/script/carbon.hpp new file mode 100644 index 00000000..09d6ae9a --- /dev/null +++ b/src/script/carbon.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include + + +namespace Lithium { +class CarbonScriptImpl; + +class CarbonScript { +public: + CarbonScript(); + ~CarbonScript(); + + static std::shared_ptr createShared(); + + void Init(); + void InitSubModules(); + void InitMyApp(); + + bool LoadScriptFile(const std::string &filename); + bool RunCommand(const std::string &command); + bool RunMultiCommand(const std::vector &commands); + bool RunScript(const std::string &filename); + +private: + std::unique_ptr impl_; +}; + +} // namespace Lithium diff --git a/src/script/custom/config.cpp b/src/script/custom/config.cpp index ae7f6c0e..086b9c29 100644 --- a/src/script/custom/config.cpp +++ b/src/script/custom/config.cpp @@ -1,45 +1,61 @@ /* - * python_config.cpp + * config.cpp * * Copyright (C) 2023-2024 Max Qian */ /************************************************* -Date: 2023-7-13 +Date: 2024-4-13 -Description: Config module for Python scripting engine +Description: Config module for PocketPy(builtin) **************************************************/ #include "pocketpy/include/pocketpy/bindings.h" using namespace pkpy; -#include "config/configor.hpp" -#include "atom/server/global_ptr.hpp" #include "atom/log/loguru.hpp" +#include "atom/server/global_ptr.hpp" #include "atom/system/system.hpp" #include "atom/type/json.hpp" +#include "config/configor.hpp" using json = nlohmann::json; namespace Lithium { +template +pkpy::PyObject *get_config(pkpy::VM *vm, pkpy::Str &key, + const std::string &log_type) { + json value = GetPtr("lithium.config") + .value() + ->getValue(key.str()) + .value(); + if (value.is_null() || value.type_name() != typeid(T).name()) { + LOG_F(ERROR, "Failed to get config value: {}", key.str()); + return pkpy::py_var(vm, T()); + } + DLOG_F(INFO, "Config value: {}", value.get()); + return pkpy::py_var(vm, value.get()); +} + +template +pkpy::PyObject *set_config(pkpy::VM *vm, pkpy::Str &key, T value) { + return pkpy::py_var(vm, GetPtr("lithium.config") + .value() + ->setValue(key.str(), value)); +} + void addConfigModule(VM *vm) { DLOG_F(INFO, "Adding config module"); - PyObject* mod = vm->new_module("li_config"); + PyObject *mod = vm->new_module("li_config"); vm->bind(mod, "get_str_config(key : str) -> str", "get specified config value and return in string type", [](pkpy::VM *vm, pkpy::ArgsView args) { pkpy::PyObject *key_obj = args[0]; pkpy::Str &key = pkpy::py_cast(vm, key_obj); - json value = GetPtr("lithium.config")->getValue(key.str()); - if (value.is_null() || !value.is_string()) { - LOG_F(ERROR, "Failed to get config value: {}", key.str()); - return pkpy::py_var(vm, ""); - } - DLOG_F(INFO, "Config value: {}", value.get()); - return pkpy::py_var(vm, value.get()); + return get_config(vm, key, "str"); }); vm->bind(mod, "get_int_config(key : str) -> int", @@ -47,13 +63,7 @@ void addConfigModule(VM *vm) { [](pkpy::VM *vm, pkpy::ArgsView args) { pkpy::PyObject *key_obj = args[0]; pkpy::Str &key = pkpy::py_cast(vm, key_obj); - json value = GetPtr("lithium.config")->getValue(key.str()); - if (value.is_null() || !value.is_number()) { - LOG_F(ERROR, "Failed to get config value: {}", key.str()); - return pkpy::py_var(vm, 0); - } - DLOG_F(INFO, "Config value: {}", value.get()); - return pkpy::py_var(vm, value.get()); + return get_config(vm, key, "int"); }); vm->bind(mod, "get_float_config(key : str) -> float", @@ -61,13 +71,7 @@ void addConfigModule(VM *vm) { [](pkpy::VM *vm, pkpy::ArgsView args) { pkpy::PyObject *key_obj = args[0]; pkpy::Str &key = pkpy::py_cast(vm, key_obj); - json value = GetPtr("lithium.config")->getValue(key.str()); - if (value.is_null() || !value.is_number()) { - LOG_F(ERROR, "Failed to get config value: {}", key.str()); - return pkpy::py_var(vm, 0.0f); - } - DLOG_F(INFO, "Config value: {}", value.get()); - return pkpy::py_var(vm, value.get()); + return get_config(vm, key, "float"); }); vm->bind(mod, "get_bool_config(key : str) -> bool", @@ -75,13 +79,7 @@ void addConfigModule(VM *vm) { [](pkpy::VM *vm, pkpy::ArgsView args) { pkpy::PyObject *key_obj = args[0]; pkpy::Str &key = pkpy::py_cast(vm, key_obj); - json value = GetPtr("lithium.config")->getValue(key.str()); - if (value.is_null() || !value.is_boolean()) { - LOG_F(ERROR, "Failed to get config value: {}", key.str()); - return pkpy::py_var(vm, false); - } - DLOG_F(INFO, "Config value: {}", value.get()); - return pkpy::py_var(vm, value.get()); + return get_config(vm, key, "bool"); }); vm->bind(mod, @@ -92,7 +90,7 @@ void addConfigModule(VM *vm) { pkpy::Str &key = pkpy::py_cast(vm, key_obj); pkpy::PyObject *value_obj = args[1]; pkpy::Str &value = pkpy::py_cast(vm, value_obj); - return pkpy::py_var(vm, GetPtr("lithium.config")->setValue(key.str(), value)); + return set_config(vm, key, value.str()); }); vm->bind(mod, @@ -103,7 +101,7 @@ void addConfigModule(VM *vm) { pkpy::Str &key = pkpy::py_cast(vm, key_obj); pkpy::PyObject *value_obj = args[1]; int value = pkpy::py_cast(vm, value_obj); - return pkpy::py_var(vm, GetPtr("lithium.config")->setValue(key.str(), value)); + return set_config(vm, key, value); }); vm->bind(mod, @@ -114,7 +112,7 @@ void addConfigModule(VM *vm) { pkpy::Str &key = pkpy::py_cast(vm, key_obj); pkpy::PyObject *value_obj = args[1]; float value = pkpy::py_cast(vm, value_obj); - return pkpy::py_var(vm, GetPtr("lithium.config")->setValue(key.str(), value)); + return set_config(vm, key, value); }); vm->bind(mod, @@ -125,7 +123,7 @@ void addConfigModule(VM *vm) { pkpy::Str &key = pkpy::py_cast(vm, key_obj); pkpy::PyObject *value_obj = args[1]; bool value = pkpy::py_cast(vm, value_obj); - return pkpy::py_var(vm, GetPtr("lithium.config")->setValue(key.str(), value)); + return set_config(vm, key, value); }); vm->bind(mod, "delete_config(key : str) -> bool", @@ -133,27 +131,35 @@ void addConfigModule(VM *vm) { [](pkpy::VM *vm, pkpy::ArgsView args) { pkpy::PyObject *key_obj = args[0]; pkpy::Str &key = pkpy::py_cast(vm, key_obj); - return pkpy::py_var(vm, GetPtr("lithium.config")->deleteValue(key.str())); - }); - - vm->bind(mod, "save_config(path : str) -> bool", - "save config to specified path", - [](pkpy::VM *vm, pkpy::ArgsView args) { - pkpy::PyObject *path_obj = args[0]; - pkpy::Str &path = pkpy::py_cast(vm, path_obj); return pkpy::py_var( - vm, GetPtr("lithium.config")->saveToFile( - path.str().empty() ? "config/config.json" : path.str())); + vm, GetPtr("lithium.config") + .value() + ->deleteValue(key.str())); }); - vm->bind(mod, "load_config(path : str) -> bool", - "load config from specified path", - [](pkpy::VM *vm, pkpy::ArgsView args) { - pkpy::PyObject *path_obj = args[0]; - pkpy::Str &path = pkpy::py_cast(vm, path_obj); - return pkpy::py_var( - vm, GetPtr("lithium.config")->loadFromFile( - path.str().empty() ? "config/config.json" : path.str())); - }); + vm->bind( + mod, "save_config(path : str) -> bool", "save config to specified path", + [](pkpy::VM *vm, pkpy::ArgsView args) { + pkpy::PyObject *path_obj = args[0]; + pkpy::Str &path = pkpy::py_cast(vm, path_obj); + return pkpy::py_var( + vm, GetPtr("lithium.config") + .value() + ->saveToFile(path.str().empty() ? "config/config.json" + : path.str())); + }); + + vm->bind( + mod, "load_config(path : str) -> bool", + "load config from specified path", + [](pkpy::VM *vm, pkpy::ArgsView args) { + pkpy::PyObject *path_obj = args[0]; + pkpy::Str &path = pkpy::py_cast(vm, path_obj); + return pkpy::py_var( + vm, GetPtr("lithium.config") + .value() + ->loadFromFile(path.str().empty() ? "config/config.json" + : path.str())); + }); } } // namespace Lithium diff --git a/src/script/custom/os.cpp b/src/script/custom/os.cpp index a025aa6f..a35a690c 100644 --- a/src/script/custom/os.cpp +++ b/src/script/custom/os.cpp @@ -1,34 +1,52 @@ +/* + * os.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-4-13 + +Description: OS module for PocketPy(builtin) + +**************************************************/ + #include "pocketpy/include/pocketpy/bindings.h" #include "config.h" -#include "atom/system/os.hpp" #include "atom/io/io.hpp" +#include "atom/log/loguru.hpp" +#include "atom/system/os.hpp" using namespace pkpy; -void addOSModule(VM* vm){ + +namespace Lithium { +void addOSModule(VM* vm) { PyObject* mod = vm->new_module("li_os"); - vm->setattr(mod, "version", VAR(LITHIUM_VERSION_STRING)); + vm->setattr(mod, "version", VAR("1.0.0")); vm->bind_func<1>(mod, "walk", [](VM* vm, ArgsView args) { std::string_view sv; - if(is_non_tagged_type(args[0], vm->tp_bytes)){ + if (is_non_tagged_type(args[0], vm->tp_bytes)) [[likely]] { sv = PK_OBJ_GET(Bytes, args[0]).sv(); - }else{ + } else { sv = CAST(Str&, args[0]).sv(); } - if (!Atom::IO::isisFolderExists(sv)) - { - + if (!Atom::IO::isFolderExists(std::string(sv))) { + LOG_F(ERROR, "Folder is not existing: {}", sv); + return vm->None; } - auto result = Atom::System::jwalk(sv); - if (result.empty()){ + auto result = Atom::System::jwalk(std::string(sv)); + if (result.empty()) { return vm->None; } - return vm->_exec(code, vm->top_frame()->_module); + return py_var(vm, result); }); vm->bind_func<1>(mod, "dumps", [](VM* vm, ArgsView args) { return vm->py_json(args[0]); }); -} \ No newline at end of file +} +} // namespace Lithium diff --git a/src/script/custom/sys.cpp b/src/script/custom/sys.cpp index 045850cf..d334e46e 100644 --- a/src/script/custom/sys.cpp +++ b/src/script/custom/sys.cpp @@ -251,35 +251,21 @@ void addSysModule(VM *vm) { DLOG_F(INFO, "Check if the current process is running as " "root"); - bool is_root = Atom::System::IsRoot(); + bool is_root = Atom::System::isRoot(); return py_var(vm, is_root); }); - vm->bind(mod, "get_current_username() -> str", - "get current username, and return a string value", - [](VM *vm, ArgsView args) { - DLOG_F(INFO, "Get current username"); - std::string current_username = - Atom::System::GetCurrentUsername(); - if (current_username.empty()) { - LOG_F(ERROR, "Failed to get current username: {}", - current_username); - } - DLOG_F(INFO, "Current username: {}", current_username); - return py_var(vm, current_username); - }); - vm->bind(mod, "shutdown() -> bool", "shutdown the system", [](VM *vm, ArgsView args) { DLOG_F(INFO, "Shutdown the system"); - Atom::System::Shutdown(); + Atom::System::shutdown(); return py_var(vm, true); }); vm->bind(mod, "reboot() -> bool", "reboot the system", [](VM *vm, ArgsView args) { DLOG_F(INFO, "Reboot the system"); - Atom::System::Reboot(); + Atom::System::reboot(); return py_var(vm, true); }); @@ -290,7 +276,7 @@ void addSysModule(VM *vm) { Str &name = py_cast(vm, name_obj); bool is_duplicate = - Atom::System::CheckDuplicateProcess(name.c_str()); + Atom::System::checkDuplicateProcess(name.c_str()); if (is_duplicate) { LOG_F(ERROR, "Failed to check duplicate process: {}", is_duplicate); @@ -318,7 +304,7 @@ void addSysModule(VM *vm) { Str &name = py_cast(vm, name_obj); Atom::System::ProcessInfo info; Dict d(vm); - if (Atom::System::GetProcessInfoByName(name.c_str(), info)) { + if (Atom::System::getProcessInfoByName(name.c_str(), info)) { d.set(VAR("id"), VAR(info.processID)); d.set(VAR("parent_id"), VAR(info.parentProcessID)); d.set(VAR("priority"), VAR(info.basePriority)); @@ -335,7 +321,7 @@ void addSysModule(VM *vm) { int id = py_cast(vm, args[0]); Atom::System::ProcessInfo info; Dict d(vm); - if (Atom::System::GetProcessInfoByID(id, info)) { + if (Atom::System::getProcessInfoByID(id, info)) { d.set(VAR("id"), VAR(info.processID)); d.set(VAR("parent_id"), VAR(info.parentProcessID)); d.set(VAR("priority"), VAR(info.basePriority)); diff --git a/src/script/sheller.cpp b/src/script/sheller.cpp index 95df83fa..c9b997e2 100644 --- a/src/script/sheller.cpp +++ b/src/script/sheller.cpp @@ -204,37 +204,8 @@ void ScriptManager::LogError(const std::string &message) { logFile << "[" << buffer << "] " << message << std::endl; logFile.close(); } -} // namespace Lithium -/* -int main() -{ - ScriptManager manager; - - // 注册Shell脚本 - manager.RegisterScript("hello", "echo \"Hello, World!\""); - manager.RegisterScript("sum", "a=$1\nb=$2\nsum=$((a+b))\necho \"Sum: -$sum\""); - - // 注册PowerShell脚本 - manager.RegisterPowerShellScript("greet", R"( - $name = Read-Host -Prompt "Enter your name" - Write-Host "Hello, $name!" - )"); - - // 查看已注册的脚本 - manager.ViewScripts(); - - // 运行脚本 - manager.RunScript("hello"); - manager.RunScript("sum", {"3", "5"}); - manager.RunScript("greet"); - - // 查看脚本输出和状态 - manager.ViewScriptOutput("hello"); - manager.ViewScriptStatus("sum"); - - return 0; -} +#if ENABLE_PEGTL -*/ +#endif +} // namespace Lithium diff --git a/src/server/App.cpp b/src/server/App.cpp new file mode 100644 index 00000000..571e6d80 --- /dev/null +++ b/src/server/App.cpp @@ -0,0 +1,45 @@ +/* + * App.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-7-13 + +Description: Main + +**************************************************/ + +#include "App.hpp" + +#include "./Runner.hpp" + +#include "./AppComponent.hpp" + +#include "oatpp/network/Server.hpp" + +#include + +void run() { + /* Register Components in scope of run() method */ + AppComponent components; + + Runner runner(OATPP_GET_COMPONENT(oatpp::Object), + OATPP_GET_COMPONENT(std::shared_ptr)); + + runner.start(); + + runner.join(); +} + +int runServer() { + oatpp::base::Environment::init(); + + run(); + + oatpp::base::Environment::destroy(); + + return 0; +} diff --git a/src/server/App.hpp b/src/server/App.hpp new file mode 100644 index 00000000..a2231778 --- /dev/null +++ b/src/server/App.hpp @@ -0,0 +1,6 @@ +#ifndef LITHIUM_SERVER_APP_HPP +#define LITHIUM_SERVER_APP_HPP + +int runServer(); + +#endif diff --git a/src/AppComponent.hpp b/src/server/AppComponent.hpp similarity index 56% rename from src/AppComponent.hpp rename to src/server/AppComponent.hpp index 0d1ec5c0..69b77635 100644 --- a/src/AppComponent.hpp +++ b/src/server/AppComponent.hpp @@ -15,23 +15,17 @@ Description: App Components #ifndef LITHIUM_APP_COMPONENT_HPP #define LITHIUM_APP_COMPONENT_HPP -#include "config.h" - #include "config/Config.hpp" #include "config/HubsConfig.hpp" #include "websocket/Registry.hpp" -#if ENABLE_ASYNC + +// Websocket #include "oatpp-websocket/AsyncConnectionHandler.hpp" #include "oatpp/web/server/AsyncHttpConnectionHandler.hpp" -#else -#include "oatpp-websocket/ConnectionHandler.hpp" -#include "oatpp/web/server/HttpConnectionHandler.hpp" -#endif #include "ErrorHandler.hpp" -#include "oatpp/core/base/CommandLineArguments.hpp" #include "oatpp/core/macro/component.hpp" #include "oatpp/core/utils/ConversionUtils.hpp" #include "oatpp/network/monitor/ConnectionInactivityChecker.hpp" @@ -42,6 +36,7 @@ Description: App Components #include "oatpp/web/protocol/http/incoming/SimpleBodyDecoder.hpp" #include "oatpp/web/server/HttpRouter.hpp" #include "oatpp/web/server/interceptor/AllowCorsGlobal.hpp" + #if ENABLE_DEBUG #include "oatpp/network/virtual_/Interface.hpp" #include "oatpp/network/virtual_/server/ConnectionProvider.hpp" @@ -49,9 +44,12 @@ Description: App Components #include "components/SwaggerComponent.hpp" +// SSL #include "oatpp-openssl/Config.hpp" #include "oatpp-openssl/configurer/TrustStore.hpp" #include "oatpp-openssl/server/ConnectionProvider.hpp" + +// GZip #include "oatpp-zlib/EncoderProvider.hpp" #include @@ -59,28 +57,18 @@ Description: App Components // #include "data/SystemCustom.hpp" +#include + /** * Class which creates and holds Application components and registers * components in oatpp::base::Environment Order of components initialization is * from top to bottom */ class AppComponent { - /* note: though I don't like this kind of initialization, but I don't know - * how to do it in a better way, segmentation fault ! */ -private: - oatpp::String m_host; - v_uint16 m_port; - - oatpp::base::CommandLineArguments m_cmdArgs; // command line arguments +public: + AppComponent() = default; public: - /** - * Create components - * @param host - host name - * @param port - port number - */ - explicit AppComponent(oatpp::String host, v_uint16 port) - : m_host(host), m_port(port) {} /** * Swagger component */ @@ -112,9 +100,11 @@ class AppComponent { */ OATPP_CREATE_COMPONENT(std::shared_ptr, hubConfig) ([] { + // We specify the default config here auto config = std::make_shared(nullptr); auto Hub1 = HubConfigDto::createShared(); auto Hub2 = HubConfigDto::createShared(); + // Script and device are the default hubs Hub1->hubId = "device"; Hub2->hubId = "script"; config->putHubConfig(Hub1); @@ -122,30 +112,11 @@ class AppComponent { return config; }()); -#if ENABLE_ASYNC /** * Create Async Executor */ OATPP_CREATE_COMPONENT(std::shared_ptr, executor) - ([] { - return std::make_shared( - std::thread::hardware_concurrency() + - 2 /* Data-Processing threads */, - 1 /* I/O threads */, 1 /* Timer threads */ - ); - }()); -#endif - - /** - * Create Debug virtual interface component - */ -#if ENABLE_DEBUG - OATPP_CREATE_COMPONENT(std::shared_ptr, - virtualInterface) - ([] { - return oatpp::network::virtual_::Interface::obtainShared("virtualhost"); - }()); -#endif + ([] { return std::make_shared(); }()); /** * Create Router component @@ -155,8 +126,8 @@ class AppComponent { ([] { return oatpp::web::server::HttpRouter::createShared(); }()); /** - * Create ObjectMapper component to serialize/deserialize DTOs in - * Controller's API + * Create ObjectMapper component to serialize/deserialize DTOs in + * Contoller's API */ OATPP_CREATE_COMPONENT(std::shared_ptr, apiObjectMapper) @@ -189,6 +160,7 @@ class AppComponent { * Create ObjectMapper component to serialize/deserialize DTOs in WS * communication */ + /* OATPP_CREATE_COMPONENT(std::shared_ptr, wsApiObjectMapper) (Constants::COMPONENT_WS_API, [] { @@ -197,89 +169,15 @@ class AppComponent { mapper->getSerializer()->getConfig()->includeNullFields = false; return mapper; }()); - + */ + /** - * Create hubs sessions Registry component. + * Create games sessions Registry component. */ - OATPP_CREATE_COMPONENT(std::shared_ptr, hubsSessionsRegistry) + OATPP_CREATE_COMPONENT(std::shared_ptr, gamesSessionsRegistry) ([] { return std::make_shared(); }()); - /** - * Create ConnectionProvider component which listens on the port - */ - OATPP_CREATE_COMPONENT( - std::shared_ptr, - serverConnectionProvider) - ([this] { - std::shared_ptr - connectionProvider; - if (m_port == 0) { -#if ENABLE_DEBUG - OATPP_COMPONENT( - std::shared_ptr, - interface); - connectionProvider = oatpp::network::virtual_::server:: - ConnectionProvider::createShared(interface); -#endif - } else { - connectionProvider = - oatpp::network::tcp::server::ConnectionProvider::createShared( -#if ENABLE_IPV6 - { m_host, m_port, oatpp::network::Address::IP_6 } -#else - {m_host, m_port, oatpp::network::Address::IP_4} -#endif - ); - } - return connectionProvider; - }()); - - /** - * Create ConnectionHandler component which uses Router component to route - * requests - */ - OATPP_CREATE_COMPONENT(std::shared_ptr, - serverConnectionHandler) - ("http", [] { // get JWT component - OATPP_COMPONENT(std::shared_ptr, - router); // get Router component - OATPP_COMPONENT(std::shared_ptr, - objectMapper); // get ObjectMapper component - /* Create HttpProcessor::Components */ - auto components = - std::make_shared( - router); - - /* Add content encoders */ - auto encoders = std::make_shared< - oatpp::web::protocol::http::encoding::ProviderCollection>(); - encoders->add(std::make_shared()); - encoders->add(std::make_shared()); - /* Set content encoders */ - components->contentEncodingProviders = encoders; - - auto decoders = std::make_shared< - oatpp::web::protocol::http::encoding::ProviderCollection>(); - decoders->add(std::make_shared()); - decoders->add(std::make_shared()); - /* Set Body Decoder */ - components->bodyDecoder = std::make_shared< - oatpp::web::protocol::http::incoming::SimpleBodyDecoder>(decoders); - -#if ENABLE_ASYNC - OATPP_COMPONENT(std::shared_ptr, - executor); // get Async executor component - auto connectionHandler = - oatpp::web::server::AsyncHttpConnectionHandler::createShared( - components, executor); - connectionHandler->setErrorHandler( - std::make_shared(objectMapper)); -#else - auto connectionHandler = oatpp::web::server::HttpConnectionHandler::createShared(components); - connectionHandler->setErrorHandler(std::make_shared(objectMapper)); -#endif - return connectionHandler; - }()); + /** * Create websocket connection handler @@ -289,15 +187,22 @@ class AppComponent { (Constants::COMPONENT_WS_API, [] { OATPP_COMPONENT(std::shared_ptr, executor); OATPP_COMPONENT(std::shared_ptr, registry); -#if ENABLE_ASYNC auto connectionHandler = oatpp::websocket::AsyncConnectionHandler::createShared(executor); connectionHandler->setSocketInstanceListener(registry); -#else - auto connectionHandler = oatpp::websocket::ConnectionHandler::createShared(); -#endif return connectionHandler; }()); + + /** + * Create Debug virtual interface component + */ +#if ENABLE_DEBUG + OATPP_CREATE_COMPONENT(std::shared_ptr, + virtualInterface) + ([] { + return oatpp::network::virtual_::Interface::obtainShared("virtualhost"); + }()); +#endif }; #endif /* LITHIUM_APP_COMPONENT_HPP */ \ No newline at end of file diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt new file mode 100644 index 00000000..6f8ccca9 --- /dev/null +++ b/src/server/CMakeLists.txt @@ -0,0 +1,38 @@ +# CMakeLists.txt for Lithium +# This project is licensed under the terms of the GPL3 license. +# +# Project Name: Lithium-WebServer +# Description: Oatpp http and websocket for Lithium +# Author: Max Qian +# License: GPL3 + +cmake_minimum_required(VERSION 3.20) + +project(lithium_webserver C CXX) + +set(server_websocket_module + websocket/Hub.cpp + websocket/Connection.cpp + websocket/Registry.cpp + websocket/Session.cpp +) + +set(server_module + App.cpp + AppComponent.hpp + ErrorHandler.cpp + Runner.cpp + + config/HubsConfig.cpp +) + +################################################################################# +# Main + +add_library(${PROJECT_NAME} STATIC ${server_websocket_module} ${server_module}) +target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_BINARY_DIR}/libs) + +target_link_libraries(${PROJECT_NAME} PRIVATE oatpp-websocket oatpp-swagger oatpp-openssl oatpp-zlib oatpp) +target_link_libraries(${PROJECT_NAME} PRIVATE loguru fmt::fmt) +target_link_libraries(${PROJECT_NAME} PRIVATE atomstatic) + diff --git a/src/Constants.hpp b/src/server/Constants.hpp similarity index 100% rename from src/Constants.hpp rename to src/server/Constants.hpp diff --git a/src/ErrorHandler.cpp b/src/server/ErrorHandler.cpp similarity index 100% rename from src/ErrorHandler.cpp rename to src/server/ErrorHandler.cpp diff --git a/src/ErrorHandler.hpp b/src/server/ErrorHandler.hpp similarity index 100% rename from src/ErrorHandler.hpp rename to src/server/ErrorHandler.hpp diff --git a/src/Runner.cpp b/src/server/Runner.cpp similarity index 67% rename from src/Runner.cpp rename to src/server/Runner.cpp index 2d4846b3..4990ec34 100644 --- a/src/Runner.cpp +++ b/src/server/Runner.cpp @@ -14,15 +14,32 @@ Description: Lithium Server Runner #include "Runner.hpp" +#include "ErrorHandler.hpp" + #include "controller/AsyncClientController.hpp" +#include "controller/AsyncConfigController.hpp" +#include "controller/AsyncDeviceController.hpp" +#include "controller/AsyncIOController.hpp" +#include "controller/AsyncStaticController.hpp" +#include "controller/AsyncSystemController.hpp" + +#include "oatpp-swagger/AsyncController.hpp" #include "oatpp-openssl/server/ConnectionProvider.hpp" #include "oatpp/network/tcp/server/ConnectionProvider.hpp" +#include "oatpp/web/protocol/http/incoming/SimpleBodyDecoder.hpp" #include "oatpp/web/server/AsyncHttpConnectionHandler.hpp" #include "oatpp/network/Server.hpp" +#include "oatpp-zlib/EncoderProvider.hpp" + +#define ADD_CONTROLLER(controller, router) \ + auto controller##_ptr = controller::createShared(); \ + docEndpoints.append(controller##_ptr->getEndpoints()); \ + router->getRouter()->addController(controller##_ptr); + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // APIServer @@ -49,9 +66,35 @@ APIServer::APIServer(const oatpp::Object& config, {config->host, config->port, oatpp::network::Address::IP_4}); } + auto components = + std::make_shared( + m_router); + /* Add content encoders */ + auto encoders = std::make_shared< + oatpp::web::protocol::http::encoding::ProviderCollection>(); + encoders->add(std::make_shared()); + encoders->add(std::make_shared()); + /* Set content encoders */ + components->contentEncodingProviders = encoders; + + auto decoders = std::make_shared< + oatpp::web::protocol::http::encoding::ProviderCollection>(); + decoders->add(std::make_shared()); + decoders->add(std::make_shared()); + /* Set Body Decoder */ + components->bodyDecoder = std::make_shared< + oatpp::web::protocol::http::incoming::SimpleBodyDecoder>(decoders); + m_connectionHandler = - oatpp::web::server::AsyncHttpConnectionHandler::createShared(m_router, + oatpp::web::server::AsyncHttpConnectionHandler::createShared(components, executor); + OATPP_COMPONENT( + std::shared_ptr, + + apiObjectMapper, + Constants::COMPONENT_REST_API); // get ObjectMapper component + m_connectionHandler->setErrorHandler( + std::make_shared(apiObjectMapper)); } std::shared_ptr APIServer::getRouter() { @@ -59,7 +102,7 @@ std::shared_ptr APIServer::getRouter() { } void APIServer::start() { - m_serverThread = std::thread([this] { + m_serverThread = std::jthread([this] { oatpp::network::Server server(m_connectionProvider, m_connectionHandler); server.run(); @@ -78,7 +121,18 @@ Runner::Runner(const oatpp::Object& config, auto hostServer = std::make_shared(config->hostAPIServer, executor); - hostServer->getRouter()->addController(std::make_shared()); + + oatpp::web::server::api::Endpoints docEndpoints; + ADD_CONTROLLER(ClientController, hostServer); + ADD_CONTROLLER(ConfigController, hostServer); + ADD_CONTROLLER(DeviceController, hostServer); + ADD_CONTROLLER(IOController, hostServer); + ADD_CONTROLLER(StaticController, hostServer); + ADD_CONTROLLER(SystemController, hostServer); + + hostServer->getRouter()->addController( + oatpp::swagger::AsyncController::createShared(docEndpoints)); + m_servers.push_back(hostServer); /* client API server */ @@ -86,16 +140,14 @@ Runner::Runner(const oatpp::Object& config, if (config->clientAPIServer->host == config->hostAPIServer->host && config->clientAPIServer->port == config->hostAPIServer->port) { - hostServer->getRouter()->addController( - std::make_shared()); + ADD_CONTROLLER(ClientController, hostServer); } else { assertServerConfig(config->clientAPIServer, "clientAPIServer", true); auto clientServer = std::make_shared(config->clientAPIServer, executor); - clientServer->getRouter()->addController( - std::make_shared()); + ADD_CONTROLLER(ClientController, clientServer); m_servers.push_back(clientServer); } } diff --git a/src/Runner.hpp b/src/server/Runner.hpp similarity index 87% rename from src/Runner.hpp rename to src/server/Runner.hpp index aebb2975..de791a5b 100644 --- a/src/Runner.hpp +++ b/src/server/Runner.hpp @@ -22,6 +22,9 @@ Description: Lithium Server Runner #include "oatpp/network/ConnectionHandler.hpp" #include "oatpp/network/ConnectionProvider.hpp" +#include "oatpp-websocket/AsyncConnectionHandler.hpp" +#include "oatpp/web/server/AsyncHttpConnectionHandler.hpp" + #include "oatpp/core/async/Executor.hpp" class APIServer { @@ -29,7 +32,8 @@ class APIServer { std::shared_ptr m_router; std::shared_ptr m_connectionProvider; - std::shared_ptr m_connectionHandler; + std::shared_ptr + m_connectionHandler; private: #if __cplusplus >= 202002L diff --git a/src/components/DatabaseComponent.hpp b/src/server/components/DatabaseComponent.hpp similarity index 100% rename from src/components/DatabaseComponent.hpp rename to src/server/components/DatabaseComponent.hpp diff --git a/src/components/SwaggerComponent.hpp b/src/server/components/SwaggerComponent.hpp similarity index 100% rename from src/components/SwaggerComponent.hpp rename to src/server/components/SwaggerComponent.hpp diff --git a/src/config/Config.hpp b/src/server/config/Config.hpp similarity index 100% rename from src/config/Config.hpp rename to src/server/config/Config.hpp diff --git a/src/config/HubsConfig.cpp b/src/server/config/HubsConfig.cpp similarity index 100% rename from src/config/HubsConfig.cpp rename to src/server/config/HubsConfig.cpp diff --git a/src/config/HubsConfig.hpp b/src/server/config/HubsConfig.hpp similarity index 100% rename from src/config/HubsConfig.hpp rename to src/server/config/HubsConfig.hpp diff --git a/src/controller/AsyncClientController.hpp b/src/server/controller/AsyncClientController.hpp similarity index 99% rename from src/controller/AsyncClientController.hpp rename to src/server/controller/AsyncClientController.hpp index 81668c25..831277b3 100644 --- a/src/controller/AsyncClientController.hpp +++ b/src/server/controller/AsyncClientController.hpp @@ -33,7 +33,7 @@ class ClientController : public oatpp::web::server::api::ApiController { private: OATPP_COMPONENT(std::shared_ptr, - websocketConnectionHandler, Constants::COMPONENT_WS_API); + websocketConnectionHandler, Constants::COMPONENT_REST_API); public: ClientController(OATPP_COMPONENT(std::shared_ptr, diff --git a/src/controller/AsyncConfigController.hpp b/src/server/controller/AsyncConfigController.hpp similarity index 92% rename from src/controller/AsyncConfigController.hpp rename to src/server/controller/AsyncConfigController.hpp index 8776668d..19ca78c8 100644 --- a/src/controller/AsyncConfigController.hpp +++ b/src/server/controller/AsyncConfigController.hpp @@ -15,8 +15,6 @@ Description: Async Config Controller #ifndef LITHIUM_ASYNC_SCRIPT_CONTROLLER_HPP #define LITHIUM_ASYNC_SCRIPT_CONTROLLER_HPP -#include "config.h" - #include "oatpp/core/macro/codegen.hpp" #include "oatpp/core/macro/component.hpp" #include "oatpp/parser/json/mapping/ObjectMapper.hpp" @@ -32,11 +30,11 @@ Description: Async Config Controller class ConfigController : public oatpp::web::server::api::ApiController { public: - static std::shared_ptr m_configManager; + static std::weak_ptr m_configManager; ConfigController(const std::shared_ptr& objectMapper) : oatpp::web::server::api::ApiController(objectMapper) { - m_configManager = GetPtr("lithium.config"); + m_configManager = GetWeakPtr("lithium.config"); } // ---------------------------------------------------------------- @@ -73,16 +71,16 @@ class ConfigController : public oatpp::web::server::api::ApiController { auto res = ReturnConfigDTO::createShared(); res->status = "getConfig"; - if (!m_configManager) { + if (m_configManager.expired()) { res->status = "error"; res->code = 500; res->error = "ConfigManager is null"; } else { std::string path = body->path.getValue(""); - if (auto tmp = m_configManager->getValue(path); tmp) { + if (auto tmp = m_configManager.lock()->getValue(path); tmp) { res->status = "success"; res->code = 200; - res->value = tmp.dump(); + res->value = tmp.value().dump(); res->type = "string"; } else { res->status = "error"; @@ -119,14 +117,14 @@ class ConfigController : public oatpp::web::server::api::ApiController { auto res = StatusDto::createShared(); res->command = "setConfig"; - if (!m_configManager) { + if (m_configManager.expired()) { res->status = "error"; res->code = 500; res->error = "ConfigManager is null"; } else { std::string path = body->path.getValue(""); std::string value = body->value.getValue(""); - if (m_configManager->setValue(path, value)) { + if (m_configManager.lock()->setValue(path, value)) { res->status = "success"; res->code = 200; } else { @@ -162,13 +160,13 @@ class ConfigController : public oatpp::web::server::api::ApiController { auto res = StatusDto::createShared(); res->command = "deleteConfig"; - if (!m_configManager) { + if (m_configManager.expired()) { res->status = "error"; res->code = 500; res->error = "ConfigManager is null"; } else { std::string path = body->path.getValue(""); - if (m_configManager->deleteValue(path)) { + if (m_configManager.lock()->deleteValue(path)) { res->status = "success"; res->code = 200; } else { @@ -205,13 +203,13 @@ class ConfigController : public oatpp::web::server::api::ApiController { auto res = StatusDto::createShared(); res->command = "loadConfig"; - if (!m_configManager) { + if (m_configManager.expired()) { res->status = "error"; res->code = 500; res->error = "ConfigManager is null"; } else { std::string path = body->path.getValue(""); - if (m_configManager->loadFromFile(path)) { + if (m_configManager.lock()->loadFromFile(path)) { res->status = "success"; res->code = 200; } else { @@ -248,14 +246,14 @@ class ConfigController : public oatpp::web::server::api::ApiController { auto res = StatusDto::createShared(); res->command = "saveConfig"; - if (!m_configManager) { + if (m_configManager.expired()) { res->status = "error"; res->code = 500; res->error = "ConfigManager is null"; } else { std::string path = body->path.getValue(""); bool isAbsolute = body->isAbsolute.getValue(true); - if (m_configManager->saveToFile(path)) { + if (m_configManager.lock()->saveToFile(path)) { res->status = "success"; res->code = 200; } else { @@ -270,8 +268,7 @@ class ConfigController : public oatpp::web::server::api::ApiController { }; }; -std::shared_ptr ConfigController::m_configManager = - GetPtr("lithium.config"); +std::weak_ptr ConfigController::m_configManager = {}; #include OATPP_CODEGEN_END(ApiController) //<- End Codegen diff --git a/src/controller/AsyncDeviceController.hpp b/src/server/controller/AsyncDeviceController.hpp similarity index 99% rename from src/controller/AsyncDeviceController.hpp rename to src/server/controller/AsyncDeviceController.hpp index 8011d0e1..4131f856 100644 --- a/src/controller/AsyncDeviceController.hpp +++ b/src/server/controller/AsyncDeviceController.hpp @@ -15,8 +15,6 @@ Description: Device Routes #ifndef LITHIUM_ASYNC_DEVICE_CONTROLLER_HPP #define LITHIUM_ASYNC_DEVICE_CONTROLLER_HPP -#include "config.h" - #include "oatpp/core/macro/codegen.hpp" #include "oatpp/core/macro/component.hpp" #include "oatpp/parser/json/mapping/ObjectMapper.hpp" @@ -29,7 +27,7 @@ Description: Device Routes #include "magic_enum/magic_enum.hpp" -#include "LithiumApp.hpp" +#include "lithiumapp.hpp" #include OATPP_CODEGEN_BEGIN(ApiController) //<- Begin Codegen diff --git a/src/controller/AsyncIOController.hpp b/src/server/controller/AsyncIOController.hpp similarity index 99% rename from src/controller/AsyncIOController.hpp rename to src/server/controller/AsyncIOController.hpp index f94858f3..388f782a 100644 --- a/src/controller/AsyncIOController.hpp +++ b/src/server/controller/AsyncIOController.hpp @@ -15,8 +15,6 @@ Description: IO Route #ifndef LITHIUM_ASYNC_IO_CONTROLLER_HPP #define LITHIUM_ASYNC_IO_CONTROLLER_HPP -#include "config.h" - #include "atom/io/compress.hpp" #include "atom/io/file.hpp" #include "atom/io/io.hpp" diff --git a/src/controller/AsyncModuleController.hpp b/src/server/controller/AsyncModuleController.hpp similarity index 100% rename from src/controller/AsyncModuleController.hpp rename to src/server/controller/AsyncModuleController.hpp diff --git a/src/controller/AsyncPHD2Controller.hpp b/src/server/controller/AsyncPHD2Controller.hpp similarity index 99% rename from src/controller/AsyncPHD2Controller.hpp rename to src/server/controller/AsyncPHD2Controller.hpp index 59c76d2f..6c5ee817 100644 --- a/src/controller/AsyncPHD2Controller.hpp +++ b/src/server/controller/AsyncPHD2Controller.hpp @@ -15,9 +15,7 @@ Description: PHD2 Route #ifndef ASYNC_PHD2_CONTROLLER_HPP #define ASYNC_PHD2_CONTROLLER_HPP -#include "LithiumApp.hpp" - -#include "config.h" +#include "lithiumapp.hpp" #include "data/PHD2Dto.hpp" #include "data/StatusDto.hpp" diff --git a/src/controller/AsyncProcessController.hpp b/src/server/controller/AsyncProcessController.hpp similarity index 99% rename from src/controller/AsyncProcessController.hpp rename to src/server/controller/AsyncProcessController.hpp index e1fb2f13..968abb2c 100644 --- a/src/controller/AsyncProcessController.hpp +++ b/src/server/controller/AsyncProcessController.hpp @@ -15,8 +15,6 @@ Description: Process Route #ifndef LITHIUM_ASYNC_PROCESS_CONTROLLER_HPP #define LITHIUM_ASYNC_PROCESS_CONTROLLER_HPP -#include "config.h" - #include "data/ProcessDto.hpp" #include "data/StatusDto.hpp" diff --git a/src/controller/AsyncScriptController.hpp b/src/server/controller/AsyncScriptController.hpp similarity index 99% rename from src/controller/AsyncScriptController.hpp rename to src/server/controller/AsyncScriptController.hpp index eb6e4868..cb91edd9 100644 --- a/src/controller/AsyncScriptController.hpp +++ b/src/server/controller/AsyncScriptController.hpp @@ -26,8 +26,6 @@ Description: Async Script Controller #ifndef Lithium_SCRIPTCONTROLLER_HPP #define Lithium_SCRIPTCONTROLLER_HPP -#include "config.h" - #include "oatpp/core/macro/codegen.hpp" #include "oatpp/core/macro/component.hpp" #include "oatpp/parser/json/mapping/ObjectMapper.hpp" @@ -36,7 +34,7 @@ Description: Async Script Controller #include "data/ScriptDto.hpp" #include "data/StatusDto.hpp" -#include "LithiumApp.hpp" +#include "lithiumapp.hpp" #include OATPP_CODEGEN_BEGIN(ApiController) //<- Begin Codegen diff --git a/src/controller/AsyncServerController.hpp b/src/server/controller/AsyncServerController.hpp similarity index 100% rename from src/controller/AsyncServerController.hpp rename to src/server/controller/AsyncServerController.hpp diff --git a/src/controller/AsyncStaticController.hpp b/src/server/controller/AsyncStaticController.hpp similarity index 99% rename from src/controller/AsyncStaticController.hpp rename to src/server/controller/AsyncStaticController.hpp index a7c7a68b..6fa5f60c 100644 --- a/src/controller/AsyncStaticController.hpp +++ b/src/server/controller/AsyncStaticController.hpp @@ -20,8 +20,6 @@ Description: Static Route #include "oatpp/parser/json/mapping/ObjectMapper.hpp" #include "oatpp/web/server/api/ApiController.hpp" -#include "config.h" - #include #include #include diff --git a/src/controller/AsyncSystemController.hpp b/src/server/controller/AsyncSystemController.hpp similarity index 99% rename from src/controller/AsyncSystemController.hpp rename to src/server/controller/AsyncSystemController.hpp index 5889e6c6..986dab01 100644 --- a/src/controller/AsyncSystemController.hpp +++ b/src/server/controller/AsyncSystemController.hpp @@ -23,8 +23,6 @@ Description: System Route #include "atom/system/module/wifi.hpp" #include "atom/system/system.hpp" -#include "config.h" - #include "oatpp/core/macro/codegen.hpp" #include "oatpp/core/macro/component.hpp" #include "oatpp/parser/json/mapping/ObjectMapper.hpp" @@ -445,7 +443,7 @@ class SystemController : public oatpp::web::server::api::ApiController { ENDPOINT_ASYNC_INIT(getUIProcesses); Action act() override { nlohmann::json res; - for (const auto &process : Atom::System::GetProcessInfo()) { + for (const auto &process : Atom::System::getProcessInfo()) { OATPP_LOGD("System", "Process Name: %s File Address: %s", process.first.c_str(), process.second.c_str()); res["value"][process.first] = process.second; @@ -465,7 +463,7 @@ class SystemController : public oatpp::web::server::api::ApiController { ENDPOINT_ASYNC("GET", "/api/system/shutdown", getUIShutdown) { ENDPOINT_ASYNC_INIT(getUIShutdown); Action act() override { - Atom::System::Shutdown(); + Atom::System::shutdown(); return _return(controller->createResponse( Status::CODE_200, "Wtf, how can you do that?")); } @@ -475,7 +473,7 @@ class SystemController : public oatpp::web::server::api::ApiController { ENDPOINT_ASYNC("GET", "/api/system/reboot", getUIReboot) { ENDPOINT_ASYNC_INIT(getUIReboot); Action act() override { - Atom::System::Reboot(); + Atom::System::reboot(); return _return(controller->createResponse( Status::CODE_200, "Wtf, how can you do that?")); } diff --git a/src/controller/AsyncUploadController.hpp b/src/server/controller/AsyncUploadController.hpp similarity index 100% rename from src/controller/AsyncUploadController.hpp rename to src/server/controller/AsyncUploadController.hpp diff --git a/src/controller/AsyncWebSocketController.hpp b/src/server/controller/AsyncWebSocketController.hpp similarity index 100% rename from src/controller/AsyncWebSocketController.hpp rename to src/server/controller/AsyncWebSocketController.hpp diff --git a/src/data/AuthDto.hpp b/src/server/data/AuthDto.hpp similarity index 100% rename from src/data/AuthDto.hpp rename to src/server/data/AuthDto.hpp diff --git a/src/data/ConfigDto.hpp b/src/server/data/ConfigDto.hpp similarity index 100% rename from src/data/ConfigDto.hpp rename to src/server/data/ConfigDto.hpp diff --git a/src/data/DeviceDto.hpp b/src/server/data/DeviceDto.hpp similarity index 100% rename from src/data/DeviceDto.hpp rename to src/server/data/DeviceDto.hpp diff --git a/src/data/IODto.hpp b/src/server/data/IODto.hpp similarity index 100% rename from src/data/IODto.hpp rename to src/server/data/IODto.hpp diff --git a/src/data/ModuleDto.hpp b/src/server/data/ModuleDto.hpp similarity index 100% rename from src/data/ModuleDto.hpp rename to src/server/data/ModuleDto.hpp diff --git a/src/data/PageDto.hpp b/src/server/data/PageDto.hpp similarity index 100% rename from src/data/PageDto.hpp rename to src/server/data/PageDto.hpp diff --git a/src/data/ProcessDto.hpp b/src/server/data/ProcessDto.hpp similarity index 100% rename from src/data/ProcessDto.hpp rename to src/server/data/ProcessDto.hpp diff --git a/src/data/SignInDto.hpp b/src/server/data/SignInDto.hpp similarity index 100% rename from src/data/SignInDto.hpp rename to src/server/data/SignInDto.hpp diff --git a/src/data/SignUpDto.hpp b/src/server/data/SignUpDto.hpp similarity index 100% rename from src/data/SignUpDto.hpp rename to src/server/data/SignUpDto.hpp diff --git a/src/data/StatusDto.hpp b/src/server/data/StatusDto.hpp similarity index 100% rename from src/data/StatusDto.hpp rename to src/server/data/StatusDto.hpp diff --git a/src/data/StoryDto.hpp b/src/server/data/StoryDto.hpp similarity index 100% rename from src/data/StoryDto.hpp rename to src/server/data/StoryDto.hpp diff --git a/src/data/SystemCustom.hpp b/src/server/data/SystemCustom.hpp similarity index 100% rename from src/data/SystemCustom.hpp rename to src/server/data/SystemCustom.hpp diff --git a/src/data/SystemDto.hpp b/src/server/data/SystemDto.hpp similarity index 100% rename from src/data/SystemDto.hpp rename to src/server/data/SystemDto.hpp diff --git a/src/data/UserDto.hpp b/src/server/data/UserDto.hpp similarity index 100% rename from src/data/UserDto.hpp rename to src/server/data/UserDto.hpp diff --git a/src/websocket/Connection.cpp b/src/server/websocket/Connection.cpp similarity index 100% rename from src/websocket/Connection.cpp rename to src/server/websocket/Connection.cpp diff --git a/src/websocket/Connection.hpp b/src/server/websocket/Connection.hpp similarity index 98% rename from src/websocket/Connection.hpp rename to src/server/websocket/Connection.hpp index 421c5714..5a6da9c6 100644 --- a/src/websocket/Connection.hpp +++ b/src/server/websocket/Connection.hpp @@ -69,7 +69,7 @@ class Connection : public oatpp::websocket::AsyncWebSocket::Listener { /* Inject application components */ OATPP_COMPONENT(std::shared_ptr, m_asyncExecutor); OATPP_COMPONENT(std::shared_ptr, - m_objectMapper, Constants::COMPONENT_WS_API); + m_objectMapper, Constants::COMPONENT_REST_API); private: CoroutineStarter handlePong(const oatpp::Object& message); diff --git a/src/websocket/Hub.cpp b/src/server/websocket/Hub.cpp similarity index 100% rename from src/websocket/Hub.cpp rename to src/server/websocket/Hub.cpp diff --git a/src/websocket/Hub.hpp b/src/server/websocket/Hub.hpp similarity index 100% rename from src/websocket/Hub.hpp rename to src/server/websocket/Hub.hpp diff --git a/src/websocket/Registry.cpp b/src/server/websocket/Registry.cpp similarity index 100% rename from src/websocket/Registry.cpp rename to src/server/websocket/Registry.cpp diff --git a/src/websocket/Registry.hpp b/src/server/websocket/Registry.hpp similarity index 97% rename from src/websocket/Registry.hpp rename to src/server/websocket/Registry.hpp index 589295a8..a86dbc0a 100644 --- a/src/websocket/Registry.hpp +++ b/src/server/websocket/Registry.hpp @@ -41,7 +41,7 @@ class Registry OATPP_COMPONENT(std::shared_ptr, m_hubConfig); OATPP_COMPONENT(std::shared_ptr, m_asyncExecutor); OATPP_COMPONENT(std::shared_ptr, - m_objectMapper, Constants::COMPONENT_WS_API); + m_objectMapper, Constants::COMPONENT_REST_API); private: oatpp::String getRequiredParameter( diff --git a/src/websocket/Session.cpp b/src/server/websocket/Session.cpp similarity index 100% rename from src/websocket/Session.cpp rename to src/server/websocket/Session.cpp diff --git a/src/websocket/Session.hpp b/src/server/websocket/Session.hpp similarity index 100% rename from src/websocket/Session.hpp rename to src/server/websocket/Session.hpp diff --git a/src/websocket/data/DTOs.hpp b/src/server/websocket/data/DTOs.hpp similarity index 100% rename from src/websocket/data/DTOs.hpp rename to src/server/websocket/data/DTOs.hpp diff --git a/src/websocket/template/error_message.hpp b/src/server/websocket/template/error_message.hpp similarity index 100% rename from src/websocket/template/error_message.hpp rename to src/server/websocket/template/error_message.hpp diff --git a/src/websocket/template/function.hpp b/src/server/websocket/template/function.hpp similarity index 100% rename from src/websocket/template/function.hpp rename to src/server/websocket/template/function.hpp diff --git a/src/websocket/template/message.hpp b/src/server/websocket/template/message.hpp similarity index 100% rename from src/websocket/template/message.hpp rename to src/server/websocket/template/message.hpp diff --git a/src/websocket/template/variable.hpp b/src/server/websocket/template/variable.hpp similarity index 100% rename from src/websocket/template/variable.hpp rename to src/server/websocket/template/variable.hpp diff --git a/src/service/AuthService.cpp b/src/service/AuthService.cpp deleted file mode 100644 index ddcf9c51..00000000 --- a/src/service/AuthService.cpp +++ /dev/null @@ -1,74 +0,0 @@ - -#include "AuthService.hpp" - -oatpp::Object AuthService::signUp( - const oatpp::Object& dto) { - auto user = UserModel::createShared(); - user->id = nullptr; - user->userName = dto->userName; - user->email = dto->email; - user->password = dto->password; - - auto userDto = UserDto::createShared(); - userDto->userName = user->userName; - userDto->email = user->email; - userDto->password = user->password; - userDto->role = Role::ADMIN; - - auto dbResult = m_database->createUser(userDto); - if (!dbResult->isSuccess()) { - OATPP_LOGE("AuthService", "DB-Error: '%s'", - dbResult->getErrorMessage()->c_str()); - } - OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_401, "Unauthorized"); - - auto result = - dbResult->fetch>>(); - OATPP_ASSERT_HTTP(result->size() == 1, Status::CODE_401, "Unauthorized") - - auto newUserId = result[0][0]; - - auto payload = std::make_shared(); - payload->userId = newUserId; - - auto auth = AuthDto::createShared(); - auth->token = m_jwt->createToken(payload); - - return auth; -} - -oatpp::Object AuthService::signIn( - const oatpp::Object& dto) { - auto dbResult = m_database->authenticateUser(dto->userName, dto->password); - if (!dbResult->isSuccess()) { - OATPP_LOGE("AuthService", "DB-Error: '%s'", - dbResult->getErrorMessage()->c_str()); - } - OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_401, "Unauthorized") - - auto result = - dbResult->fetch>>(); - OATPP_ASSERT_HTTP(result->size() == 1, Status::CODE_401, "Unauthorized") - - auto userId = result[0][0]; - - auto payload = std::make_shared(); - payload->userId = userId; - - auto auth = AuthDto::createShared(); - auth->token = m_jwt->createToken(payload); - - return auth; -} - -oatpp::Object AuthService::deleteUserById( - const oatpp::String& userId) { - auto dbResult = m_database->deleteUserById(1); - OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, - dbResult->getErrorMessage()); - auto status = StatusDto::createShared(); - status->status = "OK"; - status->code = 200; - status->message = "User was successfully deleted"; - return status; -} \ No newline at end of file diff --git a/src/service/AuthService.hpp b/src/service/AuthService.hpp deleted file mode 100644 index 10093d3e..00000000 --- a/src/service/AuthService.hpp +++ /dev/null @@ -1,31 +0,0 @@ - -#ifndef HEAL_SERVICE_JWT_AUTHSERVICE_HPP -#define HEAL_SERVICE_JWT_AUTHSERVICE_HPP - -#include "auth/JWT.hpp" - -#include "data/AuthDto.hpp" -#include "data/PageDto.hpp" -#include "data/SignInDto.hpp" -#include "data/SignUpDto.hpp" -#include "data/StatusDto.hpp" -#include "database/UserDb.hpp" - -#include "oatpp/core/macro/component.hpp" -#include "oatpp/web/protocol/http/Http.hpp" - -class AuthService { -private: - typedef oatpp::web::protocol::http::Status Status; - -private: - OATPP_COMPONENT(std::shared_ptr, - m_database); // Inject database component - OATPP_COMPONENT(std::shared_ptr, m_jwt); // Inject JWT component -public: - oatpp::Object signUp(const oatpp::Object& dto); - oatpp::Object signIn(const oatpp::Object& dto); - oatpp::Object deleteUserById(const oatpp::String& id); -}; - -#endif // HEAL_SERVICE_JWT_AUTHSERVICE_HPP diff --git a/src/service/StoryService.cpp b/src/service/StoryService.cpp deleted file mode 100644 index 5b358c22..00000000 --- a/src/service/StoryService.cpp +++ /dev/null @@ -1,95 +0,0 @@ - -#include "StoryService.hpp" - -oatpp::Object StoryService::storyDtoFromModel( - const oatpp::Object& model) { - auto dto = StoryDto::createShared(); - dto->id = model->id; - dto->content = model->content; - return dto; -} - -oatpp::Object StoryService::storyModelFromDto( - const oatpp::String& userId, const oatpp::Object& dto) { - auto model = StoryModel::createShared(); - model->userId = userId; - model->id = dto->id; - model->content = dto->content; - return model; -} - -oatpp::Object StoryService::createStory( - const oatpp::String& userId, const oatpp::Object& dto) { - auto dbResult = m_database->createStory(storyModelFromDto(userId, dto)); - OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, - dbResult->getErrorMessage()); - auto result = dbResult->fetch>>(); - OATPP_ASSERT_HTTP(result->size() == 1, Status::CODE_500, "Unknown Error"); - return storyDtoFromModel(result[0]); -} - -oatpp::Object StoryService::updateStory( - const oatpp::String& userId, const oatpp::Object& dto) { - auto dbResult = m_database->updateStory(storyModelFromDto(userId, dto)); - OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, - dbResult->getErrorMessage()); - auto result = dbResult->fetch>>(); - OATPP_ASSERT_HTTP(result->size() == 1, Status::CODE_500, "Unknown Error"); - return storyDtoFromModel(result[0]); -} - -oatpp::Object StoryService::getStoryByUserIdAndId( - const oatpp::String& userId, const oatpp::String& id, - const oatpp::provider::ResourceHandle& connection) { - auto dbResult = m_database->getStoryByUserIdAndId(userId, id, connection); - OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, - dbResult->getErrorMessage()); - OATPP_ASSERT_HTTP(dbResult->hasMoreToFetch(), Status::CODE_404, - "User story not found"); - - auto result = dbResult->fetch>>(); - OATPP_ASSERT_HTTP(result->size() == 1, Status::CODE_500, "Unknown error"); - - return storyDtoFromModel(result[0]); -} - -oatpp::Object>> StoryService::getAllUserStories( - const oatpp::String& userId, const oatpp::UInt32& offset, - const oatpp::UInt32& limit) { - oatpp::UInt32 countToFetch = limit; - - if (limit > 10) { - countToFetch = 10; - } - - auto dbResult = m_database->getAllUserStories(userId, offset, countToFetch); - OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, - dbResult->getErrorMessage()); - - auto items = dbResult->fetch>>(); - - oatpp::Vector> stories({}); - for (auto& item : *items) { - stories->push_back(storyDtoFromModel(item)); - } - - auto page = PageDto>::createShared(); - page->offset = offset; - page->limit = countToFetch; - page->count = stories->size(); - page->items = stories; - - return page; -} - -oatpp::Object StoryService::deleteStoryByUserIdAndId( - const oatpp::String& userId, const oatpp::String& id) { - auto dbResult = m_database->deleteStoryByUserIdAndId(userId, id); - OATPP_ASSERT_HTTP(dbResult->isSuccess(), Status::CODE_500, - dbResult->getErrorMessage()); - auto status = StatusDto::createShared(); - status->status = "OK"; - status->code = 200; - status->message = "User story was successfully deleted"; - return status; -} \ No newline at end of file diff --git a/src/service/StoryService.hpp b/src/service/StoryService.hpp deleted file mode 100644 index d6c722fa..00000000 --- a/src/service/StoryService.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef HEAL_SERVICE_STORYSERVICE_HPP -#define HEAL_SERVICE_STORYSERVICE_HPP - -#include "database/StoryDb.hpp" -#include "database/model/StoryModel.hpp" - -#include "data/PageDto.hpp" -#include "data/StatusDto.hpp" -#include "data/StoryDto.hpp" - -#include "oatpp/core/macro/component.hpp" -#include "oatpp/web/protocol/http/Http.hpp" - -class StoryService { -private: - typedef oatpp::web::protocol::http::Status Status; - -private: - oatpp::Object storyDtoFromModel( - const oatpp::Object& model); - oatpp::Object storyModelFromDto( - const oatpp::String& userId, const oatpp::Object& dto); - -private: - OATPP_COMPONENT(std::shared_ptr, - m_database); // Inject database component -public: - oatpp::Object createStory(const oatpp::String& userId, - const oatpp::Object& dto); - oatpp::Object updateStory(const oatpp::String& userId, - const oatpp::Object& dto); - oatpp::Object getStoryByUserIdAndId( - const oatpp::String& userId, const oatpp::String& id, - const oatpp::provider::ResourceHandle& - connection = nullptr); - oatpp::Object>> getAllUserStories( - const oatpp::String& userId, const oatpp::UInt32& offset, - const oatpp::UInt32& limit); - oatpp::Object deleteStoryByUserIdAndId( - const oatpp::String& userId, const oatpp::String& id); -}; - -#endif // HEAL_SERVICE_STORYSERVICE_HPP diff --git a/src/task/generator.cpp b/src/task/generator.cpp index 87dd95d4..20471b09 100644 --- a/src/task/generator.cpp +++ b/src/task/generator.cpp @@ -24,7 +24,7 @@ Description: Task Generator namespace Lithium { TaskGenerator::TaskGenerator() { - m_DeviceManager = GetPtr("lithium.device"); + m_DeviceManager = GetWeakPtr("lithium.device"); } std::shared_ptr TaskGenerator::createShared() { diff --git a/src/task/generator.hpp b/src/task/generator.hpp index 470d56aa..de350dcc 100644 --- a/src/task/generator.hpp +++ b/src/task/generator.hpp @@ -80,7 +80,7 @@ class TaskGenerator { std::mutex m_Mutex; - std::shared_ptr m_DeviceManager; + std::weak_ptr m_DeviceManager; }; } // namespace Lithium diff --git a/src/task/manager.cpp b/src/task/manager.cpp index 3d4bfd0a..82fd766d 100644 --- a/src/task/manager.cpp +++ b/src/task/manager.cpp @@ -20,12 +20,12 @@ Description: Task Manager namespace Lithium { TaskManager::TaskManager() : m_StopFlag(false) { // Load Task Component from Global Ptr Manager - m_TaskContainer = GetPtr("lithium.task.contianer"); - m_TaskPool = GetPtr("lithium.task.pool"); - m_TaskList = GetPtr("lithium.task.list"); - m_TaskGenerator = GetPtr("lithium.task.generator"); - m_TickScheduler = GetPtr("lithium.task.tick"); - m_TaskLoader = GetPtr("ltihium.task.loader"); + m_TaskContainer = GetWeakPtr("lithium.task.contianer"); + m_TaskPool = GetWeakPtr("lithium.task.pool"); + m_TaskList = GetWeakPtr("lithium.task.list"); + m_TaskGenerator = GetWeakPtr("lithium.task.generator"); + m_TickScheduler = GetWeakPtr("lithium.task.tick"); + m_TaskLoader = GetWeakPtr("ltihium.task.loader"); } TaskManager::~TaskManager() { saveTasksToJson(); } @@ -36,13 +36,13 @@ std::shared_ptr TaskManager::createShared() { bool TaskManager::addTask(const std::string name, const json ¶ms) { // Task Check Needed - m_TaskList->addOrUpdateTask(name, params); + m_TaskList.lock()->addOrUpdateTask(name, params); return true; } bool TaskManager::insertTask(const int &position, const std::string &name, const json ¶ms) { - if (m_TaskList->insertTask(name, params, position)) { + if (m_TaskList.lock()->insertTask(name, params, position)) { DLOG_F(INFO, "Insert {} task to {}", name, position); } else { } @@ -50,31 +50,31 @@ bool TaskManager::insertTask(const int &position, const std::string &name, } bool TaskManager::modifyTask(const std::string &name, const json ¶ms) { - m_TaskList->addOrUpdateTask(name, params); + m_TaskList.lock()->addOrUpdateTask(name, params); return true; } bool TaskManager::deleteTask(const std::string &name) { - m_TaskList->removeTask(name); + m_TaskList.lock()->removeTask(name); return true; } bool TaskManager::executeAllTasks() { - for (const auto &[name, params] : m_TaskList->getTasks()) { + for (const auto &[name, params] : m_TaskList.lock()->getTasks()) { DLOG_F(INFO, "Run task {}", name); std::string task_type = params["type"].get(); - if (auto task = m_TaskContainer->getTask(task_type); task.has_value()) { + if (auto task = m_TaskContainer.lock()->getTask(task_type); task.has_value()) { json t_params = params["params"]; - auto handle = m_TickScheduler->scheduleTask( + auto handle = m_TickScheduler.lock()->scheduleTask( 1, false, 1, std::chrono::milliseconds(0), std::nullopt, std::nullopt, std::nullopt, task.value()->m_function, t_params); if (params.contains("callbacks")) { std::vector callbacks = params["callbacks"]; for (auto callback : callbacks) { - auto c_task = m_TaskContainer->getTask(task_type); + auto c_task = m_TaskContainer.lock()->getTask(task_type); if (c_task.has_value()) { - m_TickScheduler->setCompletionCallback( + m_TickScheduler.lock()->setCompletionCallback( handle, [c_task]() { c_task.value()->m_function({}); }); } @@ -89,9 +89,9 @@ bool TaskManager::executeAllTasks() { } else { std::string timer_name = timer["name"]; int tick = timer["delay"]; - if (auto tt_task = m_TaskContainer->getTask(name); + if (auto tt_task = m_TaskContainer.lock()->getTask(name); tt_task.has_value()) { - m_TickScheduler->scheduleTask( + m_TickScheduler.lock()->scheduleTask( tick, false, 1, std::chrono::milliseconds(0), std::nullopt, std::nullopt, std::nullopt, tt_task.value()->m_function, timer["params"]); diff --git a/src/task/manager.hpp b/src/task/manager.hpp index 4369f928..49ecb132 100644 --- a/src/task/manager.hpp +++ b/src/task/manager.hpp @@ -145,12 +145,12 @@ class TaskManager { void loadBuiltinTask(); private: - std::shared_ptr m_TaskContainer; - std::shared_ptr m_TaskGenerator; - std::shared_ptr m_TaskList; - std::shared_ptr m_TaskLoader; - std::shared_ptr m_TaskPool; - std::shared_ptr m_TickScheduler; + std::weak_ptr m_TaskContainer; + std::weak_ptr m_TaskGenerator; + std::weak_ptr m_TaskList; + std::weak_ptr m_TaskLoader; + std::weak_ptr m_TaskPool; + std::weak_ptr m_TickScheduler; std::unique_ptr m_Timer; diff --git a/src/task/tick.cpp b/src/task/tick.cpp index d2bdc49a..1e6b87fb 100644 --- a/src/task/tick.cpp +++ b/src/task/tick.cpp @@ -18,7 +18,6 @@ Description: Tick Sheduler, just like Minecraft's #include "atom/server/global_ptr.hpp" #include "atom/utils/stopwatcher.hpp" - namespace Lithium { TickScheduler::TickScheduler(size_t threads) : currentTick(0), stop(false), tickLength(100) { @@ -27,7 +26,7 @@ TickScheduler::TickScheduler(size_t threads) #else schedulerThread = std::thread([this] { this->taskSchedulerLoop(); }); #endif - pool = GetPtr("lithium.task.pool"); + pool = GetWeakPtr("lithium.task.pool"); stopwatch = std::make_unique(); } @@ -121,7 +120,7 @@ void TickScheduler::triggerTasks() { while (it != tasks.end()) { auto task = *it; if (task->tick <= currentTick.load() && allDependenciesMet(task)) { - pool->enqueue([task]() { + pool.lock()->enqueue([task]() { task->func(); task->completed.store(true); if (task->onCompletion) { @@ -139,7 +138,7 @@ void TickScheduler::triggerTasks() { void TickScheduler::taskSchedulerLoop() { while (!stop.load()) { // 记录每个Tick需要的时间 - DLOG_F(INFO, "Tick %llu", currentTick.load()); + DLOG_F(INFO, "Tick {}", currentTick.load()); stopwatch->start(); if (manualMode.load()) { std::this_thread::sleep_for(std::chrono::milliseconds( @@ -163,7 +162,7 @@ void TickScheduler::taskSchedulerLoop() { auto task = *it; if (task->tick <= currentTick.load() && allDependenciesMet(task)) { - pool->enqueue([this, task]() { + pool.lock()->enqueue([this, task]() { task->isRunning.store(true); task->func(); task->completed.store(true); @@ -184,7 +183,7 @@ void TickScheduler::taskSchedulerLoop() { std::chrono::milliseconds(tickLength.load())); // Simulate a tick stopwatch->stop(); stopwatch->reset(); - DLOG_F(INFO, "Tick %llu took %f ms", currentTick.load(), + DLOG_F(INFO, "Tick {} took {} ms", currentTick.load(), stopwatch->elapsedMilliseconds()); currentTick++; } diff --git a/src/task/tick.hpp b/src/task/tick.hpp index 6702ffb6..6ed33da5 100644 --- a/src/task/tick.hpp +++ b/src/task/tick.hpp @@ -292,7 +292,7 @@ class TickScheduler { void triggerTasks(); private: - std::shared_ptr pool; // 线程池对象 + std::weak_ptr pool; // 线程池对象 std::vector> tasks; // 所有待执行的任务 std::mutex tasksMutex; // 任务队列的互斥锁 std::condition_variable cv; // 条件变量,用于暂停和恢复任务调度器的执行 diff --git a/src/utils/constant.hpp b/src/utils/constant.hpp new file mode 100644 index 00000000..5acda270 --- /dev/null +++ b/src/utils/constant.hpp @@ -0,0 +1,110 @@ +/* + * constants.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-3-16 + +Description: Constants for Lithium + +**************************************************/ + +#ifndef LITHIUM_UTILS_CONSTANTS_HPP +#define LITHIUM_UTILS_CONSTANTS_HPP + +#include +#include + +class constants { +public: +#ifdef _WIN32 +#if defined(__MINGW32__) || defined(__MINGW64__) + static constexpr const char* PATH_SEPARATOR = "/"; +#else + static constexpr const char* PATH_SEPARATOR = "\\"; +#endif + static constexpr const char* LIB_EXTENSION = ".dll"; +#elif defined(__APPLE__) + static constexpr const char* PATH_SEPARATOR = "/"; + static constexpr const char* LIB_EXTENSION = ".dylib"; +#else + static constexpr const char* PATH_SEPARATOR = "/"; + static constexpr const char* LIB_EXTENSION = ".so"; +#endif + + // Package info + static constexpr const char* PACKAGE_NAME = "package.json"; + static constexpr const char* PACKAGE_NAME_SHORT = "lithium"; + static constexpr const char* PACKAGE_VERSION = "0.1.0"; + + // Module info +#ifdef _WIN32 +#if defined(__MINGW32__) || defined(__MINGW64__) + static constexpr const char* MODULE_FOLDER = "./modules"; +#else + static constexpr const char* MODULE_FOLDER = ".\\modules"; +#endif +#else + static constexpr const char* MODULE_FOLDER = "./modules"; +#endif + +#ifdef _WIN32 + static constexpr const char* COMPILER = "cl.exe"; +#elif __APPLE__ + static constexpr const char* COMPILER = "clang++"; +#else + static constexpr const char* COMPILER = "g++"; +#endif + +#ifdef _WIN32 + static std::vector COMMON_COMPILERS; + static std::vector COMPILER_PATHS; +#elif __APPLE__ + static std::vector COMMON_COMPILERS; + static std::vector COMPILER_PATHS; +#elif __linux__ + static std::vector COMMON_COMPILERS; + static std::vector COMPILER_PATHS; +#endif + + // Env info + static constexpr const char* ENV_VAR_MODULE_PATH = "LITHIUM_MODULE_PATH"; + + // Inside Module Identifiers + + static constexpr const char* LITHIUM_MODULE_LOADER = "lithium.addon.loader"; + static constexpr const char* LITHIUM_ADDON_MANAGER = "lithium.addon.addon"; + static constexpr const char* LITHIUM_UTILS_ENV = "lithium.utils.env"; + + static std::vector LITHIUM_RESOURCES; + static std::vector LITHIUM_RESOURCES_SHA256; +}; + +#ifdef _WIN32 +std::vector constants::COMMON_COMPILERS = {"cl.exe", "g++.exe", + "clang++.exe"}; +std::vector constants::COMPILER_PATHS = { + "C:\\Program Files (x86)\\Microsoft Visual " + "Studio\\2019\\Community\\VC\\Tools\\MSVC\\14.29." + "30133\\bin\\Hostx64\\x64", + "C:\\Program Files\\Microsoft Visual " + "Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.35." + "32215\\bin\\Hostx64\\x64", + "C:\\msys64\\mingw64\\bin", "C:\\MinGW\\bin", + "C:\\Program Files\\LLVM\\bin"}; +#elif __APPLE__ +std::vector constants::COMMON_COMPILERS = {"clang++", "g++"}; +std::vector constants::COMPILER_PATHS = { + "/usr/bin", "/usr/local/bin", "/opt/local/bin"}; +#elif __linux__ +std::vector constants::COMMON_COMPILERS = {"g++", "clang++"}; +std::vector constants::COMPILER_PATHS = {"/usr/bin", + "/usr/local/bin"}; +#endif + + + +#endif // LITHIUM_UTILS_CONSTANTS_HPP diff --git a/src/utils/resource.hpp b/src/utils/resource.hpp new file mode 100644 index 00000000..e9595e3a --- /dev/null +++ b/src/utils/resource.hpp @@ -0,0 +1,41 @@ +/* + * resource.hpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2024-3-16 + +Description: Resource List for Lithium + +**************************************************/ + +#ifndef LITHIUM_UTILS_RESOURCE_HPP +#define LITHIUM_UTILS_RESOURCE_HPP + +#include +#include + +class resource { +public: + static std::unordered_map> + LITHIUM_RESOURCES; + + static constexpr const char* LITHIUM_RESOURCE_SERVER = + "https://github/ElementAstro/LithiumPackage"; +}; + +std::unordered_map> + resource::LITHIUM_RESOURCES = {{ +#ifdef _WIN32 + "lithium_server.exe" +#else + "lithium_server" +#endif + , + {"", false}}}; + +#endif \ No newline at end of file diff --git a/tests/all.cpp b/tests/all.cpp new file mode 100644 index 00000000..7d174e04 --- /dev/null +++ b/tests/all.cpp @@ -0,0 +1,7 @@ +#include "gtest/gtest.h" + +int main(int argc, char **argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/atom/algorithm/base.cpp b/tests/atom/algorithm/base.cpp new file mode 100644 index 00000000..97d0e47a --- /dev/null +++ b/tests/atom/algorithm/base.cpp @@ -0,0 +1,186 @@ +#include +#include +#include +#include "atom/algorithm/base.hpp" + + +using namespace Atom::Algorithm; + +class Base16Test : public ::testing::Test { +protected: + void SetUp() override { + // Set up test data + testData = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, + 0x57, 0x6f, 0x72, 0x6c, 0x64}; + testEncodedData = "48656c6c6f20576f726c64"; + } + + std::vector testData; + std::string testEncodedData; +}; + +TEST_F(Base16Test, EncodeBase16) { + // Encode test data + std::string encodedData = encodeBase16(testData); + + // Check the encoded data + EXPECT_EQ(encodedData, testEncodedData); +} + +TEST_F(Base16Test, DecodeBase16) { + // Decode test data + std::vector decodedData = decodeBase16(testEncodedData); + + // Check the decoded data + EXPECT_EQ(decodedData, testData); +} + +class Base32Test : public ::testing::Test { +protected: + void SetUp() override { + // Set up test data + testData = "Hello World!"; + testEncodedData = "JBSWY3DPEHPK3PXP"; + } + + std::string testData; + std::string testEncodedData; +}; + +TEST_F(Base32Test, EncodeBase32) { + // Encode test data + std::string encodedData = encodeBase32(testData); + + // Check the encoded data + EXPECT_EQ(encodedData, testEncodedData); +} + +TEST_F(Base32Test, DecodeBase32) { + // Decode test data + std::string decodedData = decodeBase32(testEncodedData); + + // Check the decoded data + EXPECT_EQ(decodedData, testData); +} + +class Base64Test : public ::testing::Test { +protected: + void SetUp() override { + // Set up test data + testData = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, + 0x57, 0x6f, 0x72, 0x6c, 0x64}; + testEncodedData = "SGVsbG8gV29ybGQ="; + } + + std::vector testData; + std::string testEncodedData; +}; + +TEST_F(Base64Test, Base64Encode) { + // Encode test data + std::string encodedData = base64Encode(testData); + + // Check the encoded data + EXPECT_EQ(encodedData, testEncodedData); +} + +TEST_F(Base64Test, Base64Decode) { + // Decode test data + std::vector decodedData = base64Decode(testEncodedData); + + // Check the decoded data + EXPECT_EQ(decodedData, testData); +} + +class Base64EnhanceTest : public ::testing::Test { +protected: + void SetUp() override { + // Set up test data + testData = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, + 0x57, 0x6f, 0x72, 0x6c, 0x64}; + testEncodedData = "SGVsbG8gV29ybGQ="; + } + + std::vector testData; + std::string testEncodedData; +}; + +TEST_F(Base64EnhanceTest, Base64EncodeEnhance) { + // Encode test data + std::string encodedData = base64EncodeEnhance(testData); + + // Check the encoded data + EXPECT_EQ(encodedData, testEncodedData); +} + +#ifndef __MAIN__ +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif + + +TEST_F(Base64EnhanceTest, Base64DecodeEnhance) { + // Decode test data + std::vector decodedData = base64DecodeEnhance(testEncodedData); + + // Check the decoded data + EXPECT_EQ(decodedData, testData); +} + +class Base85Test : public ::testing::Test { +protected: + void SetUp() override { + // Set up test data + testData = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, + 0x57, 0x6f, 0x72, 0x6c, 0x64}; + testEncodedData = "Hello World!"; + } + + std::vector testData; + std::string testEncodedData; +}; + +TEST_F(Base85Test, EncodeBase85) { + // Encode test data + std::string encodedData = encodeBase85(testData); + + // Check the encoded data + EXPECT_EQ(encodedData, testEncodedData); +} + +TEST_F(Base85Test, DecodeBase85) { + // Decode test data + std::vector decodedData = decodeBase85(testEncodedData); + + // Check the decoded data + EXPECT_EQ(decodedData, testData); +} + +class Base128Test : public ::testing::Test { +protected: + void SetUp() override { + // Set up test data + testData = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, + 0x57, 0x6f, 0x72, 0x6c, 0x64}; + } + + std::vector testData; +}; + +TEST_F(Base128Test, EncodeBase128) { + // Encode test data + std::vector encodedData = encodeBase128(testData); + + // Check the encoded data + EXPECT_EQ(encodedData, testData); +} + +TEST_F(Base128Test, DecodeBase128) { + // Decode test data + std::vector decodedData = decodeBase128(testData); + + // Check the decoded data + EXPECT_EQ(decodedData, testData); +} diff --git a/tests/atom/algorithm/base32.cpp b/tests/atom/algorithm/base32.cpp deleted file mode 100644 index 5c245479..00000000 --- a/tests/atom/algorithm/base32.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include -#include "atom/algorithm/base32.hpp" - -TEST(Base32Test, EncodeDecodeTest) -{ - std::string input = "Hello World!"; - std::string encoded = Atom::Algorithm::encodeBase32(input); - std::string decoded = Atom::Algorithm::decodeBase32(encoded); - - EXPECT_EQ(input, decoded); -} - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/atom/algorithm/base64.cpp b/tests/atom/algorithm/base64.cpp deleted file mode 100644 index 643922c3..00000000 --- a/tests/atom/algorithm/base64.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include "atom/algorithm/base64.hpp" - -TEST(Base64Test, EncodeDecodeTest) -{ - std::vector data = {'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!'}; - - // Test base64Encode and base64Decode - std::string encoded = base64Encode(data); - std::vector decoded = base64Decode(encoded); - - EXPECT_EQ(data, decoded); - - // Test base64EncodeEnhance and base64DecodeEnhance - std::vector dataEnhance = {'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g'}; - std::string encodedEnhance = base64EncodeEnhance(dataEnhance); - std::vector decodedEnhance = base64DecodeEnhance(encodedEnhance); - - EXPECT_EQ(dataEnhance, decodedEnhance); -} - -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/atom/algorithm/hash.cpp b/tests/atom/algorithm/hash.cpp new file mode 100644 index 00000000..342d8598 --- /dev/null +++ b/tests/atom/algorithm/hash.cpp @@ -0,0 +1,62 @@ +#include "atom/algorithm/hash.hpp" +#include + +using namespace Atom::Algorithm; + +TEST(ComputeHashTest, TestInt) { + int value = 123; + std::size_t expected = std::hash{}(value); + std::size_t result = computeHash(value); + EXPECT_EQ(result, expected); +} + +TEST(ComputeHashTest, TestString) { + std::string value = "hello"; + std::size_t expected = std::hash{}(value); + std::size_t result = computeHash(value); + EXPECT_EQ(result, expected); +} + +TEST(ComputeHashTest, TestVector) { + std::vector values = {1, 2, 3}; + std::size_t expected = 0; + for (const auto& value : values) { + expected ^= std::hash{}(value) + 0x9e3779b9 + (expected << 6) + + (expected >> 2); + } + std::size_t result = computeHash(values); + EXPECT_EQ(result, expected); +} + +TEST(ComputeHashTest, TestTuple) { + std::tuple tuple = std::make_tuple(123, "hello"); + std::size_t expected = 0; + apply( + [&expected](const int& value1, const std::string& value2) { + expected ^= computeHash(value1) + 0x9e3779b9 + + (expected << 6) + (expected >> 2); + expected ^= computeHash(value2) + 0x9e3779b9 + + (expected << 6) + (expected >> 2); + }, + tuple); + std::size_t result = computeHash(tuple); + EXPECT_EQ(result, expected); +} + +TEST(ComputeHashTest, TestArray) { + std::array array = {{1, 2, 3}}; + std::size_t expected = 0; + for (const auto& value : array) { + expected ^= std::hash{}(value) + 0x9e3779b9 + (expected << 6) + + (expected >> 2); + } + std::size_t result = computeHash(array); + EXPECT_EQ(result, expected); +} + +#ifndef __MAIN__ +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif diff --git a/tests/atom/experiment/memory.cpp b/tests/atom/experiment/memory.cpp new file mode 100644 index 00000000..7649f277 --- /dev/null +++ b/tests/atom/experiment/memory.cpp @@ -0,0 +1,80 @@ +#include +#include "atom/experiment/memory.hpp" + +struct TestType { + int value; +}; + +using TestPool = MemoryPool; + +class MemoryPoolTest : public ::testing::Test { +protected: + TestPool pool; +}; + +TEST_F(MemoryPoolTest, Allocate) { + auto p = pool.allocate(1); + EXPECT_NE(p, nullptr); + pool.deallocate(p, 1); +} + +TEST_F(MemoryPoolTest, AllocateMultiple) { + auto p1 = pool.allocate(1); + auto p2 = pool.allocate(2); + auto p3 = pool.allocate(3); + EXPECT_NE(p1, nullptr); + EXPECT_NE(p2, nullptr); + EXPECT_NE(p3, nullptr); + pool.deallocate(p1, 1); + pool.deallocate(p2, 2); + pool.deallocate(p3, 3); +} + +TEST_F(MemoryPoolTest, DeallocateOrder) { + auto p1 = pool.allocate(1); + auto p2 = pool.allocate(2); + pool.deallocate(p2, 2); + pool.deallocate(p1, 1); +} + +TEST_F(MemoryPoolTest, ReuseChunk) { + auto p1 = pool.allocate(1); + pool.deallocate(p1, 1); + auto p2 = pool.allocate(1); + EXPECT_EQ(p1, p2); + pool.deallocate(p2, 1); +} + +TEST_F(MemoryPoolTest, AllocateLarge) { + auto p = pool.allocate(pool.block_size() / sizeof(TestType) + 1); + EXPECT_NE(p, nullptr); + pool.deallocate(p, pool.block_size() / sizeof(TestType) + 1); +} + +TEST_F(MemoryPoolTest, AllocateHuge) { + EXPECT_THROW(pool.allocate(std::numeric_limits::max()), std::bad_alloc); +} + +TEST_F(MemoryPoolTest, Compare) { + TestPool pool2; + EXPECT_TRUE(pool.is_equal(pool)); + EXPECT_FALSE(pool.is_equal(pool2)); +} + +TEST_F(MemoryPoolTest, AsMemoryResource) { + std::pmr::vector vec{&pool}; + for (int i = 0; i < 100; ++i) { + vec.emplace_back(TestType{i}); + } + EXPECT_EQ(vec.size(), 100); + for (int i = 0; i < 100; ++i) { + EXPECT_EQ(vec[i].value, i); + } +} + +#ifndef __MAIN__ +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif diff --git a/tests/atom/experiment/object.cpp b/tests/atom/experiment/object.cpp new file mode 100644 index 00000000..437e11df --- /dev/null +++ b/tests/atom/experiment/object.cpp @@ -0,0 +1,76 @@ +#include "atom/experiment/object.hpp" +#include +#include + +class TestObject { +public: + void reset() { value_ = 0; } + int value_ = 0; +}; + +class ObjectPoolTest : public ::testing::Test { +protected: + ObjectPool pool_{10}; +}; + +TEST_F(ObjectPoolTest, Acquire) { + auto obj = pool_.acquire(); + EXPECT_TRUE(obj != nullptr); + EXPECT_EQ(obj->value_, 0); +} + +TEST_F(ObjectPoolTest, Release) { + auto obj = pool_.acquire(); + obj->value_ = 42; + pool_.release(std::move(obj)); + obj = pool_.acquire(); + EXPECT_EQ(obj->value_, 0); +} + +/* +TEST_F(ObjectPoolTest, MaxSize) { + std::vector> objects; + for (int i = 0; i < 10; ++i) { + objects.push_back(pool_.acquire()); + } + EXPECT_EQ(pool_.available(), 0); + auto obj = pool_.acquire(); + EXPECT_TRUE(obj != nullptr); + EXPECT_EQ(pool_.size(), 10); +} +*/ + +TEST_F(ObjectPoolTest, ReleaseMoreThanAcquire) { + auto obj1 = pool_.acquire(); + auto obj2 = pool_.acquire(); + pool_.release(std::move(obj1)); + pool_.release(std::move(obj2)); + auto obj3 = std::make_shared(); + pool_.release(std::move(obj3)); + EXPECT_EQ(pool_.size(), 2); + EXPECT_EQ(pool_.available(), 10); +} + +TEST_F(ObjectPoolTest, ConcurrentAccess) { + std::vector threads; + for (int i = 0; i < 10; ++i) { + threads.emplace_back([this]() { + auto obj = pool_.acquire(); + obj->value_ = 42; + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + pool_.release(std::move(obj)); + }); + } + for (auto &t : threads) { + t.join(); + } + EXPECT_EQ(pool_.size(), 10); + EXPECT_EQ(pool_.available(), 10); + auto obj = pool_.acquire(); + EXPECT_EQ(obj->value_, 0); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/tests/atom/huffman.cpp b/tests/atom/huffman.cpp deleted file mode 100644 index d1ca2685..00000000 --- a/tests/atom/huffman.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include -#include - -class HuffmanTreeTest : public ::testing::Test -{ -protected: - void SetUp() override - { - // Set up test data - frequencies = std::map{ - {'a', 5}, - {'b', 3}, - {'c', 7}, - {'d', 2}}; - expectedHuffmanCodes = std::map{ - {'a', "111"}, - {'b', "110"}, - {'c', "01"}, - {'d', "00"}}; - expectedCompressedText = "111110100100"; - expectedDecompressedText = "abcdd"; - } - - std::map frequencies; - std::map expectedHuffmanCodes; - std::string expectedCompressedText; - std::string expectedDecompressedText; -}; - -TEST_F(HuffmanTreeTest, CreateHuffmanTree) -{ - HuffmanNode *root = createHuffmanTree(frequencies); - - // Verify root node - EXPECT_EQ(root->data, '$'); - EXPECT_EQ(root->frequency, 17); - - // Verify left and right nodes - HuffmanNode *leftNode = root->left; - HuffmanNode *rightNode = root->right; - EXPECT_NE(leftNode, nullptr); - EXPECT_NE(rightNode, nullptr); - EXPECT_EQ(leftNode->data, 'c'); - EXPECT_EQ(leftNode->frequency, 7); - EXPECT_EQ(rightNode->data, 'a'); - EXPECT_EQ(rightNode->frequency, 5); -} - -TEST_F(HuffmanTreeTest, GenerateHuffmanCodes) -{ - HuffmanNode *root = createHuffmanTree(frequencies); - std::map huffmanCodes; - - generateHuffmanCodes(root, "", huffmanCodes); - - // Verify generated huffman codes - for (auto &pair : expectedHuffmanCodes) - { - EXPECT_EQ(huffmanCodes[pair.first], pair.second); - } -} - -TEST_F(HuffmanTreeTest, CompressText) -{ - HuffmanNode *root = createHuffmanTree(frequencies); - std::string text = "abcdd"; - - std::string compressedText = compressText(text, expectedHuffmanCodes); - - // Verify compressed text - EXPECT_EQ(compressedText, expectedCompressedText); -} - -TEST_F(HuffmanTreeTest, DecompressText) -{ - HuffmanNode *root = createHuffmanTree(frequencies); - std::string compressedText = expectedCompressedText; - - std::string decompressedText = decompressText(compressedText, root); - - // Verify decompressed text - EXPECT_EQ(decompressedText, expectedDecompressedText); -} \ No newline at end of file diff --git a/tests/config/configor.cpp b/tests/config/configor.cpp new file mode 100644 index 00000000..3ff7bec3 --- /dev/null +++ b/tests/config/configor.cpp @@ -0,0 +1,125 @@ +#include "config/configor.cpp" +#include + + +class ConfigManagerTest : public ::testing::Test { +protected: + void SetUp() override { + // 初始化 ConfigManager 实例 + config_manager_ = ConfigManager::createShared(); + } + + void TearDown() override { + // 清理 ConfigManager 实例 + config_manager_.reset(); + } + + std::shared_ptr config_manager_; +}; + +TEST_F(ConfigManagerTest, SetValueAndGet) { + // 设置配置值 + config_manager_->setValue("test/key1", "value1"); + config_manager_->setValue("test/key2", 42); + + // 获取配置值并验证 + auto value1 = config_manager_->getValue("test/key1"); + EXPECT_TRUE(value1.has_value()); + EXPECT_EQ(value1.value(), "value1"); + + auto value2 = config_manager_->getValue("test/key2"); + EXPECT_TRUE(value2.has_value()); + EXPECT_EQ(value2.value(), 42); +} + +TEST_F(ConfigManagerTest, DeleteValue) { + // 设置配置值 + config_manager_->setValue("test/key1", "value1"); + + // 删除配置值并验证 + EXPECT_TRUE(config_manager_->deleteValue("test/key1")); + EXPECT_FALSE(config_manager_->hasValue("test/key1")); +} + +TEST_F(ConfigManagerTest, SaveAndLoadFromFile) { + // 设置配置值 + config_manager_->setValue("test/key1", "value1"); + config_manager_->setValue("test/key2", 42); + + // 保存配置到文件 + std::string file_path = "test_config.json"; + EXPECT_TRUE(config_manager_->saveToFile(file_path)); + + // 创建新的 ConfigManager 实例并从文件加载配置 + auto new_config_manager = ConfigManager::createShared(); + EXPECT_TRUE(new_config_manager->loadFromFile(file_path)); + + // 验证新的实例中是否包含相同的配置值 + EXPECT_TRUE(new_config_manager->hasValue("test/key1")); + EXPECT_TRUE(new_config_manager->hasValue("test/key2")); + EXPECT_EQ(new_config_manager->getValue("test/key1").value(), "value1"); + EXPECT_EQ(new_config_manager->getValue("test/key2").value(), 42); +} + +TEST_F(ConfigManagerTest, TidyConfig) { + // 设置配置值 + config_manager_->setValue("test/key1", "value1"); + config_manager_->setValue("test/key2", 42); + + // 设置包含子对象的配置值 + config_manager_->setValue("test/sub/key3", "value3"); + + // 整理配置 + config_manager_->tidyConfig(); + + // 验证整理后的配置 + EXPECT_TRUE(config_manager_->hasValue("test")); + EXPECT_TRUE(config_manager_->hasValue("test/sub")); + EXPECT_TRUE(config_manager_->hasValue("test/key1")); + EXPECT_TRUE(config_manager_->hasValue("test/key2")); + EXPECT_TRUE(config_manager_->hasValue("test/sub/key3")); +} + +// 测试异常情况:尝试获取不存在的配置键时返回空的 std::optional +TEST_F(ConfigManagerTest, GetValueNonExistentKey) { + auto value = config_manager_->getValue("non_existent_key"); + EXPECT_FALSE(value.has_value()); +} + +// 测试异常情况:尝试删除不存在的配置键时返回 false +TEST_F(ConfigManagerTest, DeleteNonExistentKey) { + EXPECT_FALSE(config_manager_->deleteValue("non_existent_key")); +} + +// 测试异常情况:尝试从无效路径加载配置文件时返回 false +TEST_F(ConfigManagerTest, LoadFromInvalidFile) { + EXPECT_FALSE(config_manager_->loadFromFile("invalid_file_path.json")); +} + +// 测试边界条件:当配置文件为空时加载成功但不包含任何配置项 +TEST_F(ConfigManagerTest, LoadEmptyConfigFile) { + // 创建一个空的配置文件 + std::string file_path = "empty_config.json"; + std::ofstream ofs(file_path); + ofs.close(); + + // 加载空的配置文件 + EXPECT_FALSE(config_manager_->loadFromFile(file_path)); + + // 验证配置文件不包含任何配置项 + EXPECT_FALSE(config_manager_->hasValue("a")); +} + +// 测试边界条件:在没有配置键值对的情况下保存配置文件 +TEST_F(ConfigManagerTest, SaveEmptyConfigToFile) { + // 保存空的配置到文件 + std::string file_path = "empty_config.json"; + config_manager_->clearConfig(); + EXPECT_TRUE(config_manager_->saveToFile(file_path)); + + // 加载保存的配置文件 + EXPECT_FALSE(config_manager_->loadFromFile(file_path)); + + // 验证配置文件不包含任何配置项 + EXPECT_FALSE(config_manager_->hasValue("a")); +} \ No newline at end of file diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 4bdc9fe3..81210ef1 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -24,3 +24,6 @@ target_link_libraries(csv2json loguru) add_executable(tcp_proxy tcp_proxy.cpp) target_link_libraries(tcp_proxy loguru) +if(WIN32) +target_link_libraries(tcp_proxy ws2_32) +endif() diff --git a/tools/csv2json.cpp b/tools/csv2json.cpp index 35e66939..98c60f19 100644 --- a/tools/csv2json.cpp +++ b/tools/csv2json.cpp @@ -2,17 +2,6 @@ * csv2json.cpp * * Copyright (C) 2023-2024 Max Qian - * - * 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 3 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, see . */ /************************************************* diff --git a/tools/gui/sqmm.cpp b/tools/gui/sqmm.cpp new file mode 100644 index 00000000..550e22c8 --- /dev/null +++ b/tools/gui/sqmm.cpp @@ -0,0 +1,248 @@ +/* + * sqmm.cpp + * + * Copyright (C) 2023-2024 Max Qian + */ + +/************************************************* + +Date: 2023-7-29 + +Description: A simple GUI for SQMM calculation + +**************************************************/ + +#include + +using namespace cycfi::elements; + +#define M_PI 3.14159265358979323846 + +// Main window background color +auto constexpr bkd_color = rgba(35, 35, 37, 255); +auto background = box(bkd_color); + +auto box = draw([](context const &ctx) { + auto &c = ctx.canvas; + + c.begin_path(); + c.add_round_rect(ctx.bounds, 4); + c.fill_style(colors::gold.opacity(0.8)); + c.fill(); +}); + +auto check_number(std::string_view text) { + try { + int value = std::stoi(std::string(text)); + if (value > 0) { + return true; + } + } catch (const std::exception &e) { + } + return false; +} + +auto my_label(const std::string &text) { + return margin_right( + 10, + label(text) + .text_align(canvas::right) + .font( + font_descr{"文泉驿微米黑, \"WenQuanYi Micro Hei\""}.semi_bold()) + .font_color(colors::antique_white) + .font_size(18)); +} + +auto result_dialog_content(double A20, double B20, double a0) { + return hsize( + 300, simple_heading(margin({10, 10, 10, 10}, + vtile(htile(my_label("天空sqm:"), + my_label(std::to_string(A20)), + my_label("(mag/arcsec²)")), + htile(my_label("天空亮度:"), + my_label(std::to_string(B20)), + my_label("(nit)")), + htile(my_label("光污染等级:"), + my_label(std::to_string(a0))))), + "Result", 1.1)); +} + +auto make_result_dialog(view &_view, double A20, double B20, double a) { + auto &&on_ok = []() {}; + + auto &&on_cancel = []() { + // Do something on cancel + }; + + auto dialog = + dialog2(_view, result_dialog_content(A20, B20, a), on_ok, on_cancel); + + return dialog; +} + +auto make_app(view &view_) { + static float const grid[] = {0.32, 1.0}; + + auto my_input = [=](auto caption, auto input) { + return margin_bottom(10, hgrid(grid, my_label(caption), input)); + }; + + // This is an example on how to add an on_text callback and showing an + // error message if input validation fails: + + auto error_number_input = + message_box1(view_, "Invalid Input", icons::attention, []() {}); + + // Shared flag that tells us if we got the money! + auto got_the_money = std::make_shared(false); + + auto screen_light = input_box("must > 0"); + auto screen_exposure = input_box("must > 0"); + auto sky_exposure = input_box("must > 0"); + auto image_light = input_box("must > 0"); + auto light = input_box("must > 0"); + + screen_light.second->on_end_focus = + [input = screen_light.second.get(), &view_, + error_number_input](std::string_view text) -> bool { + if (text == "") [[likely]] { + return true; + } + if (!check_number(text)) { + open_popup(error_number_input, view_); + } + return true; + }; + + screen_exposure.second->on_end_focus = + [input = screen_exposure.second.get(), &view_, + error_number_input](std::string_view text) -> bool { + if (text == "") [[likely]] { + return true; + } + if (!check_number(text)) { + open_popup(error_number_input, view_); + } + return true; + }; + + sky_exposure.second->on_end_focus = + [input = sky_exposure.second.get(), &view_, + error_number_input](std::string_view text) -> bool { + if (text == "") [[likely]] { + return true; + } + if (!check_number(text)) { + open_popup(error_number_input, view_); + } + return true; + }; + + image_light.second->on_end_focus = + [input = image_light.second.get(), &view_, + error_number_input](std::string_view text) -> bool { + if (text == "") [[likely]] { + return true; + } + if (!check_number(text)) { + open_popup(error_number_input, view_); + } + return true; + }; + + light.second->on_end_focus = + [input = light.second.get(), &view_, + error_number_input](std::string_view text) -> bool { + if (text == "") [[likely]] { + return true; + } + if (!check_number(text)) { + open_popup(error_number_input, view_); + } + return true; + }; + + auto text_input = pane( + "Sqmm", margin({10, 5, 10, 5}, + vtile(my_input("手机屏幕亮度", screen_light.first), + my_input("屏幕曝光时间", screen_exposure.first), + my_input("天空曝光时间", sky_exposure.first), + my_input("照片亮度值", image_light.first), + my_input("亮度值", light.first)))); + + auto mbutton = share(icon_button(icons::right_circled, 1.2)); + + mbutton->on_click = [input_screen_light = screen_light.second.get(), + input_screen_exposure = screen_exposure.second.get(), + input_sky_exposure = sky_exposure.second.get(), + input_image_light = image_light.second.get(), + input_light = light.second.get(), error_number_input, + &view_](bool) mutable { + try { + auto A14 = std::stod(input_screen_light->get_text()); + auto B14 = std::stod(input_screen_exposure->get_text()); + auto A17 = std::stod(input_sky_exposure->get_text()); + auto B17 = std::stod(input_image_light->get_text()); + auto A11 = std::stod(input_light->get_text()); + + if (A14 != 0 && B14 != 0 && A17 != 0 && B17 != 0 && A11 != 0) + [[likely]] { + double A2 = 140000; + double B2 = -26.7; + double C2 = B2 + std::log(A2) / std::log(100); + double F2 = 180 / M_PI; + double D2 = std::log((std::pow(F2, 2)) * (std::pow(3600, 2)) * 4) / + std::log(100) + + C2; + double C17 = (B14 / A14) / (B17 / A17); + double B20 = A11 / C17; + double A20 = std::log(1 / B20) / std::log(100) + D2; + int a0 = 0; + if (A20 <= 18.38) { + a0 = 8; + } else if (18.38 < A20 && A20 <= 18.94) { + a0 = 7; + } else if (18.94 < A20 && A20 <= 19.5) { + a0 = 6; + } else if (19.5 < A20 && A20 <= 20.49) { + a0 = 5; + } else if (20.49 < A20 && A20 <= 21.69) { + a0 = 4; + } else if (21.69 < A20 && A20 <= 21.89) { + a0 = 3; + } else if (21.89 < A20 && A20 <= 21.99) { + a0 = 2; + } else if (21.99 <= A20) { + a0 = 1; + } + open_popup(make_result_dialog(view_, A20, B20, a0), view_); + } else [[unlikely]] { + open_popup(error_number_input, view_); + } + } catch (const std::exception &e) { + open_popup(error_number_input, view_); + } + return true; + }; + + return margin({10, 0, 10, 10}, vtile(text_input, hold(mbutton))); +} + +auto make_elements(view &view_) { + return max_size({1280, 640}, + margin({20, 10, 20, 10}, + htile(margin({20, 20, 20, 20}, make_app(view_))))); +} + +int main(int argc, char *argv[]) { + app _app("Sqmm Calculator"); + window _win(_app.name()); + _win.on_close = [&_app]() { _app.stop(); }; + + view view_(_win); + + view_.content(make_elements(view_), background); + + _app.run(); + return 0; +} diff --git a/tools/ini2json.cpp b/tools/ini2json.cpp index ca56656e..7caed6a8 100644 --- a/tools/ini2json.cpp +++ b/tools/ini2json.cpp @@ -2,17 +2,6 @@ * ini2json.cpp * * Copyright (C) 2023-2024 Max Qian - * - * 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 3 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, see . */ /************************************************* diff --git a/tools/json2ini.cpp b/tools/json2ini.cpp index e5b1e4eb..a60fb417 100644 --- a/tools/json2ini.cpp +++ b/tools/json2ini.cpp @@ -2,17 +2,6 @@ * json2ini.cpp * * Copyright (C) 2023-2024 Max Qian - * - * 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 3 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, see . */ /************************************************* diff --git a/tools/json2toml.cpp b/tools/json2toml.cpp index 75df68ba..ee89581d 100644 --- a/tools/json2toml.cpp +++ b/tools/json2toml.cpp @@ -2,17 +2,6 @@ * json2toml.cpp * * Copyright (C) 2023-2024 Max Qian - * - * 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 3 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, see . */ /************************************************* diff --git a/tools/json2xml.cpp b/tools/json2xml.cpp index ee14df6d..cba8d674 100644 --- a/tools/json2xml.cpp +++ b/tools/json2xml.cpp @@ -2,17 +2,6 @@ * json2xml.cpp * * Copyright (C) 2023-2024 Max Qian - * - * 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 3 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, see . */ /************************************************* diff --git a/tools/toml2json.cpp b/tools/toml2json.cpp index 84ff8b5e..c769ceab 100644 --- a/tools/toml2json.cpp +++ b/tools/toml2json.cpp @@ -2,17 +2,6 @@ * toml2json.cpp * * Copyright (C) 2023-2024 Max Qian - * - * 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 3 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, see . */ /************************************************* diff --git a/tools/xml2json.cpp b/tools/xml2json.cpp index b3b7908b..538632c6 100644 --- a/tools/xml2json.cpp +++ b/tools/xml2json.cpp @@ -2,17 +2,6 @@ * xml2json.cpp * * Copyright (C) 2023-2024 Max Qian - * - * 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 3 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, see . */ /************************************************* diff --git a/websrc/cobalt-mobile/.bundle/config b/websrc/cobalt-mobile/.bundle/config deleted file mode 100644 index 848943bb..00000000 --- a/websrc/cobalt-mobile/.bundle/config +++ /dev/null @@ -1,2 +0,0 @@ -BUNDLE_PATH: "vendor/bundle" -BUNDLE_FORCE_RUBY_PLATFORM: 1 diff --git a/websrc/cobalt-mobile/.eslintrc.js b/websrc/cobalt-mobile/.eslintrc.js deleted file mode 100644 index 187894b6..00000000 --- a/websrc/cobalt-mobile/.eslintrc.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - root: true, - extends: '@react-native', -}; diff --git a/websrc/cobalt-mobile/.gitignore b/websrc/cobalt-mobile/.gitignore deleted file mode 100644 index 0cab2ac6..00000000 --- a/websrc/cobalt-mobile/.gitignore +++ /dev/null @@ -1,66 +0,0 @@ -# OSX -# -.DS_Store - -# Xcode -# -build/ -*.pbxuser -!default.pbxuser -*.mode1v3 -!default.mode1v3 -*.mode2v3 -!default.mode2v3 -*.perspectivev3 -!default.perspectivev3 -xcuserdata -*.xccheckout -*.moved-aside -DerivedData -*.hmap -*.ipa -*.xcuserstate -ios/.xcode.env.local - -# Android/IntelliJ -# -build/ -.idea -.gradle -local.properties -*.iml -*.hprof -.cxx/ -*.keystore -!debug.keystore - -# node.js -# -node_modules/ -npm-debug.log -yarn-error.log - -# fastlane -# -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the -# screenshots whenever they are needed. -# For more information about the recommended setup visit: -# https://docs.fastlane.tools/best-practices/source-control/ - -**/fastlane/report.xml -**/fastlane/Preview.html -**/fastlane/screenshots -**/fastlane/test_output - -# Bundle artifact -*.jsbundle - -# Ruby / CocoaPods -/ios/Pods/ -/vendor/bundle/ - -# Temporary files created by Metro to check the health of the file watcher -.metro-health-check* - -# testing -/coverage diff --git a/websrc/cobalt-mobile/.prettierrc.js b/websrc/cobalt-mobile/.prettierrc.js deleted file mode 100644 index 2b540746..00000000 --- a/websrc/cobalt-mobile/.prettierrc.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - arrowParens: 'avoid', - bracketSameLine: true, - bracketSpacing: false, - singleQuote: true, - trailingComma: 'all', -}; diff --git a/websrc/cobalt-mobile/.watchmanconfig b/websrc/cobalt-mobile/.watchmanconfig deleted file mode 100644 index 0967ef42..00000000 --- a/websrc/cobalt-mobile/.watchmanconfig +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/websrc/cobalt-mobile/App.tsx b/websrc/cobalt-mobile/App.tsx deleted file mode 100644 index 125fe1b9..00000000 --- a/websrc/cobalt-mobile/App.tsx +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Sample React Native App - * https://github.com/facebook/react-native - * - * @format - */ - -import React from 'react'; -import type {PropsWithChildren} from 'react'; -import { - SafeAreaView, - ScrollView, - StatusBar, - StyleSheet, - Text, - useColorScheme, - View, -} from 'react-native'; - -import { - Colors, - DebugInstructions, - Header, - LearnMoreLinks, - ReloadInstructions, -} from 'react-native/Libraries/NewAppScreen'; - -type SectionProps = PropsWithChildren<{ - title: string; -}>; - -function Section({children, title}: SectionProps): React.JSX.Element { - const isDarkMode = useColorScheme() === 'dark'; - return ( - - - {title} - - - {children} - - - ); -} - -function App(): React.JSX.Element { - const isDarkMode = useColorScheme() === 'dark'; - - const backgroundStyle = { - backgroundColor: isDarkMode ? Colors.darker : Colors.lighter, - }; - - return ( - - - -
- -
- Edit App.tsx to change this - screen and then come back to see your edits. -
-
- -
-
- -
-
- Read the docs to discover what to do next: -
- -
- - - ); -} - -const styles = StyleSheet.create({ - sectionContainer: { - marginTop: 32, - paddingHorizontal: 24, - }, - sectionTitle: { - fontSize: 24, - fontWeight: '600', - }, - sectionDescription: { - marginTop: 8, - fontSize: 18, - fontWeight: '400', - }, - highlight: { - fontWeight: '700', - }, -}); - -export default App; diff --git a/websrc/cobalt-mobile/Gemfile b/websrc/cobalt-mobile/Gemfile deleted file mode 100644 index 6a7d5c7a..00000000 --- a/websrc/cobalt-mobile/Gemfile +++ /dev/null @@ -1,7 +0,0 @@ -source 'https://rubygems.org' - -# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version -ruby ">= 2.6.10" - -gem 'cocoapods', '~> 1.13' -gem 'activesupport', '>= 6.1.7.3', '< 7.1.0' diff --git a/websrc/cobalt-mobile/README.md b/websrc/cobalt-mobile/README.md deleted file mode 100644 index 12470c30..00000000 --- a/websrc/cobalt-mobile/README.md +++ /dev/null @@ -1,79 +0,0 @@ -This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli). - -# Getting Started - ->**Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding. - -## Step 1: Start the Metro Server - -First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native. - -To start Metro, run the following command from the _root_ of your React Native project: - -```bash -# using npm -npm start - -# OR using Yarn -yarn start -``` - -## Step 2: Start your Application - -Let Metro Bundler run in its _own_ terminal. Open a _new_ terminal from the _root_ of your React Native project. Run the following command to start your _Android_ or _iOS_ app: - -### For Android - -```bash -# using npm -npm run android - -# OR using Yarn -yarn android -``` - -### For iOS - -```bash -# using npm -npm run ios - -# OR using Yarn -yarn ios -``` - -If everything is set up _correctly_, you should see your new app running in your _Android Emulator_ or _iOS Simulator_ shortly provided you have set up your emulator/simulator correctly. - -This is one way to run your app — you can also run it directly from within Android Studio and Xcode respectively. - -## Step 3: Modifying your App - -Now that you have successfully run the app, let's modify it. - -1. Open `App.tsx` in your text editor of choice and edit some lines. -2. For **Android**: Press the R key twice or select **"Reload"** from the **Developer Menu** (Ctrl + M (on Window and Linux) or Cmd ⌘ + M (on macOS)) to see your changes! - - For **iOS**: Hit Cmd ⌘ + R in your iOS Simulator to reload the app and see your changes! - -## Congratulations! :tada: - -You've successfully run and modified your React Native App. :partying_face: - -### Now what? - -- If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps). -- If you're curious to learn more about React Native, check out the [Introduction to React Native](https://reactnative.dev/docs/getting-started). - -# Troubleshooting - -If you can't get this to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page. - -# Learn More - -To learn more about React Native, take a look at the following resources: - -- [React Native Website](https://reactnative.dev) - learn more about React Native. -- [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment. -- [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**. -- [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts. -- [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native. diff --git a/websrc/cobalt-mobile/__tests__/App.test.tsx b/websrc/cobalt-mobile/__tests__/App.test.tsx deleted file mode 100644 index 9eac6fbc..00000000 --- a/websrc/cobalt-mobile/__tests__/App.test.tsx +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @format - */ - -import 'react-native'; -import React from 'react'; -import App from '../App'; - -// Note: import explicitly to use the types shipped with jest. -import {it} from '@jest/globals'; - -// Note: test renderer must be required after react-native. -import renderer from 'react-test-renderer'; - -it('renders correctly', () => { - renderer.create(); -}); diff --git a/websrc/cobalt-mobile/android/app/build.gradle b/websrc/cobalt-mobile/android/app/build.gradle deleted file mode 100644 index ac4dc8f4..00000000 --- a/websrc/cobalt-mobile/android/app/build.gradle +++ /dev/null @@ -1,119 +0,0 @@ -apply plugin: "com.android.application" -apply plugin: "org.jetbrains.kotlin.android" -apply plugin: "com.facebook.react" - -/** - * This is the configuration block to customize your React Native Android app. - * By default you don't need to apply any configuration, just uncomment the lines you need. - */ -react { - /* Folders */ - // The root of your project, i.e. where "package.json" lives. Default is '..' - // root = file("../") - // The folder where the react-native NPM package is. Default is ../node_modules/react-native - // reactNativeDir = file("../node_modules/react-native") - // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen - // codegenDir = file("../node_modules/@react-native/codegen") - // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js - // cliFile = file("../node_modules/react-native/cli.js") - - /* Variants */ - // The list of variants to that are debuggable. For those we're going to - // skip the bundling of the JS bundle and the assets. By default is just 'debug'. - // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. - // debuggableVariants = ["liteDebug", "prodDebug"] - - /* Bundling */ - // A list containing the node command and its flags. Default is just 'node'. - // nodeExecutableAndArgs = ["node"] - // - // The command to run when bundling. By default is 'bundle' - // bundleCommand = "ram-bundle" - // - // The path to the CLI configuration file. Default is empty. - // bundleConfig = file(../rn-cli.config.js) - // - // The name of the generated asset file containing your JS bundle - // bundleAssetName = "MyApplication.android.bundle" - // - // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' - // entryFile = file("../js/MyApplication.android.js") - // - // A list of extra flags to pass to the 'bundle' commands. - // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle - // extraPackagerArgs = [] - - /* Hermes Commands */ - // The hermes compiler command to run. By default it is 'hermesc' - // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" - // - // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" - // hermesFlags = ["-O", "-output-source-map"] -} - -/** - * Set this to true to Run Proguard on Release builds to minify the Java bytecode. - */ -def enableProguardInReleaseBuilds = false - -/** - * The preferred build flavor of JavaScriptCore (JSC) - * - * For example, to use the international variant, you can use: - * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` - * - * The international variant includes ICU i18n library and necessary data - * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that - * give correct results when using with locales other than en-US. Note that - * this variant is about 6MiB larger per architecture than default. - */ -def jscFlavor = 'org.webkit:android-jsc:+' - -android { - ndkVersion rootProject.ext.ndkVersion - buildToolsVersion rootProject.ext.buildToolsVersion - compileSdk rootProject.ext.compileSdkVersion - - namespace "com.cobaltmobile" - defaultConfig { - applicationId "com.cobaltmobile" - minSdkVersion rootProject.ext.minSdkVersion - targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 1 - versionName "1.0" - } - signingConfigs { - debug { - storeFile file('debug.keystore') - storePassword 'android' - keyAlias 'androiddebugkey' - keyPassword 'android' - } - } - buildTypes { - debug { - signingConfig signingConfigs.debug - } - release { - // Caution! In production, you need to generate your own keystore file. - // see https://reactnative.dev/docs/signed-apk-android. - signingConfig signingConfigs.debug - minifyEnabled enableProguardInReleaseBuilds - proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" - } - } -} - -dependencies { - // The version of react-native is set by the React Native Gradle Plugin - implementation("com.facebook.react:react-android") - implementation("com.facebook.react:flipper-integration") - - if (hermesEnabled.toBoolean()) { - implementation("com.facebook.react:hermes-android") - } else { - implementation jscFlavor - } -} - -apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) diff --git a/websrc/cobalt-mobile/android/app/debug.keystore b/websrc/cobalt-mobile/android/app/debug.keystore deleted file mode 100644 index 364e105e..00000000 Binary files a/websrc/cobalt-mobile/android/app/debug.keystore and /dev/null differ diff --git a/websrc/cobalt-mobile/android/app/proguard-rules.pro b/websrc/cobalt-mobile/android/app/proguard-rules.pro deleted file mode 100644 index 11b02572..00000000 --- a/websrc/cobalt-mobile/android/app/proguard-rules.pro +++ /dev/null @@ -1,10 +0,0 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: diff --git a/websrc/cobalt-mobile/android/app/src/main/java/com/cobaltmobile/MainActivity.kt b/websrc/cobalt-mobile/android/app/src/main/java/com/cobaltmobile/MainActivity.kt deleted file mode 100644 index cc23f7bd..00000000 --- a/websrc/cobalt-mobile/android/app/src/main/java/com/cobaltmobile/MainActivity.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.cobaltmobile - -import com.facebook.react.ReactActivity -import com.facebook.react.ReactActivityDelegate -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled -import com.facebook.react.defaults.DefaultReactActivityDelegate - -class MainActivity : ReactActivity() { - - /** - * Returns the name of the main component registered from JavaScript. This is used to schedule - * rendering of the component. - */ - override fun getMainComponentName(): String = "cobaltmobile" - - /** - * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] - * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] - */ - override fun createReactActivityDelegate(): ReactActivityDelegate = - DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) -} diff --git a/websrc/cobalt-mobile/android/app/src/main/java/com/cobaltmobile/MainApplication.kt b/websrc/cobalt-mobile/android/app/src/main/java/com/cobaltmobile/MainApplication.kt deleted file mode 100644 index c2d6bb46..00000000 --- a/websrc/cobalt-mobile/android/app/src/main/java/com/cobaltmobile/MainApplication.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.cobaltmobile - -import android.app.Application -import com.facebook.react.PackageList -import com.facebook.react.ReactApplication -import com.facebook.react.ReactHost -import com.facebook.react.ReactNativeHost -import com.facebook.react.ReactPackage -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load -import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost -import com.facebook.react.defaults.DefaultReactNativeHost -import com.facebook.react.flipper.ReactNativeFlipper -import com.facebook.soloader.SoLoader - -class MainApplication : Application(), ReactApplication { - - override val reactNativeHost: ReactNativeHost = - object : DefaultReactNativeHost(this) { - override fun getPackages(): List = - PackageList(this).packages.apply { - // Packages that cannot be autolinked yet can be added manually here, for example: - // add(MyReactNativePackage()) - } - - override fun getJSMainModuleName(): String = "index" - - override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG - - override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED - override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED - } - - override val reactHost: ReactHost - get() = getDefaultReactHost(this.applicationContext, reactNativeHost) - - override fun onCreate() { - super.onCreate() - SoLoader.init(this, false) - if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { - // If you opted-in for the New Architecture, we load the native entry point for this app. - load() - } - ReactNativeFlipper.initializeFlipper(this, reactNativeHost.reactInstanceManager) - } -} diff --git a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/websrc/cobalt-mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index a2f59082..00000000 Binary files a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/websrc/cobalt-mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 1b523998..00000000 Binary files a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/websrc/cobalt-mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index ff10afd6..00000000 Binary files a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/websrc/cobalt-mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index 115a4c76..00000000 Binary files a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index dcd3cd80..00000000 Binary files a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 459ca609..00000000 Binary files a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 8ca12fe0..00000000 Binary files a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 8e19b410..00000000 Binary files a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index b824ebdd..00000000 Binary files a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 4c19a13c..00000000 Binary files a/websrc/cobalt-mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/websrc/cobalt-mobile/android/build.gradle b/websrc/cobalt-mobile/android/build.gradle deleted file mode 100644 index cb9d6232..00000000 --- a/websrc/cobalt-mobile/android/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -buildscript { - ext { - buildToolsVersion = "34.0.0" - minSdkVersion = 21 - compileSdkVersion = 34 - targetSdkVersion = 34 - ndkVersion = "25.1.8937393" - kotlinVersion = "1.8.0" - } - repositories { - google() - mavenCentral() - } - dependencies { - classpath("com.android.tools.build:gradle") - classpath("com.facebook.react:react-native-gradle-plugin") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") - } -} - -apply plugin: "com.facebook.react.rootproject" diff --git a/websrc/cobalt-mobile/android/gradle.properties b/websrc/cobalt-mobile/android/gradle.properties deleted file mode 100644 index a46a5b90..00000000 --- a/websrc/cobalt-mobile/android/gradle.properties +++ /dev/null @@ -1,41 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m -org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true - -# AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app's APK -# https://developer.android.com/topic/libraries/support-library/androidx-rn -android.useAndroidX=true -# Automatically convert third-party libraries to use AndroidX -android.enableJetifier=true - -# Use this property to specify which architecture you want to build. -# You can also override it from the CLI using -# ./gradlew -PreactNativeArchitectures=x86_64 -reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 - -# Use this property to enable support to the new architecture. -# This will allow you to use TurboModules and the Fabric render in -# your application. You should enable this flag either if you want -# to write custom TurboModules/Fabric components OR use libraries that -# are providing them. -newArchEnabled=false - -# Use this property to enable or disable the Hermes JS engine. -# If set to false, you will be using JSC instead. -hermesEnabled=true diff --git a/websrc/cobalt-mobile/android/gradle/wrapper/gradle-wrapper.jar b/websrc/cobalt-mobile/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 7f93135c..00000000 Binary files a/websrc/cobalt-mobile/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/websrc/cobalt-mobile/android/gradle/wrapper/gradle-wrapper.properties b/websrc/cobalt-mobile/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index d11cdd90..00000000 --- a/websrc/cobalt-mobile/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/websrc/cobalt-mobile/android/gradlew b/websrc/cobalt-mobile/android/gradlew deleted file mode 100644 index 0adc8e1a..00000000 --- a/websrc/cobalt-mobile/android/gradlew +++ /dev/null @@ -1,249 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/websrc/cobalt-mobile/android/gradlew.bat b/websrc/cobalt-mobile/android/gradlew.bat deleted file mode 100644 index 93e3f59f..00000000 --- a/websrc/cobalt-mobile/android/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/websrc/cobalt-mobile/android/settings.gradle b/websrc/cobalt-mobile/android/settings.gradle deleted file mode 100644 index 102f74ae..00000000 --- a/websrc/cobalt-mobile/android/settings.gradle +++ /dev/null @@ -1,4 +0,0 @@ -rootProject.name = 'cobaltmobile' -apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) -include ':app' -includeBuild('../node_modules/@react-native/gradle-plugin') diff --git a/websrc/cobalt-mobile/app.json b/websrc/cobalt-mobile/app.json deleted file mode 100644 index 4924b29c..00000000 --- a/websrc/cobalt-mobile/app.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "cobaltmobile", - "displayName": "cobaltmobile" -} diff --git a/websrc/cobalt-mobile/babel.config.js b/websrc/cobalt-mobile/babel.config.js deleted file mode 100644 index f7b3da3b..00000000 --- a/websrc/cobalt-mobile/babel.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - presets: ['module:@react-native/babel-preset'], -}; diff --git a/websrc/cobalt-mobile/index.js b/websrc/cobalt-mobile/index.js deleted file mode 100644 index a850d031..00000000 --- a/websrc/cobalt-mobile/index.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @format - */ - -import {AppRegistry} from 'react-native'; -import App from './App'; -import {name as appName} from './app.json'; - -AppRegistry.registerComponent(appName, () => App); diff --git a/websrc/cobalt-mobile/ios/.xcode.env b/websrc/cobalt-mobile/ios/.xcode.env deleted file mode 100644 index 3d5782c7..00000000 --- a/websrc/cobalt-mobile/ios/.xcode.env +++ /dev/null @@ -1,11 +0,0 @@ -# This `.xcode.env` file is versioned and is used to source the environment -# used when running script phases inside Xcode. -# To customize your local environment, you can create an `.xcode.env.local` -# file that is not versioned. - -# NODE_BINARY variable contains the PATH to the node executable. -# -# Customize the NODE_BINARY variable here. -# For example, to use nvm with brew, add the following line -# . "$(brew --prefix nvm)/nvm.sh" --no-use -export NODE_BINARY=$(command -v node) diff --git a/websrc/cobalt-mobile/ios/Podfile b/websrc/cobalt-mobile/ios/Podfile deleted file mode 100644 index 7ebbb5f3..00000000 --- a/websrc/cobalt-mobile/ios/Podfile +++ /dev/null @@ -1,55 +0,0 @@ -# Resolve react_native_pods.rb with node to allow for hoisting -require Pod::Executable.execute_command('node', ['-p', - 'require.resolve( - "react-native/scripts/react_native_pods.rb", - {paths: [process.argv[1]]}, - )', __dir__]).strip - -platform :ios, min_ios_version_supported -prepare_react_native_project! - -# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. -# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded -# -# To fix this you can also exclude `react-native-flipper` using a `react-native.config.js` -# ```js -# module.exports = { -# dependencies: { -# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), -# ``` -flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled - -linkage = ENV['USE_FRAMEWORKS'] -if linkage != nil - Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green - use_frameworks! :linkage => linkage.to_sym -end - -target 'cobaltmobile' do - config = use_native_modules! - - use_react_native!( - :path => config[:reactNativePath], - # Enables Flipper. - # - # Note that if you have use_frameworks! enabled, Flipper will not work and - # you should disable the next line. - :flipper_configuration => flipper_config, - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/.." - ) - - target 'cobaltmobileTests' do - inherit! :complete - # Pods for testing - end - - post_install do |installer| - # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false - ) - end -end diff --git a/websrc/cobalt-mobile/ios/cobaltmobile.xcodeproj/project.pbxproj b/websrc/cobalt-mobile/ios/cobaltmobile.xcodeproj/project.pbxproj deleted file mode 100644 index 67f6711f..00000000 --- a/websrc/cobalt-mobile/ios/cobaltmobile.xcodeproj/project.pbxproj +++ /dev/null @@ -1,684 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 00E356F31AD99517003FC87E /* cobaltmobileTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* cobaltmobileTests.m */; }; - 0C80B921A6F3F58F76C31292 /* libPods-cobaltmobile.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-cobaltmobile.a */; }; - 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 7699B88040F8A987B510C191 /* libPods-cobaltmobile-cobaltmobileTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-cobaltmobile-cobaltmobileTests.a */; }; - 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 13B07F861A680F5B00A75B9A; - remoteInfo = cobaltmobile; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXFileReference section */ - 00E356EE1AD99517003FC87E /* cobaltmobileTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = cobaltmobileTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 00E356F21AD99517003FC87E /* cobaltmobileTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = cobaltmobileTests.m; sourceTree = ""; }; - 13B07F961A680F5B00A75B9A /* cobaltmobile.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = cobaltmobile.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = cobaltmobile/AppDelegate.h; sourceTree = ""; }; - 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = cobaltmobile/AppDelegate.mm; sourceTree = ""; }; - 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = cobaltmobile/Images.xcassets; sourceTree = ""; }; - 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = cobaltmobile/Info.plist; sourceTree = ""; }; - 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = cobaltmobile/main.m; sourceTree = ""; }; - 19F6CBCC0A4E27FBF8BF4A61 /* libPods-cobaltmobile-cobaltmobileTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-cobaltmobile-cobaltmobileTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 3B4392A12AC88292D35C810B /* Pods-cobaltmobile.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-cobaltmobile.debug.xcconfig"; path = "Target Support Files/Pods-cobaltmobile/Pods-cobaltmobile.debug.xcconfig"; sourceTree = ""; }; - 5709B34CF0A7D63546082F79 /* Pods-cobaltmobile.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-cobaltmobile.release.xcconfig"; path = "Target Support Files/Pods-cobaltmobile/Pods-cobaltmobile.release.xcconfig"; sourceTree = ""; }; - 5B7EB9410499542E8C5724F5 /* Pods-cobaltmobile-cobaltmobileTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-cobaltmobile-cobaltmobileTests.debug.xcconfig"; path = "Target Support Files/Pods-cobaltmobile-cobaltmobileTests/Pods-cobaltmobile-cobaltmobileTests.debug.xcconfig"; sourceTree = ""; }; - 5DCACB8F33CDC322A6C60F78 /* libPods-cobaltmobile.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-cobaltmobile.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = cobaltmobile/LaunchScreen.storyboard; sourceTree = ""; }; - 89C6BE57DB24E9ADA2F236DE /* Pods-cobaltmobile-cobaltmobileTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-cobaltmobile-cobaltmobileTests.release.xcconfig"; path = "Target Support Files/Pods-cobaltmobile-cobaltmobileTests/Pods-cobaltmobile-cobaltmobileTests.release.xcconfig"; sourceTree = ""; }; - ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 00E356EB1AD99517003FC87E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 7699B88040F8A987B510C191 /* libPods-cobaltmobile-cobaltmobileTests.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 0C80B921A6F3F58F76C31292 /* libPods-cobaltmobile.a in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 00E356EF1AD99517003FC87E /* cobaltmobileTests */ = { - isa = PBXGroup; - children = ( - 00E356F21AD99517003FC87E /* cobaltmobileTests.m */, - 00E356F01AD99517003FC87E /* Supporting Files */, - ); - path = cobaltmobileTests; - sourceTree = ""; - }; - 00E356F01AD99517003FC87E /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 00E356F11AD99517003FC87E /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - 13B07FAE1A68108700A75B9A /* cobaltmobile */ = { - isa = PBXGroup; - children = ( - 13B07FAF1A68108700A75B9A /* AppDelegate.h */, - 13B07FB01A68108700A75B9A /* AppDelegate.mm */, - 13B07FB51A68108700A75B9A /* Images.xcassets */, - 13B07FB61A68108700A75B9A /* Info.plist */, - 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, - 13B07FB71A68108700A75B9A /* main.m */, - ); - name = cobaltmobile; - sourceTree = ""; - }; - 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { - isa = PBXGroup; - children = ( - ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 5DCACB8F33CDC322A6C60F78 /* libPods-cobaltmobile.a */, - 19F6CBCC0A4E27FBF8BF4A61 /* libPods-cobaltmobile-cobaltmobileTests.a */, - ); - name = Frameworks; - sourceTree = ""; - }; - 832341AE1AAA6A7D00B99B32 /* Libraries */ = { - isa = PBXGroup; - children = ( - ); - name = Libraries; - sourceTree = ""; - }; - 83CBB9F61A601CBA00E9B192 = { - isa = PBXGroup; - children = ( - 13B07FAE1A68108700A75B9A /* cobaltmobile */, - 832341AE1AAA6A7D00B99B32 /* Libraries */, - 00E356EF1AD99517003FC87E /* cobaltmobileTests */, - 83CBBA001A601CBA00E9B192 /* Products */, - 2D16E6871FA4F8E400B85C8A /* Frameworks */, - BBD78D7AC51CEA395F1C20DB /* Pods */, - ); - indentWidth = 2; - sourceTree = ""; - tabWidth = 2; - usesTabs = 0; - }; - 83CBBA001A601CBA00E9B192 /* Products */ = { - isa = PBXGroup; - children = ( - 13B07F961A680F5B00A75B9A /* cobaltmobile.app */, - 00E356EE1AD99517003FC87E /* cobaltmobileTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - BBD78D7AC51CEA395F1C20DB /* Pods */ = { - isa = PBXGroup; - children = ( - 3B4392A12AC88292D35C810B /* Pods-cobaltmobile.debug.xcconfig */, - 5709B34CF0A7D63546082F79 /* Pods-cobaltmobile.release.xcconfig */, - 5B7EB9410499542E8C5724F5 /* Pods-cobaltmobile-cobaltmobileTests.debug.xcconfig */, - 89C6BE57DB24E9ADA2F236DE /* Pods-cobaltmobile-cobaltmobileTests.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 00E356ED1AD99517003FC87E /* cobaltmobileTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "cobaltmobileTests" */; - buildPhases = ( - A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */, - 00E356EA1AD99517003FC87E /* Sources */, - 00E356EB1AD99517003FC87E /* Frameworks */, - 00E356EC1AD99517003FC87E /* Resources */, - C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */, - F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - 00E356F51AD99517003FC87E /* PBXTargetDependency */, - ); - name = cobaltmobileTests; - productName = cobaltmobileTests; - productReference = 00E356EE1AD99517003FC87E /* cobaltmobileTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 13B07F861A680F5B00A75B9A /* cobaltmobile */ = { - isa = PBXNativeTarget; - buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "cobaltmobile" */; - buildPhases = ( - C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */, - 13B07F871A680F5B00A75B9A /* Sources */, - 13B07F8C1A680F5B00A75B9A /* Frameworks */, - 13B07F8E1A680F5B00A75B9A /* Resources */, - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */, - E235C05ADACE081382539298 /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = cobaltmobile; - productName = cobaltmobile; - productReference = 13B07F961A680F5B00A75B9A /* cobaltmobile.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 83CBB9F71A601CBA00E9B192 /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1210; - TargetAttributes = { - 00E356ED1AD99517003FC87E = { - CreatedOnToolsVersion = 6.2; - TestTargetID = 13B07F861A680F5B00A75B9A; - }; - 13B07F861A680F5B00A75B9A = { - LastSwiftMigration = 1120; - }; - }; - }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "cobaltmobile" */; - compatibilityVersion = "Xcode 12.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 83CBB9F61A601CBA00E9B192; - productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 13B07F861A680F5B00A75B9A /* cobaltmobile */, - 00E356ED1AD99517003FC87E /* cobaltmobileTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 00E356EC1AD99517003FC87E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 13B07F8E1A680F5B00A75B9A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, - 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "$(SRCROOT)/.xcode.env.local", - "$(SRCROOT)/.xcode.env", - ); - name = "Bundle React Native code and images"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; - }; - 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-cobaltmobile/Pods-cobaltmobile-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-cobaltmobile/Pods-cobaltmobile-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-cobaltmobile/Pods-cobaltmobile-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-cobaltmobile-cobaltmobileTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-cobaltmobile-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-cobaltmobile-cobaltmobileTests/Pods-cobaltmobile-cobaltmobileTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-cobaltmobile-cobaltmobileTests/Pods-cobaltmobile-cobaltmobileTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-cobaltmobile-cobaltmobileTests/Pods-cobaltmobile-cobaltmobileTests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-cobaltmobile/Pods-cobaltmobile-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-cobaltmobile/Pods-cobaltmobile-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-cobaltmobile/Pods-cobaltmobile-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-cobaltmobile-cobaltmobileTests/Pods-cobaltmobile-cobaltmobileTests-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-cobaltmobile-cobaltmobileTests/Pods-cobaltmobile-cobaltmobileTests-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-cobaltmobile-cobaltmobileTests/Pods-cobaltmobile-cobaltmobileTests-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 00E356EA1AD99517003FC87E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 00E356F31AD99517003FC87E /* cobaltmobileTests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 13B07F871A680F5B00A75B9A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, - 13B07FC11A68108700A75B9A /* main.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 13B07F861A680F5B00A75B9A /* cobaltmobile */; - targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 00E356F61AD99517003FC87E /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-cobaltmobile-cobaltmobileTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = cobaltmobileTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.4; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - "$(inherited)", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/cobaltmobile.app/cobaltmobile"; - }; - name = Debug; - }; - 00E356F71AD99517003FC87E /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 89C6BE57DB24E9ADA2F236DE /* Pods-cobaltmobile-cobaltmobileTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - COPY_PHASE_STRIP = NO; - INFOPLIST_FILE = cobaltmobileTests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.4; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - OTHER_LDFLAGS = ( - "-ObjC", - "-lc++", - "$(inherited)", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/cobaltmobile.app/cobaltmobile"; - }; - name = Release; - }; - 13B07F941A680F5B00A75B9A /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-cobaltmobile.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = cobaltmobile/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = cobaltmobile; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 13B07F951A680F5B00A75B9A /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-cobaltmobile.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; - INFOPLIST_FILE = cobaltmobile/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - OTHER_LDFLAGS = ( - "$(inherited)", - "-ObjC", - "-lc++", - ); - PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; - PRODUCT_NAME = cobaltmobile; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; - 83CBBA201A601CBA00E9B192 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++20"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_SYMBOLS_PRIVATE_EXTERN = NO; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.4; - LD_RUNPATH_SEARCH_PATHS = ( - /usr/lib/swift, - "$(inherited)", - ); - LIBRARY_SEARCH_PATHS = ( - "\"$(SDKROOT)/usr/lib/swift\"", - "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", - "\"$(inherited)\"", - ); - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - OTHER_CPLUSPLUSFLAGS = ( - "$(OTHER_CFLAGS)", - "-DFOLLY_NO_CONFIG", - "-DFOLLY_MOBILE=1", - "-DFOLLY_USE_LIBCPP=1", - "-DFOLLY_CFG_NO_COROUTINES=1", - ); - SDKROOT = iphoneos; - }; - name = Debug; - }; - 83CBBA211A601CBA00E9B192 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_CXX_LANGUAGE_STANDARD = "c++20"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 13.4; - LD_RUNPATH_SEARCH_PATHS = ( - /usr/lib/swift, - "$(inherited)", - ); - LIBRARY_SEARCH_PATHS = ( - "\"$(SDKROOT)/usr/lib/swift\"", - "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", - "\"$(inherited)\"", - ); - MTL_ENABLE_DEBUG_INFO = NO; - OTHER_CPLUSPLUSFLAGS = ( - "$(OTHER_CFLAGS)", - "-DFOLLY_NO_CONFIG", - "-DFOLLY_MOBILE=1", - "-DFOLLY_USE_LIBCPP=1", - "-DFOLLY_CFG_NO_COROUTINES=1", - ); - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "cobaltmobileTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 00E356F61AD99517003FC87E /* Debug */, - 00E356F71AD99517003FC87E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "cobaltmobile" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 13B07F941A680F5B00A75B9A /* Debug */, - 13B07F951A680F5B00A75B9A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "cobaltmobile" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 83CBBA201A601CBA00E9B192 /* Debug */, - 83CBBA211A601CBA00E9B192 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; -} diff --git a/websrc/cobalt-mobile/ios/cobaltmobile.xcodeproj/xcshareddata/xcschemes/cobaltmobile.xcscheme b/websrc/cobalt-mobile/ios/cobaltmobile.xcodeproj/xcshareddata/xcschemes/cobaltmobile.xcscheme deleted file mode 100644 index 74800166..00000000 --- a/websrc/cobalt-mobile/ios/cobaltmobile.xcodeproj/xcshareddata/xcschemes/cobaltmobile.xcscheme +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/websrc/cobalt-mobile/ios/cobaltmobile/AppDelegate.h b/websrc/cobalt-mobile/ios/cobaltmobile/AppDelegate.h deleted file mode 100644 index 5d280825..00000000 --- a/websrc/cobalt-mobile/ios/cobaltmobile/AppDelegate.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface AppDelegate : RCTAppDelegate - -@end diff --git a/websrc/cobalt-mobile/ios/cobaltmobile/AppDelegate.mm b/websrc/cobalt-mobile/ios/cobaltmobile/AppDelegate.mm deleted file mode 100644 index a40998e5..00000000 --- a/websrc/cobalt-mobile/ios/cobaltmobile/AppDelegate.mm +++ /dev/null @@ -1,31 +0,0 @@ -#import "AppDelegate.h" - -#import - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - self.moduleName = @"cobaltmobile"; - // You can add your custom initial props in the dictionary below. - // They will be passed down to the ViewController used by React Native. - self.initialProps = @{}; - - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge -{ - return [self getBundleURL]; -} - -- (NSURL *)getBundleURL -{ -#if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; -#else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; -#endif -} - -@end diff --git a/websrc/cobalt-mobile/ios/cobaltmobile/Images.xcassets/AppIcon.appiconset/Contents.json b/websrc/cobalt-mobile/ios/cobaltmobile/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 81213230..00000000 --- a/websrc/cobalt-mobile/ios/cobaltmobile/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" - }, - { - "idiom" : "ios-marketing", - "scale" : "1x", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/websrc/cobalt-mobile/ios/cobaltmobile/Images.xcassets/Contents.json b/websrc/cobalt-mobile/ios/cobaltmobile/Images.xcassets/Contents.json deleted file mode 100644 index 2d92bd53..00000000 --- a/websrc/cobalt-mobile/ios/cobaltmobile/Images.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/websrc/cobalt-mobile/ios/cobaltmobile/Info.plist b/websrc/cobalt-mobile/ios/cobaltmobile/Info.plist deleted file mode 100644 index bf5578a7..00000000 --- a/websrc/cobalt-mobile/ios/cobaltmobile/Info.plist +++ /dev/null @@ -1,52 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleDisplayName - cobaltmobile - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleSignature - ???? - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - LSRequiresIPhoneOS - - NSAppTransportSecurity - - - NSAllowsArbitraryLoads - - NSAllowsLocalNetworking - - - NSLocationWhenInUseUsageDescription - - UILaunchStoryboardName - LaunchScreen - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/websrc/cobalt-mobile/ios/cobaltmobile/LaunchScreen.storyboard b/websrc/cobalt-mobile/ios/cobaltmobile/LaunchScreen.storyboard deleted file mode 100644 index a8d78f6f..00000000 --- a/websrc/cobalt-mobile/ios/cobaltmobile/LaunchScreen.storyboard +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/websrc/cobalt-mobile/ios/cobaltmobile/main.m b/websrc/cobalt-mobile/ios/cobaltmobile/main.m deleted file mode 100644 index d645c724..00000000 --- a/websrc/cobalt-mobile/ios/cobaltmobile/main.m +++ /dev/null @@ -1,10 +0,0 @@ -#import - -#import "AppDelegate.h" - -int main(int argc, char *argv[]) -{ - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/websrc/cobalt-mobile/ios/cobaltmobileTests/Info.plist b/websrc/cobalt-mobile/ios/cobaltmobileTests/Info.plist deleted file mode 100644 index ba72822e..00000000 --- a/websrc/cobalt-mobile/ios/cobaltmobileTests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - diff --git a/websrc/cobalt-mobile/ios/cobaltmobileTests/cobaltmobileTests.m b/websrc/cobalt-mobile/ios/cobaltmobileTests/cobaltmobileTests.m deleted file mode 100644 index 55e88234..00000000 --- a/websrc/cobalt-mobile/ios/cobaltmobileTests/cobaltmobileTests.m +++ /dev/null @@ -1,66 +0,0 @@ -#import -#import - -#import -#import - -#define TIMEOUT_SECONDS 600 -#define TEXT_TO_LOOK_FOR @"Welcome to React" - -@interface cobaltmobileTests : XCTestCase - -@end - -@implementation cobaltmobileTests - -- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test -{ - if (test(view)) { - return YES; - } - for (UIView *subview in [view subviews]) { - if ([self findSubviewInView:subview matching:test]) { - return YES; - } - } - return NO; -} - -- (void)testRendersWelcomeScreen -{ - UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; - NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; - BOOL foundElement = NO; - - __block NSString *redboxError = nil; -#ifdef DEBUG - RCTSetLogFunction( - ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { - if (level >= RCTLogLevelError) { - redboxError = message; - } - }); -#endif - - while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { - [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; - - foundElement = [self findSubviewInView:vc.view - matching:^BOOL(UIView *view) { - if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { - return YES; - } - return NO; - }]; - } - -#ifdef DEBUG - RCTSetLogFunction(RCTDefaultLogFunction); -#endif - - XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); - XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); -} - -@end diff --git a/websrc/cobalt-mobile/jest.config.js b/websrc/cobalt-mobile/jest.config.js deleted file mode 100644 index 8eb675e9..00000000 --- a/websrc/cobalt-mobile/jest.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - preset: 'react-native', -}; diff --git a/websrc/cobalt-mobile/metro.config.js b/websrc/cobalt-mobile/metro.config.js deleted file mode 100644 index ad8f87b6..00000000 --- a/websrc/cobalt-mobile/metro.config.js +++ /dev/null @@ -1,11 +0,0 @@ -const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); - -/** - * Metro configuration - * https://facebook.github.io/metro/docs/configuration - * - * @type {import('metro-config').MetroConfig} - */ -const config = {}; - -module.exports = mergeConfig(getDefaultConfig(__dirname), config); diff --git a/websrc/cobalt-mobile/package-lock.json b/websrc/cobalt-mobile/package-lock.json deleted file mode 100644 index b8d6c1ea..00000000 --- a/websrc/cobalt-mobile/package-lock.json +++ /dev/null @@ -1,12803 +0,0 @@ -{ - "name": "cobaltmobile", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "cobaltmobile", - "version": "0.0.1", - "dependencies": { - "@rneui/base": "^4.0.0-rc.7", - "@rneui/themed": "^4.0.0-rc.8", - "react": "18.2.0", - "react-native": "0.73.2", - "react-native-webview": "^13.6.4" - }, - "devDependencies": { - "@babel/core": "^7.20.0", - "@babel/preset-env": "^7.20.0", - "@babel/runtime": "^7.20.0", - "@react-native/babel-preset": "0.73.19", - "@react-native/eslint-config": "0.73.2", - "@react-native/metro-config": "0.73.3", - "@react-native/typescript-config": "0.73.1", - "@types/react": "^18.2.6", - "@types/react-test-renderer": "^18.0.0", - "babel-jest": "^29.6.3", - "eslint": "^8.19.0", - "jest": "^29.6.3", - "prettier": "2.8.8", - "react-test-renderer": "18.2.0", - "typescript": "5.0.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmmirror.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.23.9", - "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.9", - "@babel/parser": "^7.23.9", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/eslint-parser": { - "version": "7.23.9", - "resolved": "https://registry.npmmirror.com/@babel/eslint-parser/-/eslint-parser-7.23.9.tgz", - "integrity": "sha512-xPndlO7qxiJbn0ATvfXQBjCS7qApc9xmKHArgI/FTEFxXas5dnjC/VqM37lfZun9dclRYcn+YQAr6uDFy0bB2g==", - "dev": true, - "dependencies": { - "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.1" - }, - "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/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", - "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmmirror.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", - "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.23.9", - "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.9.tgz", - "integrity": "sha512-B2L9neXTIyPQoXDm+NtovPvG6VOLWnaXu3BIeVDWwdKFgG30oNa6CqVGiJPDWQwIAK49t9gnQI9c6K6RzabiKw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmmirror.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.5.0", - "resolved": "https://registry.npmmirror.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", - "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmmirror.com/@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-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmmirror.com/@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.npmmirror.com/@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.23.0", - "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", - "dependencies": { - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmmirror.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmmirror.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmmirror.com/@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.23.4", - "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmmirror.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", - "dependencies": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.23.9", - "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.23.9.tgz", - "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", - "dependencies": { - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.9", - "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.9.tgz", - "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", - "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.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", - "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", - "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.23.7", - "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz", - "integrity": "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@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.npmmirror.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", - "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-export-default-from": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.23.3.tgz", - "integrity": "sha512-Q23MpLZfSGZL1kU7fWqV262q65svLSCIP5kZ/JCW/rKTCm/FrLjpvEd2kfUYMVeHh4QhV/xzyoRAHWrAZJrE3Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-default-from": "^7.23.3" - }, - "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.npmmirror.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", - "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.npmmirror.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", - "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.20.7", - "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - }, - "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.npmmirror.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.", - "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.21.0", - "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmmirror.com/@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.npmmirror.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmmirror.com/@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.npmmirror.com/@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-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmmirror.com/@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-default-from": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.23.3.tgz", - "integrity": "sha512-KeENO5ck1IeZ/l2lFZNy+mpobV3D2Zy5C1YFnWm+YuY5mQiAWc4yAp13dqgguwsBsFVLh4LPCEqCa5qW13N+hw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmmirror.com/@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.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.23.3.tgz", - "integrity": "sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", - "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", - "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "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.npmmirror.com/@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.npmmirror.com/@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.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", - "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "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.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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.npmmirror.com/@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.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", - "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "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" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", - "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.9", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", - "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", - "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", - "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", - "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", - "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", - "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", - "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.23.8", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz", - "integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-split-export-declaration": "^7.22.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.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", - "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", - "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", - "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", - "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", - "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", - "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", - "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@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-transform-flow-strip-types": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.23.3.tgz", - "integrity": "sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-flow": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.23.6", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", - "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", - "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", - "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", - "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", - "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@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-transform-member-expression-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", - "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", - "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.23.9", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz", - "integrity": "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==", - "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", - "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", - "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", - "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@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-transform-numeric-separator": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", - "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", - "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", - "dependencies": { - "@babel/compat-data": "^7.23.3", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", - "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", - "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@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-transform-optional-chaining": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", - "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", - "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", - "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", - "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@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-transform-property-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", - "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz", - "integrity": "sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.23.4", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", - "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/types": "^7.23.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", - "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", - "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", - "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", - "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz", - "integrity": "sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ==", - "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", - "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", - "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", - "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", - "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", - "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.23.6", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", - "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", - "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", - "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", - "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", - "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.23.9", - "resolved": "https://registry.npmmirror.com/@babel/preset-env/-/preset-env-7.23.9.tgz", - "integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==", - "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@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.23.3", - "@babel/plugin-syntax-import-attributes": "^7.23.3", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.9", - "@babel/plugin-transform-async-to-generator": "^7.23.3", - "@babel/plugin-transform-block-scoped-functions": "^7.23.3", - "@babel/plugin-transform-block-scoping": "^7.23.4", - "@babel/plugin-transform-class-properties": "^7.23.3", - "@babel/plugin-transform-class-static-block": "^7.23.4", - "@babel/plugin-transform-classes": "^7.23.8", - "@babel/plugin-transform-computed-properties": "^7.23.3", - "@babel/plugin-transform-destructuring": "^7.23.3", - "@babel/plugin-transform-dotall-regex": "^7.23.3", - "@babel/plugin-transform-duplicate-keys": "^7.23.3", - "@babel/plugin-transform-dynamic-import": "^7.23.4", - "@babel/plugin-transform-exponentiation-operator": "^7.23.3", - "@babel/plugin-transform-export-namespace-from": "^7.23.4", - "@babel/plugin-transform-for-of": "^7.23.6", - "@babel/plugin-transform-function-name": "^7.23.3", - "@babel/plugin-transform-json-strings": "^7.23.4", - "@babel/plugin-transform-literals": "^7.23.3", - "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", - "@babel/plugin-transform-member-expression-literals": "^7.23.3", - "@babel/plugin-transform-modules-amd": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-modules-systemjs": "^7.23.9", - "@babel/plugin-transform-modules-umd": "^7.23.3", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.23.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", - "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.23.4", - "@babel/plugin-transform-object-super": "^7.23.3", - "@babel/plugin-transform-optional-catch-binding": "^7.23.4", - "@babel/plugin-transform-optional-chaining": "^7.23.4", - "@babel/plugin-transform-parameters": "^7.23.3", - "@babel/plugin-transform-private-methods": "^7.23.3", - "@babel/plugin-transform-private-property-in-object": "^7.23.4", - "@babel/plugin-transform-property-literals": "^7.23.3", - "@babel/plugin-transform-regenerator": "^7.23.3", - "@babel/plugin-transform-reserved-words": "^7.23.3", - "@babel/plugin-transform-shorthand-properties": "^7.23.3", - "@babel/plugin-transform-spread": "^7.23.3", - "@babel/plugin-transform-sticky-regex": "^7.23.3", - "@babel/plugin-transform-template-literals": "^7.23.3", - "@babel/plugin-transform-typeof-symbol": "^7.23.3", - "@babel/plugin-transform-unicode-escapes": "^7.23.3", - "@babel/plugin-transform-unicode-property-regex": "^7.23.3", - "@babel/plugin-transform-unicode-regex": "^7.23.3", - "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", - "core-js-compat": "^3.31.0", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-flow": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/preset-flow/-/preset-flow-7.23.3.tgz", - "integrity": "sha512-7yn6hl8RIv+KNk6iIrGZ+D06VhVY35wLVf23Cz/mMu1zOr7u4MMP4j0nZ9tLf8+4ZFpnib8cFYgB/oYg9hfswA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-transform-flow-strip-types": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmmirror.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmmirror.com/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", - "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-typescript": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/register": { - "version": "7.23.7", - "resolved": "https://registry.npmmirror.com/@babel/register/-/register-7.23.7.tgz", - "integrity": "sha512-EjJeB6+kvpk+Y5DAkEAmbOBEFkh9OASx0huoEkqYTFxAZHzOAX2Oh5uwAUuL2rUddqfM0SA+KPXV2TbzoZ2kvQ==", - "dependencies": { - "clone-deep": "^4.0.1", - "find-cache-dir": "^2.0.0", - "make-dir": "^2.1.0", - "pirates": "^4.0.6", - "source-map-support": "^0.5.16" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/register/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@babel/register/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/@babel/register/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmmirror.com/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/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmmirror.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, - "node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.23.9", - "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.23.9.tgz", - "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.9", - "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.23.9.tgz", - "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.23.9", - "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.23.9.tgz", - "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", - "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmmirror.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@eslint/js": { - "version": "8.56.0", - "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-8.56.0.tgz", - "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmmirror.com/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - }, - "node_modules/@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmmirror.com/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true - }, - "node_modules/@isaacs/ttlcache": { - "version": "1.4.1", - "resolved": "https://registry.npmmirror.com/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", - "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmmirror.com/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/console/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@jest/console/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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/console/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/console/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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/core": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@jest/core/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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/core/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/core/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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/create-cache-key-function": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", - "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", - "dependencies": { - "@jest/types": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@jest/reporters/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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/reporters/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/reporters/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", - "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@jest/reporters/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@jest/reporters/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@jest/reporters/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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/reporters/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmmirror.com/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmmirror.com/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@jest/transform/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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/transform/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/transform/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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/types": { - "version": "29.6.3", - "resolved": "https://registry.npmmirror.com/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@jest/types/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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/types/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@jest/types/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.22", - "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { - "version": "5.1.1-v1", - "resolved": "https://registry.npmmirror.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", - "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", - "dev": true, - "dependencies": { - "eslint-scope": "5.1.1" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@react-native-community/cli": { - "version": "12.3.0", - "resolved": "https://registry.npmmirror.com/@react-native-community/cli/-/cli-12.3.0.tgz", - "integrity": "sha512-XeQohi2E+S2+MMSz97QcEZ/bWpi8sfKiQg35XuYeJkc32Til2g0b97jRpn0/+fV0BInHoG1CQYWwHA7opMsrHg==", - "dependencies": { - "@react-native-community/cli-clean": "12.3.0", - "@react-native-community/cli-config": "12.3.0", - "@react-native-community/cli-debugger-ui": "12.3.0", - "@react-native-community/cli-doctor": "12.3.0", - "@react-native-community/cli-hermes": "12.3.0", - "@react-native-community/cli-plugin-metro": "12.3.0", - "@react-native-community/cli-server-api": "12.3.0", - "@react-native-community/cli-tools": "12.3.0", - "@react-native-community/cli-types": "12.3.0", - "chalk": "^4.1.2", - "commander": "^9.4.1", - "deepmerge": "^4.3.0", - "execa": "^5.0.0", - "find-up": "^4.1.0", - "fs-extra": "^8.1.0", - "graceful-fs": "^4.1.3", - "prompts": "^2.4.2", - "semver": "^7.5.2" - }, - "bin": { - "react-native": "build/bin.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-native-community/cli-clean": { - "version": "12.3.0", - "resolved": "https://registry.npmmirror.com/@react-native-community/cli-clean/-/cli-clean-12.3.0.tgz", - "integrity": "sha512-iAgLCOWYRGh9ukr+eVQnhkV/OqN3V2EGd/in33Ggn/Mj4uO6+oUncXFwB+yjlyaUNz6FfjudhIz09yYGSF+9sg==", - "dependencies": { - "@react-native-community/cli-tools": "12.3.0", - "chalk": "^4.1.2", - "execa": "^5.0.0" - } - }, - "node_modules/@react-native-community/cli-clean/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-clean/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@react-native-community/cli-clean/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-clean/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@react-native-community/cli-clean/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-clean/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-config": { - "version": "12.3.0", - "resolved": "https://registry.npmmirror.com/@react-native-community/cli-config/-/cli-config-12.3.0.tgz", - "integrity": "sha512-BrTn5ndFD9uOxO8kxBQ32EpbtOvAsQExGPI7SokdI4Zlve70FziLtTq91LTlTUgMq1InVZn/jJb3VIDk6BTInQ==", - "dependencies": { - "@react-native-community/cli-tools": "12.3.0", - "chalk": "^4.1.2", - "cosmiconfig": "^5.1.0", - "deepmerge": "^4.3.0", - "glob": "^7.1.3", - "joi": "^17.2.1" - } - }, - "node_modules/@react-native-community/cli-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@react-native-community/cli-config/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-config/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@react-native-community/cli-config/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-config/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-debugger-ui": { - "version": "12.3.0", - "resolved": "https://registry.npmmirror.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-12.3.0.tgz", - "integrity": "sha512-w3b0iwjQlk47GhZWHaeTG8kKH09NCMUJO729xSdMBXE8rlbm4kHpKbxQY9qKb6NlfWSJN4noGY+FkNZS2rRwnQ==", - "dependencies": { - "serve-static": "^1.13.1" - } - }, - "node_modules/@react-native-community/cli-doctor": { - "version": "12.3.0", - "resolved": "https://registry.npmmirror.com/@react-native-community/cli-doctor/-/cli-doctor-12.3.0.tgz", - "integrity": "sha512-BPCwNNesoQMkKsxB08Ayy6URgGQ8Kndv6mMhIvJSNdST3J1+x3ehBHXzG9B9Vfi+DrTKRb8lmEl/b/7VkDlPkA==", - "dependencies": { - "@react-native-community/cli-config": "12.3.0", - "@react-native-community/cli-platform-android": "12.3.0", - "@react-native-community/cli-platform-ios": "12.3.0", - "@react-native-community/cli-tools": "12.3.0", - "chalk": "^4.1.2", - "command-exists": "^1.2.8", - "deepmerge": "^4.3.0", - "envinfo": "^7.10.0", - "execa": "^5.0.0", - "hermes-profile-transformer": "^0.0.6", - "ip": "^1.1.5", - "node-stream-zip": "^1.9.1", - "ora": "^5.4.1", - "semver": "^7.5.2", - "strip-ansi": "^5.2.0", - "wcwidth": "^1.0.1", - "yaml": "^2.2.1" - } - }, - "node_modules/@react-native-community/cli-doctor/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/@react-native-community/cli-doctor/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-doctor/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@react-native-community/cli-doctor/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-doctor/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@react-native-community/cli-doctor/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-doctor/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@react-native-community/cli-doctor/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-doctor/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@react-native-community/cli-doctor/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-doctor/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/@react-native-community/cli-hermes": { - "version": "12.3.0", - "resolved": "https://registry.npmmirror.com/@react-native-community/cli-hermes/-/cli-hermes-12.3.0.tgz", - "integrity": "sha512-G6FxpeZBO4AimKZwtWR3dpXRqTvsmEqlIkkxgwthdzn3LbVjDVIXKpVYU9PkR5cnT+KuAUxO0WwthrJ6Nmrrlg==", - "dependencies": { - "@react-native-community/cli-platform-android": "12.3.0", - "@react-native-community/cli-tools": "12.3.0", - "chalk": "^4.1.2", - "hermes-profile-transformer": "^0.0.6", - "ip": "^1.1.5" - } - }, - "node_modules/@react-native-community/cli-hermes/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-hermes/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@react-native-community/cli-hermes/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-hermes/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@react-native-community/cli-hermes/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-hermes/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-platform-android": { - "version": "12.3.0", - "resolved": "https://registry.npmmirror.com/@react-native-community/cli-platform-android/-/cli-platform-android-12.3.0.tgz", - "integrity": "sha512-VU1NZw63+GLU2TnyQ919bEMThpHQ/oMFju9MCfrd3pyPJz4Sn+vc3NfnTDUVA5Z5yfLijFOkHIHr4vo/C9bjnw==", - "dependencies": { - "@react-native-community/cli-tools": "12.3.0", - "chalk": "^4.1.2", - "execa": "^5.0.0", - "fast-xml-parser": "^4.2.4", - "glob": "^7.1.3", - "logkitty": "^0.7.1" - } - }, - "node_modules/@react-native-community/cli-platform-android/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-platform-android/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@react-native-community/cli-platform-android/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-platform-android/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@react-native-community/cli-platform-android/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-platform-android/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-platform-ios": { - "version": "12.3.0", - "resolved": "https://registry.npmmirror.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-12.3.0.tgz", - "integrity": "sha512-H95Sgt3wT7L8V75V0syFJDtv4YgqK5zbu69ko4yrXGv8dv2EBi6qZP0VMmkqXDamoPm9/U7tDTdbcf26ctnLfg==", - "dependencies": { - "@react-native-community/cli-tools": "12.3.0", - "chalk": "^4.1.2", - "execa": "^5.0.0", - "fast-xml-parser": "^4.0.12", - "glob": "^7.1.3", - "ora": "^5.4.1" - } - }, - "node_modules/@react-native-community/cli-platform-ios/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-platform-ios/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@react-native-community/cli-platform-ios/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-platform-ios/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@react-native-community/cli-platform-ios/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-platform-ios/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-plugin-metro": { - "version": "12.3.0", - "resolved": "https://registry.npmmirror.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-12.3.0.tgz", - "integrity": "sha512-tYNHIYnNmxrBcsqbE2dAnLMzlKI3Cp1p1xUgTrNaOMsGPDN1epzNfa34n6Nps3iwKElSL7Js91CzYNqgTalucA==" - }, - "node_modules/@react-native-community/cli-server-api": { - "version": "12.3.0", - "resolved": "https://registry.npmmirror.com/@react-native-community/cli-server-api/-/cli-server-api-12.3.0.tgz", - "integrity": "sha512-Rode8NrdyByC+lBKHHn+/W8Zu0c+DajJvLmOWbe2WY/ECvnwcd9MHHbu92hlT2EQaJ9LbLhGrSbQE3cQy9EOCw==", - "dependencies": { - "@react-native-community/cli-debugger-ui": "12.3.0", - "@react-native-community/cli-tools": "12.3.0", - "compression": "^1.7.1", - "connect": "^3.6.5", - "errorhandler": "^1.5.1", - "nocache": "^3.0.1", - "pretty-format": "^26.6.2", - "serve-static": "^1.13.1", - "ws": "^7.5.1" - } - }, - "node_modules/@react-native-community/cli-server-api/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmmirror.com/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@react-native-community/cli-server-api/node_modules/@types/yargs": { - "version": "15.0.19", - "resolved": "https://registry.npmmirror.com/@types/yargs/-/yargs-15.0.19.tgz", - "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@react-native-community/cli-server-api/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-server-api/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@react-native-community/cli-server-api/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-server-api/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@react-native-community/cli-server-api/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-server-api/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@react-native-community/cli-server-api/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "node_modules/@react-native-community/cli-server-api/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-tools": { - "version": "12.3.0", - "resolved": "https://registry.npmmirror.com/@react-native-community/cli-tools/-/cli-tools-12.3.0.tgz", - "integrity": "sha512-2GafnCr8D88VdClwnm9KZfkEb+lzVoFdr/7ybqhdeYM0Vnt/tr2N+fM1EQzwI1DpzXiBzTYemw8GjRq+Utcz2Q==", - "dependencies": { - "appdirsjs": "^1.2.4", - "chalk": "^4.1.2", - "find-up": "^5.0.0", - "mime": "^2.4.1", - "node-fetch": "^2.6.0", - "open": "^6.2.0", - "ora": "^5.4.1", - "semver": "^7.5.2", - "shell-quote": "^1.7.3", - "sudo-prompt": "^9.0.0" - } - }, - "node_modules/@react-native-community/cli-tools/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-tools/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@react-native-community/cli-tools/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-tools/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@react-native-community/cli-tools/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@react-native-community/cli-tools/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli-tools/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@react-native-community/cli-tools/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@react-native-community/cli-tools/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@react-native-community/cli-tools/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-tools/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-native-community/cli-tools/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/@react-native-community/cli-types": { - "version": "12.3.0", - "resolved": "https://registry.npmmirror.com/@react-native-community/cli-types/-/cli-types-12.3.0.tgz", - "integrity": "sha512-MgOkmrXH4zsGxhte4YqKL7d+N8ZNEd3w1wo56MZlhu5WabwCJh87wYpU5T8vyfujFLYOFuFK5jjlcbs8F4/WDw==", - "dependencies": { - "joi": "^17.2.1" - } - }, - "node_modules/@react-native-community/cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@react-native-community/cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-native-community/cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@react-native-community/cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native-community/cli/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@react-native-community/cli/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmmirror.com/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-native-community/cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-native-community/cli/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/@react-native/assets-registry": { - "version": "0.73.1", - "resolved": "https://registry.npmmirror.com/@react-native/assets-registry/-/assets-registry-0.73.1.tgz", - "integrity": "sha512-2FgAbU7uKM5SbbW9QptPPZx8N9Ke2L7bsHb+EhAanZjFZunA9PaYtyjUQ1s7HD+zDVqOQIvjkpXSv7Kejd2tqg==", - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-native/babel-plugin-codegen": { - "version": "0.73.2", - "resolved": "https://registry.npmmirror.com/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.73.2.tgz", - "integrity": "sha512-PadyFZWVaWXIBP7Q5dgEL7eAd7tnsgsLjoHJB1hIRZZuVUg1Zqe3nULwC7RFAqOtr5Qx7KXChkFFcKQ3WnZzGw==", - "dependencies": { - "@react-native/codegen": "0.73.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-native/babel-preset": { - "version": "0.73.19", - "resolved": "https://registry.npmmirror.com/@react-native/babel-preset/-/babel-preset-0.73.19.tgz", - "integrity": "sha512-ujon01uMOREZecIltQxPDmJ6xlVqAUFGI/JCSpeVYdxyXBoBH5dBb0ihj7h6LKH1q1jsnO9z4MxfddtypKkIbg==", - "dependencies": { - "@babel/core": "^7.20.0", - "@babel/plugin-proposal-async-generator-functions": "^7.0.0", - "@babel/plugin-proposal-class-properties": "^7.18.0", - "@babel/plugin-proposal-export-default-from": "^7.0.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.0", - "@babel/plugin-proposal-numeric-separator": "^7.0.0", - "@babel/plugin-proposal-object-rest-spread": "^7.20.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.0.0", - "@babel/plugin-proposal-optional-chaining": "^7.20.0", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", - "@babel/plugin-syntax-export-default-from": "^7.0.0", - "@babel/plugin-syntax-flow": "^7.18.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.0.0", - "@babel/plugin-syntax-optional-chaining": "^7.0.0", - "@babel/plugin-transform-arrow-functions": "^7.0.0", - "@babel/plugin-transform-async-to-generator": "^7.20.0", - "@babel/plugin-transform-block-scoping": "^7.0.0", - "@babel/plugin-transform-classes": "^7.0.0", - "@babel/plugin-transform-computed-properties": "^7.0.0", - "@babel/plugin-transform-destructuring": "^7.20.0", - "@babel/plugin-transform-flow-strip-types": "^7.20.0", - "@babel/plugin-transform-function-name": "^7.0.0", - "@babel/plugin-transform-literals": "^7.0.0", - "@babel/plugin-transform-modules-commonjs": "^7.0.0", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.0.0", - "@babel/plugin-transform-parameters": "^7.0.0", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.11", - "@babel/plugin-transform-react-display-name": "^7.0.0", - "@babel/plugin-transform-react-jsx": "^7.0.0", - "@babel/plugin-transform-react-jsx-self": "^7.0.0", - "@babel/plugin-transform-react-jsx-source": "^7.0.0", - "@babel/plugin-transform-runtime": "^7.0.0", - "@babel/plugin-transform-shorthand-properties": "^7.0.0", - "@babel/plugin-transform-spread": "^7.0.0", - "@babel/plugin-transform-sticky-regex": "^7.0.0", - "@babel/plugin-transform-typescript": "^7.5.0", - "@babel/plugin-transform-unicode-regex": "^7.0.0", - "@babel/template": "^7.0.0", - "@react-native/babel-plugin-codegen": "0.73.2", - "babel-plugin-transform-flow-enums": "^0.0.2", - "react-refresh": "^0.14.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@babel/core": "*" - } - }, - "node_modules/@react-native/codegen": { - "version": "0.73.2", - "resolved": "https://registry.npmmirror.com/@react-native/codegen/-/codegen-0.73.2.tgz", - "integrity": "sha512-lfy8S7umhE3QLQG5ViC4wg5N1Z+E6RnaeIw8w1voroQsXXGPB72IBozh8dAHR3+ceTxIU0KX3A8OpJI8e1+HpQ==", - "dependencies": { - "@babel/parser": "^7.20.0", - "flow-parser": "^0.206.0", - "glob": "^7.1.1", - "invariant": "^2.2.4", - "jscodeshift": "^0.14.0", - "mkdirp": "^0.5.1", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@babel/preset-env": "^7.1.6" - } - }, - "node_modules/@react-native/community-cli-plugin": { - "version": "0.73.12", - "resolved": "https://registry.npmmirror.com/@react-native/community-cli-plugin/-/community-cli-plugin-0.73.12.tgz", - "integrity": "sha512-xWU06OkC1cX++Duh/cD/Wv+oZ0oSY3yqbtxAqQA2H3Q+MQltNNJM6MqIHt1VOZSabRf/LVlR1JL6U9TXJirkaw==", - "dependencies": { - "@react-native-community/cli-server-api": "12.3.0", - "@react-native-community/cli-tools": "12.3.0", - "@react-native/dev-middleware": "0.73.7", - "@react-native/metro-babel-transformer": "0.73.13", - "chalk": "^4.0.0", - "execa": "^5.1.1", - "metro": "^0.80.3", - "metro-config": "^0.80.3", - "metro-core": "^0.80.3", - "node-fetch": "^2.2.0", - "readline": "^1.3.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-native/community-cli-plugin/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native/community-cli-plugin/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/@react-native/community-cli-plugin/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-native/community-cli-plugin/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/@react-native/community-cli-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native/community-cli-plugin/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-native/debugger-frontend": { - "version": "0.73.3", - "resolved": "https://registry.npmmirror.com/@react-native/debugger-frontend/-/debugger-frontend-0.73.3.tgz", - "integrity": "sha512-RgEKnWuoo54dh7gQhV7kvzKhXZEhpF9LlMdZolyhGxHsBqZ2gXdibfDlfcARFFifPIiaZ3lXuOVVa4ei+uPgTw==", - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-native/dev-middleware": { - "version": "0.73.7", - "resolved": "https://registry.npmmirror.com/@react-native/dev-middleware/-/dev-middleware-0.73.7.tgz", - "integrity": "sha512-BZXpn+qKp/dNdr4+TkZxXDttfx8YobDh8MFHsMk9usouLm22pKgFIPkGBV0X8Do4LBkFNPGtrnsKkWk/yuUXKg==", - "dependencies": { - "@isaacs/ttlcache": "^1.4.1", - "@react-native/debugger-frontend": "0.73.3", - "chrome-launcher": "^0.15.2", - "chromium-edge-launcher": "^1.0.0", - "connect": "^3.6.5", - "debug": "^2.2.0", - "node-fetch": "^2.2.0", - "open": "^7.0.3", - "serve-static": "^1.13.1", - "temp-dir": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-native/dev-middleware/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@react-native/dev-middleware/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/@react-native/dev-middleware/node_modules/open": { - "version": "7.4.2", - "resolved": "https://registry.npmmirror.com/open/-/open-7.4.2.tgz", - "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", - "dependencies": { - "is-docker": "^2.0.0", - "is-wsl": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@react-native/eslint-config": { - "version": "0.73.2", - "resolved": "https://registry.npmmirror.com/@react-native/eslint-config/-/eslint-config-0.73.2.tgz", - "integrity": "sha512-YzMfes19loTfbrkbYNAfHBDXX4oRBzc5wnvHs4h2GIHUj6YKs5ZK5lldqSrBJCdZAI3nuaO9Qj+t5JRwou571w==", - "dev": true, - "dependencies": { - "@babel/core": "^7.20.0", - "@babel/eslint-parser": "^7.20.0", - "@react-native/eslint-plugin": "0.73.1", - "@typescript-eslint/eslint-plugin": "^5.57.1", - "@typescript-eslint/parser": "^5.57.1", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-eslint-comments": "^3.2.0", - "eslint-plugin-ft-flow": "^2.0.1", - "eslint-plugin-jest": "^26.5.3", - "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-react": "^7.30.1", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-native": "^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "eslint": ">=8", - "prettier": ">=2" - } - }, - "node_modules/@react-native/eslint-plugin": { - "version": "0.73.1", - "resolved": "https://registry.npmmirror.com/@react-native/eslint-plugin/-/eslint-plugin-0.73.1.tgz", - "integrity": "sha512-8BNMFE8CAI7JLWLOs3u33wcwcJ821LYs5g53Xyx9GhSg0h8AygTwDrwmYb/pp04FkCNCPjKPBoaYRthQZmxgwA==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-native/gradle-plugin": { - "version": "0.73.4", - "resolved": "https://registry.npmmirror.com/@react-native/gradle-plugin/-/gradle-plugin-0.73.4.tgz", - "integrity": "sha512-PMDnbsZa+tD55Ug+W8CfqXiGoGneSSyrBZCMb5JfiB3AFST3Uj5e6lw8SgI/B6SKZF7lG0BhZ6YHZsRZ5MlXmg==", - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-native/js-polyfills": { - "version": "0.73.1", - "resolved": "https://registry.npmmirror.com/@react-native/js-polyfills/-/js-polyfills-0.73.1.tgz", - "integrity": "sha512-ewMwGcumrilnF87H4jjrnvGZEaPFCAC4ebraEK+CurDDmwST/bIicI4hrOAv+0Z0F7DEK4O4H7r8q9vH7IbN4g==", - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-native/metro-babel-transformer": { - "version": "0.73.13", - "resolved": "https://registry.npmmirror.com/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.73.13.tgz", - "integrity": "sha512-k9AQifogQfgUXPlqQSoMtX2KUhniw4XvJl+nZ4hphCH7qiMDAwuP8OmkJbz5E/N+Ro9OFuLE7ax4GlwxaTsAWg==", - "dependencies": { - "@babel/core": "^7.20.0", - "@react-native/babel-preset": "0.73.19", - "hermes-parser": "0.15.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@babel/core": "*" - } - }, - "node_modules/@react-native/metro-config": { - "version": "0.73.3", - "resolved": "https://registry.npmmirror.com/@react-native/metro-config/-/metro-config-0.73.3.tgz", - "integrity": "sha512-aIVh+lM52n7/RFDXLDiIp1vI21jc9thm2VxdkP7KwxMut7VvW+2tO38zKt74/2ker2ca0205tbf3pyCYBvV6Ww==", - "dev": true, - "dependencies": { - "@react-native/js-polyfills": "0.73.1", - "@react-native/metro-babel-transformer": "0.73.13", - "metro-config": "^0.80.3", - "metro-runtime": "^0.80.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@react-native/normalize-colors": { - "version": "0.73.2", - "resolved": "https://registry.npmmirror.com/@react-native/normalize-colors/-/normalize-colors-0.73.2.tgz", - "integrity": "sha512-bRBcb2T+I88aG74LMVHaKms2p/T8aQd8+BZ7LuuzXlRfog1bMWWn/C5i0HVuvW4RPtXQYgIlGiXVDy9Ir1So/w==" - }, - "node_modules/@react-native/typescript-config": { - "version": "0.73.1", - "resolved": "https://registry.npmmirror.com/@react-native/typescript-config/-/typescript-config-0.73.1.tgz", - "integrity": "sha512-7Wrmdp972ZO7xvDid+xRGtvX6xz47cpGj7Y7VKlUhSVFFqbOGfB5WCpY1vMr6R/fjl+Og2fRw+TETN2+JnJi0w==", - "dev": true - }, - "node_modules/@react-native/virtualized-lists": { - "version": "0.73.4", - "resolved": "https://registry.npmmirror.com/@react-native/virtualized-lists/-/virtualized-lists-0.73.4.tgz", - "integrity": "sha512-HpmLg1FrEiDtrtAbXiwCgXFYyloK/dOIPIuWW3fsqukwJEWAiTzm1nXGJ7xPU5XTHiWZ4sKup5Ebaj8z7iyWog==", - "dependencies": { - "invariant": "^2.2.4", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react-native": "*" - } - }, - "node_modules/@rneui/base": { - "version": "4.0.0-rc.7", - "resolved": "https://registry.npmmirror.com/@rneui/base/-/base-4.0.0-rc.7.tgz", - "integrity": "sha512-dffzoYek3Qp+7wJzC42QjI/Fu1HOUNxFIR88t1laDrBV5QZQB55f+Vu5zLbC80/bh1b8fYtl63HTIWpORuA3Eg==", - "dependencies": { - "@types/react-native-vector-icons": "^6.4.10", - "color": "^3.2.1", - "deepmerge": "^4.2.2", - "hoist-non-react-statics": "^3.3.2", - "react-native-ratings": "^8.1.0", - "react-native-size-matters": "^0.4.0" - }, - "peerDependencies": { - "react-native-safe-area-context": "^3.1.9 || ^4.0.0", - "react-native-vector-icons": ">7.0.0" - } - }, - "node_modules/@rneui/themed": { - "version": "4.0.0-rc.8", - "resolved": "https://registry.npmmirror.com/@rneui/themed/-/themed-4.0.0-rc.8.tgz", - "integrity": "sha512-8L/XOrL9OK/r+/iBLvx63TbIdZOXF8SIjN9eArMYm6kRbMr8m4BitXllDN8nBhBsSPNYvL6EAgjk+i2MfY4sBA==", - "peerDependencies": { - "@rneui/base": "4.0.0-rc.7" - } - }, - "node_modules/@sideway/address": { - "version": "4.1.4", - "resolved": "https://registry.npmmirror.com/@sideway/address/-/address-4.1.4.tgz", - "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", - "dependencies": { - "@hapi/hoek": "^9.0.0" - } - }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmmirror.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmmirror.com/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmmirror.com/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmmirror.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmmirror.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmmirror.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.11.10", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.11.10.tgz", - "integrity": "sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" - }, - "node_modules/@types/react": { - "version": "18.2.48", - "resolved": "https://registry.npmmirror.com/@types/react/-/react-18.2.48.tgz", - "integrity": "sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==", - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-native": { - "version": "0.70.19", - "resolved": "https://registry.npmmirror.com/@types/react-native/-/react-native-0.70.19.tgz", - "integrity": "sha512-c6WbyCgWTBgKKMESj/8b4w+zWcZSsCforson7UdXtXMecG3MxCinYi6ihhrHVPyUrVzORsvEzK8zg32z4pK6Sg==", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/react-native-vector-icons": { - "version": "6.4.18", - "resolved": "https://registry.npmmirror.com/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.18.tgz", - "integrity": "sha512-YGlNWb+k5laTBHd7+uZowB9DpIK3SXUneZqAiKQaj1jnJCZM0x71GDim5JCTMi4IFkhc9m8H/Gm28T5BjyivUw==", - "dependencies": { - "@types/react": "*", - "@types/react-native": "^0.70" - } - }, - "node_modules/@types/react-test-renderer": { - "version": "18.0.7", - "resolved": "https://registry.npmmirror.com/@types/react-test-renderer/-/react-test-renderer-18.0.7.tgz", - "integrity": "sha512-1+ANPOWc6rB3IkSnElhjv6VLlKg2dSv/OWClUyZimbLsQyBn8Js9Vtdsi3UICJ2rIQ3k2la06dkB+C92QfhKmg==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/scheduler": { - "version": "0.16.8", - "resolved": "https://registry.npmmirror.com/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" - }, - "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmmirror.com/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", - "dev": true - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" - }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmmirror.com/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmmirror.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "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/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.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" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmmirror.com/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.11.3", - "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "node_modules/anser": { - "version": "1.4.10", - "resolved": "https://registry.npmmirror.com/anser/-/anser-1.4.10.tgz", - "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==" - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-fragments": { - "version": "0.2.1", - "resolved": "https://registry.npmmirror.com/ansi-fragments/-/ansi-fragments-0.2.1.tgz", - "integrity": "sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==", - "dependencies": { - "colorette": "^1.0.7", - "slice-ansi": "^2.0.0", - "strip-ansi": "^5.0.0" - } - }, - "node_modules/ansi-fragments/node_modules/ansi-regex": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-fragments/node_modules/strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dependencies": { - "ansi-regex": "^4.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.3", - "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/appdirsjs": { - "version": "1.2.7", - "resolved": "https://registry.npmmirror.com/appdirsjs/-/appdirsjs-1.2.7.tgz", - "integrity": "sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - } - }, - "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmmirror.com/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmmirror.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmmirror.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", - "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmmirror.com/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" - }, - "node_modules/ast-types": { - "version": "0.15.2", - "resolved": "https://registry.npmmirror.com/ast-types/-/ast-types-0.15.2.tgz", - "integrity": "sha512-c27loCv9QkZinsa5ProX751khO9DJl/AcB5c2KNtA6NRvHKS0PgLfcftz72KVq504vB0Gku5s2kUZzDBvQWvHg==", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "engines": { - "node": ">=4" - } - }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, - "node_modules/asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/babel-core": { - "version": "7.0.0-bridge.0", - "resolved": "https://registry.npmmirror.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz", - "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/babel-jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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/babel-jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/babel-jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmmirror.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmmirror.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.8", - "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz", - "integrity": "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==", - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.5.0", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.9.0", - "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", - "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0", - "core-js-compat": "^3.34.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.5", - "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", - "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-transform-flow-enums": { - "version": "0.0.2", - "resolved": "https://registry.npmmirror.com/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz", - "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==", - "dependencies": { - "@babel/plugin-syntax-flow": "^7.12.1" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "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": "29.6.3", - "resolved": "https://registry.npmmirror.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmmirror.com/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.2", - "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.22.3", - "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.22.3.tgz", - "integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==", - "dependencies": { - "caniuse-lite": "^1.0.30001580", - "electron-to-chromium": "^1.4.648", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - } - }, - "node_modules/caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", - "dependencies": { - "callsites": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/caller-callsite/node_modules/callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", - "dependencies": { - "caller-callsite": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001581", - "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz", - "integrity": "sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ==" - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmmirror.com/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/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/chrome-launcher": { - "version": "0.15.2", - "resolved": "https://registry.npmmirror.com/chrome-launcher/-/chrome-launcher-0.15.2.tgz", - "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", - "dependencies": { - "@types/node": "*", - "escape-string-regexp": "^4.0.0", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0" - }, - "bin": { - "print-chrome-path": "bin/print-chrome-path.js" - }, - "engines": { - "node": ">=12.13.0" - } - }, - "node_modules/chrome-launcher/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/chromium-edge-launcher": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz", - "integrity": "sha512-pgtgjNKZ7i5U++1g1PWv75umkHvhVTDOQIZ+sjeUX9483S7Y6MUvO0lrd7ShGlQlFHMN4SwKTCq/X8hWrbv2KA==", - "dependencies": { - "@types/node": "*", - "escape-string-regexp": "^4.0.0", - "is-wsl": "^2.2.0", - "lighthouse-logger": "^1.0.0", - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - } - }, - "node_modules/chromium-edge-launcher/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/chromium-edge-launcher/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmmirror.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmmirror.com/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==" - }, - "node_modules/command-exists": { - "version": "1.2.9", - "resolved": "https://registry.npmmirror.com/command-exists/-/command-exists-1.2.9.tgz", - "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" - }, - "node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmmirror.com/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "engines": { - "node": "^12.20.0 || >=14" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmmirror.com/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" - }, - "node_modules/core-js-compat": { - "version": "3.35.1", - "resolved": "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.35.1.tgz", - "integrity": "sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw==", - "dependencies": { - "browserslist": "^4.22.2" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" - }, - "node_modules/cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", - "dependencies": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cosmiconfig/node_modules/import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", - "dependencies": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cosmiconfig/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cosmiconfig/node_modules/resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/create-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/create-jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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/create-jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/create-jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/create-jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" - }, - "node_modules/dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmmirror.com/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/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmmirror.com/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": { - "clone": "^1.0.2" - } - }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/denodeify": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/denodeify/-/denodeify-1.2.1.tgz", - "integrity": "sha512-KNTihKNmQENUZeKu5fzfpzRqR5S2VMp4gl9RFHiWzj9DfvYQPMJ6XHKNaQxaGCXwPk6y9yme3aUoaiAe+KX+vg==" - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/deprecated-react-native-prop-types": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-5.0.0.tgz", - "integrity": "sha512-cIK8KYiiGVOFsKdPMmm1L3tA/Gl+JopXL6F5+C7x39MyPsQYnP57Im/D6bNUzcborD7fcMwiwZqcBdBXXZucYQ==", - "dependencies": { - "@react-native/normalize-colors": "^0.73.0", - "invariant": "^2.2.4", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmmirror.com/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" - }, - "node_modules/electron-to-chromium": { - "version": "1.4.648", - "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.648.tgz", - "integrity": "sha512-EmFMarXeqJp9cUKu/QEciEApn0S/xRcpZWuAm32U7NgoZCimjsilKXHRO9saeEW55eHZagIDg6XTUOv32w9pjg==" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmmirror.com/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/envinfo": { - "version": "7.11.0", - "resolved": "https://registry.npmmirror.com/envinfo/-/envinfo-7.11.0.tgz", - "integrity": "sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==", - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "dependencies": { - "stackframe": "^1.3.4" - } - }, - "node_modules/errorhandler": { - "version": "1.5.1", - "resolved": "https://registry.npmmirror.com/errorhandler/-/errorhandler-1.5.1.tgz", - "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", - "dependencies": { - "accepts": "~1.3.7", - "escape-html": "~1.0.3" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "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-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.0.15", - "resolved": "https://registry.npmmirror.com/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", - "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", - "dev": true, - "dependencies": { - "asynciterator.prototype": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.1", - "es-set-tostringtag": "^2.0.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.0.1" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint": { - "version": "8.56.0", - "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.56.0.tgz", - "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.56.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint-config-prettier": { - "version": "8.10.0", - "resolved": "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", - "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-eslint-comments": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz", - "integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5", - "ignore": "^5.0.5" - }, - "engines": { - "node": ">=6.5.0" - }, - "peerDependencies": { - "eslint": ">=4.19.1" - } - }, - "node_modules/eslint-plugin-ft-flow": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/eslint-plugin-ft-flow/-/eslint-plugin-ft-flow-2.0.3.tgz", - "integrity": "sha512-Vbsd/b+LYA99jUbsL6viEUWShFaYQt2YQs3QN3f+aeszOhh2sgdcU0mjzDyD4yyBvMc8qy2uwvBBWfMzEX06tg==", - "dev": true, - "dependencies": { - "lodash": "^4.17.21", - "string-natural-compare": "^3.0.1" - }, - "engines": { - "node": ">=12.22.0" - }, - "peerDependencies": { - "@babel/eslint-parser": "^7.12.0", - "eslint": "^8.1.0" - } - }, - "node_modules/eslint-plugin-jest": { - "version": "26.9.0", - "resolved": "https://registry.npmmirror.com/eslint-plugin-jest/-/eslint-plugin-jest-26.9.0.tgz", - "integrity": "sha512-TWJxWGp1J628gxh2KhaH1H1paEdgE2J61BBF1I59c6xWeL5+D1BzMxGDN/nXAfX+aSkR5u80K+XhskK6Gwq9ng==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "^5.10.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^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-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" - }, - "peerDependenciesMeta": { - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.33.2", - "resolved": "https://registry.npmmirror.com/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", - "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.12", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.8" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmmirror.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "dev": true, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/eslint-plugin-react-native": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/eslint-plugin-react-native/-/eslint-plugin-react-native-4.1.0.tgz", - "integrity": "sha512-QLo7rzTBOl43FvVqDdq5Ql9IoElIuTdjrz9SKAXCvULvBoRZ44JGSkx9z4999ZusCsb4rK3gjS8gOGyeYqZv2Q==", - "dev": true, - "dependencies": { - "eslint-plugin-react-native-globals": "^0.1.1" - }, - "peerDependencies": { - "eslint": "^3.17.0 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react-native-globals": { - "version": "0.1.2", - "resolved": "https://registry.npmmirror.com/eslint-plugin-react-native-globals/-/eslint-plugin-react-native-globals-0.1.2.tgz", - "integrity": "sha512-9aEPf1JEpiTjcFAmmyw8eiIXmcNZOqaZyHO77wgm0/dWfT/oxC1SrIq8ET38pMxHYrcB6Uew+TzUVsBeczF88g==", - "dev": true - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmmirror.com/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/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.5.0", - "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmmirror.com/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fast-xml-parser": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/fast-xml-parser/-/fast-xml-parser-4.3.3.tgz", - "integrity": "sha512-coV/D1MhrShMvU6D0I+VAK3umz6hUaxxhL0yp/9RjfiYUfAv14rDhGQL+PLForhMdr0wq3PiV07WtkkNjJjNHg==", - "dependencies": { - "strnum": "^1.0.5" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fastq": { - "version": "1.17.0", - "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.17.0.tgz", - "integrity": "sha512-zGygtijUMT7jnk3h26kUms3BkSDp4IfIKjmnqI2tvx6nuBfiF1UqOxbnLfzdv+apBy+53oaImsKtMw/xYbW+1w==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dependencies": { - "locate-path": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/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/find-cache-dir/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/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/find-cache-dir/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/find-cache-dir/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dependencies": { - "find-up": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/find-cache-dir/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/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/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, - "node_modules/flow-enums-runtime": { - "version": "0.0.6", - "resolved": "https://registry.npmmirror.com/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", - "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==" - }, - "node_modules/flow-parser": { - "version": "0.206.0", - "resolved": "https://registry.npmmirror.com/flow-parser/-/flow-parser-0.206.0.tgz", - "integrity": "sha512-HVzoK3r6Vsg+lKvlIZzaWNBVai+FXTX1wdYhz/wVlH13tb/gOdLXmlTqy6odmTBhT5UoWUbq0k8263Qhr9d88w==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "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.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.2", - "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmmirror.com/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmmirror.com/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": "*" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/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.1", - "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hermes-estree": { - "version": "0.15.0", - "resolved": "https://registry.npmmirror.com/hermes-estree/-/hermes-estree-0.15.0.tgz", - "integrity": "sha512-lLYvAd+6BnOqWdnNbP/Q8xfl8LOGw4wVjfrNd9Gt8eoFzhNBRVD95n4l2ksfMVOoxuVyegs85g83KS9QOsxbVQ==" - }, - "node_modules/hermes-parser": { - "version": "0.15.0", - "resolved": "https://registry.npmmirror.com/hermes-parser/-/hermes-parser-0.15.0.tgz", - "integrity": "sha512-Q1uks5rjZlE9RjMMjSUCkGrEIPI5pKJILeCtK1VmTj7U4pf3wVPoo+cxfu+s4cBAPy2JzikIIdCZgBoR6x7U1Q==", - "dependencies": { - "hermes-estree": "0.15.0" - } - }, - "node_modules/hermes-profile-transformer": { - "version": "0.0.6", - "resolved": "https://registry.npmmirror.com/hermes-profile-transformer/-/hermes-profile-transformer-0.0.6.tgz", - "integrity": "sha512-cnN7bQUm65UWOy6cbGcCcZ3rpwW8Q/j4OP5aWRhEry4Z2t2aR1cjrbp0BS+KiBN0smvP1caBgAuxutvyvJILzQ==", - "dependencies": { - "source-map": "^0.7.3" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/hermes-profile-transformer/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmmirror.com/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/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/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-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/image-size": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/image-size/-/image-size-1.1.1.tgz", - "integrity": "sha512-541xKlUw6jr/6gGuk92F+mYM5zaFAc5ahphvkqvNe2bQ6gVBkd6bfrmVJ2t4KDAfikAYZyIqTnktX3i6/aQDrQ==", - "dependencies": { - "queue": "6.0.2" - }, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmmirror.com/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/ip": { - "version": "1.1.8", - "resolved": "https://registry.npmmirror.com/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==" - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmmirror.com/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "dev": true - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmmirror.com/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.11" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - } - }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmmirror.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmmirror.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmmirror.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-circus/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-circus/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-circus/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-config": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-config/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-config/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-config/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-diff/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-each/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-each/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-each/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-each/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmmirror.com/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-matcher-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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.7.0", - "resolved": "https://registry.npmmirror.com/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-message-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-message-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-message-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmmirror.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmmirror.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-resolve/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-resolve/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-resolve/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-runner/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-runner/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-runner/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-runtime/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-runtime/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-runtime/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-snapshot/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-snapshot/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-snapshot/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-snapshot/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jest-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-validate/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jest-watcher/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-watcher/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-watcher/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/joi": { - "version": "17.12.0", - "resolved": "https://registry.npmmirror.com/joi/-/joi-17.12.0.tgz", - "integrity": "sha512-HSLsmSmXz+PV9PYoi3p7cgIbj06WnEBNT28n+bbBNcPZXZFqCzzvGqpTBPujx/Z0nh1+KNQPDrNgdmQ8dq0qYw==", - "dependencies": { - "@hapi/hoek": "^9.3.0", - "@hapi/topo": "^5.1.0", - "@sideway/address": "^4.1.4", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmmirror.com/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/jsc-android": { - "version": "250231.0.0", - "resolved": "https://registry.npmmirror.com/jsc-android/-/jsc-android-250231.0.0.tgz", - "integrity": "sha512-rS46PvsjYmdmuz1OAWXY/1kCYG7pnf1TBqeTiOJr1iDz7s5DLxxC9n/ZMknLDxzYzNVfI7R95MH10emSSG1Wuw==" - }, - "node_modules/jsc-safe-url": { - "version": "0.2.4", - "resolved": "https://registry.npmmirror.com/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", - "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==" - }, - "node_modules/jscodeshift": { - "version": "0.14.0", - "resolved": "https://registry.npmmirror.com/jscodeshift/-/jscodeshift-0.14.0.tgz", - "integrity": "sha512-7eCC1knD7bLUPuSCwXsMZUH51O8jIcoVyKtI6P0XM0IVzlGjckPy3FIwQlorzbN0Sg79oK+RlohN32Mqf/lrYA==", - "dependencies": { - "@babel/core": "^7.13.16", - "@babel/parser": "^7.13.16", - "@babel/plugin-proposal-class-properties": "^7.13.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", - "@babel/plugin-proposal-optional-chaining": "^7.13.12", - "@babel/plugin-transform-modules-commonjs": "^7.13.8", - "@babel/preset-flow": "^7.13.13", - "@babel/preset-typescript": "^7.13.0", - "@babel/register": "^7.13.16", - "babel-core": "^7.0.0-bridge.0", - "chalk": "^4.1.2", - "flow-parser": "0.*", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.4", - "neo-async": "^2.5.0", - "node-dir": "^0.1.17", - "recast": "^0.21.0", - "temp": "^0.8.4", - "write-file-atomic": "^2.3.0" - }, - "bin": { - "jscodeshift": "bin/jscodeshift.js" - }, - "peerDependencies": { - "@babel/preset-env": "^7.1.6" - } - }, - "node_modules/jscodeshift/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jscodeshift/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/jscodeshift/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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/jscodeshift/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/jscodeshift/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/jscodeshift/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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/jscodeshift/node_modules/write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmmirror.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dependencies": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmmirror.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lighthouse-logger": { - "version": "1.4.2", - "resolved": "https://registry.npmmirror.com/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", - "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", - "dependencies": { - "debug": "^2.6.9", - "marky": "^1.2.2" - } - }, - "node_modules/lighthouse-logger/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/lighthouse-logger/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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/log-symbols/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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/logkitty": { - "version": "0.7.1", - "resolved": "https://registry.npmmirror.com/logkitty/-/logkitty-0.7.1.tgz", - "integrity": "sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ==", - "dependencies": { - "ansi-fragments": "^0.2.1", - "dayjs": "^1.8.15", - "yargs": "^15.1.0" - }, - "bin": { - "logkitty": "bin/logkitty.js" - } - }, - "node_modules/logkitty/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/logkitty/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/logkitty/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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/logkitty/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/logkitty/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/logkitty/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - }, - "node_modules/logkitty/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/logkitty/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmmirror.com/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/marky": { - "version": "1.2.5", - "resolved": "https://registry.npmmirror.com/marky/-/marky-1.2.5.tgz", - "integrity": "sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==" - }, - "node_modules/memoize-one": { - "version": "5.2.1", - "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/metro": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro/-/metro-0.80.5.tgz", - "integrity": "sha512-OE/CGbOgbi8BlTN1QqJgKOBaC27dS0JBQw473JcivrpgVnqIsluROA7AavEaTVUrB9wPUZvoNVDROn5uiM2jfw==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@babel/core": "^7.20.0", - "@babel/generator": "^7.20.0", - "@babel/parser": "^7.20.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.20.0", - "@babel/types": "^7.20.0", - "accepts": "^1.3.7", - "chalk": "^4.0.0", - "ci-info": "^2.0.0", - "connect": "^3.6.5", - "debug": "^2.2.0", - "denodeify": "^1.2.1", - "error-stack-parser": "^2.0.6", - "graceful-fs": "^4.2.4", - "hermes-parser": "0.18.2", - "image-size": "^1.0.2", - "invariant": "^2.2.4", - "jest-worker": "^29.6.3", - "jsc-safe-url": "^0.2.2", - "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.80.5", - "metro-cache": "0.80.5", - "metro-cache-key": "0.80.5", - "metro-config": "0.80.5", - "metro-core": "0.80.5", - "metro-file-map": "0.80.5", - "metro-resolver": "0.80.5", - "metro-runtime": "0.80.5", - "metro-source-map": "0.80.5", - "metro-symbolicate": "0.80.5", - "metro-transform-plugins": "0.80.5", - "metro-transform-worker": "0.80.5", - "mime-types": "^2.1.27", - "node-fetch": "^2.2.0", - "nullthrows": "^1.1.1", - "rimraf": "^3.0.2", - "serialize-error": "^2.1.0", - "source-map": "^0.5.6", - "strip-ansi": "^6.0.0", - "throat": "^5.0.0", - "ws": "^7.5.1", - "yargs": "^17.6.2" - }, - "bin": { - "metro": "src/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/metro-babel-transformer": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro-babel-transformer/-/metro-babel-transformer-0.80.5.tgz", - "integrity": "sha512-sxH6hcWCorhTbk4kaShCWsadzu99WBL4Nvq4m/sDTbp32//iGuxtAnUK+ZV+6IEygr2u9Z0/4XoZ8Sbcl71MpA==", - "dependencies": { - "@babel/core": "^7.20.0", - "hermes-parser": "0.18.2", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/metro-babel-transformer/node_modules/hermes-estree": { - "version": "0.18.2", - "resolved": "https://registry.npmmirror.com/hermes-estree/-/hermes-estree-0.18.2.tgz", - "integrity": "sha512-KoLsoWXJ5o81nit1wSyEZnWUGy9cBna9iYMZBR7skKh7okYAYKqQ9/OczwpMHn/cH0hKDyblulGsJ7FknlfVxQ==" - }, - "node_modules/metro-babel-transformer/node_modules/hermes-parser": { - "version": "0.18.2", - "resolved": "https://registry.npmmirror.com/hermes-parser/-/hermes-parser-0.18.2.tgz", - "integrity": "sha512-1eQfvib+VPpgBZ2zYKQhpuOjw1tH+Emuib6QmjkJWJMhyjM8xnXMvA+76o9LhF0zOAJDZgPfQhg43cyXEyl5Ew==", - "dependencies": { - "hermes-estree": "0.18.2" - } - }, - "node_modules/metro-cache": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro-cache/-/metro-cache-0.80.5.tgz", - "integrity": "sha512-2u+dQ4PZwmC7eZo9uMBNhQQMig9f+w4QWBZwXCdVy/RYOHM0eObgGdMEOwODo73uxie82T9lWzxr3aZOZ+Nqtw==", - "dependencies": { - "metro-core": "0.80.5", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/metro-cache-key": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro-cache-key/-/metro-cache-key-0.80.5.tgz", - "integrity": "sha512-fr3QLZUarsB3tRbVcmr34kCBsTHk0Sh9JXGvBY/w3b2lbre+Lq5gtgLyFElHPecGF7o4z1eK9r3ubxtScHWcbA==", - "engines": { - "node": ">=18" - } - }, - "node_modules/metro-config": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro-config/-/metro-config-0.80.5.tgz", - "integrity": "sha512-elqo/lwvF+VjZ1OPyvmW/9hSiGlmcqu+rQvDKw5F5WMX48ZC+ySTD1WcaD7e97pkgAlJHVYqZ98FCjRAYOAFRQ==", - "dependencies": { - "connect": "^3.6.5", - "cosmiconfig": "^5.0.5", - "jest-validate": "^29.6.3", - "metro": "0.80.5", - "metro-cache": "0.80.5", - "metro-core": "0.80.5", - "metro-runtime": "0.80.5" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/metro-core": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro-core/-/metro-core-0.80.5.tgz", - "integrity": "sha512-vkLuaBhnZxTVpaZO8ZJVEHzjaqSXpOdpAiztSZ+NDaYM6jEFgle3/XIbLW91jTSf2+T8Pj5yB1G7KuOX+BcVwg==", - "dependencies": { - "lodash.throttle": "^4.1.1", - "metro-resolver": "0.80.5" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/metro-file-map": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro-file-map/-/metro-file-map-0.80.5.tgz", - "integrity": "sha512-bKCvJ05drjq6QhQxnDUt3I8x7bTcHo3IIKVobEr14BK++nmxFGn/BmFLRzVBlghM6an3gqwpNEYxS5qNc+VKcg==", - "dependencies": { - "anymatch": "^3.0.3", - "debug": "^2.2.0", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.4", - "invariant": "^2.2.4", - "jest-worker": "^29.6.3", - "micromatch": "^4.0.4", - "node-abort-controller": "^3.1.1", - "nullthrows": "^1.1.1", - "walker": "^1.0.7" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/metro-file-map/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/metro-file-map/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/metro-minify-terser": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro-minify-terser/-/metro-minify-terser-0.80.5.tgz", - "integrity": "sha512-S7oZLLcab6YXUT6jYFX/ZDMN7Fq6xBGGAG8liMFU1UljX6cTcEC2u+UIafYgCLrdVexp/+ClxrIetVPZ5LtL/g==", - "dependencies": { - "terser": "^5.15.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/metro-resolver": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro-resolver/-/metro-resolver-0.80.5.tgz", - "integrity": "sha512-haJ/Hveio3zv/Fr4eXVdKzjUeHHDogYok7OpRqPSXGhTXisNXB+sLN7CpcUrCddFRUDLnVaqQOYwhYsFndgUwA==", - "engines": { - "node": ">=18" - } - }, - "node_modules/metro-runtime": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro-runtime/-/metro-runtime-0.80.5.tgz", - "integrity": "sha512-L0syTWJUdWzfUmKgkScr6fSBVTh6QDr8eKEkRtn40OBd8LPagrJGySBboWSgbyn9eIb4ayW3Y347HxgXBSAjmg==", - "dependencies": { - "@babel/runtime": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/metro-source-map": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro-source-map/-/metro-source-map-0.80.5.tgz", - "integrity": "sha512-DwSF4l03mKPNqCtyQ6K23I43qzU1BViAXnuH81eYWdHglP+sDlPpY+/7rUahXEo6qXEHXfAJgVoo1sirbXbmsQ==", - "dependencies": { - "@babel/traverse": "^7.20.0", - "@babel/types": "^7.20.0", - "invariant": "^2.2.4", - "metro-symbolicate": "0.80.5", - "nullthrows": "^1.1.1", - "ob1": "0.80.5", - "source-map": "^0.5.6", - "vlq": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/metro-source-map/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/metro-symbolicate": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro-symbolicate/-/metro-symbolicate-0.80.5.tgz", - "integrity": "sha512-IsM4mTYvmo9JvIqwEkCZ5+YeDVPST78Q17ZgljfLdHLSpIivOHp9oVoiwQ/YGbLx0xRHRIS/tKiXueWBnj3UWA==", - "dependencies": { - "invariant": "^2.2.4", - "metro-source-map": "0.80.5", - "nullthrows": "^1.1.1", - "source-map": "^0.5.6", - "through2": "^2.0.1", - "vlq": "^1.0.0" - }, - "bin": { - "metro-symbolicate": "src/index.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/metro-symbolicate/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/metro-transform-plugins": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro-transform-plugins/-/metro-transform-plugins-0.80.5.tgz", - "integrity": "sha512-7IdlTqK/k5+qE3RvIU5QdCJUPk4tHWEqgVuYZu8exeW+s6qOJ66hGIJjXY/P7ccucqF+D4nsbAAW5unkoUdS6g==", - "dependencies": { - "@babel/core": "^7.20.0", - "@babel/generator": "^7.20.0", - "@babel/template": "^7.0.0", - "@babel/traverse": "^7.20.0", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/metro-transform-worker": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/metro-transform-worker/-/metro-transform-worker-0.80.5.tgz", - "integrity": "sha512-Q1oM7hfP+RBgAtzRFBDjPhArELUJF8iRCZ8OidqCpYzQJVGuJZ7InSnIf3hn1JyqiUQwv2f1LXBO78i2rAjzyA==", - "dependencies": { - "@babel/core": "^7.20.0", - "@babel/generator": "^7.20.0", - "@babel/parser": "^7.20.0", - "@babel/types": "^7.20.0", - "metro": "0.80.5", - "metro-babel-transformer": "0.80.5", - "metro-cache": "0.80.5", - "metro-cache-key": "0.80.5", - "metro-minify-terser": "0.80.5", - "metro-source-map": "0.80.5", - "metro-transform-plugins": "0.80.5", - "nullthrows": "^1.1.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/metro/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/metro/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/metro/node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" - }, - "node_modules/metro/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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/metro/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/metro/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/metro/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/metro/node_modules/hermes-estree": { - "version": "0.18.2", - "resolved": "https://registry.npmmirror.com/hermes-estree/-/hermes-estree-0.18.2.tgz", - "integrity": "sha512-KoLsoWXJ5o81nit1wSyEZnWUGy9cBna9iYMZBR7skKh7okYAYKqQ9/OczwpMHn/cH0hKDyblulGsJ7FknlfVxQ==" - }, - "node_modules/metro/node_modules/hermes-parser": { - "version": "0.18.2", - "resolved": "https://registry.npmmirror.com/hermes-parser/-/hermes-parser-0.18.2.tgz", - "integrity": "sha512-1eQfvib+VPpgBZ2zYKQhpuOjw1tH+Emuib6QmjkJWJMhyjM8xnXMvA+76o9LhF0zOAJDZgPfQhg43cyXEyl5Ew==", - "dependencies": { - "hermes-estree": "0.18.2" - } - }, - "node_modules/metro/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/metro/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/metro/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/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.8", - "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/nocache": { - "version": "3.0.4", - "resolved": "https://registry.npmmirror.com/nocache/-/nocache-3.0.4.tgz", - "integrity": "sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" - }, - "node_modules/node-dir": { - "version": "0.1.17", - "resolved": "https://registry.npmmirror.com/node-dir/-/node-dir-0.1.17.tgz", - "integrity": "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==", - "dependencies": { - "minimatch": "^3.0.2" - }, - "engines": { - "node": ">= 0.10.5" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==" - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" - }, - "node_modules/node-stream-zip": { - "version": "1.15.0", - "resolved": "https://registry.npmmirror.com/node-stream-zip/-/node-stream-zip-1.15.0.tgz", - "integrity": "sha512-LN4fydt9TqhZhThkZIVQnF9cwjU3qmUH9h78Mx/K7d3VvfRqqwthLwJEUOEL0QPZ0XQmNN7be5Ggit5+4dq3Bw==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/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/nullthrows": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" - }, - "node_modules/ob1": { - "version": "0.80.5", - "resolved": "https://registry.npmmirror.com/ob1/-/ob1-0.80.5.tgz", - "integrity": "sha512-zYDMnnNrFi/1Tqh0vo3PE4p97Tpl9/4MP2k2ECvkbLOZzQuAYZJLTUYVLZb7hJhbhjT+JJxAwBGS8iu5hCSd1w==", - "engines": { - "node": ">=18" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmmirror.com/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmmirror.com/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.hasown": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/object.hasown/-/object.hasown-1.1.3.tgz", - "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmmirror.com/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/open": { - "version": "6.4.0", - "resolved": "https://registry.npmmirror.com/open/-/open-6.4.0.tgz", - "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", - "dependencies": { - "is-wsl": "^1.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/open/node_modules/is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/ora/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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/ora/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/ora/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/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/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "engines": { - "node": ">=6" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "6.0.4", - "resolved": "https://registry.npmmirror.com/pure-rand/-/pure-rand-6.0.4.tgz", - "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", - "dev": true - }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmmirror.com/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "dependencies": { - "inherits": "~2.0.3" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmmirror.com/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-devtools-core": { - "version": "4.28.5", - "resolved": "https://registry.npmmirror.com/react-devtools-core/-/react-devtools-core-4.28.5.tgz", - "integrity": "sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA==", - "dependencies": { - "shell-quote": "^1.6.1", - "ws": "^7" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/react-native": { - "version": "0.73.2", - "resolved": "https://registry.npmmirror.com/react-native/-/react-native-0.73.2.tgz", - "integrity": "sha512-7zj9tcUYpJUBdOdXY6cM8RcXYWkyql4kMyGZflW99E5EuFPoC7Ti+ZQSl7LP9ZPzGD0vMfslwyDW0I4tPWUCFw==", - "dependencies": { - "@jest/create-cache-key-function": "^29.6.3", - "@react-native-community/cli": "12.3.0", - "@react-native-community/cli-platform-android": "12.3.0", - "@react-native-community/cli-platform-ios": "12.3.0", - "@react-native/assets-registry": "0.73.1", - "@react-native/codegen": "0.73.2", - "@react-native/community-cli-plugin": "0.73.12", - "@react-native/gradle-plugin": "0.73.4", - "@react-native/js-polyfills": "0.73.1", - "@react-native/normalize-colors": "0.73.2", - "@react-native/virtualized-lists": "0.73.4", - "abort-controller": "^3.0.0", - "anser": "^1.4.9", - "ansi-regex": "^5.0.0", - "base64-js": "^1.5.1", - "deprecated-react-native-prop-types": "^5.0.0", - "event-target-shim": "^5.0.1", - "flow-enums-runtime": "^0.0.6", - "invariant": "^2.2.4", - "jest-environment-node": "^29.6.3", - "jsc-android": "^250231.0.0", - "memoize-one": "^5.0.0", - "metro-runtime": "^0.80.3", - "metro-source-map": "^0.80.3", - "mkdirp": "^0.5.1", - "nullthrows": "^1.1.1", - "pretty-format": "^26.5.2", - "promise": "^8.3.0", - "react-devtools-core": "^4.27.7", - "react-refresh": "^0.14.0", - "react-shallow-renderer": "^16.15.0", - "regenerator-runtime": "^0.13.2", - "scheduler": "0.24.0-canary-efb381bbf-20230505", - "stacktrace-parser": "^0.1.10", - "whatwg-fetch": "^3.0.0", - "ws": "^6.2.2", - "yargs": "^17.6.2" - }, - "bin": { - "react-native": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "18.2.0" - } - }, - "node_modules/react-native-ratings": { - "version": "8.1.0", - "resolved": "https://registry.npmmirror.com/react-native-ratings/-/react-native-ratings-8.1.0.tgz", - "integrity": "sha512-+QOJ4G3NjVkI1D+tk4EGx1dCvVfbD2nQdkrj9cXrcAoEiwmbep4z4bZbCKmWMpQ5h2dqbxABU8/eBnbDmvAc3g==", - "dependencies": { - "lodash": "^4.17.15" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-safe-area-context": { - "version": "4.8.2", - "resolved": "https://registry.npmmirror.com/react-native-safe-area-context/-/react-native-safe-area-context-4.8.2.tgz", - "integrity": "sha512-ffUOv8BJQ6RqO3nLml5gxJ6ab3EestPiyWekxdzO/1MQ7NF8fW1Mzh1C5QE9yq573Xefnc7FuzGXjtesZGv7cQ==", - "peer": true, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-size-matters": { - "version": "0.4.2", - "resolved": "https://registry.npmmirror.com/react-native-size-matters/-/react-native-size-matters-0.4.2.tgz", - "integrity": "sha512-DKE3f/sdcozd24oASgkP1iGg+YU3HoajRa5k3a4wkRzpiqREq8SGX12Y5zBgAt/8IivLQoTMYkyQu1/Giuy+zQ==", - "peerDependencies": { - "react-native": "*" - } - }, - "node_modules/react-native-vector-icons": { - "version": "10.0.3", - "resolved": "https://registry.npmmirror.com/react-native-vector-icons/-/react-native-vector-icons-10.0.3.tgz", - "integrity": "sha512-ZgVlV5AdQgnPHHvBEihGf2xwyziT1acpXV1U+WfCgCv3lcEeCRsmwAsBU+kUSNsU+8TcWVsX04kdI6qUaS8D7w==", - "peer": true, - "dependencies": { - "prop-types": "^15.7.2", - "yargs": "^16.1.1" - }, - "bin": { - "fa-upgrade.sh": "bin/fa-upgrade.sh", - "fa5-upgrade": "bin/fa5-upgrade.sh", - "fa6-upgrade": "bin/fa6-upgrade.sh", - "generate-icon": "bin/generate-icon.js" - } - }, - "node_modules/react-native-vector-icons/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmmirror.com/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "peer": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/react-native-vector-icons/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "peer": true, - "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/react-native-vector-icons/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "peer": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/react-native-webview": { - "version": "13.6.4", - "resolved": "https://registry.npmmirror.com/react-native-webview/-/react-native-webview-13.6.4.tgz", - "integrity": "sha512-AdgmaMBHPcyERTvng9eSGgHX6AleyUlSusWAxngSOSdiYGgHW81T6C5A8j/ImJAF9oZg0bQDxp43Hu56tzENZQ==", - "dependencies": { - "escape-string-regexp": "2.0.0", - "invariant": "2.2.4" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, - "node_modules/react-native-webview/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/react-native/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmmirror.com/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/react-native/node_modules/@types/yargs": { - "version": "15.0.19", - "resolved": "https://registry.npmmirror.com/@types/yargs/-/yargs-15.0.19.tgz", - "integrity": "sha512-2XUaGVmyQjgyAZldf0D0c14vvo/yv0MhQBSTJcejMMaitsn3nxCB6TmH4G0ZQf+uxROOa9mpanoSm8h6SG/1ZA==", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/react-native/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/react-native/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/react-native/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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-native/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/react-native/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/react-native/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmmirror.com/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/react-native/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmmirror.com/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "node_modules/react-native/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, - "node_modules/react-native/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/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-native/node_modules/ws": { - "version": "6.2.2", - "resolved": "https://registry.npmmirror.com/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "dependencies": { - "async-limiter": "~1.0.0" - } - }, - "node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-shallow-renderer": { - "version": "16.15.0", - "resolved": "https://registry.npmmirror.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", - "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", - "dependencies": { - "object-assign": "^4.1.1", - "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-test-renderer": { - "version": "18.2.0", - "resolved": "https://registry.npmmirror.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz", - "integrity": "sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==", - "dev": true, - "dependencies": { - "react-is": "^18.2.0", - "react-shallow-renderer": "^16.15.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-test-renderer/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/react-test-renderer/node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dev": true, - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readline": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/readline/-/readline-1.3.0.tgz", - "integrity": "sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==" - }, - "node_modules/recast": { - "version": "0.21.5", - "resolved": "https://registry.npmmirror.com/recast/-/recast-0.21.5.tgz", - "integrity": "sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg==", - "dependencies": { - "ast-types": "0.15.2", - "esprima": "~4.0.0", - "source-map": "~0.6.1", - "tslib": "^2.0.1" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", - "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmmirror.com/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmmirror.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmmirror.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/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-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/safe-array-concat/-/safe-array-concat-1.1.0.tgz", - "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "node_modules/safe-regex-test": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.0.2.tgz", - "integrity": "sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/scheduler": { - "version": "0.24.0-canary-efb381bbf-20230505", - "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.24.0-canary-efb381bbf-20230505.tgz", - "integrity": "sha512-ABvovCDe/k9IluqSh4/ISoq8tIJnW8euVAWYt5j/bg6dRnqwQwiGO1F/V4AyK96NGF/FB04FhOUDuWj8IKfABA==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmmirror.com/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "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.npmmirror.com/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.npmmirror.com/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" - }, - "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/send/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmmirror.com/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/send/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serialize-error": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/serialize-error/-/serialize-error-2.1.0.tgz", - "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" - }, - "node_modules/set-function-length": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.0.tgz", - "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.1", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.2", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==" - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dependencies": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmmirror.com/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "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.npmmirror.com/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.npmmirror.com/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" - }, - "node_modules/stacktrace-parser": { - "version": "0.1.10", - "resolved": "https://registry.npmmirror.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", - "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", - "dependencies": { - "type-fest": "^0.7.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/stacktrace-parser/node_modules/type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmmirror.com/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "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.npmmirror.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz", - "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", - "dev": true - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/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/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.10", - "resolved": "https://registry.npmmirror.com/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", - "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "regexp.prototype.flags": "^1.5.0", - "set-function-name": "^2.0.0", - "side-channel": "^1.0.4" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" - }, - "node_modules/sudo-prompt": { - "version": "9.2.1", - "resolved": "https://registry.npmmirror.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz", - "integrity": "sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw==" - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/temp": { - "version": "0.8.4", - "resolved": "https://registry.npmmirror.com/temp/-/temp-0.8.4.tgz", - "integrity": "sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==", - "dependencies": { - "rimraf": "~2.6.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/temp/node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/terser": { - "version": "5.27.0", - "resolved": "https://registry.npmmirror.com/terser/-/terser-5.27.0.tgz", - "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "node_modules/terser/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmmirror.com/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/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/throat": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==" - }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmmirror.com/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/through2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" - }, - "node_modules/through2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "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/through2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==" - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "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.npmmirror.com/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - } - }, - "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=12.20" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.1.0", - "resolved": "https://registry.npmmirror.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/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/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmmirror.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vlq": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/vlq/-/vlq-1.0.1.tgz", - "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==" - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-fetch": { - "version": "3.6.20", - "resolved": "https://registry.npmmirror.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "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" - } - }, - "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "dev": true, - "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" - }, - "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmmirror.com/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "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/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/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.npmmirror.com/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - } - } - } -} diff --git a/websrc/cobalt-mobile/package.json b/websrc/cobalt-mobile/package.json deleted file mode 100644 index be5dae2e..00000000 --- a/websrc/cobalt-mobile/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "cobaltmobile", - "version": "0.0.1", - "private": true, - "scripts": { - "android": "react-native run-android", - "ios": "react-native run-ios", - "lint": "eslint .", - "start": "react-native start", - "test": "jest" - }, - "dependencies": { - "@rneui/base": "^4.0.0-rc.7", - "@rneui/themed": "^4.0.0-rc.8", - "react": "18.2.0", - "react-native": "0.73.2", - "react-native-webview": "^13.6.4" - }, - "devDependencies": { - "@babel/core": "^7.20.0", - "@babel/preset-env": "^7.20.0", - "@babel/runtime": "^7.20.0", - "@react-native/babel-preset": "0.73.19", - "@react-native/eslint-config": "0.73.2", - "@react-native/metro-config": "0.73.3", - "@react-native/typescript-config": "0.73.1", - "@types/react": "^18.2.6", - "@types/react-test-renderer": "^18.0.0", - "babel-jest": "^29.6.3", - "eslint": "^8.19.0", - "jest": "^29.6.3", - "prettier": "2.8.8", - "react-test-renderer": "18.2.0", - "typescript": "5.0.4" - }, - "engines": { - "node": ">=18" - } -} diff --git a/websrc/cobalt-mobile/tsconfig.json b/websrc/cobalt-mobile/tsconfig.json deleted file mode 100644 index 304ab4e2..00000000 --- a/websrc/cobalt-mobile/tsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "@react-native/typescript-config/tsconfig.json" -} diff --git a/websrc/cobalt-web/.eslintignore b/websrc/cobalt-web/.eslintignore new file mode 100644 index 00000000..54ab3529 --- /dev/null +++ b/websrc/cobalt-web/.eslintignore @@ -0,0 +1,6 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +node_modules/* \ No newline at end of file diff --git a/websrc/cobalt-web/.prettierignore b/websrc/cobalt-web/.prettierignore new file mode 100644 index 00000000..54ab3529 --- /dev/null +++ b/websrc/cobalt-web/.prettierignore @@ -0,0 +1,6 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local +node_modules/* \ No newline at end of file diff --git a/websrc/cobalt-web/README.md b/websrc/cobalt-web/README.md index 2384c95f..a3878777 100644 --- a/websrc/cobalt-web/README.md +++ b/websrc/cobalt-web/README.md @@ -1,5 +1,4 @@ -Cobalt Web Client -================= +# Cobalt Web Client ## Introduction @@ -15,8 +14,7 @@ interact with the server. The client is currently in beta. -Installation -============ +## Installation To install the client, you can either clone the repository and build it yourself, or use the pre-built versions. @@ -57,60 +55,3 @@ If you have any questions or suggestions, please contact us at: ## Support If you like this project, just give us a star on GitHub. - -钴-网页客户端 -============ - -## 介绍 - -这是Lithium服务器的官方网页客户端,用于与服务器进行交互。 - -## 依赖 - -[React](https://facebook.github.io/react/) 框架。 -[Bootstrap](http://getbootstrap.com/) 用于UI。 -[React-Bootstrap](https://react-bootstrap.github.io/) 用于使用Bootstrap与React。 -[React-Router](https://github.com/ReactTraining/react-router) 用于处理路由。 - -客户端目前处于测试阶段。 - -安装 -==== -你可以通过克隆仓库并自行构建,或者使用预构建的版本。 - -```shell -git clone https://github.com/ElementAstro/Lithium.git # 克隆仓库 -cd Lithium/websrc/cobalt-web # 进入客户端目录 -npm install # 安装依赖 -npm run dev # 启动开发服务器 -``` - -要构建客户端,你可以使用以下命令: - -```shell -npm run build # 构建客户端 -``` - -客户端将被构建在`build`目录下。 - -## 许可 - -该项目采用GPLv3许可。有关更多信息,请参阅[LICENSE](LICENSE)文件。 - -## 致谢 - -* [React](https://facebook.github.io/react/) -* [Bootstrap](http://getbootstrap.com/) -* [React-Bootstrap](https://react-bootstrap.github.io/) -* [React-Router](https://github.com/ReactTraining/react-router) - -## 联系 - -如果有任何问题或建议,请联系我们: - -* [GitHub](https://github.com/ElementAstro) -* [Email](mailto:astro_air@126.com) - -## 支持 - -如果您喜欢这个项目,请在GitHub上给我们一个星星。 diff --git a/websrc/cobalt-web/README_ZH.md b/websrc/cobalt-web/README_ZH.md new file mode 100644 index 00000000..eef51ad8 --- /dev/null +++ b/websrc/cobalt-web/README_ZH.md @@ -0,0 +1,55 @@ +# 钴-网页客户端 + +## 介绍 + +这是Lithium服务器的官方网页客户端,用于与服务器进行交互。 + +## 依赖 + +[React](https://facebook.github.io/react/) 框架。 +[Bootstrap](http://getbootstrap.com/) 用于UI。 +[React-Bootstrap](https://react-bootstrap.github.io/) 用于使用Bootstrap与React。 +[React-Router](https://github.com/ReactTraining/react-router) 用于处理路由。 + +客户端目前处于测试阶段。 + +## 安装 + +你可以通过克隆仓库并自行构建,或者使用预构建的版本。 + +```shell +git clone https://github.com/ElementAstro/Lithium.git # 克隆仓库 +cd Lithium/websrc/cobalt-web # 进入客户端目录 +npm install # 安装依赖 +npm run dev # 启动开发服务器 +``` + +要构建客户端,你可以使用以下命令: + +```shell +npm run build # 构建客户端 +``` + +客户端将被构建在`build`目录下。 + +## 许可 + +该项目采用GPLv3许可。有关更多信息,请参阅[LICENSE](LICENSE)文件。 + +## 致谢 + +* [React](https://facebook.github.io/react/) +* [Bootstrap](http://getbootstrap.com/) +* [React-Bootstrap](https://react-bootstrap.github.io/) +* [React-Router](https://github.com/ReactTraining/react-router) + +## 联系 + +如果有任何问题或建议,请联系我们: + +* [GitHub](https://github.com/ElementAstro) +* [Email](mailto:astro_air@126.com) + +## 支持 + +如果您喜欢这个项目,请在GitHub上给我们一个星星。 diff --git a/websrc/cobalt-web/commitlint.config.cjs b/websrc/cobalt-web/commitlint.config.cjs new file mode 100644 index 00000000..234bb79c --- /dev/null +++ b/websrc/cobalt-web/commitlint.config.cjs @@ -0,0 +1,4 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: {} +}; diff --git a/websrc/cobalt-web/index.html b/websrc/cobalt-web/index.html index 247704f0..f98bd8ba 100644 --- a/websrc/cobalt-web/index.html +++ b/websrc/cobalt-web/index.html @@ -2,13 +2,28 @@ - - - Cobalt Client + + Cobalt +
+ diff --git a/websrc/cobalt-web/mock/test.ts b/websrc/cobalt-web/mock/test.ts new file mode 100644 index 00000000..3671ca2a --- /dev/null +++ b/websrc/cobalt-web/mock/test.ts @@ -0,0 +1,148 @@ +// 测试配置用的mock数据 + +// 数据1:brand相关,用户进到页面的时候自动向后端请求brand列表数据并渲染 +// 同时,为了保证数据一致性,后续的数据post也按照这个格式 +const brandList = + { + "camera": [ + { + "zh": "相机品牌1", + "en": "camera_brand1", + "driver": "camera_driver1" + }, + { + "zh": "相机品牌2", + "en": "camera_brand2", + "driver": "camera_driver2" + }, + { + "zh": "相机品牌3", + "en": "camera_brand3", + "driver": "camera_driver3" + } + ], + "telescope": [ + { + "zh": "赤道仪品牌1", + "en": "telescope_brand1", + "driver": "telescope_driver1" + }, + { + "zh": "赤道仪品牌2", + "en": "telescope_brand2", + "driver": "telescope_driver2" + } + ], + "focus": [ + { + "zh": "电调焦品牌1", + "en": "focus_brand1", + "driver": "focus_driver1" + }, + { + "zh": "电调焦品牌2", + "en": "focus_brand2", + "driver": "focus_driver2" + }, + ], + "filter": [ + { + "zh": "滤镜轮品牌1", + "en": "filter_brand1", + "driver": "filter_driver1" + }, + { + "zh": "滤镜轮品牌2", + "en": "filter_brand2", + "driver": "filter_driver2" + }, + ], + "guider": [ + { + "zh": "导航仪品牌1", + "en": "guider_brand1", + "driver": "guider_driver1" + }, + { + "zh": "导航仪品牌2", + "en": "guider_brand2", + "driver": "guider_driver2" + }, + ], + "polar": [ + { + "zh": "极轴镜品牌1", + "en": "polar_brand1", + "driver": "polar_driver1" + }, + { + "zh": "极轴镜品牌2", + "en": "polar_brand2", + "driver": "polar_driver2" + }, + ], + } +; + +// 设备配置参数,在选定brand后隔1-2s向后端请求 +const deviceList= [ + { + device_name: 'telescope1', + device_driver_name: 'telescope_driver1', + device_driver_exec: 'telescope_exec1', + },{ + device_name: 'telescope2', + device_driver_name: 'telescope_driver2', + device_driver_exec: 'telescope_exec2', + },{ + device_name: 'camera1', + device_driver_name: 'camera_driver1', + device_driver_exec: 'camera_exec1', + },{ + device_name: 'guider1', + device_driver_name: 'guider_driver1', + device_driver_exec: 'guider_exec1', + },{ + device_name: 'focus1', + device_driver_name: 'focus_driver1', + device_driver_exec: 'focus_exec1', + },{ + device_name: 'filter1', + device_driver_name: 'filter_driver1', + device_driver_exec: 'filter_exec1', + },{ + device_name: 'polar1', + device_driver_name: 'polar_driver1', + device_driver_exec: 'polar_exec1', + }, + +]; + +export default[ + { + url: '/api/driver_connect/device_brand', + type: 'get', + response() { + return { + code: 200, + msg: 'success', + data: { + brandList, + } + }; + } + }, + { + url: '/api/driver_connect/device_list/', + type: 'get', + response() { + return { + code: 200, + msg: 'success', + data: { + deviceList + } + }; + } + }, +]; \ No newline at end of file diff --git a/websrc/cobalt-web/package-lock.json b/websrc/cobalt-web/package-lock.json index 8d9d2d50..53aea5f6 100644 --- a/websrc/cobalt-web/package-lock.json +++ b/websrc/cobalt-web/package-lock.json @@ -10,12 +10,10 @@ "dependencies": { "axios": "^1.6.2", "bootstrap": "^5.3.2", - "chart.js": "^4.4.1", "easy-peasy": "^6.0.4", "echarts-for-react": "^3.0.2", "formik": "^2.4.5", "i18next": "^23.7.16", - "isomorphic-ws": "^5.0.0", "less": "^4.2.0", "localforage": "^1.10.0", "lodash": "^4.17.21", @@ -28,18 +26,21 @@ "react-ace-builds": "^7.4.1", "react-beautiful-dnd": "^13.1.1", "react-bootstrap": "^2.9.1", - "react-bootstrap-icons": "^1.10.3", + "react-bootstrap-icons": "^1.11.4", "react-cropper": "^2.3.3", "react-dom": "^18.2.0", "react-draggable": "^4.4.6", "react-i18next": "^14.0.0", "react-router-bootstrap": "^0.26.2", "react-router-dom": "^6.21.1", + "react-slick": "^0.30.2", "react-spinners": "^0.13.8", + "react-svg": "^16.1.34", "react-swipeable": "^7.0.1", "react-syntax-highlighter": "^15.5.0", "react-tsparticles": "^2.12.2", "redux": "^4.2.1", + "slick-carousel": "^1.8.1", "sort-by": "^0.0.2", "styled-components": "^6.1.8", "ts-essentials": "^9.4.1", @@ -98,25 +99,25 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz", - "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.3.tgz", - "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", + "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.1", + "@babel/generator": "^7.24.4", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.1", - "@babel/parser": "^7.24.1", + "@babel/helpers": "^7.24.4", + "@babel/parser": "^7.24.4", "@babel/template": "^7.24.0", "@babel/traverse": "^7.24.1", "@babel/types": "^7.24.0", @@ -135,9 +136,9 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz", - "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", + "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", "dependencies": { "@babel/types": "^7.24.0", "@jridgewell/gen-mapping": "^0.3.5", @@ -279,9 +280,9 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz", - "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", + "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", "dependencies": { "@babel/template": "^7.24.0", "@babel/traverse": "^7.24.1", @@ -306,9 +307,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", - "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", + "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -347,9 +348,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", - "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", + "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -877,9 +878,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "node_modules/@jridgewell/gen-mapping": { @@ -925,11 +926,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@kurkle/color": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", - "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1057,9 +1053,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.2.tgz", - "integrity": "sha512-3XFIDKWMFZrMnao1mJhnOT1h2g0169Os848NhhmGweEcfJ4rCi+3yMCOLG4zA61rbJdkcrM/DjVZm9Hg5p5w7g==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.3.tgz", + "integrity": "sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==", "cpu": [ "arm" ], @@ -1069,9 +1065,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.2.tgz", - "integrity": "sha512-GdxxXbAuM7Y/YQM9/TwwP+L0omeE/lJAR1J+olu36c3LqqZEBdsIWeQ91KBe6nxwOnb06Xh7JS2U5ooWU5/LgQ==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.3.tgz", + "integrity": "sha512-eQK5JIi+POhFpzk+LnjKIy4Ks+pwJ+NXmPxOCSvOKSNRPONzKuUvWE+P9JxGZVxrtzm6BAYMaL50FFuPe0oWMQ==", "cpu": [ "arm64" ], @@ -1081,9 +1077,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.2.tgz", - "integrity": "sha512-mCMlpzlBgOTdaFs83I4XRr8wNPveJiJX1RLfv4hggyIVhfB5mJfN4P8Z6yKh+oE4Luz+qq1P3kVdWrCKcMYrrA==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.3.tgz", + "integrity": "sha512-Od4vE6f6CTT53yM1jgcLqNfItTsLt5zE46fdPaEmeFHvPs5SjZYlLpHrSiHEKR1+HdRfxuzXHjDOIxQyC3ptBA==", "cpu": [ "arm64" ], @@ -1093,9 +1089,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.2.tgz", - "integrity": "sha512-yUoEvnH0FBef/NbB1u6d3HNGyruAKnN74LrPAfDQL3O32e3k3OSfLrPgSJmgb3PJrBZWfPyt6m4ZhAFa2nZp2A==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.3.tgz", + "integrity": "sha512-0IMAO21axJeNIrvS9lSe/PGthc8ZUS+zC53O0VhF5gMxfmcKAP4ESkKOCwEi6u2asUrt4mQv2rjY8QseIEb1aw==", "cpu": [ "x64" ], @@ -1105,9 +1101,21 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.2.tgz", - "integrity": "sha512-GYbLs5ErswU/Xs7aGXqzc3RrdEjKdmoCrgzhJWyFL0r5fL3qd1NPcDKDowDnmcoSiGJeU68/Vy+OMUluRxPiLQ==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.3.tgz", + "integrity": "sha512-ge2DC7tHRHa3caVEoSbPRJpq7azhG+xYsd6u2MEnJ6XzPSzQsTKyXvh6iWjXRf7Rt9ykIUWHtl0Uz3T6yXPpKw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.14.3.tgz", + "integrity": "sha512-ljcuiDI4V3ySuc7eSk4lQ9wU8J8r8KrOUvB2U+TtK0TiW6OFDmJ+DdIjjwZHIw9CNxzbmXY39wwpzYuFDwNXuw==", "cpu": [ "arm" ], @@ -1117,9 +1125,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.2.tgz", - "integrity": "sha512-L1+D8/wqGnKQIlh4Zre9i4R4b4noxzH5DDciyahX4oOz62CphY7WDWqJoQ66zNR4oScLNOqQJfNSIAe/6TPUmQ==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.3.tgz", + "integrity": "sha512-Eci2us9VTHm1eSyn5/eEpaC7eP/mp5n46gTRB3Aar3BgSvDQGJZuicyq6TsH4HngNBgVqC5sDYxOzTExSU+NjA==", "cpu": [ "arm64" ], @@ -1129,9 +1137,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.2.tgz", - "integrity": "sha512-tK5eoKFkXdz6vjfkSTCupUzCo40xueTOiOO6PeEIadlNBkadH1wNOH8ILCPIl8by/Gmb5AGAeQOFeLev7iZDOA==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.3.tgz", + "integrity": "sha512-UrBoMLCq4E92/LCqlh+blpqMz5h1tJttPIniwUgOFJyjWI1qrtrDhhpHPuFxULlUmjFHfloWdixtDhSxJt5iKw==", "cpu": [ "arm64" ], @@ -1141,11 +1149,11 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.13.2.tgz", - "integrity": "sha512-zvXvAUGGEYi6tYhcDmb9wlOckVbuD+7z3mzInCSTACJ4DQrdSLPNUeDIcAQW39M3q6PDquqLWu7pnO39uSMRzQ==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.3.tgz", + "integrity": "sha512-5aRjvsS8q1nWN8AoRfrq5+9IflC3P1leMoy4r2WjXyFqf3qcqsxRCfxtZIV58tCxd+Yv7WELPcO9mY9aeQyAmw==", "cpu": [ - "ppc64le" + "ppc64" ], "optional": true, "os": [ @@ -1153,9 +1161,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.2.tgz", - "integrity": "sha512-C3GSKvMtdudHCN5HdmAMSRYR2kkhgdOfye4w0xzyii7lebVr4riCgmM6lRiSCnJn2w1Xz7ZZzHKuLrjx5620kw==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.3.tgz", + "integrity": "sha512-sk/Qh1j2/RJSX7FhEpJn8n0ndxy/uf0kI/9Zc4b1ELhqULVdTfN6HL31CDaTChiBAOgLcsJ1sgVZjWv8XNEsAQ==", "cpu": [ "riscv64" ], @@ -1165,9 +1173,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.13.2.tgz", - "integrity": "sha512-l4U0KDFwzD36j7HdfJ5/TveEQ1fUTjFFQP5qIt9gBqBgu1G8/kCaq5Ok05kd5TG9F8Lltf3MoYsUMw3rNlJ0Yg==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.3.tgz", + "integrity": "sha512-jOO/PEaDitOmY9TgkxF/TQIjXySQe5KVYB57H/8LRP/ux0ZoO8cSHCX17asMSv3ruwslXW/TLBcxyaUzGRHcqg==", "cpu": [ "s390x" ], @@ -1177,9 +1185,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.2.tgz", - "integrity": "sha512-xXMLUAMzrtsvh3cZ448vbXqlUa7ZL8z0MwHp63K2IIID2+DeP5iWIT6g1SN7hg1VxPzqx0xZdiDM9l4n9LRU1A==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.3.tgz", + "integrity": "sha512-8ybV4Xjy59xLMyWo3GCfEGqtKV5M5gCSrZlxkPGvEPCGDLNla7v48S662HSGwRd6/2cSneMQWiv+QzcttLrrOA==", "cpu": [ "x64" ], @@ -1189,9 +1197,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.2.tgz", - "integrity": "sha512-M/JYAWickafUijWPai4ehrjzVPKRCyDb1SLuO+ZyPfoXgeCEAlgPkNXewFZx0zcnoIe3ay4UjXIMdXQXOZXWqA==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.3.tgz", + "integrity": "sha512-s+xf1I46trOY10OqAtZ5Rm6lzHre/UiLA1J2uOhCFXWkbZrJRkYBPO6FhvGfHmdtQ3Bx793MNa7LvoWFAm93bg==", "cpu": [ "x64" ], @@ -1201,9 +1209,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.2.tgz", - "integrity": "sha512-2YWwoVg9KRkIKaXSh0mz3NmfurpmYoBBTAXA9qt7VXk0Xy12PoOP40EFuau+ajgALbbhi4uTj3tSG3tVseCjuA==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.3.tgz", + "integrity": "sha512-+4h2WrGOYsOumDQ5S2sYNyhVfrue+9tc9XcLWLh+Kw3UOxAvrfOrSMFon60KspcDdytkNDh7K2Vs6eMaYImAZg==", "cpu": [ "arm64" ], @@ -1213,9 +1221,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.2.tgz", - "integrity": "sha512-2FSsE9aQ6OWD20E498NYKEQLneShWes0NGMPQwxWOdws35qQXH+FplabOSP5zEe1pVjurSDOGEVCE2agFwSEsw==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.3.tgz", + "integrity": "sha512-T1l7y/bCeL/kUwh9OD4PQT4aM7Bq43vX05htPJJ46RTI4r5KNt6qJRzAfNfM+OYMNEVBWQzR2Gyk+FXLZfogGw==", "cpu": [ "ia32" ], @@ -1225,9 +1233,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.2.tgz", - "integrity": "sha512-7h7J2nokcdPePdKykd8wtc8QqqkqxIrUz7MHj6aNr8waBRU//NLDVnNjQnqQO6fqtjrtCdftpbTuOKAyrAQETQ==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz", + "integrity": "sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==", "cpu": [ "x64" ], @@ -1438,9 +1446,9 @@ } }, "node_modules/@swc/helpers": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.8.tgz", - "integrity": "sha512-lruDGw3pnfM3wmZHeW7JuhkGQaJjPyiKjxeGhdmfoOT53Ic9qb5JLDNaK2HUdl1zLDeX28H221UvKjfdvSLVMg==", + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.10.tgz", + "integrity": "sha512-CU+RF9FySljn7HVSkkjiB84hWkvTaI3rtLvF433+jRSBL2hMu3zX5bGhHS8C80SM++h4xy8hBSnUHFQHmRXSBw==", "dependencies": { "tslib": "^2.4.0" } @@ -1450,6 +1458,21 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, + "node_modules/@tanem/svg-injector": { + "version": "10.1.68", + "resolved": "https://registry.npmjs.org/@tanem/svg-injector/-/svg-injector-10.1.68.tgz", + "integrity": "sha512-UkJajeR44u73ujtr5GVSbIlELDWD/mzjqWe54YMK61ljKxFcJoPd9RBSaO7xj02ISCWUqJW99GjrS+sVF0UnrA==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "content-type": "^1.0.5", + "tslib": "^2.6.2" + } + }, + "node_modules/@tanem/svg-injector/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1523,9 +1546,9 @@ } }, "node_modules/@types/node": { - "version": "20.11.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", - "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "devOptional": true, "dependencies": { "undici-types": "~5.26.4" @@ -1537,9 +1560,9 @@ "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/react": { - "version": "18.2.73", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.73.tgz", - "integrity": "sha512-XcGdod0Jjv84HOC7N5ziY3x+qL0AfmubvKOZ9hJjJ2yd5EE+KYjWhdOjt387e9HPheHkdggF9atTifMRtyAaRA==", + "version": "18.2.79", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz", + "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -1555,9 +1578,9 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.23", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.23.tgz", - "integrity": "sha512-ZQ71wgGOTmDYpnav2knkjr3qXdAFu0vsk8Ci5w3pGAIdj7/kKAyn+VsQDhXsmzzzepAiI9leWMmubXz690AI/A==", + "version": "18.2.25", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.25.tgz", + "integrity": "sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==", "devOptional": true, "dependencies": { "@types/react": "*" @@ -1985,9 +2008,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001600", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz", - "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==", + "version": "1.0.30001610", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001610.tgz", + "integrity": "sha512-QFutAY4NgaelojVMjY63o6XlZyORPaLfyMnsl3HgnWdJUcX6K0oaJymHjH8PT5Gk7sTm8rvC/c5COUQKXqmOMA==", "funding": [ { "type": "opencollective", @@ -2043,17 +2066,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/chart.js": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.2.tgz", - "integrity": "sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==", - "dependencies": { - "@kurkle/color": "^0.3.0" - }, - "engines": { - "pnpm": ">=8" - } - }, "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", @@ -2106,6 +2118,14 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "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": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2431,9 +2451,14 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.722", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.722.tgz", - "integrity": "sha512-5nLE0TWFFpZ80Crhtp4pIp8LXCztjYX41yUcV6b+bKR2PqzjskTMOOlBi1VjBHlvHwS+4gar7kNKOrsbsewEZQ==" + "version": "1.4.738", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.738.tgz", + "integrity": "sha512-lwKft2CLFztD+vEIpesrOtCrko/TFnEJlHFdRhazU7Y/jx5qc4cqsocfVrBg4So4gGe9lvxnbLIoev47WMpg+A==" + }, + "node_modules/enquire.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz", + "integrity": "sha512-/KujNpO+PT63F7Hlpu4h3pE3TokKRHN26JYmQpPyjkRD/N57R7bPDNojMXdi7uveAKjYB7yQnartCxZnFWr0Xw==" }, "node_modules/entities": { "version": "4.5.0", @@ -3465,9 +3490,9 @@ } }, "node_modules/i18next": { - "version": "23.10.1", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.10.1.tgz", - "integrity": "sha512-NDiIzFbcs3O9PXpfhkjyf7WdqFn5Vq6mhzhtkXzj51aOcNuPNcTwuYNuXCpHsanZGHlHKL35G7huoFeVic1hng==", + "version": "23.11.2", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.11.2.tgz", + "integrity": "sha512-qMBm7+qT8jdpmmDw/kQD16VpmkL9BdL+XNAK5MNbNFaf1iQQq35ZbPrSlqmnNPOSUY4m342+c0t0evinF5l7sA==", "funding": [ { "type": "individual", @@ -3995,14 +4020,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/isomorphic-ws": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", - "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", - "peerDependencies": { - "ws": "*" - } - }, "node_modules/iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", @@ -4016,6 +4033,12 @@ "set-function-name": "^2.0.1" } }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "peer": true + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4066,6 +4089,14 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "dependencies": { + "string-convert": "^0.2.0" + } + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -4185,6 +4216,11 @@ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, + "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.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -4905,9 +4941,9 @@ } }, "node_modules/react-bootstrap-icons": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/react-bootstrap-icons/-/react-bootstrap-icons-1.11.3.tgz", - "integrity": "sha512-f/DAy4UXnjdbaZyUcZKR2I3xim56uCznb9t+u3ojwzDG1p2RUrua/d8R4xplAQ8Bj/LVZwHVSrvO+npvp3l3pw==", + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/react-bootstrap-icons/-/react-bootstrap-icons-1.11.4.tgz", + "integrity": "sha512-lnkOpNEZ/Zr7mNxvjA9efuarCPSgtOuGA55XiRj7ASJnBjb1wEAdtJOd2Aiv9t07r7FLI1IgyZPg9P6jqWD/IA==", "dependencies": { "prop-types": "^15.7.2" }, @@ -5067,6 +5103,22 @@ "react-dom": ">=16.8" } }, + "node_modules/react-slick": { + "version": "0.30.2", + "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.30.2.tgz", + "integrity": "sha512-XvQJi7mRHuiU3b9irsqS9SGIgftIfdV5/tNcURTb5LdIokRA5kIIx3l4rlq2XYHfxcSntXapoRg/GxaVOM1yfg==", + "dependencies": { + "classnames": "^2.2.5", + "enquire.js": "^2.1.6", + "json2mq": "^0.2.0", + "lodash.debounce": "^4.0.8", + "resize-observer-polyfill": "^1.5.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-spinners": { "version": "0.13.8", "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.13.8.tgz", @@ -5076,6 +5128,21 @@ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-svg": { + "version": "16.1.34", + "resolved": "https://registry.npmjs.org/react-svg/-/react-svg-16.1.34.tgz", + "integrity": "sha512-L4ak1qNFLgzVbHm0xQEpHoIOqb3um/B0ybahd3U2TKoGZxb0JaPVI5lsAhvSng2P1kcsYEok2Z7RpcKx7arJGw==", + "dependencies": { + "@babel/runtime": "^7.24.1", + "@tanem/svg-injector": "^10.1.68", + "@types/prop-types": "^15.7.12", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-swipeable": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/react-swipeable/-/react-swipeable-7.0.1.tgz", @@ -5228,6 +5295,11 @@ "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, "node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", @@ -5279,9 +5351,9 @@ } }, "node_modules/rollup": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz", - "integrity": "sha512-MIlLgsdMprDBXC+4hsPgzWUasLO9CE4zOkj/u6j+Z6j5A4zRY+CtiXAdJyPtgCsc42g658Aeh1DlrdVEJhsL2g==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.3.tgz", + "integrity": "sha512-ag5tTQKYsj1bhrFC9+OEWqb5O6VYgtQDO9hPDBMmIbePwhfSr+ExlcU741t8Dhw5DkPCQf6noz0jb36D6W9/hw==", "dependencies": { "@types/estree": "1.0.5" }, @@ -5293,21 +5365,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.2", - "@rollup/rollup-android-arm64": "4.13.2", - "@rollup/rollup-darwin-arm64": "4.13.2", - "@rollup/rollup-darwin-x64": "4.13.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.2", - "@rollup/rollup-linux-arm64-gnu": "4.13.2", - "@rollup/rollup-linux-arm64-musl": "4.13.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.13.2", - "@rollup/rollup-linux-riscv64-gnu": "4.13.2", - "@rollup/rollup-linux-s390x-gnu": "4.13.2", - "@rollup/rollup-linux-x64-gnu": "4.13.2", - "@rollup/rollup-linux-x64-musl": "4.13.2", - "@rollup/rollup-win32-arm64-msvc": "4.13.2", - "@rollup/rollup-win32-ia32-msvc": "4.13.2", - "@rollup/rollup-win32-x64-msvc": "4.13.2", + "@rollup/rollup-android-arm-eabi": "4.14.3", + "@rollup/rollup-android-arm64": "4.14.3", + "@rollup/rollup-darwin-arm64": "4.14.3", + "@rollup/rollup-darwin-x64": "4.14.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.3", + "@rollup/rollup-linux-arm-musleabihf": "4.14.3", + "@rollup/rollup-linux-arm64-gnu": "4.14.3", + "@rollup/rollup-linux-arm64-musl": "4.14.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.3", + "@rollup/rollup-linux-riscv64-gnu": "4.14.3", + "@rollup/rollup-linux-s390x-gnu": "4.14.3", + "@rollup/rollup-linux-x64-gnu": "4.14.3", + "@rollup/rollup-linux-x64-musl": "4.14.3", + "@rollup/rollup-win32-arm64-msvc": "4.14.3", + "@rollup/rollup-win32-ia32-msvc": "4.14.3", + "@rollup/rollup-win32-x64-msvc": "4.14.3", "fsevents": "~2.3.2" } }, @@ -5478,6 +5551,14 @@ "resolved": "https://registry.npmjs.org/size-sensor/-/size-sensor-1.0.2.tgz", "integrity": "sha512-2NCmWxY7A9pYKGXNBfteo4hy14gWu47rg5692peVMst6lQLPKrVjhY+UTEsPI5ceFRJSl3gVgMYaUi/hKuaiKw==" }, + "node_modules/slick-carousel": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/slick-carousel/-/slick-carousel-1.8.1.tgz", + "integrity": "sha512-XB9Ftrf2EEKfzoQXt3Nitrt/IPbT+f1fgqBdoxO3W/+JYvtEOW6EgxnWfr9GH6nmULv7Y2tPmEX3koxThVmebA==", + "peerDependencies": { + "jquery": ">=1.8.0" + } + }, "node_modules/snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", @@ -5518,6 +5599,11 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==" + }, "node_modules/string.prototype.matchall": { "version": "4.0.11", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", @@ -5722,9 +5808,9 @@ "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==" }, "node_modules/ts-essentials": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.4.1.tgz", - "integrity": "sha512-oke0rI2EN9pzHsesdmrOrnqv1eQODmJpd/noJjwj2ZPC3Z4N2wbjrOEqnsEgmvlO2+4fBb0a794DCna2elEVIQ==", + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.4.2.tgz", + "integrity": "sha512-mB/cDhOvD7pg3YCLk2rOtejHjjdSi9in/IBYE13S+8WA5FBSraYf4V/ws55uvs0IvQ/l0wBOlXy5yBNZ9Bl8ZQ==", "peerDependencies": { "typescript": ">=4.1.0" }, @@ -6351,9 +6437,9 @@ } }, "node_modules/vite": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.7.tgz", - "integrity": "sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==", + "version": "5.2.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.9.tgz", + "integrity": "sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==", "dependencies": { "esbuild": "^0.20.1", "postcss": "^8.4.38", diff --git a/websrc/cobalt-web/package.json b/websrc/cobalt-web/package.json index 7fa5d2c3..5c99d22c 100644 --- a/websrc/cobalt-web/package.json +++ b/websrc/cobalt-web/package.json @@ -12,12 +12,10 @@ "dependencies": { "axios": "^1.6.2", "bootstrap": "^5.3.2", - "chart.js": "^4.4.1", "easy-peasy": "^6.0.4", "echarts-for-react": "^3.0.2", "formik": "^2.4.5", "i18next": "^23.7.16", - "isomorphic-ws": "^5.0.0", "less": "^4.2.0", "localforage": "^1.10.0", "lodash": "^4.17.21", @@ -30,18 +28,21 @@ "react-ace-builds": "^7.4.1", "react-beautiful-dnd": "^13.1.1", "react-bootstrap": "^2.9.1", - "react-bootstrap-icons": "^1.10.3", + "react-bootstrap-icons": "^1.11.4", "react-cropper": "^2.3.3", "react-dom": "^18.2.0", "react-draggable": "^4.4.6", "react-i18next": "^14.0.0", "react-router-bootstrap": "^0.26.2", "react-router-dom": "^6.21.1", + "react-slick": "^0.30.2", "react-spinners": "^0.13.8", + "react-svg": "^16.1.34", "react-swipeable": "^7.0.1", "react-syntax-highlighter": "^15.5.0", "react-tsparticles": "^2.12.2", "redux": "^4.2.1", + "slick-carousel": "^1.8.1", "sort-by": "^0.0.2", "styled-components": "^6.1.8", "ts-essentials": "^9.4.1", diff --git a/websrc/cobalt-web/server/http.js b/websrc/cobalt-web/server/http.js new file mode 100644 index 00000000..0c608cf0 --- /dev/null +++ b/websrc/cobalt-web/server/http.js @@ -0,0 +1,128 @@ +import { createServer } from "http"; +import { parse } from "url"; + +// 创建 HTTP 服务器 +const server = createServer((req, res) => { + // 解析请求 URL + const parsedUrl = parse(req.url, true); + + // 获取请求路径 + const path = parsedUrl.pathname; + + // 获取请求方法 + const method = req.method; + + // 设置响应头 + res.setHeader("Content-Type", "application/json"); + + // 处理不同的接口 + if (path === "/GlobalParameter/" && method === "GET") { + // 处理 /api/data GET 请求 + const responseData = { + message: "Hello, this is your data!", + data: { + meridian_flip: { + before_meridian: 2, + after_meridian: 2, + recenter_after_flip: true, + autofocus_after_flip: false, + settle_time_after: 30, + }, + guider_start_guide_settle: { + time: 30, + timeout: 300, + pixels: 1.5, + }, + guider_dither_settle: { + time: 5, + timeout: 30, + pixels: 1.5, + }, + filter_setting: { + filter_number: 8, + filter_info: [ + { + filter_name: "filter1", + focus_offset: 0, + af_exposure_time: 1, + }, + { + filter_name: "filter2", + focus_offset: 0, + af_exposure_time: 1, + }, + ], + }, + autofocus: { + use_filter_offset: true, + step_size: 50, + initial_steps: 5, + default_exposure_time: 1, + retry_times: 1, + each_step_exposure: 1, + af_after_filter_change: false, + af_offset_base_filter_slot: 0, + }, + plate_solve: { + use: "astap", + exposure_time: 2, + use_filter: "Current", + downsample: 4, + tolerance: 100, + }, + telescope_info: { + name: "", + aperture: 80, + focal_length: 480, + guider_aperture: 60, + guider_focal_length: 240, + }, + info_get: { + camera: false, + guider_camera: false, + }, + camera_info: { + CCD_MAX_X: 0, + CCD_MAX_Y: 0, + CCD_PIXEL_SIZE: 0, + }, + guider_camera_info: { + CCD_MAX_X: 0, + CCD_MAX_Y: 0, + CCD_PIXEL_SIZE: 0, + }, + dither: { + amount: 2, + ra_only: false, + }, + }, + }; + res.statusCode = 200; + res.end(JSON.stringify(responseData)); + } else if (path === "/api/postData" && method === "POST") { + // 处理 /api/postData POST 请求 + let body = ""; + req.on("data", (chunk) => { + body += chunk.toString(); + }); + req.on("end", () => { + const postData = JSON.parse(body); + const responseData = { + message: "Data received successfully!", + receivedData: postData, + }; + res.statusCode = 200; + res.end(JSON.stringify(responseData)); + }); + } else { + // 处理未知路由 + res.statusCode = 404; + res.end(JSON.stringify({ error: "Not Found" })); + } +}); + +// 监听端口 +const port = 3000; +server.listen(port, () => { + console.log(`Server is running on port ${port}`); +}); diff --git a/websrc/cobalt-web/src/App.tsx b/websrc/cobalt-web/src/App.tsx index d948183c..e679eb53 100644 --- a/websrc/cobalt-web/src/App.tsx +++ b/websrc/cobalt-web/src/App.tsx @@ -1,32 +1,35 @@ -import React, { useEffect } from "react"; -import { AppProviders } from "./AppProvider"; -import { HashRouter as Router } from "react-router-dom"; -import Body from "./layout/index"; +import { useEffect } from 'react'; +import { AppProviders } from './AppProvider'; +import { HashRouter as Router } from 'react-router-dom'; +import Body from './layout/index'; import Route from "./routes/root"; - -import WS_LISTENER_COMP from "./pages/home/init_ws_listener"; +import { getResouceList } from './services/api'; +import WS_LISTENER_COMP from './pages/home/init_ws_listener'; import "bootstrap/dist/css/bootstrap.min.css"; +import React from 'react'; function Main() { + // useEffect(()=>{ // getResouceList().then(res=>{ // console.log(res); // }); // },[]) + return ( - - + + - - ); + ) } + export default () => (
-); +) diff --git a/websrc/cobalt-web/src/AppProvider.tsx b/websrc/cobalt-web/src/AppProvider.tsx index 787f6a25..e9c6b306 100644 --- a/websrc/cobalt-web/src/AppProvider.tsx +++ b/websrc/cobalt-web/src/AppProvider.tsx @@ -1,6 +1,9 @@ -import { GlobalStoreProvider } from "./store/globalStore"; -import { combineProviders } from "./utils/combineProviders"; -import { EchoWebSocketProvider } from "./utils/websocketProvider"; +import { GlobalStoreProvider } from './store/globalStore'; +import { combineProviders } from './utils/combineProviders'; +import { EchoWebSocketProvider } from './utils/websocketProvider'; +import { CustomThemeProvider } from './theme'; -export const AppProviders = ({ children }: { children?: React.ReactNode }) => - combineProviders([GlobalStoreProvider, EchoWebSocketProvider], children); +export const AppProviders = ({ children }: { children?: React.ReactNode }) => combineProviders([ + GlobalStoreProvider, + EchoWebSocketProvider, +], children); \ No newline at end of file diff --git a/websrc/cobalt-web/src/assets/connection.css b/websrc/cobalt-web/src/assets/connection.css deleted file mode 100644 index 9844172b..00000000 --- a/websrc/cobalt-web/src/assets/connection.css +++ /dev/null @@ -1,27 +0,0 @@ -.container { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100vh; -} - -form { - width: 400px; - padding: 20px; - border-radius: 5px; - box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2); -} - -.form-group { - margin-bottom: 20px; -} - -.form-check-label { - font-weight: normal; -} - -.btn-primary, -.btn-success { - margin-right: 10px; -} \ No newline at end of file diff --git a/websrc/cobalt-web/public/background.png b/websrc/cobalt-web/src/assets/imgs/bg.png similarity index 100% rename from websrc/cobalt-web/public/background.png rename to websrc/cobalt-web/src/assets/imgs/bg.png diff --git a/websrc/cobalt-web/src/assets/react.svg b/websrc/cobalt-web/src/assets/react.svg deleted file mode 100644 index 6c87de9b..00000000 --- a/websrc/cobalt-web/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/websrc/cobalt-web/src/components/skymap/aladin_wrapper.tsx b/websrc/cobalt-web/src/components/skymap/aladin_wrapper.tsx index d7f4e103..86bbc413 100644 --- a/websrc/cobalt-web/src/components/skymap/aladin_wrapper.tsx +++ b/websrc/cobalt-web/src/components/skymap/aladin_wrapper.tsx @@ -1,70 +1,73 @@ -import React, { useRef, useEffect, memo } from "react"; +import * as React from "react"; -const AladinLiteView = memo( - ({ - fovPoints, - onCenterChange, - fovSize, - ra, - dec, - }: { - fovPoints: Array< - [[number, number], [number, number], [number, number], [number, number]] - >; - onCenterChange: (ra: number, dec: number) => void; - fovSize: number; - ra: number; - dec: number; - }) => { - const alaRef = useRef(null); - let aladin: any = null; +interface aladin_lite_props { + fov_points: Array< + [[number, number], [number, number], [number, number], [number, number]] + >; + onCenterChange: (new_ra: number, new_dec: number) => void; + fov_size: number; + ra: number; + dec: number; +} - useEffect(() => { - aladin = A.aladin("#aladin-lite-div", { - survey: "P/DSS2/color", - fov: 60, - showCooGrid: true, - showCooGridControl: true, - projection: "SIN", - showProjectionControl: false, - showZoomControl: true, - showFullscreenControl: false, - showLayersControl: false, - showGotoControl: false, - showFrame: true, - cooframe: "equatorial", - showSimbadPointrerControl: false, - }); - - aladin.on( - "positionChanged", - ({ ra, dec }: { ra: number; dec: number }) => { - onCenterChange(ra, dec); - } - ); +const AladinLiteView: React.FC = (props) => { + const alaRef = React.useRef(null); + let aladin = React.useRef(null); + React.useEffect(() => { + aladin.current = A.aladin("#aladin-lite-div", { + survey: "P/DSS2/color", + fov: 60, + showCooGrid: false, + showCooGridControl: false, + projection: "SIN", + showProjectionControl: false, + showZoomControl: false, + showFullscreenControl: false, + showLayersControl: false, + showGotoControl: false, + showFrame: false, // false in development, + cooframe: "equatorial", + showSimbadPointrerControl: false, + }); + aladin.current.setFov(props.fov_size); + aladin.current.on("positionChanged", function (new_pos: any) { + props.onCenterChange(new_pos.ra, new_pos.dec); + // console.log('aladin position changed', new_pos); + }); + }, []); + React.useEffect(() => { + // draw rects + // console.log('aladin got', props.fov_points); + aladin.current.removeLayers(); + if (props.fov_points.length > 0) { + for (let i = 0; i < props.fov_points.length; i++) { + let current_points_list = props.fov_points[i]; + current_points_list.push(current_points_list[0]); // make a circle + // console.log('aladin points', current_points_list); + let overlay = A.graphicOverlay({ color: "#FFFFFF", lineWidth: 2 }); + aladin.current.addOverlay(overlay); + // need to replace data + overlay.add(A.polyline(current_points_list)); + } + } + }, [props.fov_points]); - return () => { - aladin = null; // 清理 aladin 实例 - }; - }, []); + React.useEffect(() => { + if (aladin.current !== undefined) { + aladin.current.gotoRaDec(props.ra, props.dec); + } + }, [props.ra, props.dec]); - useEffect(() => { - if (aladin && fovPoints.length > 0) { - aladin.removeLayers(); - const overlay = A.graphicOverlay({ color: "#ee2345", lineWidth: 2 }); - aladin.addOverlay(overlay); - overlay.add(A.polyline(fovPoints)); - } - }, [fovPoints]); + React.useEffect(() => { + if (aladin.current !== undefined) { + aladin.current.setFov(props.fov_size); + } + }, [props.fov_size]); - useEffect(() => { - if (aladin) { - aladin.gotoRaDec(ra, dec); - } - }, [ra, dec]); + // event listener for center change is positionChanged + // another method is always return a ra dec value to its parents when asked? - return
; - } -); + return
; +}; export default AladinLiteView; diff --git a/websrc/cobalt-web/src/components/skymap/target_detail.tsx b/websrc/cobalt-web/src/components/skymap/target_detail.tsx index 8507eba7..27e4cb59 100644 --- a/websrc/cobalt-web/src/components/skymap/target_detail.tsx +++ b/websrc/cobalt-web/src/components/skymap/target_detail.tsx @@ -10,13 +10,14 @@ import { DateTime } from "luxon"; import { GlobalStore } from "../../store/globalStore"; import Spinner from "react-bootstrap/esm/Spinner"; +import { Tab } from "react-bootstrap"; interface TargetSmallCardProps { open_dialog: number; target_info: IDSOObjectDetailedInfo; in_updating: boolean; on_choice_maken: (() => void) | null; - in_manage: boolean; + in_manage?: boolean; } const alt_fig_options_template: any = { @@ -24,7 +25,7 @@ const alt_fig_options_template: any = { top: 10, bottom: 20, right: "1%", - left: "10%", + left: "32", }, tooltip: { trigger: "axis", @@ -89,8 +90,8 @@ const fig_line_data_template: any = [ ]; const polar_fig_options_template: any = { grid: { - top: 0, - bottom: 0, + top: 1, + bottom: 1, right: "0%", left: "0%", }, @@ -218,137 +219,147 @@ const TargetDetailCard: React.FC = (props) => { }; return ( - <> - set_open(true)} - > - - -
{props.target_info.name}
- - -
RA:
- {props.in_updating ? ( - - Loading... - - ) : ( - props.target_info.ra.toFixed(4) - )} + + + + + + + + + + + {props.target_info.name} + + + + + {props.in_updating ? ( +
+ +
+ ) : ( + + )} + +
- -
DEC:
+ {props.in_updating ? ( - - Loading... - +
+ +
) : ( - props.target_info.dec.toFixed(4) + )}
- {!props.in_manage && ( - - - - - - - - - )} -
-
- - - - {props.target_info.name} - - - - - + /> + + {props.target_info.name} + + +

Ra: {props.target_info.ra.toFixed(7)} °

+ + +

Dec: {props.target_info.dec.toFixed(7)} °

+ + +

+ 目标类型{" "} + {TranslateTargetType(props.target_info.target_type)} +

+ + +

+ 目标视角大小 {props.target_info.size} ′ +

+ +
+
+ - -
基本信息
- - -
英文名:
- {props.target_info.aliases.en} - - -
中文名:
- {props.target_info.aliases.zh} - -
-
描述:
- {props.target_info.description} - - -
RA:
- {props.in_updating ? ( - - Loading... - - ) : ( - props.target_info.ra.toFixed(4) - )} - - -
DEC:
- {props.in_updating ? ( - - Loading... - - ) : ( - props.target_info.dec.toFixed(4) - )} - -
+ + + + +

当前高度: {current_alt.toFixed(0)}°

+ + +

最高高度: {highest_alt.toFixed(0)}°

+ + +

估计可拍摄时间: {available_time.toFixed(1)}h

+ + +

+ +
+
+ + +
+ {/* 目标维基小百科 */} +
+
+ -
- -
高度角曲线
- {props.in_updating ? ( - - Loading... - - ) : ( - - )} - -
- -
方位角-高度角图像
- {props.in_updating ? ( - - Loading... - - ) : ( - - )} -
-
- +
+ {props.in_manage ? ( + <> + ) : ( + + )} + + +
+ ); }; diff --git a/websrc/cobalt-web/src/components/skymap/target_small.tsx b/websrc/cobalt-web/src/components/skymap/target_small.tsx index 497a8d32..064f89d8 100644 --- a/websrc/cobalt-web/src/components/skymap/target_small.tsx +++ b/websrc/cobalt-web/src/components/skymap/target_small.tsx @@ -1,23 +1,21 @@ -import React, { useState, useEffect } from "react"; +import * as React from "react"; import TargetDetailCard from "./target_detail"; -import Figure from "react-bootstrap/Figure"; -import Button from "react-bootstrap/Button"; -import Form from "react-bootstrap/Form"; import Card from "react-bootstrap/Card"; -import Accordion from "react-bootstrap/Accordion"; -import { Heart, ThreeDots } from "react-bootstrap-icons"; -import Image from "react-bootstrap/Image"; +import Button from "react-bootstrap/Button"; +import OverlayTrigger from "react-bootstrap/OverlayTrigger"; +import Tooltip from "react-bootstrap/Tooltip"; import ReactECharts from "echarts-for-react"; -import * as AXIOSOF from "../../services/object_finding_api"; import { DateTime } from "luxon"; import { GlobalStore } from "../../store/globalStore"; +import { XCircle, Check, FilePlus, CheckCircle } from "react-bootstrap-icons"; +import * as AXIOSOF from "../../services/object_finding_api"; interface TargetSmallCardProps { target_info: IDSOObjectDetailedInfo | IDSOFramingObjectInfo; card_index: number; on_card_clicked: ((card_index: number, checked: boolean) => void) | null; on_choice_maken: (() => void) | null; - in_manage: boolean; + in_manage?: boolean; } const fig_options_template: any = { @@ -32,6 +30,9 @@ const fig_options_template: any = { }, xAxis: { type: "time", + axisLabel: { + formatter: "{HH}", + }, }, yAxis: { type: "value", @@ -47,6 +48,9 @@ const fig_options_template: any = { silent: true, data: [], }, + tooltip: { + trigger: "none", + }, }, ], }; @@ -88,7 +92,7 @@ const fig_line_data_template: any = [ lineStyle: { color: "black" }, }, ]; -// todo, how to auto get the target pic url? + function isDetailed(object: any): object is IDSOObjectDetailedInfo { return "altitude" in object; } @@ -97,32 +101,39 @@ const TargetSmallCard: React.FC = (props) => { // ui control const [show_detail, set_show_detail] = React.useState(0); const [this_checked, set_this_checked] = React.useState(false); - const [add_btn_color, set_add_btn_color] = - React.useState("primary"); + const [added_flag, set_added_flag] = React.useState(false); + const [target_icon_link, set_target_icon_link] = React.useState(""); + const [add_tooltip_open, set_add_tooltip_open] = React.useState(false); + // display data // display data const [echarts_options, set_echarts_options] = React.useState(fig_options_template); const [real_target_info, set_real_target_info] = React.useState({ name: "", + alias: "", ra: 0, dec: 0, target_type: "", - bmag: 0, - vmag: 0, + const: "", size: 0, - moon_distance: 0, + transit_month: 0, + transit_date: "", + filter: "", + focal_length: 0, altitude: [], + Top200: null, }); const [in_updating, set_in_updating] = React.useState(true); - // store ddata + // store data const target_store = GlobalStore.useAppState( (state) => state.TargetListStore ); const add_target_to_store = GlobalStore.actions.TargetListStore.add_target_and_focus; + const save_all_targets = GlobalStore.actions.TargetListStore.save_all_targets; const set_focus_target_to_store = GlobalStore.actions.TargetListStore.change_focus_target; @@ -134,33 +145,31 @@ const TargetSmallCard: React.FC = (props) => { ra: props.target_info.ra, dec: props.target_info.dec, target_type: props.target_info.target_type, - bmag: props.target_info.bmag, - vmag: props.target_info.vmag, size: props.target_info.size, - moon_distance: props.target_info.moon_distance, altitude: props.target_info.altitude, + alias: props.target_info.alias, + const: props.target_info.const, + transit_month: props.target_info.transit_month, + transit_date: props.target_info.transit_date, + filter: props.target_info.filter, + focal_length: props.target_info.focal_length, + Top200: props.target_info.Top200, }; set_real_target_info(new_target_frame_info); } else { - // console.log('is framing object!'); - // let new_target_frame_info: IDSOObjectDetailedInfo = { - // name: props.target_info.name, - // ra: props.target_info.ra, - // dec: props.target_info.dec, - // target_type: props.target_info.target_type, - // bmag: props.target_info.bmag, - // vmag: props.target_info.vmag, - // size: props.target_info.size, - // moon_distance: 0, - // altitude: [], - // } - // set_real_target_info(new_target_frame_info); construct_framing_info2card_info(); } + if (process.env.NODE_ENV == "development") { + set_target_icon_link("/api/file/DSO/" + props.target_info.name + ".jpg"); + } else { + set_target_icon_link("/file/DSO/" + props.target_info.name + ".jpg"); + } }, []); + React.useEffect(() => { initial_fig_data(); }, [real_target_info]); + React.useEffect(() => { if ("checked" in props.target_info) { set_this_checked(props.target_info.checked); @@ -168,10 +177,8 @@ const TargetSmallCard: React.FC = (props) => { }, [props.target_info]); const initial_fig_data = () => { - // process target info to display data let new_data = []; for (let i = 0; i < real_target_info.altitude.length; i++) { - // console.log(props.target_info.altitude[i][0], DateTime.fromFormat(props.target_info.altitude[i][0], 'yyyy-MM-dd HH:mm:ss')); new_data.push([ DateTime.fromFormat( real_target_info.altitude[i][0], @@ -193,6 +200,7 @@ const TargetSmallCard: React.FC = (props) => { set_echarts_options(new_options); set_in_updating(false); }; + const construct_framing_info2card_info = async () => { try { const new_curve_data = await AXIOSOF.getTargetALtCurveOnly( @@ -205,11 +213,15 @@ const TargetSmallCard: React.FC = (props) => { ra: props.target_info.ra, dec: props.target_info.dec, target_type: props.target_info.target_type, - bmag: props.target_info.bmag, - vmag: props.target_info.vmag, size: props.target_info.size, - moon_distance: new_curve_data.data.moon_distance, altitude: new_curve_data.data.altitude, + alias: "", + const: "", + transit_month: 0, + transit_date: "", + filter: "", + focal_length: 0, + Top200: null, }; set_real_target_info(new_target_frame_info); initial_fig_data(); @@ -221,6 +233,7 @@ const TargetSmallCard: React.FC = (props) => { return null; } }; + const on_add_target_to_framing_clicked = () => { let to_add_object: IDSOFramingObjectInfo = { name: props.target_info.name, @@ -230,119 +243,154 @@ const TargetSmallCard: React.FC = (props) => { flag: "", tag: "", target_type: props.target_info.target_type, - bmag: props.target_info.bmag, - vmag: props.target_info.vmag, size: props.target_info.size, checked: false, }; - // add_target_to_store(to_add_object); set_focus_target_to_store(to_add_object); if (props.on_choice_maken != null) { props.on_choice_maken(); } }; + const on_add_target_to_list_clicked = () => { - let to_add_object: IDSOFramingObjectInfo = { - name: props.target_info.name, - ra: props.target_info.ra, - dec: props.target_info.dec, - rotation: 0, - flag: "", - tag: "", - target_type: props.target_info.target_type, - bmag: props.target_info.bmag, - vmag: props.target_info.vmag, - size: props.target_info.size, - checked: false, - }; - add_target_to_store(to_add_object); - set_focus_target_to_store({ - name: props.target_info.name, - ra: props.target_info.ra, - dec: props.target_info.dec, - rotation: 0, - flag: "", - tag: "", - target_type: props.target_info.target_type, - bmag: props.target_info.bmag, - vmag: props.target_info.vmag, - size: props.target_info.size, - checked: false, - }); - set_add_btn_color("danger"); - // if (props.on_choice_maken!=null){ - // props.on_choice_maken(); - // } + if (added_flag) { + set_add_tooltip_open(true); + setTimeout(() => set_add_tooltip_open(false), 3000); + } else { + let to_add_object: IDSOFramingObjectInfo = { + name: props.target_info.name, + ra: props.target_info.ra, + dec: props.target_info.dec, + rotation: 0, + flag: "", + tag: "", + target_type: props.target_info.target_type, + size: props.target_info.size, + checked: false, + }; + add_target_to_store(to_add_object); + save_all_targets(); + set_focus_target_to_store({ + name: props.target_info.name, + ra: props.target_info.ra, + dec: props.target_info.dec, + rotation: 0, + flag: "", + tag: "", + target_type: props.target_info.target_type, + size: props.target_info.size, + checked: false, + }); + set_added_flag(true); + } }; return ( - -
- { - set_this_checked(!this_checked); - if (props.on_card_clicked != null) { - props.on_card_clicked(props.card_index, !this_checked); - } - }} - /> - - -
-
- - {props.target_info.name} - Ra: {props.target_info.ra.toFixed(7)} ° - Dec: {props.target_info.dec.toFixed(7)} ° - -
- - + + +
+
+ { + set_this_checked(!this_checked); + if (props.on_card_clicked != null) { + props.on_card_clicked(props.card_index, !this_checked); + } + }} + /> + + + 已添加到待拍摄列表,如需要删除目标,请到目标管理界面删除 + + } + > + + + {props.on_card_clicked != null && ( + + )} +
+
+ + {props.target_info.name} + + Ra: {props.target_info.ra.toFixed(5)} ° + Dec: {props.target_info.dec.toFixed(5)} ° +
+
+
+ {in_updating ? ( +
+
+ Loading... +
+
+ ) : ( + + )} +
- {props.on_card_clicked == null ? ( - <> - ) : ( - { - set_this_checked(!this_checked); - if (props.on_card_clicked != null) { - props.on_card_clicked(props.card_index, !this_checked); - } - }} - > - {this_checked ? "checked" : "unchecked"} - - )} = (props) => {
); }; + TargetSmallCard.defaultProps = { on_choice_maken: null, in_manage: false, diff --git a/websrc/cobalt-web/src/components/skymap/target_super_simle.tsx b/websrc/cobalt-web/src/components/skymap/target_super_simle.tsx new file mode 100644 index 00000000..7307754f --- /dev/null +++ b/websrc/cobalt-web/src/components/skymap/target_super_simle.tsx @@ -0,0 +1,221 @@ +import * as React from "react"; +import Card from "react-bootstrap/Card"; +import Button from "react-bootstrap/Button"; +import Row from "react-bootstrap/Row"; +import Col from "react-bootstrap/Col"; +import Placeholder from "react-bootstrap/Placeholder"; +import * as AXIOSOF from "../../services/object_finding_api"; + +interface TargetSuperSimpleCardProps { + target_name: string; + ra: number; + dec: number; + target_type: string; + on_target_selected: (index: number) => void; + index: number; +} + +export const TranslateTargetType = (target_type: string) => { + /* + all case types: + Asterism + Dark Neb + Em Neb + Gal Chain + Gal Clus + Gal Group + Gal-BCD + Gal-Dwarf + Gal-Ell + Gal-Lent + Gal-Mag + Galaxy + Glob Cl + HH + HII Neb + Mol Cld + Nova + Open Cl + PN + PPN + Quasar + Ref Neb + SNR + Star + Star Cld + WR Neb + YSO + */ + switch (target_type) { + case "Asterism": + return "星群"; + case "Dark Neb": + return "暗星云"; + case "Em Neb": + return "发射星云"; + case "Gal Chain": + return "星系链"; + case "Gal Clus": + return "星系团"; + case "Gal Group": + return "星系群"; + case "Gal-BCD": + return "致密蓝矮星系"; + case "Gal-Dwarf": + return "矮星系"; + case "Gal-Ell": + return "椭圆星系"; + case "Gal-Lent": + return "透镜状星系"; + case "Gal-Mag": + return "麦哲伦星系"; + case "Galaxy": + return "星系"; + case "Glob Cl": + return "球状星团"; + case "HH": + return "赫比格-哈罗天体"; + case "HII Neb": + return "电离氢发射星云"; + case "Mol Cld": + return "分子云"; + case "Nova": + return "新星"; + case "Open Cl": + return "疏散星团"; + case "PN": + return "行星状星云"; + case "PPN": + return "原行星云"; + case "Quasar": + return "类星体"; + case "Ref Neb": + return "反射星云"; + case "SNR": + return "超新星遗迹"; + case "Star": + return "恒星"; + case "Star Cld": + return "恒星云"; + case "WR Neb": + return "沃尔夫-拉叶星云"; + case "YSO": + return "初期恒星体"; + default: + return "未知"; + } +}; + +const TargetSuperSimpleCard: React.FC = (props) => { + // ui data + const [target_icon_link, set_target_icon_link] = React.useState(""); + const [display_type, set_display_type] = React.useState(""); + const [updated, set_updated] = React.useState(false); + const [current_alt, set_current_alt] = React.useState(0); + const [highest_alt, set_highest_alt] = React.useState(0); + const [available_time, set_available_time] = React.useState(0); + + const update_simple_data = async () => { + try { + let this_result = await AXIOSOF.getSimpleCardInfo(props.ra, props.dec); + if (this_result.success) { + set_current_alt(this_result.data.current); + set_highest_alt(this_result.data.highest); + set_available_time(this_result.data.available_shoot_time); + } + set_updated(true); + } catch (err) { + set_updated(true); + } + }; + React.useEffect(() => { + update_simple_data(); + if (process.env.NODE_ENV == "development") { + set_target_icon_link( + "/api/file/DSO/DSOObjects/Small/" + props.target_name + ".jpg" + ); + } else { + set_target_icon_link( + "/file/DSO/DSOObjects/Small/" + props.target_name + ".jpg" + ); + } + set_display_type(TranslateTargetType(props.target_type)); + }, []); + + const handle_img_error = () => { + set_target_icon_link("/file/DSO/DSOObjects/Small/default.jpg"); + }; + + return ( + + + + + + + + + {display_type} + + {props.target_name} + + + + + + + + + + + + + + + + {updated ? ( + <> + + + 当前{current_alt.toFixed(0)}° + + + + + 最高{highest_alt.toFixed(0)}° + + + + + 可拍{available_time.toFixed(1)}h + + + + ) : ( + + + + + + )} + + + + ); +}; + +export default TargetSuperSimpleCard; diff --git a/websrc/cobalt-web/src/constants/net-config.ts b/websrc/cobalt-web/src/constants/net-config.ts index 51138129..f1c63f0e 100644 --- a/websrc/cobalt-web/src/constants/net-config.ts +++ b/websrc/cobalt-web/src/constants/net-config.ts @@ -1,6 +1,6 @@ export default { // axios 基础url地址 - baseURL: process.env.NODE_ENV === 'development' ? 'http://localhost:5173/api' : '', + baseURL: process.env.NODE_ENV === 'development' ? 'http://localhost:3000/' : '', // 操作正常返回的code,根据后端第一 successCode: [200, 0], // 超时时间 diff --git a/websrc/cobalt-web/src/icons/console.svg b/websrc/cobalt-web/src/icons/console.svg new file mode 100644 index 00000000..7274c102 --- /dev/null +++ b/websrc/cobalt-web/src/icons/console.svg @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/websrc/cobalt-web/src/icons/console/camera_shoot.svg b/websrc/cobalt-web/src/icons/console/camera_shoot.svg new file mode 100644 index 00000000..5fa002eb --- /dev/null +++ b/websrc/cobalt-web/src/icons/console/camera_shoot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/websrc/cobalt-web/src/icons/console/filter.svg b/websrc/cobalt-web/src/icons/console/filter.svg new file mode 100644 index 00000000..7f7e2f3d --- /dev/null +++ b/websrc/cobalt-web/src/icons/console/filter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/websrc/cobalt-web/src/icons/console/guide.svg b/websrc/cobalt-web/src/icons/console/guide.svg new file mode 100644 index 00000000..44bf204d --- /dev/null +++ b/websrc/cobalt-web/src/icons/console/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/websrc/cobalt-web/src/icons/console/polar_align.svg b/websrc/cobalt-web/src/icons/console/polar_align.svg new file mode 100644 index 00000000..df062ee5 --- /dev/null +++ b/websrc/cobalt-web/src/icons/console/polar_align.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/websrc/cobalt-web/src/icons/console/telescope_control.svg b/websrc/cobalt-web/src/icons/console/telescope_control.svg new file mode 100644 index 00000000..af1f8bc7 --- /dev/null +++ b/websrc/cobalt-web/src/icons/console/telescope_control.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git "a/websrc/cobalt-web/src/icons/console/\346\211\213\345\212\250\345\257\271\347\204\246 AF\347\232\204\345\233\276\346\240\207\345\275\223\350\207\252\345\212\250\345\257\271\347\204\246.svg" "b/websrc/cobalt-web/src/icons/console/\346\211\213\345\212\250\345\257\271\347\204\246 AF\347\232\204\345\233\276\346\240\207\345\275\223\350\207\252\345\212\250\345\257\271\347\204\246.svg" new file mode 100644 index 00000000..3a5e1daf --- /dev/null +++ "b/websrc/cobalt-web/src/icons/console/\346\211\213\345\212\250\345\257\271\347\204\246 AF\347\232\204\345\233\276\346\240\207\345\275\223\350\207\252\345\212\250\345\257\271\347\204\246.svg" @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/websrc/cobalt-web/src/icons/device_connect.svg b/websrc/cobalt-web/src/icons/device_connect.svg new file mode 100644 index 00000000..1d14ca13 --- /dev/null +++ b/websrc/cobalt-web/src/icons/device_connect.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/websrc/cobalt-web/src/icons/galaxy.svg b/websrc/cobalt-web/src/icons/galaxy.svg new file mode 100644 index 00000000..cebfc9e9 --- /dev/null +++ b/websrc/cobalt-web/src/icons/galaxy.svg @@ -0,0 +1,13 @@ + + + \ No newline at end of file diff --git a/websrc/cobalt-web/src/icons/list.svg b/websrc/cobalt-web/src/icons/list.svg new file mode 100644 index 00000000..23afec3e --- /dev/null +++ b/websrc/cobalt-web/src/icons/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/websrc/cobalt-web/src/icons/main_page.svg b/websrc/cobalt-web/src/icons/main_page.svg new file mode 100644 index 00000000..e599850f --- /dev/null +++ b/websrc/cobalt-web/src/icons/main_page.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/websrc/cobalt-web/src/icons/setting.svg b/websrc/cobalt-web/src/icons/setting.svg new file mode 100644 index 00000000..77e2bc47 --- /dev/null +++ b/websrc/cobalt-web/src/icons/setting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/websrc/cobalt-web/src/icons/telescope.svg b/websrc/cobalt-web/src/icons/telescope.svg new file mode 100644 index 00000000..d3970797 --- /dev/null +++ b/websrc/cobalt-web/src/icons/telescope.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/websrc/cobalt-web/src/icons/welcome.jpg b/websrc/cobalt-web/src/icons/welcome.jpg new file mode 100644 index 00000000..66564c81 Binary files /dev/null and b/websrc/cobalt-web/src/icons/welcome.jpg differ diff --git a/websrc/cobalt-web/src/index.css b/websrc/cobalt-web/src/index.css deleted file mode 100644 index 6119ad9a..00000000 --- a/websrc/cobalt-web/src/index.css +++ /dev/null @@ -1,68 +0,0 @@ -:root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/websrc/cobalt-web/src/index.less b/websrc/cobalt-web/src/index.less new file mode 100644 index 00000000..1ebdfb6e --- /dev/null +++ b/websrc/cobalt-web/src/index.less @@ -0,0 +1,42 @@ +:root { + font-family: PingFang TC, Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + color-scheme: light dark; + color:#ffffff; + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; + + width: 100vw; + height: 100vh; + // background: linear-gradient(112.1deg, rgb(32, 38, 57) 11.4%, rgb(63, 76, 119) 70.2%); + background: linear-gradient(112.1deg, rgb(255, 255, 255) 11.4%, rgb(217, 224, 232) 70.2%); +} + + + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + + +@media (prefers-color-scheme: light) { + :root { + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/websrc/cobalt-web/src/interfaces/ConnectControl.d.ts b/websrc/cobalt-web/src/interfaces/ConnectControl.d.ts index b87cef5c..f1206816 100644 --- a/websrc/cobalt-web/src/interfaces/ConnectControl.d.ts +++ b/websrc/cobalt-web/src/interfaces/ConnectControl.d.ts @@ -1,5 +1,28 @@ -declare interface ConnectionDeviceInfo{ - device_type: string; - device_name: string; - - } \ No newline at end of file +declare interface ConnectionDeviceInfo { + device_type: string; + device_name: string; +} + +declare interface IConnectBrandSelection { + zh: string; + en: string; + driver: string; +} + +declare interface IConnectSelectedBrandList { + camera: IConnectBrandSelection; + telescope: IConnectBrandSelection; + guider: IConnectBrandSelection; + focus: IConnectBrandSelection; + filter: IConnectBrandSelection; + polar?: IConnectBrandSelection; +} + +declare interface IConnectSupportedDevicesNameENType { + camera: null; + telescope: null; + guider: null; + focus: null; + filter: null; + polar: null; +} diff --git a/websrc/cobalt-web/src/interfaces/ConsoleGao.d.ts b/websrc/cobalt-web/src/interfaces/ConsoleGao.d.ts index 5bea6a38..ffad306f 100644 --- a/websrc/cobalt-web/src/interfaces/ConsoleGao.d.ts +++ b/websrc/cobalt-web/src/interfaces/ConsoleGao.d.ts @@ -1,13 +1,13 @@ -interface ICThreePointTarget { +declare interface ICThreePointTarget { ra: number; dec: number; } // hfr星点数据 -interface ICHFRDataPointList { +declare interface ICHFRDataPointList { star_count: Array; average_hfr: Array; } -interface ICSingleHFRPointData { +declare interface ICSingleHFRPointData { star_count: number; average_hfr: number; max_star: number; @@ -16,7 +16,7 @@ interface ICSingleHFRPointData { } // phd2导星数据记录 -interface ICPHD2GuideDataPointList { +declare interface ICPHD2GuideDataPointList { dx: Array; dy: Array; RaDistance: Array; @@ -25,7 +25,7 @@ interface ICPHD2GuideDataPointList { DecControl: Array; } -interface ICPHD2InterfaceGuideStep { +declare interface ICPHD2InterfaceGuideStep { Frame: number; Time: number; Mount: string; @@ -43,26 +43,37 @@ interface ICPHD2InterfaceGuideStep { SNR: number; HFD: number; AvgDist: number; - RALimited: boolean; - DecLimited: boolean; + RALimited: bool; + DecLimited: bool; ErrorCode: number; } +declare interface ICPHD2InterfaceCalibrationResult { + calibrated: boolean; + xAngle: number; + xRate: number; + xParity: "+" | "-"; + yAngle: number; + yRate: number; + yParity: "+" | "-"; + declination: number; +} + // general interface -interface IResponseFlagGeneral { +declare interface IResponseFlagGeneral { success: boolean; data: { flag: boolean; }; } -interface IResponseDataAny { +declare interface IResponseDataAny { success: boolean; data: any; } // phd2 related http interface -interface IPHD2ResponseStatus { +declare interface IPHD2ResponseStatus { success: boolean; data: { flag: diff --git a/websrc/cobalt-web/src/interfaces/GlobalParameters.d.ts b/websrc/cobalt-web/src/interfaces/GlobalParameters.d.ts index 3379709c..99c1ac16 100644 --- a/websrc/cobalt-web/src/interfaces/GlobalParameters.d.ts +++ b/websrc/cobalt-web/src/interfaces/GlobalParameters.d.ts @@ -51,7 +51,7 @@ declare interface IPlateSolve { declare interface ITelescopeInfo { name: string; - apeture: number; + aperture: number; focal_length: number; guider_aperture: number; guider_focal_length: number; @@ -64,8 +64,8 @@ declare interface AnyCameraInfo { } declare interface ICameraGet { - camera: boolean; - guider_camera: boolean; + camera: boolean = false; + guider_camera: boolean = false; } declare interface IGlobalParameters { @@ -107,6 +107,7 @@ declare interface IGPParameterExplain { declare interface IGPPorfileList { data: any; + success: boolean; current_profile: string; all_profiles: Array; } @@ -115,3 +116,7 @@ declare interface IGPFilterSelection { label: string; value: number | string; } + +declare interface IGPManualHelpSetting { + show_three_point_help: boolean; +} diff --git a/websrc/cobalt-web/src/interfaces/PAA.d.ts b/websrc/cobalt-web/src/interfaces/PAA.d.ts new file mode 100644 index 00000000..dcd563f2 --- /dev/null +++ b/websrc/cobalt-web/src/interfaces/PAA.d.ts @@ -0,0 +1,119 @@ +declare interface PAABaseStepsInfo { + device: string; + instruction: string; + params: any; + id: string; + count?: number; + repeat?: number; + children?: Array; + conditions?: Array; + name?: string; + ra?: number; + dec?: number; + after_loop?: Array; +} + +declare interface PAABseCondiitonInfo { + if_condition: string; + params: any; +} + +declare interface PAABaseAfterLoopInfo { + if_condition: string; + params: any; +} + +declare interface PAAScriptInfo { + file_name: string; + modified_time: string; +} + +declare interface PAAlog_info { + data: { + params?: { + count: number; + }; + }; + device_name: string; + instruction: string; + message: string; + type: string; +} + +// script type +declare interface exposureProps { + exposure_time: number; + exposure_count: number; + dither: number; + type: string; + binning: number; + filter?: number; +} + +declare interface targetProps { + target_name: string; + ra: number; + dec: number; + rotation?: number; + exposure_list: Array; +} + +declare interface IPAAColorScriptDataSetting { + target_list: Array; + warm_camera: boolean; + cool_camera: boolean; + cool_temperature: number; + af_on_start: boolean; + af_on_hfr_change: number; + af_after_exposure: number; + af_after_temperature: number; + recenter_if_drift: number; + unpark: boolean; + park: boolean; + find_home: boolean; + meridian_flip: boolean; + guide_exposure?: number; + restore_guide?: boolean; +} + +declare interface IPAAMonoScriptDataSetting { + target_list: Array; + warm_camera: boolean; + cool_camera: boolean; + cool_temperature: number; + af_on_start: boolean; + af_on_hfr_change: number; + af_after_exposure: number; + af_after_temperature: number; + af_after_filter_change: boolean; + recenter_if_drift: number; + unpark: boolean; + park: boolean; + find_home: boolean; + meridian_flip: boolean; + guide_exposure?: number; + restore_guide?: boolean; +} + +declare interface IPAADarkScriptDataSetting { + cool_temperature: number; + dark_time: Array; + repeat: number; + warm_camera: boolean; + bias: boolean; +} + +// ----------------------- +// IO related + +declare interface IPAAScriptDataType { + script: Array; + setting: any; + type: string; +} + +declare interface IPAAScripteResponse { + success: boolean; + data: IPAAScriptDataType; + message?: string; +} diff --git a/websrc/cobalt-web/src/interfaces/PAAFixedProcedure.d.ts b/websrc/cobalt-web/src/interfaces/PAAFixedProcedure.d.ts index d06f1ca8..6a099cb8 100644 --- a/websrc/cobalt-web/src/interfaces/PAAFixedProcedure.d.ts +++ b/websrc/cobalt-web/src/interfaces/PAAFixedProcedure.d.ts @@ -1,4 +1,3 @@ - declare interface IPAAFixedStatus { success: boolean; data: { @@ -28,10 +27,39 @@ declare interface IPAAFixedGotoRequest { dec: number; } -declare interface IPAAFixedPolarAlignmentRequest{ - start_from: 'West' | 'East'; +declare interface IPAAFixedPolarAlignmentRequest { + start_from: "West" | "East"; move_time: number; solve_retry: number; manual_start?: boolean; + search_radius?: number; +} + +declare interface IPAAFixedAutofocusRequest { + filter_index: "current" | number; + start_side: boolean; +} + +// PPA scripts + +declare interface PAAEmptyProps {} + +declare interface PAAScriptBaseHanlder { + savePAAScript: () => void; } +declare interface TargetSelectDialogHandler { + open_dialog: () => void; +} + +declare interface PAA_step_info { + data: { + params?: { + count: number; + }; + }; + device_name: string; + instruction: string; + message: string; + type: string; +} diff --git a/websrc/cobalt-web/src/interfaces/TargetSearch.d.ts b/websrc/cobalt-web/src/interfaces/TargetSearch.d.ts index 53d108f4..d6d14c16 100644 --- a/websrc/cobalt-web/src/interfaces/TargetSearch.d.ts +++ b/websrc/cobalt-web/src/interfaces/TargetSearch.d.ts @@ -1,156 +1,169 @@ declare interface IDSOObjectInfo { - name: string, - ra: number, - dec: number, + name: string; + ra: float; + dec: float; } - -declare interface IDSOFramingObjectInfo{ - name: string, - ra: number, - dec: number, - rotation: number, - flag: string, // flag is user editable, any string is ok - tag: string, // tag is system set filters. - target_type: string, - bmag: number, - vmag: number, - size: number, - checked: boolean, +declare interface IDSOFramingObjectInfo { + name: string; + ra: float; + dec: float; + rotation: float; + flag: string; // flag is user editable, any string is ok + tag: string; // tag is system set filters. + target_type: string; + size: float; + checked: boolean; + // depreciated + bmag?: float; + vmag?: float; } - declare interface IDSOObjectDetailedInfo { - name: string, - ra: number, - dec: number, - target_type: string, - bmag: number, - vmag: number, - size: number, - moon_distance: number, - altitude: Array<[string, number, number]> + name: string; + alias: string; + ra: float; + dec: float; + target_type: string; + const: string; + size: float; + transit_month: number; + transit_date: string; + filter: string; + focal_length: number; + altitude: Array<[string, float, float]>; + Top200: number | null; + // depreciated + moon_distance?: float | null; + bmag?: float | null; + vmag?: float | null; } +declare interface IDSOObjectSimpleInfo { + current: number; + highest: number; + available_shoot_time: number; +} declare interface ILightStarInfo { - name: string, - show_name: string, - ra: number, - dec: number, - Const: string, - Const_Zh: string, - magnitude: number, - alt: number, - az: number, - sky: string, + name: string; + show_name: string; + ra: float; + dec: float; + Const: string; + Const_Zh: string; + magnitude: float; + alt: float; + az: float; + sky: string; } - declare interface ITwilightDataString { - evening: { - sun_set_time: string, - evening_civil_time: string, - evening_nautical_time: string, - evening_astro_time: string, - }, - morning: { - sun_rise_time: string, - morning_civil_time: string, - morning_nautical_time: string, - morning_astro_time: string, - } + evening: { + sun_set_time: string; + evening_civil_time: string; + evening_nautical_time: string; + evening_astro_time: string; + }; + morning: { + sun_rise_time: string; + morning_civil_time: string; + morning_nautical_time: string; + morning_astro_time: string; + }; } declare interface ITwilightData { - evening: { - sun_set_time: Date, - evening_civil_time: Date, - evening_nautical_time: Date, - evening_astro_time: Date, - }, - morning: { - sun_rise_time: Date, - morning_civil_time: Date, - morning_nautical_time: Date, - morning_astro_time: Date, - } + evening: { + sun_set_time: Date; + evening_civil_time: Date; + evening_nautical_time: Date; + evening_astro_time: Date; + }; + morning: { + sun_rise_time: Date; + morning_civil_time: Date; + morning_nautical_time: Date; + morning_astro_time: Date; + }; } // interface from the api declare interface IOFRequestLightStar { - sky_range?: Array, - max_mag?: number, + sky_range?: Array; + max_mag?: number; } declare interface IOFResponseLightStar { - success: boolean; - data: Array; + success: boolean; + data: Array; } declare interface IOFResponseFindTargetName { - success: boolean, - data: Array, + success: boolean; + data: Array; } declare interface IOFRequestFOVpoints { - x_pixels: number, - y_pixels: number, - x_pixel_size: number, - y_pixel_size: number, - focal_length: number, - target_ra: number, - target_dec: number, - camera_rotation: number, + x_pixels: number; + y_pixels: number; + x_pixel_size: number; + y_pixel_size: number; + focal_length: number; + target_ra: number; + target_dec: number; + camera_rotation: number; } declare interface IOFResponseFOVpoints { - success: boolean, - data: [ - [number, number], - [number, number], - [number, number], - [number, number] - ], - message?: string, + success: boolean; + data: [ + [number, number], + [number, number], + [number, number], + [number, number] + ]; + message?: string; } declare interface IOFRequestFOVpointsTiles { - x_pixels: number, - y_pixels: number, - x_pixel_size: number, - y_pixel_size: number, - focal_length: number, - target_ra: number, - target_dec: number, - camera_rotation: number, - x_tiles: number, - y_tiles: number, - overlap: number, + x_pixels: number; + y_pixels: number; + x_pixel_size: number; + y_pixel_size: number; + focal_length: number; + target_ra: number; + target_dec: number; + camera_rotation: number; + x_tiles: number; + y_tiles: number; + overlap: number; } declare interface IOFResponseFOVpointsTiles { - success: boolean, - data: Array<[ - [number, number], - [number, number], - [number, number], - [number, number] - ]>, - message?: string, + success: boolean; + data: Array< + [[number, number], [number, number], [number, number], [number, number]] + >; + message?: string; } declare interface IOFResponseAltCurve { - success: boolean, - data: { - moon_distance: number, - altitude: Array<[string, number, number]> - } + success: boolean; + data: { + moon_distance: float; + altitude: Array<[string, float, float]>; + }; } declare interface IOFResponseTwilightData { - success: boolean, - data: ITwilightDataString + success: boolean; + data: ITwilightDataString; +} + +declare interface IOFResponseOBJSimple { + success: boolean; + data: IDSOObjectSimpleInfo; } -// end of interface from the api \ No newline at end of file +// end of interface from the api diff --git a/websrc/cobalt-web/src/interfaces/api.d.ts b/websrc/cobalt-web/src/interfaces/api.d.ts index 8dbd62e1..7cdfac72 100644 --- a/websrc/cobalt-web/src/interfaces/api.d.ts +++ b/websrc/cobalt-web/src/interfaces/api.d.ts @@ -1,11 +1,11 @@ declare interface IRequestParams { - url:string; - method?:"GET"|"POST", - body?:any + url: string; + method?: "GET" | "POST"; + body?: any; } declare interface IRequestResponse { - success: boolean; - data?: T; - message?: string; -} \ No newline at end of file + success: boolean; + data?: T; + message?: string; +} diff --git a/websrc/cobalt-web/src/interfaces/global.d.ts b/websrc/cobalt-web/src/interfaces/global.d.ts index 327940c1..420bc761 100644 --- a/websrc/cobalt-web/src/interfaces/global.d.ts +++ b/websrc/cobalt-web/src/interfaces/global.d.ts @@ -1,7 +1,7 @@ declare global { - interface Window { - A:any - } + interface Window { + A: any; } - - export {} \ No newline at end of file +} + +export {}; diff --git a/websrc/cobalt-web/src/interfaces/react.d.ts b/websrc/cobalt-web/src/interfaces/react.d.ts index e69de29b..df5f1863 100644 --- a/websrc/cobalt-web/src/interfaces/react.d.ts +++ b/websrc/cobalt-web/src/interfaces/react.d.ts @@ -0,0 +1,7 @@ +declare type DeepPartial = Partial<{ + [k in keyof T]: T[k] extends object ? DeepPartial : T[k]; +}>; + +declare type IProps = { + children?: React.ReactNode; +}; diff --git a/websrc/cobalt-web/src/layout/header/QuickControlComp.tsx b/websrc/cobalt-web/src/layout/header/QuickControlComp.tsx new file mode 100644 index 00000000..c1bb9421 --- /dev/null +++ b/websrc/cobalt-web/src/layout/header/QuickControlComp.tsx @@ -0,0 +1,51 @@ +import * as React from 'react'; +import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight'; +import KeyboardArrowLeftIcon from '@mui/icons-material/KeyboardArrowLeft'; +import Collapse from "@mui/material/Collapse"; +import { Box } from '@mui/material'; +import { IconButton, Button, ButtonGroup } from '@mui/material'; +import { GlobalStore } from '@/store/globalStore'; + + +const QuickControlComp: React.FC = () => { + const [expand, setExpand] = React.useState(false); + const state = GlobalStore.useAppState(state=>state.console); + + const on_quick_control_click = (target: 'camera' | 'telescope' | 'focuser' | 'filter' | 'guider' | 'phd2') => { + GlobalStore.actions.console.setState({ + menu: target, + drawerVisible: true, + selec_console_drawer_open: false, + }); + window.location.hash = 'console'; + } + + return ( + <> + + + + + + + {/* */} + + + + + { + (expand)? + ( setExpand(false)} color="secondary" sx={{zIndex: 99}}>): + ( setExpand(true)} color="secondary" sx={{zIndex: 99}}>) + } + + + ) +} + +export default QuickControlComp; \ No newline at end of file diff --git a/websrc/cobalt-web/src/layout/header/index.tsx b/websrc/cobalt-web/src/layout/header/index.tsx new file mode 100644 index 00000000..5743cf4c --- /dev/null +++ b/websrc/cobalt-web/src/layout/header/index.tsx @@ -0,0 +1,39 @@ +import {useCallback, useState} from 'react'; +import {ReactComponent as ListIcon} from '@/icons/list.svg'; +import {GlobalStore} from '@/store/globalStore'; +import { IconButton, Button, ButtonGroup } from '@mui/joy'; +import QuickControlComp from './QuickControlComp'; +import horivisison_logo from '../../assets/imgs/logo-title.png'; + +import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted'; + + + +import './style.less'; + +const Header = ()=>{ + const {drawerVisible} = GlobalStore.useAppState(state=>state.visible); + + const toggleNavigation = useCallback(()=>{ + GlobalStore.actions.visible.setState({ + drawerVisible:!drawerVisible + }) + },[drawerVisible]); + + return ( +
+ + + + + {/* HORIVISION */} + +
+ //
+ // + // LightGPT + //
+ ) +} + +export default Header; \ No newline at end of file diff --git a/websrc/cobalt-web/src/layout/header/style.less b/websrc/cobalt-web/src/layout/header/style.less new file mode 100644 index 00000000..bc5104d9 --- /dev/null +++ b/websrc/cobalt-web/src/layout/header/style.less @@ -0,0 +1,51 @@ +.app-header{ + width: 100vw; + height: 32px; + // background-color: #333; + background-color: #4a83b8; + display: flex; + align-items: center; + padding: 6px; + &-title{ + color:#ffffff; + margin-left: 10px; + font-weight: 900; + font-size: 28px; + } +} + +.app-drawer-btn{ + width: calc(100vw - 12px); + height: 32px; + display: flex; + align-items: center; + background-color: transparent; + z-index: 100; + padding: 6px; + &-title{ + color:#ffffff; + margin-left: 10px; + font-weight: 900; + font-size: 28px; + } + &-logo{ + height: 32px; /* 设置logo的高度 */ + width: auto; /* 自动调整宽度以保持图片的等比例 */ + background-color: transparent; /* 设置背景为透明 */ + z-index: 99; + } + + } + +.app-quick-control{ + position: absolute; + &-child1 { + right: 0; + top: 0; + } + + &-child2 { + right: 40; + top: 0; + } +} \ No newline at end of file diff --git a/websrc/cobalt-web/src/layout/index.tsx b/websrc/cobalt-web/src/layout/index.tsx index ac1c50b7..498865a9 100644 --- a/websrc/cobalt-web/src/layout/index.tsx +++ b/websrc/cobalt-web/src/layout/index.tsx @@ -1,8 +1,7 @@ -import { useEffect } from "react"; +import React, { useEffect } from "react"; import Navigation from "./navigation"; import { useNavigate } from "react-router-dom"; import "./style.less"; -import React from "react"; type Props = { children: JSX.Element | JSX.Element[] | string | string[]; @@ -17,8 +16,8 @@ export default ({ children }: Props) => { return (
- -
{children}
+ {} +
{children}
); }; diff --git a/websrc/cobalt-web/src/layout/navigation/index.tsx b/websrc/cobalt-web/src/layout/navigation/index.tsx index 091f9dca..2d36d5e1 100644 --- a/websrc/cobalt-web/src/layout/navigation/index.tsx +++ b/websrc/cobalt-web/src/layout/navigation/index.tsx @@ -11,7 +11,8 @@ import { Gear, Webcam, Box, - Bookmark + Bookmark, + HouseAdd, } from "react-bootstrap-icons"; import { useTranslation } from "react-i18next"; @@ -21,12 +22,13 @@ function Navigation() { const [language, setLanguage] = useState("en"); const navLinks = [ - { to: "/dashboard", text: "Dashboard", icon: House }, + { to: "/home", text: "Home", icon: HouseAdd }, + { to: "/console", text: "Dashboard", icon: House }, { to: "/server", text: "Server Finder", icon: Info }, - { to: "/device_connect", text: "Device Connector", icon: Webcam }, + { to: "/connect", text: "Device Connector", icon: Webcam }, { to: "/device", text: "Device Control", icon: Box }, { to: "/skymap", text: "Skymap & Object", icon: Map }, - { to: "/settings", text: "Settings", icon: Gear }, + { to: "/config", text: "Settings", icon: Gear }, { to: "/about", text: "About", icon: Bookmark }, ]; @@ -36,7 +38,7 @@ function Navigation() { return ( <> - + = (props) => { - const [open, setOpen] = useState(false); - - const handleTooltipClose = () => { - setOpen(false); - }; - - const handleTooltipOpen = () => { - setOpen(true); - }; + const renderTooltip = (tooltip: string) => ( + {tooltip} + ); if (props.show_changing) { return ( - + {props.tooltip}} - rootClose + overlay={renderTooltip(props.tooltip || "")} > - @@ -39,12 +31,10 @@ const GPRedDotComp: React.FC = (props) => { } else { return ( {props.tooltip}} - rootClose + overlay={renderTooltip(props.tooltip || "")} > - diff --git a/websrc/cobalt-web/src/pages/config/OneSettingEntryComp.tsx b/websrc/cobalt-web/src/pages/config/OneSettingEntryComp.tsx index ec3eda49..068c286f 100644 --- a/websrc/cobalt-web/src/pages/config/OneSettingEntryComp.tsx +++ b/websrc/cobalt-web/src/pages/config/OneSettingEntryComp.tsx @@ -15,6 +15,7 @@ import OverlayTrigger from "react-bootstrap/OverlayTrigger"; import Dropdown from "react-bootstrap/Dropdown"; import GPRedDotComp from "./ClickQuestionTooltips"; +import { GlobalStore } from "../../store/globalStore"; interface SingleSettingPartProps { setting_part_key: string; @@ -84,7 +85,7 @@ const GlobalSettingSingleSettingEntry: React.FC = ( props.handleSettingChange( props.setting_part_key, diff --git a/websrc/cobalt-web/src/pages/config/components/camera.tsx b/websrc/cobalt-web/src/pages/config/components/camera.tsx new file mode 100644 index 00000000..e69de29b diff --git a/websrc/cobalt-web/src/pages/config/components/filter.tsx b/websrc/cobalt-web/src/pages/config/components/filter.tsx new file mode 100644 index 00000000..9e359523 --- /dev/null +++ b/websrc/cobalt-web/src/pages/config/components/filter.tsx @@ -0,0 +1,148 @@ +import * as React from "react"; +import { GlobalStore } from "../../../store/globalStore"; +import Table from "react-bootstrap/Table"; +import Button from "react-bootstrap/Button"; +import Modal from "react-bootstrap/Modal"; +import Form from "react-bootstrap/Form"; +import { Card } from "react-bootstrap"; + +const GPFilterEasyUseComp: React.FC = () => { + const global_parameter = GlobalStore.useAppState( + (state) => state.GlobalParameterStore + ); + const [showFilterSetDialog, setShowFilterSetDialog] = + React.useState(false); + const [toSetFilterIndex, setToSetFilterIndex] = React.useState(0); + const [singleFilterDialogData, setSingleDialogData] = + React.useState({ + filter_name: "", + focus_offset: 0, + af_exposure_time: 1, + }); + const [filterTouched, setFilterTouched] = React.useState(false); + + // functions + + const onSubmitClicked = (filterIndex: number) => { + // if (filterTouched) { + // props.handle_single_filter_setting(filterIndex, singleFilterDialogData); + // } + setShowFilterSetDialog(false); + }; + + return ( + <> + {global_parameter.global_parameter.filter_setting === null ? ( + + 全局变量加载失败! + + ) : ( +
+ + + + + + + + + + + + {global_parameter.global_parameter.filter_setting?.filter_info.map( + (singleFilterInfo, singleFilterIndex) => { + return ( + + + + + + + + ); + } + )} + +
滤镜编号滤镜名称滤镜对焦偏置值自动对焦曝光时间倍率
{singleFilterIndex + 1}{singleFilterInfo.filter_name}{singleFilterInfo.focus_offset}{singleFilterInfo.af_exposure_time} + +
+
+ )} + + setShowFilterSetDialog(false)} + > + + 修改滤镜编号{toSetFilterIndex + 1} + + +
) => { + event.preventDefault(); + setShowFilterSetDialog(false); + onSubmitClicked(toSetFilterIndex); + }} + > + + 滤镜名称 + { + setFilterTouched(true); + setSingleDialogData({ + ...singleFilterDialogData, + filter_name: event.target.value, + }); + }} + /> + + + 滤镜对焦偏置值 + { + setFilterTouched(true); + setSingleDialogData({ + ...singleFilterDialogData, + focus_offset: parseInt(event.target.value), + }); + }} + /> + + + 自动对焦曝光时间倍率 + { + setFilterTouched(true); + setSingleDialogData({ + ...singleFilterDialogData, + af_exposure_time: parseInt(event.target.value), + }); + }} + /> + + +
+
+
+ + ); +}; + +export default GPFilterEasyUseComp; diff --git a/websrc/cobalt-web/src/pages/config/components/location.tsx b/websrc/cobalt-web/src/pages/config/components/location.tsx new file mode 100644 index 00000000..94817565 --- /dev/null +++ b/websrc/cobalt-web/src/pages/config/components/location.tsx @@ -0,0 +1,183 @@ +import React from "react"; +import { GlobalStore } from "../../../store/globalStore"; +import { Card, Button, Row, Col } from "react-bootstrap"; +import { Compass } from "react-bootstrap-icons"; + +const GPLocationEasyUseComp: React.FC = () => { + const global_parameter = GlobalStore.useAppState( + (state) => state.GlobalParameterStore + ); + + const [error_in_range, set_error_in_range] = React.useState(false); + + const calculate_distance = () => { + if ( + "GlobalLocation" in window && + global_parameter.global_parameter.geo_location !== null + ) { + let GlobalLocation = (window as any).GlobalLocation as { + lng: number; + lat: number; + }; + const long1 = GlobalLocation.lng as number; + const lat1 = GlobalLocation.lat as number; + const long2 = global_parameter.global_parameter.geo_location.longitude; + const lat2 = global_parameter.global_parameter.geo_location.latitude; + // calculate distance on earth between two points. unit in km. + const R = 6371; + const dLat = ((lat2 - lat1) * Math.PI) / 180; + const dLon = ((long2 - long1) * Math.PI) / 180; + const a = + Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos((lat1 * Math.PI) / 180) * + Math.cos((lat2 * Math.PI) / 180) * + Math.sin(dLon / 2) * + Math.sin(dLon / 2); + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + const d = R * c; + return d; + } else { + return -1; + } + }; + const on_import_location_2_gp_setting = () => { + if ("GlobalLocation" in window) { + let GlobalLocation = (window as any).GlobalLocation as { + lng: number; + lat: number; + }; + GlobalStore.actions.GlobalParameterStore.set_parameter({ + parameter_name: "geo_location", + to_set_parameter: { + longitude: GlobalLocation.lng, + latitude: GlobalLocation.lat, + // height: + }, + }); + } + }; + + React.useEffect(() => { + const distance = calculate_distance(); + if (distance !== -1 && distance > 5) { + set_error_in_range(false); + } else { + set_error_in_range(true); + } + }, []); + React.useEffect(() => { + const distance = calculate_distance(); + if (distance !== -1 && distance > 5) { + set_error_in_range(false); + } else { + set_error_in_range(true); + } + }, [ + global_parameter.global_parameter.geo_location?.latitude, + global_parameter.global_parameter.geo_location?.longitude, + ]); + React.useEffect(() => { + const distance = calculate_distance(); + if (distance !== -1 && distance > 5) { + set_error_in_range(false); + } else { + set_error_in_range(true); + } + }, [window.GlobalLocation?.lat]); + + return ( + <> + + + {/* 手机定位信息 */} + {"GlobalLocation" in window ? ( + + + + 定位经度: {window.GlobalLocation?.lng} ° + 定位纬度: {window.GlobalLocation?.lat} ° + 定位高度: 米 + + + {error_in_range ? ( + + ) : ( + + )} + + + + ) : ( + + + 未成功获取手机定位 + + + )} + + + {/* 全局变量内部的信息 */} + {global_parameter.global_parameter.geo_location === null ? ( + + + Failed to get phone location + + + ) : ( + + + + + 系统设置经度:{" "} + {global_parameter.global_parameter.geo_location.longitude} ° + + + 系统设置纬度:{" "} + {global_parameter.global_parameter.geo_location.latitude} ° + + + 系统设置高度:{" "} + {global_parameter.global_parameter.geo_location.height} 米 + + + 系统设置时区:{" "} + {global_parameter.global_parameter.geo_location.time_zone} + + + + + + + )} + + + + + + + + + Please confirm the longitude and latitude information before + starting device connection! + +
+ If you modify the system's longitude, latitude or other + information after connecting to the equatorial mount, it will + not automatically sync to the equatorial mount. If you have + modified the location information, you must disconnect and + reconnect the equatorial mount! +
+
+
+ +
+ + ); +}; + +export default GPLocationEasyUseComp; diff --git a/websrc/cobalt-web/src/pages/config/components/telescope.tsx b/websrc/cobalt-web/src/pages/config/components/telescope.tsx new file mode 100644 index 00000000..0d7b3590 --- /dev/null +++ b/websrc/cobalt-web/src/pages/config/components/telescope.tsx @@ -0,0 +1,194 @@ +import React from "react"; +import { Card, Form, Row, Col, InputGroup, Button } from "react-bootstrap"; +import { InfoCircle } from "react-bootstrap-icons"; +import { GlobalStore } from "../../../store/globalStore"; + +const GPTelescopeEasyUseComp: React.FC = () => { + const global_parameter = GlobalStore.useAppState( + (state) => state.GlobalParameterStore + ); + + const [main_focal_length, set_main_focal_length] = React.useState("0"); + const [main_apeture, set_main_apeture] = React.useState("0"); + const [guider_focal_length, set_guider_focal_length] = React.useState("0"); + const [guider_apeture, set_guider_apeture] = React.useState("0"); + + React.useEffect(() => { + if (global_parameter.global_parameter.telescope_info !== null) { + set_main_apeture( + String(global_parameter.global_parameter.telescope_info?.aperture) + ); + set_main_focal_length( + String(global_parameter.global_parameter.telescope_info?.focal_length) + ); + set_guider_apeture( + String( + global_parameter.global_parameter.telescope_info?.guider_aperture + ) + ); + set_guider_focal_length( + String( + global_parameter.global_parameter.telescope_info?.guider_focal_length + ) + ); + } + }, [ + global_parameter.global_parameter.telescope_info?.focal_length, + global_parameter.global_parameter.telescope_info?.aperture, + global_parameter.global_parameter.telescope_info?.guider_aperture, + global_parameter.global_parameter.telescope_info?.guider_focal_length, + ]); + + const on_telescope_modify_blur = (data_type: string) => { + let to_set_value: number; + switch (data_type) { + case "aperture": + try { + to_set_value = Number(main_apeture); + GlobalStore.actions.GlobalParameterStore.set_parameter({ + parameter_name: "telescope_info", + to_set_parameter: { + aperture: to_set_value, + }, + }); + } catch (error) {} + break; + case "focal_length": + try { + to_set_value = Number(main_focal_length); + GlobalStore.actions.GlobalParameterStore.set_parameter({ + parameter_name: "telescope_info", + to_set_parameter: { + focal_length: to_set_value, + }, + }); + } catch (error) {} + break; + case "guider_aperture": + try { + to_set_value = Number(guider_apeture); + GlobalStore.actions.GlobalParameterStore.set_parameter({ + parameter_name: "telescope_info", + to_set_parameter: { + guider_aperture: to_set_value, + }, + }); + } catch (error) {} + break; + case "guider_focal_length": + try { + to_set_value = Number(guider_focal_length); + GlobalStore.actions.GlobalParameterStore.set_parameter({ + parameter_name: "telescope_info", + to_set_parameter: { + guider_focal_length: to_set_value, + }, + }); + } catch (error) {} + break; + default: + break; + } + }; + return ( + <> + + + +

+ + 主镜参数 +

+
+
+ + + + 主镜焦距 + + + + set_main_focal_length(e.target.value)} + onBlur={() => on_telescope_modify_blur("focal_length")} + /> + mm + + + + + + + 主镜口径 + + + + set_main_apeture(e.target.value)} + onBlur={() => on_telescope_modify_blur("aperture")} + /> + mm + + + + +
+ + + + +

+ + 导星镜参数 +

+
+
+ + + + 导星焦距 + + + + set_guider_focal_length(e.target.value)} + onBlur={() => on_telescope_modify_blur("guider_focal_length")} + /> + mm + + + + + + + 导星口径 + + + + set_guider_apeture(e.target.value)} + onBlur={() => on_telescope_modify_blur("guider_aperture")} + /> + mm + + + + +
+ + ); +}; + +export default GPTelescopeEasyUseComp; diff --git a/websrc/cobalt-web/src/pages/config/index.tsx b/websrc/cobalt-web/src/pages/config/index.tsx index 1feaf8d3..c23f1672 100644 --- a/websrc/cobalt-web/src/pages/config/index.tsx +++ b/websrc/cobalt-web/src/pages/config/index.tsx @@ -10,20 +10,16 @@ import GlobalParameterAllFilterSettings from "./OneSettingSPFilterEntryComp"; import { GlobalStore } from "../../store/globalStore"; const GlobalParameterSettingPage = () => { - const parameter_explain = useRef<{ - [key: string]: { name: string; tooltips: string; range?: number[] }; - }>(parameter_explain_data); + const parameter_explain = useRef(parameter_explain_data); const global_parameter = GlobalStore.useAppState( (state) => state.GlobalParameterStore ); const get_all_global_parameters = GlobalStore.actions.GlobalParameterStore.get_all_paramters; - useEffect(() => { console.log("test"); get_all_global_parameters(); }, []); - const handleSettingChange = ( setting_part_key: string, setting_key: string, @@ -33,12 +29,14 @@ const GlobalParameterSettingPage = () => { let to_change_parameter = global_parameter.global_parameter[setting_part_key]; to_change_parameter[setting_key] = set_value; + // part 1, dispatch store action, change data. GlobalStore.actions.GlobalParameterStore.set_parameter({ parameter_name: setting_part_key, to_set_parameter: to_change_parameter, }); + // part 2, retrive new store data. this step is in set parameter to make sure it is blocking. + // GlobalStore.actions.GlobalParameterStore.get_parameter(setting_part_key); }; - const handleSettingPartChange = ( setting_part_key: string, setting_value: Partial diff --git a/websrc/cobalt-web/src/pages/config/style.tsx b/websrc/cobalt-web/src/pages/config/style.tsx new file mode 100644 index 00000000..32a9ff0e --- /dev/null +++ b/websrc/cobalt-web/src/pages/config/style.tsx @@ -0,0 +1,7 @@ +import styled from "styled-components"; + +const GlobalSettingSettingPage = styled.div` + overflow-y: auto !important; + overflow-x: hidden !important; + height: calc(100vh - 32px) !important; +`; diff --git a/websrc/cobalt-web/src/pages/connect.tsx b/websrc/cobalt-web/src/pages/connect.tsx deleted file mode 100644 index 89c82064..00000000 --- a/websrc/cobalt-web/src/pages/connect.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { - Container, - Row, - Col, - Button, - Alert, - Form, - FormControl, -} from "react-bootstrap"; - -const DeviceConnection = () => { - const [camera, setCamera] = useState(""); - const [telescope, setTelescope] = useState(""); - const [filterwheel, setFilterwheel] = useState(""); - const [focuser, setFocuser] = useState(""); - const [guider, setGuider] = useState(""); - const [solver, setSolver] = useState(""); - const [plugins, setPlugins] = useState([]); - - useEffect(() => { - // Send a request to the backend to get device types in JSON format - // Update the state variables accordingly - const fetchDeviceTypes = async () => { - try { - const response = await fetch("/api/device-types"); - if (!response.ok) { - throw new Error("Failed to fetch device types"); - } - const data = await response.json(); - setCamera(data.camera); - setTelescope(data.telescope); - setFilterwheel(data.filterwheel); - setFocuser(data.focuser); - setGuider(data.guider); - setSolver(data.solver); - setPlugins(data.plugins); - } catch (error) { - console.error(error); - } - }; - fetchDeviceTypes(); - }, []); - - const handleCameraChange = (e) => { - setCamera(e.target.value); - }; - - const handleServerCommand = () => { - // Perform server command action - }; - - const handleClientCommand = () => { - // Perform client command action - }; - - // Add similar handleChange functions for other select inputs - - const handleSubmit = async (e) => { - e.preventDefault(); - const jsonData = { - camera, - telescope, - filterwheel, - focuser, - guider, - solver, - plugins, - }; - try { - const response = await fetch("/api/device-connection", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(jsonData), - }); - if (!response.ok) { - throw new Error("Failed to send device connection data"); - } - // Handle successful response - } catch (error) { - console.error(error); - } - }; - - return ( - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {/* Add similar Form.Group components for other select inputs */} - - - {/* Add similar Rows and Form.Group components for other select inputs */} - - - - - - - - - -
- - - - {/* 服务器通知 */} - - - - - - -

客户端未连接

-
- -
-
- ); -}; - -export default DeviceConnection; diff --git a/websrc/cobalt-web/src/pages/connection/brand_connection.tsx b/websrc/cobalt-web/src/pages/connection/brand_connection.tsx index 2e5fdbe6..405cfc6f 100644 --- a/websrc/cobalt-web/src/pages/connection/brand_connection.tsx +++ b/websrc/cobalt-web/src/pages/connection/brand_connection.tsx @@ -1,32 +1,87 @@ -import React, { ChangeEvent, useEffect } from "react"; +import React, { ChangeEvent, useEffect, useState } from "react"; import { Form, Button, Row, Col, Container, Card } from "react-bootstrap"; -import { Trash, ArrowRight, Plus } from "react-bootstrap-icons"; +import { Trash, ArrowRight, Plus, Send } from "react-bootstrap-icons"; import { useTranslation } from "react-i18next"; import { GlobalStore } from "../../store/globalStore"; +import BrandDriverSelectDialog from "./brand_select"; + const BrandConnection = () => { const { t } = useTranslation(); const { brand_list, brand_type_cn, brand_type_en, brand_selections } = GlobalStore.useAppState((state) => state.connect); + const [dialog_open, set_dialog_open] = useState(false); + const [dialog_brand_index, set_dialog_brand_index] = useState(0); + const [dialog_selections, set_dialog_selections] = useState>([]); + + const handle_dialog_close = () => { + set_dialog_open(false); + }; + + const handle_dialog_select = ( + selection: IConnectBrandSelection, + device_type_index: number + ) => { + // console.log(selection, device_type_index); + const deviceType = brand_type_en[device_type_index]; + GlobalStore.actions.connect.setSelectBrand({ + type: deviceType, + brand_name: selection, + }); + }; + const on_select_clicked = (device_type_index: number) => { + set_dialog_open(true); + if (brand_selections !== null) { + set_dialog_brand_index(device_type_index); + set_dialog_selections( + brand_list[ + brand_type_en[device_type_index] as keyof typeof brand_selections + ] + ); + } else { + set_dialog_selections([]); + } + }; + useEffect(() => { GlobalStore.actions.connect.getBrandList(); }, []); const handleConnectBrand = async () => { + // 允许多次连接 + let count_time = 0; + let connect_ready = false; + + // 启动驱动 let deviceServerConnectReady = - GlobalStore.actions.connect.connectDeviceServer(); + await GlobalStore.actions.connect.connectDeviceServer(); - if (deviceServerConnectReady) { - GlobalStore.actions.connect.getDeviceList(); + if (!deviceServerConnectReady) { + alert("当前未选择任何品牌,请重新选择"); + return; + } + const res = await GlobalStore.actions.connect.getDeviceList(); + if (res) { + connect_ready = true; GlobalStore.actions.connect.setState({ brand_selection_over: true, + config_name: null, }); - } else { - console.error(t("品牌连接失败")); + return; } + // 异常情况, 关闭服务器, 提示用户 + else { + alert("未扫描得到任何设备, 请重新选择"); + await GlobalStore.actions.connect.closeDeviceServer(); + await Sleep(1000); + } + }; + + const Sleep = (ms: number) => { + return new Promise((resolve) => setTimeout(resolve, ms)); }; const handleBrandChange = ( @@ -47,95 +102,74 @@ const BrandConnection = () => { const handleResetBrandSelections = () => { GlobalStore.actions.connect.setState({ brand_selections: { - camera: "", - telescope: "", - focus: "", - filter: "", - guider: "", - polar: "", + camera: { zh: "", en: "", driver: "" }, + telescope: { zh: "", en: "", driver: "" }, + guider: { zh: "", en: "", driver: "" }, + focus: { zh: "", en: "", driver: "" }, + filter: { zh: "", en: "", driver: "" }, + polar: { zh: "", en: "", driver: "" }, }, }); }; return ( - + + + {brand_type_cn.map((item: string, type_index: number) => ( + + + { + on_select_clicked(type_index); + }} + readOnly + /> + + + ))} + + +
- - - - - {t("Device Brand")} - - {brand_type_cn.map((item: string, type_index: number) => ( - - - - {item} - {t("Brand")} - - handleBrandChange(e, type_index)} - > - - {brand_list !== null - ? brand_list[ - brand_type_en[ - type_index - ] as keyof typeof brand_selections - ].map((item: any, index: number) => ( - - )) - : null} - - - - ))} - - - - + + - - - - - - - - - - - - - + +
diff --git a/websrc/cobalt-web/src/pages/connection/brand_select.tsx b/websrc/cobalt-web/src/pages/connection/brand_select.tsx new file mode 100644 index 00000000..68995422 --- /dev/null +++ b/websrc/cobalt-web/src/pages/connection/brand_select.tsx @@ -0,0 +1,61 @@ +import React, { useState } from "react"; +import { Modal, Button, Form, ListGroup } from "react-bootstrap"; +import { XCircle } from "react-bootstrap-icons"; + +const BrandDriverSelectDialog = (props) => { + const [filter, setFilter] = useState(""); + + const handleInputChange = (event) => { + setFilter(event.target.value); + }; + + const handleListItemClick = (option) => { + props.handleSelect(option, props.device_type_index); + props.handleClose(); + }; + + return ( + + + 搜索驱动 + + + + + {props.options + .filter((option) => { + const { zh, en, driver } = option; + const lowerCaseFilter = filter.toLowerCase(); + return ( + zh.toLowerCase().includes(lowerCaseFilter) || + en.toLowerCase().includes(lowerCaseFilter) || + driver.toLowerCase().includes(lowerCaseFilter) + ); + }) + .map((option) => ( + handleListItemClick(option)} + > + {option.zh} + + ))} + + + + + + + ); +}; + +export default BrandDriverSelectDialog; diff --git a/websrc/cobalt-web/src/pages/connection/create_config.tsx b/websrc/cobalt-web/src/pages/connection/create_config.tsx new file mode 100644 index 00000000..a57a6af5 --- /dev/null +++ b/websrc/cobalt-web/src/pages/connection/create_config.tsx @@ -0,0 +1,139 @@ +import React, { useState } from "react"; +import { Button, Modal, Form } from "react-bootstrap"; +import { Save } from "react-bootstrap-icons"; + +const ConfigCreationModal = () => { + const [show, setShow] = useState(false); + const handleClose = () => setShow(false); + const handleShow = () => setShow(true); + + // Initial state for the configuration values + const [config, setConfig] = useState({ + server: { + configpath: "config.json", + host: "0.0.0.0", + maxprocess: 10, + maxthread: 10, + modulepath: "modules", + port: 3000, + }, + terminal: { + enabled: true, + }, + }); + + // Function to handle changes in the configuration values + const handleChange = (e) => { + const { name, value, checked } = e.target; + const section = name.split(".")[0]; + const key = name.split(".")[1]; + setConfig((prevConfig) => ({ + ...prevConfig, + [section]: { + ...prevConfig[section], + [key]: value === "true" || value === "false" ? checked : value, + }, + })); + }; + + // Function to submit the configuration + const handleSubmit = (e) => { + e.preventDefault(); + console.log(config); // You can send the config to the server or perform other actions here + handleClose(); // Close the modal after submission + }; + + return ( + <> +
+ +
+ + + + + Configuration Creation + + +
+ {/* Server Configuration Section */} + + Config Path + + + + Host + + + + Max Process + + + + Max Thread + + + + Module Path + + + + Port + + + + {/* Terminal Configuration Section */} + + + + + {/* Submit Button */} + +
+
+
+ + ); +}; + +export default ConfigCreationModal; diff --git a/websrc/cobalt-web/src/pages/connection/device_connection.tsx b/websrc/cobalt-web/src/pages/connection/device_connection.tsx index 939dc636..d2a79537 100644 --- a/websrc/cobalt-web/src/pages/connection/device_connection.tsx +++ b/websrc/cobalt-web/src/pages/connection/device_connection.tsx @@ -1,7 +1,17 @@ import React, { useState } from "react"; -import { Modal, Form, Button, Row, Col } from "react-bootstrap"; -import { Trash, ArrowRight, CheckCircle } from "react-bootstrap-icons"; +import { Modal, Form, Button, Row, Col, Container } from "react-bootstrap"; +import { + Trash, + ArrowRight, + CheckCircle, + Send, + Plus, + CircleFill, + XCircle, + Archive, + CodeSquare, +} from "react-bootstrap-icons"; import { useTranslation } from "react-i18next"; @@ -71,9 +81,7 @@ const DeviceConnection = () => { GlobalStore.actions.connect.setState({ config_name: value, }); - if (value !== "") { - GlobalStore.actions.connect.setProfile(selectedConfig); - } + GlobalStore.actions.connect.setProfile(selectedConfig); }; // 重置 const handleResetDeviceSelections = () => { @@ -101,6 +109,7 @@ const DeviceConnection = () => { const handleStartPHD2 = async () => { let connectReady = await GlobalStore.actions.connect.startPhd2(); if (connectReady) { + // todo modify later. alert("PHD2 start OK"); } else { alert("PHD2 Error"); @@ -128,119 +137,145 @@ const DeviceConnection = () => { }; return ( - <> - {brand_type_cn.map((item, type_index) => ( - - -
+ + + +
+ {brand_type_cn.map((item: string, type_index: number) => ( + +
+ + 选择{item}配置 + handleDeviceChange(e, type_index)} + > + + {device_list !== null + ? device_list[brand_type_en[type_index]].map( + (item: any, index: number) => ( + + ) + ) + : null} + + + {brand_connection[brand_type_en[type_index]] === 0 ? null : ( +
+ {brand_connection[brand_type_en[type_index]] === 1 ? ( + + ) : ( + + )} +
+ )} +
+
+ ))} +
+ + +
+ + 加载已有配置 handleDeviceChange(e, type_index)} + aria-label="加载已有配置" + value={device_selections !== null ? device_selections.name : ""} > - {device_list !== null - ? device_list[brand_type_en[type_index]].map( - (item, index) => ( - - ) - ) + {user_config_list !== null + ? user_config_list.map((item: any, index: number) => ( + + )) : null} - {brand_connection[brand_type_en[type_index]] === 0 ? null : ( - - {brand_connection[brand_type_en[type_index]] === 1 ? ( - - ) : ( - - )} - - )} -
- -
- ))} - - - - - {user_config_list !== null - ? user_config_list.map((item, index) => ( - - )) - : null} - - - - + + + {phd2_connect_ready && ( + + )} +
+ + + 配置命名 + + + + 配置名称 + + + + + + + + + +
- {phd2_connect_ready ? ( - - ) : null} - - - 配置命名 - - -

给新配置取一个名字,使得之后可以使用

- -
- - - - -
- - - +
); }; diff --git a/websrc/cobalt-web/src/pages/connection/index.tsx b/websrc/cobalt-web/src/pages/connection/index.tsx index b0695ffa..64061242 100644 --- a/websrc/cobalt-web/src/pages/connection/index.tsx +++ b/websrc/cobalt-web/src/pages/connection/index.tsx @@ -33,7 +33,7 @@ const Connect = () => { }; return ( - + @@ -44,7 +44,7 @@ const Connect = () => { diff --git a/websrc/cobalt-web/src/pages/connection/profile.tsx b/websrc/cobalt-web/src/pages/connection/profile.tsx index 01ddfc89..81bf2bd6 100644 --- a/websrc/cobalt-web/src/pages/connection/profile.tsx +++ b/websrc/cobalt-web/src/pages/connection/profile.tsx @@ -1,8 +1,18 @@ import React, { useEffect, useState } from "react"; -import { Accordion, Button, Card, Container, Form } from "react-bootstrap"; +import { + Accordion, + Button, + Card, + Col, + Container, + Form, + Row, +} from "react-bootstrap"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { dark } from "react-syntax-highlighter/dist/esm/styles/prism"; +import ConfigCreationModal from "./create_config"; + import { useTranslation } from "react-i18next"; const ProfileConnection = () => { @@ -15,12 +25,11 @@ const ProfileConnection = () => { useEffect(() => { async function fetchConfigOptions() { try { - const response = await fetch("/api/config/options"); // 替换成实际的后端接口地址 + const response = await fetch("/api/config/options"); if (response.ok) { const data = await response.json(); setConfigOptions(data.options); - // 从 Cookie 中读取已有的配置 const selectedOption = getCookie("selectedConfig"); if (selectedOption && data.options.includes(selectedOption)) { setSelectedConfig(selectedOption); @@ -39,7 +48,6 @@ const ProfileConnection = () => { }, []); useEffect(() => { - // 将选中的配置和内容写入 Cookie if (selectedConfig) { setCookie("selectedConfig", selectedConfig); setCookie(`configContent_${selectedConfig}`, configContent); @@ -52,7 +60,7 @@ const ProfileConnection = () => { if (selectedOption) { try { - const response = await fetch(`/api/config/${selectedOption}`); // 替换成实际的后端接口地址 + const response = await fetch(`/api/config/${selectedOption}`); if (response.ok) { const data = await response.json(); setConfigContent(data.content); @@ -69,48 +77,50 @@ const ProfileConnection = () => { }; const handleCreateConfig = () => { - window.location.href = "/create-config"; // 替换为实际的创建配置页面 URL + window.location.href = "/create-config"; }; return ( -

Configuration Selection

-
- - Select Configuration: - - - {configOptions.map((option) => ( - - ))} - - -
- - - - {t("Config Details")} - - - - {configContent ? ( - - {configContent} - - ) : ( -

{t("No configuration selected.")}

- )} -
-
-
-
-
- - + + +

{t("配置选择")}

+
+ + {t("选择配置")}: + + + {configOptions.map((option) => ( + + ))} + + +
+ + + + {t("配置详细信息")} + + + + {configContent ? ( + + {configContent} + + ) : ( +

{t("未选择任何配置")}

+ )} +
+
+
+
+
+ + + +
); }; diff --git a/websrc/cobalt-web/src/pages/dashboard/CameraView.tsx b/websrc/cobalt-web/src/pages/dashboard/CameraView.tsx deleted file mode 100644 index 0f3c9a8d..00000000 --- a/websrc/cobalt-web/src/pages/dashboard/CameraView.tsx +++ /dev/null @@ -1,89 +0,0 @@ -// CameraView.jsx -import React, { useState, useEffect, useRef } from "react"; -import Cropper, { ReactCropperElement } from "react-cropper"; -import "cropperjs/dist/cropper.css"; -import axios from "axios"; -import { Button, Modal, Row } from "react-bootstrap"; -import { Crop, ArrowRepeat } from "react-bootstrap-icons"; - -const CameraView = () => { - const [imageData, setImageData] = useState(""); - const [refreshKey, setRefreshKey] = useState(0); - const [croppedData, setCroppedData] = useState(""); - const [showModal, setShowModal] = useState(false); - const cropperRef = useRef(null); - - useEffect(() => { - // 发送请求获取最新的图片 - axios.get("/api/getImage").then((response) => { - setImageData(response.data); - }); - }, [refreshKey]); - - const handleRefresh = () => { - // 更新刷新键以重新加载图片 - setRefreshKey((prevKey) => prevKey + 1); - }; - - const handleCrop = () => { - const cropper = cropperRef.current?.cropper; - if (cropper) { - setCroppedData(cropper.getCroppedCanvas().toDataURL()); - setShowModal(true); - } - }; - - const handleSave = () => { - // 将裁剪后的图片数据发送到服务器进行保存 - axios.post("/api/saveImage", { imageData: croppedData }).then(() => { - setShowModal(false); - // 刷新页面以更新图片 - handleRefresh(); - }); - }; - - return ( -
- - - {/* - - - */} - - - setShowModal(false)} - backdrop="static" - keyboard={false} - > - - 编辑图片 - - - Cropped - - - - - - -
- ); -}; - -export default CameraView; diff --git a/websrc/cobalt-web/src/pages/dashboard/FloatingUI.tsx b/websrc/cobalt-web/src/pages/dashboard/FloatingUI.tsx deleted file mode 100644 index 89db0eff..00000000 --- a/websrc/cobalt-web/src/pages/dashboard/FloatingUI.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React, { useState } from "react"; -import styled from "styled-components"; -import { Button, ButtonGroup } from "react-bootstrap"; -import { - PlusSquareFill, - GearFill, - PencilSquare, - TrashFill, - XCircle, -} from "react-bootstrap-icons"; - -const FloatingUIContainer = styled.div` - position: fixed; - bottom: 50px; - left: 20px; - display: flex; - flex-direction: column; - align-items: flex-end; -`; - -const FloatingButton = styled(Button)` - margin-top: 10px; - background-color: #1e90ff; - color: #fff; - border: none; - box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2); - - &:hover { - background-color: #187bcd; - } -`; - -const FloatingUI = () => { - const [isOpen, setIsOpen] = useState(false); - - const handleActionClick = (name) => { - // 处理按钮点击事件 - console.log("Clicked", name); - }; - - const toggleMenu = () => { - setIsOpen(!isOpen); - }; - - return ( - - - {isOpen && ( - - handleActionClick("main")}> - - Add - - handleActionClick("settings")}> - - Settings - - handleActionClick("edit")}> - - Edit - - handleActionClick("delete")}> - - Delete - - - )} - - ); -}; - -export default FloatingUI; diff --git a/websrc/cobalt-web/src/pages/dashboard/FloatingWindow.tsx b/websrc/cobalt-web/src/pages/dashboard/FloatingWindow.tsx deleted file mode 100644 index 257433fe..00000000 --- a/websrc/cobalt-web/src/pages/dashboard/FloatingWindow.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { Button, Card } from "react-bootstrap"; -import { - ThreeDotsVertical, - ArrowsMove, - ArrowsAngleContract, -} from "react-bootstrap-icons"; -import { useSwipeable } from "react-swipeable"; -import { Resizable } from "re-resizable"; -import styled from "styled-components"; - -const ToggleButton = styled(Button)` - position: absolute; - top: 10px; - right: 10px; - z-index: 999; -`; - -const FloatingWindowContainer = styled(Card)` - position: fixed; - background-color: #ffffff; - border-radius: 4px; - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); - z-index: 998; -`; - -const DragIcon = styled(ArrowsMove)` - cursor: move; - margin-right: 8px; -`; - -const ResizeIcon = styled(ArrowsAngleContract)` - cursor: nwse-resize; - margin-left: auto; -`; - -const ContentContainer = styled.div` - padding: 16px; -`; - -const FloatingWindow = ({ children }) => { - const [isOpen, setIsOpen] = useState(false); - const [position, setPosition] = useState({ x: 0, y: 0 }); - const [dragging, setDragging] = useState(false); - const [size, setSize] = useState({ - width: window.innerWidth / 2, - height: window.innerHeight / 2, - }); - - useEffect(() => { - const handleResize = () => { - setSize({ width: window.innerWidth / 2, height: window.innerHeight / 2 }); - }; - window.addEventListener("resize", handleResize); - return () => { - window.removeEventListener("resize", handleResize); - }; - }, []); - - const toggleOpen = () => { - setIsOpen(!isOpen); - }; - - const handleMouseDown = (e) => { - setDragging(true); - }; - - const handleMouseMove = (e) => { - if (dragging) { - setPosition({ x: e.clientX, y: e.clientY }); - } - }; - - const handleMouseUp = () => { - setDragging(false); - }; - - const handleResize = (event, direction, ref, delta, position) => { - setSize({ width: ref.offsetWidth, height: ref.offsetHeight }); - }; - - const handlers = useSwipeable({ - onSwipedDown: () => { - setIsOpen(false); - }, - }); - - return ( - <> - - - - {isOpen && ( - - - - - - - - {children} - - - - )} - - ); -}; - -export default FloatingWindow; diff --git a/websrc/cobalt-web/src/pages/dashboard/Footer.tsx b/websrc/cobalt-web/src/pages/dashboard/Footer.tsx deleted file mode 100644 index b3f6e340..00000000 --- a/websrc/cobalt-web/src/pages/dashboard/Footer.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from "react"; -import { FooterContainer, FooterText } from "./style/Footer"; - -export const Footer = ({ systemInfo }) => { - return ( - - {systemInfo} - - ); -}; diff --git a/websrc/cobalt-web/src/pages/dashboard/ParameterModal.tsx b/websrc/cobalt-web/src/pages/dashboard/ParameterModal.tsx deleted file mode 100644 index 8c4d456c..00000000 --- a/websrc/cobalt-web/src/pages/dashboard/ParameterModal.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import React from "react"; -import { Modal, Button, Form } from "react-bootstrap"; -import { XCircleFill, SaveFill } from "react-bootstrap-icons"; - -const ParameterModal = ({ show, onHide, parameters, onSave }) => { - // 创建一个本地状态来处理表单输入 - const [localParameters, setLocalParameters] = React.useState(parameters); - - // 处理输入变化 - const handleInputChange = (key, value) => { - setLocalParameters((prev) => ({ ...prev, [key]: value })); - }; - - // 处理保存操作 - const handleSave = () => { - onSave(localParameters); - onHide(); // 关闭模态框 - }; - - return ( - - - 设置参数 - - - {/* 根据参数生成对应的表单元素 */} - {Object.entries(localParameters).map(([key, value], index) => ( - - {key} - handleInputChange(key, e.target.value)} - /> - - ))} - - - - - - - ); -}; - -export default ParameterModal; diff --git a/websrc/cobalt-web/src/pages/dashboard/SystemPanel.tsx b/websrc/cobalt-web/src/pages/dashboard/SystemPanel.tsx deleted file mode 100644 index a0679f2b..00000000 --- a/websrc/cobalt-web/src/pages/dashboard/SystemPanel.tsx +++ /dev/null @@ -1,251 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Button, Card, Accordion, Table } from 'react-bootstrap'; -import { BarChartLineFill } from 'react-bootstrap-icons'; -import ReactEcharts from 'echarts-for-react'; - -const CpuUsageChart = ({ testData }) => { - const [cpuData, setCpuData] = useState(testData.cpuData); - - // 模拟数据变化 - useEffect(() => { - const intervalId = setInterval(() => { - const newData = testData.generateData(); - setCpuData(newData.cpuData); - }, testData.interval); - return () => clearInterval(intervalId); - }, [testData]); - - // 渲染折线图 - const renderChart = () => { - if (!cpuData) { - return null; - } - - const options = { - tooltip: { - trigger: 'axis', - }, - xAxis: { - type: 'category', - data: cpuData.categories, - }, - yAxis: { - type: 'value', - }, - series: [ - { - name: 'CPU使用率', - type: 'line', - data: cpuData.data, - }, - ], - }; - - return ; - }; - - return ( - - - - CPU使用率 - - - - {renderChart()} - - - ); -}; - -const RamUsageChart = ({ testData }) => { - const [ramData, setRamData] = useState(testData.ramData); - - // 模拟数据变化 - useEffect(() => { - const intervalId = setInterval(() => { - const newData = testData.generateData(); - setRamData(newData.ramData); - }, testData.interval); - return () => clearInterval(intervalId); - }, [testData]); - - // 渲染折线图 - const renderChart = () => { - if (!ramData) { - return null; - } - - const options = { - tooltip: { - trigger: 'axis', - }, - xAxis: { - type: 'category', - data: ramData.categories, - }, - yAxis: { - type: 'value', - }, - series: [ - { - name: '内存使用率', - type: 'line', - data: ramData.data, - }, - ], - }; - - return ; - }; - - return ( - - - - 内存使用率 - - - - {renderChart()} - - - ); -}; - -const DiskInfoTable = ({ testData }) => { - const [diskData, setDiskData] = useState(testData.diskData); - - // 模拟数据变化 - useEffect(() => { - const intervalId = setInterval(() => { - const newData = testData.generateData(); - setDiskData(newData.diskData); - }, testData.interval); - return () => clearInterval(intervalId); - }, [testData]); - - // 渲染Accordion组件 - const renderTable = () => { - if (!diskData) { - return null; - } - - return ( - - {diskData.map((disk, index) => ( - - - {disk.name} - - - - - - - - - - - - - - - - - - - - -
类型大小已用空间可用空间
本地磁盘{disk.size}{disk.used}{disk.available}
-
-
-
- ))} -
- ); - }; - - return ( - - - - 磁盘信息 - - - - {renderTable()} - - - ); -}; - -const testData = { - interval: 3000, - cpuData: { - categories: ['2024-01-24 14:31:33', '2024-01-24 14:31:36', '2024-01-24 14:31:39', '2024-01-24 14:31:42', '2024-01-24 14:31:45'], - data: [80, 60, 50, 70, 90], - }, - ramData: { - categories: ['2024-01-24 14:31:33', '2024-01-24 14:31:36', '2024-01-24 14:31:39', '2024-01-24 14:31:42', '2024-01-24 14:31:45'], - data: [30, 40, 50, 60, 70], - }, - diskData: [ - { - name: 'C:', - size: '256GB', - used: '100GB', - available: '156GB', - }, - { - name: 'D:', - size: '1TB', - used: '400GB', - available: '600GB', - }, - ], - generateData: function () { - const newCpuData = { ...this.cpuData }; - const newRamData = { ...this.ramData }; - const newDiskData = this.diskData.map(disk => ({ - ...disk, - used: `${parseInt(disk.used) + Math.round(Math.random() * 10)}GB`, - available: `${parseInt(disk.available) - Math.round(Math.random() * 10)}GB`, - })); - newCpuData.categories.push(new Date().toLocaleString()); - newCpuData.data.push(Math.round(Math.random() * 100)); - newRamData.categories.push(new Date().toLocaleString()); - newRamData.data.push(Math.round(Math.random() * 100)); - if (newCpuData.categories.length > 10) { - newCpuData.categories.shift(); - newCpuData.data.shift(); - } - if (newRamData.categories.length > 10) { - newRamData.categories.shift(); - newRamData.data.shift(); - } - return { - cpuData: newCpuData, - ramData: newRamData, - diskData: newDiskData, - }; - }, -}; - -const SystemPanel = () => { - return ( - <> - - - - - ); -}; - -export default SystemPanel; diff --git a/websrc/cobalt-web/src/pages/dashboard/Toolbar.tsx b/websrc/cobalt-web/src/pages/dashboard/Toolbar.tsx deleted file mode 100644 index 8eadbf31..00000000 --- a/websrc/cobalt-web/src/pages/dashboard/Toolbar.tsx +++ /dev/null @@ -1,47 +0,0 @@ -// Toolbar.jsx -import React from "react"; -import { Button } from "react-bootstrap"; -import { SToolbar } from "./style"; -import { - Boxes, - ChevronLeft, - ChevronRight, - Gear, - GraphDown, - MapFill, - Tools, - XCircle, -} from "react-bootstrap-icons"; - -const Toolbar = ({ visible, onToggle }) => { - return ( - - - - - - - - ); -}; - -export default Toolbar; diff --git a/websrc/cobalt-web/src/pages/dashboard/index.tsx b/websrc/cobalt-web/src/pages/dashboard/index.tsx deleted file mode 100644 index 358db70d..00000000 --- a/websrc/cobalt-web/src/pages/dashboard/index.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { Container, Row, Col, Button, Form } from "react-bootstrap"; -import CameraView from "./CameraView"; -import { SCaptureButton } from "./CaptureButton"; -import Toolbar from "./Toolbar"; -import FloatingUI from "./FloatingUI"; -import SystemPanel from "./SystemPanel"; -import FloatingWindow from "./FloatingWindow"; -import { CameraContainer } from "./style/main"; -import { Footer } from "./Footer"; -import { - RightSidebar, - FullHeightContainer, - StyledRow, -} from "./style/RightSidebar"; -import { ArrowRepeat, Crop, GearFill, XCircle } from "react-bootstrap-icons"; -import SquareButton from "./style/SquareButton"; - -const Dashboard = () => { - const [sidebarVisible, setSidebarVisible] = useState(false); - const [toolbarVisible, setToolbarVisible] = useState(false); - const [handleCrop, setHandleCrop] = useState(false); - - const [modalShow, setExpModalShow] = useState(false); - - const systemInfo = "© 2024 Lithium & Cobalt. All rights reserved."; - - const handleCapture = () => { - // 处理捕捉逻辑 - }; - - const handleToggleSidebar = () => { - setSidebarVisible(!sidebarVisible); - }; - - const handleToggleToolbar = () => { - setToolbarVisible(!toolbarVisible); - }; - - function disableScroll() { - document.body.style.overflow = "hidden"; - } - - function enableScroll() { - document.body.style.overflow = "auto"; - } - - // 在组件挂载时添加事件监听器 - useEffect(() => { - disableScroll(); - - // 在组件卸载时删除事件监听器 - return () => { - enableScroll(); - }; - }, []); - - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -export default Dashboard; diff --git a/websrc/cobalt-web/src/pages/dashboard/style.tsx b/websrc/cobalt-web/src/pages/dashboard/style.tsx deleted file mode 100644 index 4dbba987..00000000 --- a/websrc/cobalt-web/src/pages/dashboard/style.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import styled from "styled-components"; - -// styles for the toolbar -export const SToolbar = styled.div` - position: fixed; - top: 25%; - left: ${(props) => (props.visible ? "0" : "-50px")}; - width: 75px; - height: 50%; - background-color: #fff; - z-index: 2; - transition: all 0.3s ease; - - /* 添加更多样式 */ - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - padding: 20px; - box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2); - border-radius: 10px; - @media (max-width: 768px) { - top: 15%; - } - transparent: ${(props) => (props.visible ? "1" : "0")}; -`; diff --git a/websrc/cobalt-web/src/pages/dashboard/style/Footer.tsx b/websrc/cobalt-web/src/pages/dashboard/style/Footer.tsx deleted file mode 100644 index 4c0c45a4..00000000 --- a/websrc/cobalt-web/src/pages/dashboard/style/Footer.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import styled from "styled-components"; - -export const FooterContainer = styled.footer` - width: 100%; - background-color: #f8f9fa; // 浅灰色背景 - color: #6c757d; // 深灰色文字 - text-align: center; - padding: 10px 0; - position: fixed; - bottom: 0; - left: 0; - z-index: 0; -`; - -export const FooterText = styled.p` - margin: 0; - padding: 0; - font-size: 14px; -`; \ No newline at end of file diff --git a/websrc/cobalt-web/src/pages/dashboard/style/RightSidebar.tsx b/websrc/cobalt-web/src/pages/dashboard/style/RightSidebar.tsx deleted file mode 100644 index 09093a53..00000000 --- a/websrc/cobalt-web/src/pages/dashboard/style/RightSidebar.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import Container from "react-bootstrap/esm/Container"; -import Row from "react-bootstrap/esm/Row"; -import styled from "styled-components"; - -export const RightSidebar = styled.div` - position: fixed; - top: 0; - right: 0; - height: 100vh; - width: 80px; // 你可以根据需要调整宽度 - background-color: #333; // 暗色背景 - display: flex; - flex-direction: column; - justify-content: space-between; -`; - -// 自定义Row,使其在容器中等间距分布 -export const StyledRow = styled(Row)` - display: flex; - justify-content: center; // 水平居中 - align-items: center; // 垂直居中 - flex: 1; // 使所有Row等高 -`; - -// 调整Container样式以填满高度 -export const FullHeightContainer = styled(Container)` - height: 100vh; // 使容器高度充满视口 - display: flex; - flex-direction: column; // 竖直排列子元素 -`; \ No newline at end of file diff --git a/websrc/cobalt-web/src/pages/dashboard/style/SquareButton.tsx b/websrc/cobalt-web/src/pages/dashboard/style/SquareButton.tsx deleted file mode 100644 index 941824cb..00000000 --- a/websrc/cobalt-web/src/pages/dashboard/style/SquareButton.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; -import styled from "styled-components"; - -const StyledSquareButton = styled.button` - width: 50px; // 定义按钮的宽度 - height: 50px; // 定义按钮的高度 - display: flex; - justify-content: center; - align-items: center; - border: none; - cursor: pointer; - background-color: #007bff; // 示例背景色 - color: white; // 文字颜色 - &:hover { - background-color: #0056b3; - } -`; - -const SquareButton = ({ text, onClick }) => { - return {text}; -}; - -export default SquareButton; diff --git a/websrc/cobalt-web/src/pages/dashboard/style/main.tsx b/websrc/cobalt-web/src/pages/dashboard/style/main.tsx deleted file mode 100644 index 862e465d..00000000 --- a/websrc/cobalt-web/src/pages/dashboard/style/main.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import styled from "styled-components"; -import Container from "react-bootstrap/Container"; -import Button from "react-bootstrap/Button"; - -export const CameraContainer = styled(Container)` - height: 100vh; - justify-content: center; - display: flex; - align-items: center; - position: relative; - background-color: black; -`; - -export const CaptureButton = styled(Button)` - width: 60px; - height: 60px; - border-radius: 50%; - border: 2px solid white; - background-color: transparent; - position: absolute; - bottom: 20px; -`; diff --git a/websrc/cobalt-web/src/pages/dashboard/CaptureButton.tsx b/websrc/cobalt-web/src/pages/dashboard2/CaptureButton.tsx similarity index 90% rename from websrc/cobalt-web/src/pages/dashboard/CaptureButton.tsx rename to websrc/cobalt-web/src/pages/dashboard2/CaptureButton.tsx index 5846495d..c8f059cc 100644 --- a/websrc/cobalt-web/src/pages/dashboard/CaptureButton.tsx +++ b/websrc/cobalt-web/src/pages/dashboard2/CaptureButton.tsx @@ -12,6 +12,7 @@ export const CaptureButton = styled(Button)` background-color: transparent; color: white; font-size: 16px; + z-index: 21; &:hover { background-color: #555; // 鼠标悬停时变色 @@ -36,10 +37,10 @@ export const CaptureButton = styled(Button)` } `; -export const SCaptureButton = ({ onCapture }) => { +export const SCaptureButton = ({ onCapture, Icon }) => { return ( - 拍摄 + {Icon} ); }; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/align/ThreePointDataDisplay.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/align/ThreePointDataDisplay.tsx new file mode 100644 index 00000000..9a5a2352 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/align/ThreePointDataDisplay.tsx @@ -0,0 +1,137 @@ +import * as React from "react"; +import { Row, Col } from "react-bootstrap"; +import Card from "react-bootstrap/Card"; +import { + ArrowLeft, + ArrowRight, + ArrowUp, + ArrowDown, +} from "react-bootstrap-icons"; +import ProgressBar from "react-bootstrap/ProgressBar"; + +interface ThreePointDisplayProps { + east_west_angle: number | null; + up_down_angle: number | null; + target_radec: ICThreePointTarget | null; + current_radec: ICThreePointTarget | null; +} + +function degToDMS(deg: number) { + // Get the degrees, minutes, and seconds + let degrees = Math.floor(deg); + let minutes = Math.floor((deg - degrees) * 60); + let seconds = Math.round((deg - degrees - minutes / 60) * 3600); + + // Return the DMS values as a string + return { degrees, minutes, seconds }; +} + +const ThreePointDataDisplay: React.FC = (props) => { + // ui display data + const [east_west, set_east_west] = React.useState(false); + const [up_down, set_up_down] = React.useState(false); + const [east_west_degree, set_east_west_degree] = React.useState(""); + const [up_down_degree, set_up_down_degree] = React.useState(""); + const [can_show, set_can_show] = React.useState(false); + + React.useEffect(() => {}, []); + React.useEffect(() => { + if (props.east_west_angle != null && props.up_down_angle != null) { + // 注意,在误差标记这块,这里顺时针是正,是东,是true。逆时针的方向,是西,是false。z轴指向天顶。 + // 但是在移动,因为东是顺时针,所以向西移动。 + // 同理,上下,正是朝上,下是朝下,但是修正是如果在上面,要朝下移动。 + // 与后台的算法在确认。 + set_can_show(true); + if (props.east_west_angle > 0) { + set_east_west(true); + } else { + set_east_west(false); + } + let new_east_west = degToDMS(props.east_west_angle); + set_east_west_degree( + new_east_west.degrees + + "°" + + new_east_west.minutes + + "'" + + new_east_west.seconds + + '"' + ); + if (props.up_down_angle > 0) { + set_up_down(true); + } else { + set_up_down(false); + } + let new_up_down = degToDMS(props.up_down_angle); + set_up_down_degree( + new_up_down.degrees + + "°" + + new_up_down.minutes + + "'" + + new_up_down.seconds + + '"' + ); + } else { + set_can_show(false); + } + }, [props.east_west_angle, props.up_down_angle]); + React.useEffect(() => {}, [props.current_radec]); + + return ( + <> + + + + +
+ +
+ {east_west ? ( + + ) : ( + + )} +
+
+ + {east_west ? "向西移动" : "向东移动"} + + {east_west_degree} +
+
+ + + + +
+ +
+ {up_down ? : } +
+
+ + {up_down ? "向下移动" : "向上移动"} + + {up_down_degree} +
+
+ +
+ + ); +}; + +export default ThreePointDataDisplay; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/align/ThreePointManualDialog.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/align/ThreePointManualDialog.tsx new file mode 100644 index 00000000..a61941d4 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/align/ThreePointManualDialog.tsx @@ -0,0 +1,70 @@ +import * as React from "react"; +import Slider from "react-slick"; +import "slick-carousel/slick/slick.css"; +import "slick-carousel/slick/slick-theme.css"; +import { Modal, Button } from "react-bootstrap"; + +interface ThreePointManualDialogProps { + open_dialog: number; + handleClose: (flag: boolean) => void; +} + +const ThreePointManualDialog: React.FC = ( + props +) => { + // ui control + const [open, set_open] = React.useState(false); + + const settings = { + dots: true, + }; + + // open dialog + React.useEffect(() => { + if (props.open_dialog > 0) { + set_open(true); + } + }, [props.open_dialog]); + + return ( + { + props.handleClose(false); + }} + > + + +
+ Slide 1 +
+
+ Slide 2 +
+
+
+ + + + +
+ ); +}; + +export default ThreePointManualDialog; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/align/ThreePointSettings.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/align/ThreePointSettings.tsx new file mode 100644 index 00000000..1a24db09 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/align/ThreePointSettings.tsx @@ -0,0 +1,168 @@ +import * as React from "react"; +import { + Modal, + Button, + Form, + FormControl, + FormLabel, + FormText, + FormGroup, +} from "react-bootstrap"; + +interface ThreePointSettingDialogProps { + open_dialog: number; + direction_switch: boolean; + move_time: number; + solve_retry_time: number; + manual_start: boolean; + search_radius: number; + on_update_data: ( + direction_switch: boolean, + move_time: number, + solve_retry_time: number, + manual_start: boolean, + search_radius: number + ) => void; +} + +const ThreePointSettingDialog: React.FC = ( + props +) => { + // ui control + const [open, set_open] = React.useState(false); + + // input data + const [direction_switch, set_direction_switch] = React.useState(false); + const [move_time, set_move_time] = React.useState(10); + const [solve_retry_time, set_solve_retry_time] = React.useState(3); + const [manual_start, set_manual_start] = React.useState(true); + const [search_radius, set_search_radius] = React.useState(30); + + // useEffect + // on mount + React.useEffect(() => { + set_direction_switch(props.direction_switch); + set_move_time(props.move_time); + set_solve_retry_time(props.solve_retry_time); + set_manual_start(props.manual_start); + set_search_radius(props.search_radius); + }, []); + // open dialog + React.useEffect(() => { + if (props.open_dialog > 0) { + set_open(true); + } + }, [props.open_dialog]); + + // functions + const handleClose = (save_flag: boolean) => { + if (save_flag) { + props.on_update_data( + direction_switch, + move_time, + solve_retry_time, + manual_start, + search_radius + ); + } + set_open(false); + }; + + return ( + { + handleClose(true); + }} + > + + 对极轴初始化设置 + + +

+ 注意,为了比较准确的进行极轴校准,请务必进行至少2次以上的极轴校准流程。 + 如果没有连接赤道仪和相机,是无法进行本流程的! +

+

+ 对极轴过程中会进行拍摄解析,解析的相关设置请到全局变量中进行设置。 +

+

开始本流程前,请先进行粗对极轴,并且确认赤道仪的坐标信息正确

+
+ { + set_direction_switch(!direction_switch); + }} + /> + + 赤道仪移动时间 + { + let new_value = parseInt(event.target.value); + if (new_value < 3) { + new_value = 3; + } else if (new_value > 20) { + new_value = 20; + } + set_move_time(new_value); + }} + /> + 单位:秒 + + + 解析失败重试次数 + { + let new_value = parseInt(event.target.value); + if (new_value < 10) { + new_value = 10; + } else if (new_value > 180) { + new_value = 180; + } + set_solve_retry_time(new_value); + }} + /> + 单位:次 + + { + set_manual_start(!manual_start); + }} + /> + + 解析搜索半径 + { + set_search_radius(parseInt(event.target.value)); + }} + /> + 单位:度 + + +
+ + + + +
+ ); +}; + +export default ThreePointSettingDialog; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/align/ThreePointStepper.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/align/ThreePointStepper.tsx new file mode 100644 index 00000000..05bb0f16 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/align/ThreePointStepper.tsx @@ -0,0 +1,539 @@ +import * as React from "react"; +import { ListGroup } from "react-bootstrap"; +import { + Hourglass, + CheckCircleFill, + XCircleFill, + StopFill, +} from "react-bootstrap-icons"; +import { Spinner } from "react-bootstrap"; +import { useImmer } from "use-immer"; +import { Card } from "react-bootstrap"; + +import ThreePointDataDisplay from "./ThreePointDataDisplay"; + +interface ThreePointStepperProps { + current_step: number; + incoming_data: any; +} + +interface StepProps { + step_complete_status: number; + step_exposure_fig_status: number; + step_number: string; + step_data: any; +} + +const MoveStep: React.FC = (props) => { + const [completed, set_completed] = React.useState(false); + const [active, set_active] = React.useState(false); + const [step_color, set_step_color] = React.useState("primary"); + + React.useEffect(() => { + if (props.step_complete_status == 0) { + set_completed(false); + set_active(true); + set_step_color("primary"); + } else if (props.step_complete_status == 1) { + set_completed(true); + set_active(false); + set_step_color("success"); + } else if (props.step_complete_status == -1) { + set_completed(false); + set_active(false); + set_step_color("danger"); + } + }, [props.step_complete_status]); + + return ( + +
+ {props.step_exposure_fig_status == 0 && ( + + )} + {props.step_exposure_fig_status == 1 && ( + + )} + {props.step_exposure_fig_status == 2 && ( + + )} + {props.step_exposure_fig_status == 3 && ( + + )} +
移动望远镜
+
+
+ ); +}; + +const ExposureStep: React.FC = (props) => { + const [completed, set_completed] = React.useState(false); + const [active, set_active] = React.useState(false); + const [step_color, set_step_color] = React.useState("primary"); + + React.useEffect(() => { + if (props.step_complete_status == 0) { + set_completed(false); + set_active(true); + set_step_color("primary"); + } else if (props.step_complete_status == 1) { + set_completed(true); + set_active(false); + set_step_color("success"); + } else if (props.step_complete_status == -1) { + set_completed(false); + set_active(false); + set_step_color("danger"); + } + }, [props.step_complete_status]); + + return ( + +
+ {props.step_exposure_fig_status == 0 && ( + + )} + {props.step_exposure_fig_status == 1 && ( + + )} + {props.step_exposure_fig_status == 2 && ( + + )} + {props.step_exposure_fig_status == 3 && ( + + )} +
拍摄解析第{props.step_number}张
+
+
+ ); +}; + +const ThreePointStepper: React.FC = (props) => { + // ui control + const [measure_finished, set_measure_finished] = React.useState(false); + const [step_show_data, update_step_show_data] = useImmer({ + first_exposure_complete_status: 0, + first_exposure_fig_status: 0, + first_move_complete_status: 0, + first_move_fig_status: 0, + second_exposure_complete_status: 0, + second_exposure_fig_status: 0, + second_move_complete_status: 0, + second_move_fig_status: 0, + third_exposure_complete_status: 0, + third_exposure_fig_status: 0, + update_exposure_complete_status: 0, + update_exposure_fig_status: 0, + }); + const [current_discription, set_current_discription] = React.useState(""); + const [first_move, set_first_move] = React.useState(false); + + // polar error related data + const [east_west_angle, set_east_west_angle] = React.useState( + null + ); + const [up_down_angle, set_up_down_angle] = React.useState( + null + ); + const [target_ra_dec, set_target_ra_dec] = + React.useState(null); + const [current_ra_dec, set_current_ra_dec] = + React.useState(null); + const [solved_ra_dec, update_solved_ra_dec] = useImmer< + [ + ICThreePointTarget | null, + ICThreePointTarget | null, + ICThreePointTarget | null, + ICThreePointTarget | null + ] + >([null, null, null, null]); + + // use effect + React.useEffect(() => { + switch (props.current_step) { + case -1: { + set_measure_finished(false); + update_step_show_data((draft) => { + draft.first_exposure_complete_status = 0; // 0 for normal, 1 for complete, -1 for fail + draft.first_exposure_fig_status = 0; // 0 for waiting, 1 for in progress, 2 for finished, 4 for failed + draft.first_move_complete_status = 0; + draft.first_move_fig_status = 0; + draft.second_exposure_complete_status = 0; + draft.second_exposure_fig_status = 0; + draft.second_move_complete_status = 0; + draft.second_move_fig_status = 0; + draft.third_exposure_complete_status = 0; + draft.third_exposure_fig_status = 0; + draft.update_exposure_complete_status = 0; + draft.update_exposure_fig_status = 0; + }); + update_solved_ra_dec((draft) => { + draft = [null, null, null, null]; + }); + set_current_discription("等待启动"); + break; + } + case 91: { + set_measure_finished(false); + set_first_move(true); + break; + } + case 0: { + set_measure_finished(false); + update_step_show_data((draft) => { + draft.first_exposure_complete_status = 0; // 0 for normal, 1 for complete, -1 for fail + draft.first_exposure_fig_status = 0; // 0 for waiting, 1 for in progress, 2 for finished, 4 for failed + draft.first_move_complete_status = 0; + draft.first_move_fig_status = 0; + draft.second_exposure_complete_status = 0; + draft.second_exposure_fig_status = 0; + draft.second_move_complete_status = 0; + draft.second_move_fig_status = 0; + draft.third_exposure_complete_status = 0; + draft.third_exposure_fig_status = 0; + draft.update_exposure_complete_status = 0; + draft.update_exposure_fig_status = 0; + }); + update_solved_ra_dec((draft) => { + draft = [null, null, null, null]; + }); + set_first_move(true); + set_current_discription("初始化设置完成"); + break; + } + case 1: { + // step 1: start exposure + set_first_move(true); + update_step_show_data((draft) => { + draft.first_exposure_complete_status = 0; + draft.first_exposure_fig_status = 1; + }); + set_current_discription("开始第一次曝光"); + break; + } + case 11: { + // step 11: exposure and solve finished + update_step_show_data((draft) => { + draft.first_exposure_complete_status = 1; + draft.first_exposure_fig_status = 2; + }); + set_current_discription("第一张曝光解析完成"); + update_solved_ra_dec((draft) => { + draft[0] = { + ra: props.incoming_data.ra, + dec: props.incoming_data.dec, + }; + }); + break; + } + case 15: { + // step 15: starttelescope move + if (first_move) { + update_step_show_data((draft) => { + draft.first_move_complete_status = 0; + draft.first_move_fig_status = 1; + }); + } else { + update_step_show_data((draft) => { + draft.second_move_complete_status = 0; + draft.second_move_fig_status = 1; + }); + } + set_current_discription("移动望远镜中"); + break; + } + case 16: { + // step 16: move finished + if (first_move) { + update_step_show_data((draft) => { + draft.first_move_complete_status = 1; + draft.first_move_fig_status = 1; + }); + } else { + update_step_show_data((draft) => { + draft.second_move_complete_status = 1; + draft.second_move_fig_status = 1; + }); + } + set_current_discription("等待望远镜稳定中"); + break; + } + case 17: { + // step 17: settle finished + if (first_move) { + update_step_show_data((draft) => { + draft.first_move_complete_status = 1; + draft.first_move_fig_status = 2; + }); + } else { + update_step_show_data((draft) => { + draft.second_move_complete_status = 1; + draft.second_move_fig_status = 2; + }); + } + set_current_discription("望远镜稳定完成"); + break; + } + case 19: { + // step 19: first exposure solve failed + update_step_show_data((draft) => { + draft.first_exposure_complete_status = -1; + draft.first_exposure_fig_status = 4; + }); + set_current_discription("第一次曝光解析失败,终止流程"); + break; + } + case 2: { + // step 2: start second exposure + set_first_move(false); + update_step_show_data((draft) => { + draft.second_exposure_complete_status = 0; + draft.second_exposure_fig_status = 1; + }); + set_current_discription("开始第二次曝光"); + break; + } + case 21: { + // step 21: exposure and solve finished + update_step_show_data((draft) => { + draft.second_exposure_complete_status = 1; + draft.second_exposure_fig_status = 2; + }); + set_current_discription("第二张曝光解析完成"); + update_solved_ra_dec((draft) => { + draft[1] = { + ra: props.incoming_data.ra, + dec: props.incoming_data.dec, + }; + }); + break; + } + case 25: { + // step 25: starttelescope move + update_step_show_data((draft) => { + draft.second_move_complete_status = 0; + draft.second_move_fig_status = 1; + }); + set_current_discription("移动望远镜中"); + break; + } + case 26: { + // step 26: move finished + set_current_discription("等待望远镜稳定中"); + break; + } + case 27: { + // step 27: settle finished + update_step_show_data((draft) => { + draft.second_move_complete_status = 1; + draft.second_move_fig_status = 2; + }); + set_current_discription("望远镜稳定完成"); + break; + } + case 29: { + // step 29: second exposure solve failed + update_step_show_data((draft) => { + draft.second_exposure_complete_status = -1; + draft.second_exposure_fig_status = 4; + }); + set_current_discription("第二次曝光解析失败,终止流程"); + break; + } + case 3: { + // step 3: start thrid exposure + update_step_show_data((draft) => { + draft.third_exposure_complete_status = 0; + draft.third_exposure_fig_status = 1; + }); + set_current_discription("开始第三次曝光"); + break; + } + case 31: { + // step 31: exposure and solve finished + update_step_show_data((draft) => { + draft.third_exposure_complete_status = 1; + draft.third_exposure_fig_status = 2; + }); + set_current_discription("第三张曝光解析完成"); + update_solved_ra_dec((draft) => { + draft[2] = { + ra: props.incoming_data.ra, + dec: props.incoming_data.dec, + }; + }); + set_current_ra_dec({ + ra: props.incoming_data.ra, + dec: props.incoming_data.dec, + }); + break; + } + case 38: { + // step 38: error calculation finished + set_measure_finished(true); + set_east_west_angle(props.incoming_data.east_west_move); + set_up_down_angle(props.incoming_data.up_down_move); + set_target_ra_dec({ + ra: props.incoming_data.target_ra, + dec: props.incoming_data.target_dec, + }); + break; + } + case 39: { + // step 39: third exposure solve failed + update_step_show_data((draft) => { + draft.third_exposure_complete_status = -1; + draft.third_exposure_fig_status = 4; + }); + set_current_discription("第三次曝光解析失败,终止流程"); + break; + } + case 4: { + // step 4: update exposure start + set_measure_finished(true); + update_step_show_data((draft) => { + draft.update_exposure_complete_status = 0; + draft.update_exposure_fig_status = 1; + }); + set_current_discription("开始修正极轴曝光"); + break; + } + case 41: { + // step 41: update exposure and solved finished + set_measure_finished(true); + update_step_show_data((draft) => { + draft.update_exposure_complete_status = 1; + draft.update_exposure_fig_status = 2; + }); + set_current_discription("修正极轴曝光解析完成"); + update_solved_ra_dec((draft) => { + draft[3] = { + ra: props.incoming_data.ra, + dec: props.incoming_data.dec, + }; + }); + set_target_ra_dec({ + ra: props.incoming_data.target_ra, + dec: props.incoming_data.target_dec, + }); + break; + } + case 42: { + // step 42: update exposure new error calculated + set_measure_finished(true); + set_east_west_angle(props.incoming_data.east_west_move); + set_up_down_angle(props.incoming_data.up_down_move); + break; + } + case 49: { + // step 49: update exposure solve failed + update_step_show_data((draft) => { + draft.update_exposure_complete_status = -1; + draft.update_exposure_fig_status = 4; + }); + set_current_discription("修正极轴曝光解析失败等待中"); + break; + } + case 99: { + // update timeout + break; + } + default: { + break; + } + } + }, [props.current_step]); + + return ( + + {measure_finished ? ( + <> + + + + +
+ +
停止
+
+
+ 单次对极轴的时间不建议超过5分钟。
+ 粗对之后再重启一次流程会更准。
+
+
+
+ + ) : ( + + + + + + + + + + + + +
+ +
计算误差
+
+
+
+ )} +
+ ); +}; + +export default ThreePointStepper; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/align/index.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/align/index.tsx new file mode 100644 index 00000000..5519515d --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/align/index.tsx @@ -0,0 +1,459 @@ +import * as React from "react"; +import { Button } from "react-bootstrap"; +import { Card } from "react-bootstrap"; +import { Row, Col } from "react-bootstrap"; +import { Play, Stop, Gear, QuestionCircle } from "react-bootstrap-icons"; +import Alert from "react-bootstrap/Alert"; +import * as AXIOSPAAF from "../../../../services/paa_fixed_procedure_api"; + +import ThreePointStepper from "./ThreePointStepper"; +import ConfirmDialog from "../light/ConfirmDialog"; + +import ThreePointSettingDialog from "./ThreePointSettings"; +import ThreePointManualDialog from "./ThreePointManualDialog"; +import { useEchoWebSocket } from "../../../../utils/websocketProvider"; +import { GlobalStore } from "../../../../store/globalStore"; + +const Align: React.FC = () => { + //ui control + const [can_start_paa, set_can_start_paa] = React.useState(false); + const [dialog_open, set_dialog_open] = React.useState(0); + + const [show_setting, set_show_setting] = React.useState(0); + const [manual_show, set_manual_show] = React.useState(0); + // const [alert_color, set_alert_color] = React.useState('success'); + // const [alert_text, set_alert_text] = React.useState('待机'); + + // progress data + const [step_number, set_step_number] = React.useState(0); + const [incoming_data, set_incoming_data] = React.useState({}); + const [direction_switch, set_direction_switch] = React.useState(false); + const [move_time, set_move_time] = React.useState(10); + const [solve_retry_time, set_solve_retry_time] = React.useState(3); + const [manual_start, set_manual_start] = React.useState(false); + const [search_radius, set_search_radius] = React.useState(30); + // store + const console_state = GlobalStore.useAppState((state) => state.console); + const global_parameter_ui_setting = GlobalStore.useAppState( + (state) => state.GlobalParameterStore.ui_setting.show_three_point_help + ); + const set_gp_ui = GlobalStore.actions.GlobalParameterStore.set_ui_setting; + const set_console_state = GlobalStore.actions.console.setState; + // function + const process_ws_message = (msg: any): void => { + if (msg.type == "error") { + return; + } + if ( + msg.device_name == "PAA" && + msg.instruction == "three point alignment" + ) { + switch (msg.message) { + case "start tracking": { + set_step_number(0); + set_incoming_data({}); + set_console_state({ + alert_type: "success", + alert_message: "设置赤道仪开始跟踪", + }); + // set_alert_color('success'); + // set_alert_text('设置赤道仪开始跟踪'); + break; + } + case "manual start move telescope": { + set_step_number(91); + set_incoming_data(msg.data); + set_console_state({ + alert_type: "success", + alert_message: "自动移动初始位置", + }); + break; + } + case "start first exposure": { + set_step_number(1); + set_incoming_data({}); + set_console_state({ + alert_type: "success", + alert_message: "开始第一次曝光", + }); + // set_alert_color('success'); + // set_alert_text('开始第一次曝光'); + break; + } + case "first exposure solved": { + set_incoming_data(msg.data); + set_step_number(11); + set_console_state({ + alert_type: "success", + alert_message: "第一次曝光解析成功", + }); + // set_alert_color('success'); + // set_alert_text('第一次曝光解析成功'); + break; + } + case "!FAIL! first exposure failed": { + set_step_number(19); + set_incoming_data({}); + set_console_state({ + alert_type: "danger", + alert_message: "第一次曝光解析失败!终止流程", + }); + setTimeout(update_paa_status_and_clean, 100); + // set_alert_color('danger'); + // set_alert_text('第一次曝光解析失败!终止流程'); + break; + } + case "start second exposure": { + set_step_number(2); + set_incoming_data({}); + set_console_state({ + alert_type: "success", + alert_message: "开始第二次曝光", + }); + // set_alert_color('success'); + // set_alert_text('开始第二次曝光'); + break; + } + case "second exposure solved": { + set_incoming_data(msg.data); + set_step_number(21); + set_console_state({ + alert_type: "success", + alert_message: "第二次曝光解析成功", + }); + // set_alert_color('success'); + // set_alert_text('第二次曝光解析成功'); + break; + } + case "!FAIL! second exposure failed": { + set_step_number(29); + set_incoming_data({}); + set_console_state({ + alert_type: "danger", + alert_message: "第二次曝光解析失败!终止流程", + }); + setTimeout(update_paa_status_and_clean, 100); + // set_alert_color('danger'); + // set_alert_text('第二次曝光解析失败!终止流程'); + break; + } + case "start third exposure": { + set_step_number(3); + set_incoming_data({}); + set_console_state({ + alert_type: "success", + alert_message: "开始第三次曝光", + }); + // set_alert_color('success'); + // set_alert_text('开始第三次曝光'); + break; + } + case "third exposure solved": { + set_incoming_data(msg.data); + set_step_number(31); + set_console_state({ + alert_type: "success", + alert_message: "第三次曝光解析成功", + }); + // set_alert_color('success'); + // set_alert_text('第三次曝光解析成功'); + break; + } + case "!FAIL! third exposure failed": { + set_step_number(39); + set_incoming_data({}); + set_console_state({ + alert_type: "danger", + alert_message: "第三次曝光解析失败!终止流程", + }); + setTimeout(update_paa_status_and_clean, 100); + // set_alert_color('danger'); + // set_alert_text('第三次曝光解析失败!终止流程'); + break; + } + case "polar error result": { + set_incoming_data(msg.data); + set_step_number(38); + set_console_state({ + alert_type: "success", + alert_message: "极轴误差计算成功", + }); + // set_alert_color('success'); + // set_alert_text('极轴误差计算成功'); + break; + } + case "start update exposure": { + set_step_number(4); + set_incoming_data({}); + set_console_state({ + alert_type: "success", + alert_message: "开始校准曝光", + }); + // set_alert_color('success'); + // set_alert_text('开始校准曝光'); + break; + } + case "update exposure solved": { + set_incoming_data(msg.data); + set_step_number(41); + set_console_state({ + alert_type: "success", + alert_message: "校准曝光解析成功", + }); + // set_alert_color('success'); + // set_alert_text('校准曝光解析成功'); + break; + } + case "polar error update": { + set_incoming_data(msg.data); + set_step_number(42); + set_console_state({ + alert_type: "success", + alert_message: "极轴误差更新", + }); + // set_alert_color('warning'); + // set_alert_text('极轴误差校准更新失败'); + break; + } + case "!FAIL! update exposure failed": { + set_step_number(49); + set_incoming_data({}); + set_console_state({ + alert_type: "warning", + alert_message: "极轴校准曝光解析失败", + }); + // set_alert_color('warning'); + // set_alert_text('极轴校准曝光解析失败'); + break; + } + case "moving telescope": { + set_step_number(15); + set_incoming_data({}); + set_console_state({ + alert_type: "success", + alert_message: "移动赤道仪中", + }); + // set_alert_color('success'); + // set_alert_text('移动赤道仪中'); + break; + } + case "moving telescope finished, settling": { + set_step_number(16); + set_incoming_data({}); + set_console_state({ + alert_type: "success", + alert_message: "赤道仪移动完成,稳定中", + }); + // set_alert_color('success'); + // set_alert_text('赤道仪移动完成,稳定中'); + break; + } + case "settle telescope finished": { + set_step_number(17); + set_incoming_data({}); + set_console_state({ + alert_type: "success", + alert_message: "赤道仪稳定完成", + }); + // set_alert_color('success'); + // set_alert_text('赤道仪稳定完成'); + break; + } + case "update timeout": { + set_step_number(99); + set_incoming_data({}); + set_console_state({ + alert_type: "warning", + alert_message: "单次对极轴时间过长,自动终止", + }); + setTimeout(update_paa_status_and_clean, 100); + // set_alert_color('warning'); + // set_alert_text('单次对极轴时间过长,自动终止'); + break; + } + } + } + }; + // websocket handler + const { sendMessage, removeListener } = useEchoWebSocket(process_ws_message); + + const update_paa_status_and_clean = async () => { + set_can_start_paa(false); + try { + let paa_running_status = await AXIOSPAAF.get_update_paa_running_status(); + if (paa_running_status.data) { + // true means in running + set_can_start_paa(false); + } else { + set_can_start_paa(true); + } + } catch (err) { + return null; + } + }; + const get_paa_status_after_start = async () => { + set_can_start_paa(false); + try { + let paa_status_check = await AXIOSPAAF.get_paa_status(); + if (paa_status_check.data.flag) { + // true meas in running + set_can_start_paa(false); + } else { + set_can_start_paa(true); + } + } catch (err) { + return null; + } + }; + + const post_stop_paa = async () => { + try { + let stop_result = await AXIOSPAAF.post_stop_paa(); + if (stop_result.success) { + set_can_start_paa(true); + update_paa_status_and_clean(); + } else { + set_can_start_paa(true); + console.log(stop_result.message); + } + } catch (err) { + return null; + } + }; + + // useEffect + React.useEffect(() => { + // check the initial status. + if (global_parameter_ui_setting) { + set_manual_show(manual_show + 1); + } else { + // open setting tab to confirm + set_show_setting(show_setting + 1); + } + // check if there is paa in progres + update_paa_status_and_clean(); + return () => { + removeListener(process_ws_message); + }; + }, []); + + // functions + const on_start_polar_clicked = async () => { + try { + let start_result = await AXIOSPAAF.start_fixed_PolarAlignment({ + start_from: direction_switch ? "East" : "West", + move_time: move_time, + solve_retry: solve_retry_time, + manual_start: manual_start, + }); + if (start_result.success) { + // successfully started + set_can_start_paa(false); + } else { + console.log(start_result.message); + get_paa_status_after_start(); + } + } catch (err) { + return null; + } + }; + const on_stop_polar_clicked = () => { + set_dialog_open(dialog_open + 1); + }; + const on_setting_closed_update = ( + direction_switch: boolean, + move_time: number, + solve_retry_time: number, + manual_start: boolean, + search_radius: number + ) => { + set_direction_switch(direction_switch); + set_move_time(move_time); + set_solve_retry_time(solve_retry_time); + set_manual_start(manual_start); + set_search_radius(search_radius); + }; + + return ( +
+ + + + + + {!can_start_paa ? ( + + ) : ( + + )} + + + + + + + +
+ +
+ + {}} + /> + { + if (flag) { + set_gp_ui({ + show_three_point_help: false, + }); + } + set_show_setting(show_setting + 1); + }} + /> +
+ ); +}; + +Align.displayName = "对极轴"; + +export default Align; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/autofocus/AutoFocusSettingDialog.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/autofocus/AutoFocusSettingDialog.tsx new file mode 100644 index 00000000..6a62dcfc --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/autofocus/AutoFocusSettingDialog.tsx @@ -0,0 +1,103 @@ +import React, { useState, useEffect } from "react"; +import { Modal, Button, Form, Col, Row } from "react-bootstrap"; +import { GlobalStore } from "../../../../store/globalStore"; + +const AutoFocusSettingDialog = (props) => { + // ui control + const [open, setOpen] = useState(false); + + // input data + const [startSide, setStartSide] = useState(false); + + // filter selections + const initialFilterNames = + GlobalStore.useAppState( + (state) => state.GlobalParameterStore.get_filter_names_full + ) || []; + const [allFilters, setAllFilters] = useState(initialFilterNames); + const [selectedFilter, setSelectedFilter] = useState(1); + + const { brand_connection } = GlobalStore.useAppState( + (state) => state.connect + ); + + // useEffect + // on mount + useEffect(() => { + setOpen(props.open_dialog > 0); + setStartSide(props.start_side); + }, []); + + // open dialog + useEffect(() => { + setOpen(props.open_dialog > 0); + }, [props.open_dialog]); + + // functions + const handleClose = () => { + props.on_update_data(selectedFilter, startSide); + setOpen(false); + }; + + return ( + + + 自动对焦初始化设置 + + +

单独执行自动对焦流程的初始化配置。

+

+ 一部分参数在全局变量的自动对焦参数里进行修改!全局变量里的自动对焦参数在所有运行的自动对焦流程都有效,包括PAA的流程。 +

+

+ + 开始自动对焦前,请务必保证 + 望远镜已经在焦点附近! + 否则将无法进行自动对焦。请在拍摄界面确认已经可以看到比较细的星点的情况下再进行本流程! + +

+
+ + + {startSide ? "初始向外移动" : "初始向内移动"} + + + setStartSide(!startSide)} + /> + + + + {/* 滤镜选择 */} + {brand_connection["filter"] == 2 ? ( + + 滤镜选择 + setSelectedFilter(e.target.value)} + > + {allFilters.map((filter, index) => ( + + ))} + + + ) : ( +

滤镜轮未连接

+ )} +
+
+ + + +
+ ); +}; + +export default AutoFocusSettingDialog; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/autofocus/BackSlashMeasure.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/autofocus/BackSlashMeasure.tsx new file mode 100644 index 00000000..e69de29b diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/autofocus/index.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/autofocus/index.tsx new file mode 100644 index 00000000..5f9a5d2e --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/autofocus/index.tsx @@ -0,0 +1,298 @@ +import * as React from "react"; +import { Card } from "react-bootstrap"; +import { Button } from "react-bootstrap"; +import { Play, Stop, Gear, QuestionCircle } from "react-bootstrap-icons"; +import * as AXIOSPAAF from "../../../../services/paa_fixed_procedure_api"; + +import AutoFocusSettingDialog from "./AutoFocusSettingDialog"; +import ConfirmDialog from "../light/ConfirmDialog"; + +import { useEchoWebSocket } from "../../../../utils/websocketProvider"; +import { GlobalStore } from "../../../../store/globalStore"; + +const Autofocus = () => { + //ui control + const [can_start_paa, set_can_start_paa] = React.useState(false); + const [show_setting, set_show_setting] = React.useState(0); + const [manual_show, set_manual_show] = React.useState(0); + const [dialog_open, set_dialog_open] = React.useState(0); + const [show_af_curve, set_show_af_curve] = React.useState(false); + // af data + const [af_measure_data, set_af_measure_data] = React.useState< + Array<[number, number]> + >([]); + const [af_model, set_af_model] = React.useState< + null | [number, number, number] + >(null); + const [af_r_square, set_af_r_square] = React.useState(0); + // setting data control + const [filter_index, set_filter_index] = React.useState( + 1 + ); + const [start_side, set_start_side] = React.useState(true); + // store + const console_state = GlobalStore.useAppState((state) => state.console); + const set_console_state = GlobalStore.actions.console.setState; + // function + const process_ws_message = (msg: any): void => { + if (msg.type == "error") { + return; + } + if (msg.device_name == "PAA" && msg.instruction == "AutoFocus") { + switch (msg.message) { + case "initial": { + set_console_state({ + alert_type: "success", + alert_message: "初始化中", + }); + break; + } + case "out side": { + set_console_state({ + alert_type: "success", + alert_message: "调焦器讲向外移动", + }); + break; + } + case "in side": { + set_console_state({ + alert_type: "success", + alert_message: "调焦器讲向内移动", + }); + break; + } + case "move focus": { + set_console_state({ + alert_type: "success", + alert_message: "调焦移动中", + }); + break; + } + case "start exposure": { + set_console_state({ + alert_type: "success", + alert_message: "开始曝光", + }); + break; + } + case "single exposure finished": { + set_console_state({ + alert_type: "success", + alert_message: "单次曝光完成", + }); + break; + } + case "step exposure": { + // important + set_af_measure_data((af_measure_data) => [ + ...af_measure_data, + [msg.data.step, msg.data.mean], + ]); + set_console_state({ + alert_type: "success", + alert_message: "测量完成", + }); + break; + } + case "no useful measure": { + set_console_state({ + alert_type: "warning", + alert_message: "没有测量到有效星点大小", + }); + break; + } + case "first side finished!": { + set_console_state({ + alert_type: "success", + alert_message: "第一侧测量完成", + }); + break; + } + case "first side failed": { + set_console_state({ + alert_type: "danger", + alert_message: "第一侧测量失败", + }); + break; + } + case "other side init": { + set_console_state({ + alert_type: "success", + alert_message: "初始化第二侧测量", + }); + break; + } + case "second side finished!": { + set_console_state({ + alert_type: "success", + alert_message: "第二侧测量完成", + }); + break; + } + case "focus found": { + set_console_state({ + alert_type: "success", + alert_message: "找到焦点!", + }); + set_af_model(msg.data.model); + set_af_r_square(msg.data.r_square); + break; + } + case "moveto focus": { + set_console_state({ + alert_type: "success", + alert_message: "移动至焦点", + }); + break; + } + case "min focus not found": { + set_console_state({ + alert_type: "danger", + alert_message: "焦点没有找到!", + }); + break; + } + } + } + }; + + // websocket handler + const { sendMessage, removeListener } = useEchoWebSocket(process_ws_message); + + const update_paa_status_and_clean = async () => { + set_can_start_paa(false); + try { + let paa_running_status = await AXIOSPAAF.get_update_paa_running_status(); + if (paa_running_status.data) { + // true means in running + set_can_start_paa(false); + } else { + set_can_start_paa(true); + } + } catch (err) { + return null; + } + }; + + const post_stop_paa = async () => { + try { + let stop_result = await AXIOSPAAF.post_stop_paa(); + if (stop_result.success) { + set_can_start_paa(true); + update_paa_status_and_clean(); + } else { + set_can_start_paa(true); + console.log(stop_result.message); + } + } catch (err) { + return null; + } + }; + const on_stop_paa_clicked = () => { + set_dialog_open(dialog_open + 1); + }; + const on_start_autofocus_clicked = async () => { + set_af_model(null); + try { + let start_result = await AXIOSPAAF.start_fixed_autofocus({ + filter_index: 0, + start_side: false, + }); + if (start_result.success) { + // successfully started + set_can_start_paa(false); + } else { + console.log(start_result.message); + get_paa_status_after_start(); + } + } catch (err) { + return null; + } + }; + const get_paa_status_after_start = async () => { + set_can_start_paa(false); + try { + let paa_status_check = await AXIOSPAAF.get_paa_status(); + if (paa_status_check.data.flag) { + // true meas in running + set_can_start_paa(false); + } else { + set_can_start_paa(true); + } + } catch (err) { + return null; + } + }; + // useEffect + React.useEffect(() => { + // open setting tab to confirm + set_show_setting(show_setting + 1); + // check if there is paa in progres + update_paa_status_and_clean(); + return () => { + removeListener(process_ws_message); + }; + }, []); + + const on_setting_closed_update = ( + filter_index: "current" | number, + start_side: boolean + ) => { + set_filter_index(filter_index); + set_start_side(start_side); + }; + + return ( + <> + + +
+ {!can_start_paa ? ( + + ) : ( + + )} + + +
+
+
+ + + {}} + /> + + ); +}; + +Autofocus.displayName = "AF"; + +export default Autofocus; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/controls/CameraControl.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/controls/CameraControl.tsx new file mode 100644 index 00000000..244590c7 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/controls/CameraControl.tsx @@ -0,0 +1,304 @@ +import * as React from "react"; +import { useEchoWebSocket } from "../../../../utils/websocketProvider"; +import { + Card, + Form, + Button, + Stack, + Row, + Col, + Container, +} from "react-bootstrap"; +import { + ArrowUp, + ArrowRight, + CloudSnow, + Grid3x3GapFill, +} from "react-bootstrap-icons"; + +const DeviceCameraSimpleControlPanel: React.FC = () => { + const [cool_status, set_cool_status] = React.useState(false); + const [camera_temperature, set_camera_temperature] = React.useState(0); + const [binning, set_binning] = React.useState(1); + const [gain, set_gain] = React.useState(100); + const [offset, set_offset] = React.useState(0); + + // input data + const [input_target_temperature, set_input_target_temperature] = + React.useState("-10"); + const [input_binning, set_input_binning] = React.useState("1"); + const [input_gain, set_input_gain] = React.useState(""); + const [input_offset, set_input_offset] = React.useState(""); + + // functions + const process_ws_message = (msg: any): void => { + if (msg.device_name == "camera") { + switch (msg.instruction) { + case "get_real_time_info": { + set_camera_temperature(msg.data.temperature); + set_cool_status(msg.data.in_cooling); + break; + } + case "get_set_params": { + set_gain(msg.data.gain); + set_offset(msg.data.offset); + // binning + if (msg.data.binning !== null) { + set_binning(msg.data.binning); + } + } + } + } + }; + + const { sendMessage, removeListener } = useEchoWebSocket(process_ws_message); + const MINUTE_MS = 1000; + React.useEffect(() => { + set_input_gain(String(gain)); + }, [gain]); + React.useEffect(() => { + set_input_offset(String(offset)); + }, [offset]); + React.useEffect(() => { + set_input_binning(String(binning)); + }, [binning]); + React.useEffect(() => { + const interval = setInterval(() => { + sendMessage( + JSON.stringify({ + device_name: "camera", + instruction: "get_real_time_info", + params: [], + }) + ); + }, MINUTE_MS); + + return () => { + clearInterval(interval); + removeListener(process_ws_message); + }; + }, []); + + return ( + + + + + {cool_status ? ( + 制冷中 + ) : ( + 未制冷 + )} + { + set_cool_status(event.target.checked); + if (event.target.checked) { + sendMessage( + JSON.stringify({ + device_name: "camera", + instruction: "start_cool_camera", + params: [], + }) + ); + } else { + sendMessage( + JSON.stringify({ + device_name: "camera", + instruction: "stop_cool_camera", + params: [], + }) + ); + } + }} + label="开关冷冻" + /> + + + + 相机温度 + + + {camera_temperature < 0 ? ( + {camera_temperature} ℃ + ) : ( + {camera_temperature} ℃ + )} + + + + + + 修改冷冻目标温度 ℃ + + set_input_target_temperature(event.target.value) + } + /> + + + + + + + 当前增益gain + + + {gain} + + + + + + 修改增益 + set_input_gain(event.target.value)} + /> + + + + + + + 当前偏置值offset + + + {offset} + + + + + + 修改偏置值 + set_input_offset(event.target.value)} + /> + + + + + + + 当前binning + + + {binning} + + + + + + 修改binning值 + { + let real_binning = parseInt(event.target.value); + if (real_binning > 4) { + real_binning = 4; + } + if (real_binning == 3) { + real_binning = 4; + } + if (real_binning < 1) { + real_binning = 1; + } + set_input_binning(String(real_binning)); + }} + /> + + + + + + + + ); +}; + +DeviceCameraSimpleControlPanel.displayName = "相机控制"; + +export default DeviceCameraSimpleControlPanel; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/controls/FilterControl.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/controls/FilterControl.tsx new file mode 100644 index 00000000..60041c77 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/controls/FilterControl.tsx @@ -0,0 +1,175 @@ +import * as React from "react"; +import { Card } from "react-bootstrap"; +import { GlobalStore } from "../../../../store/globalStore"; +import { useEchoWebSocket } from "../../../../utils/websocketProvider"; +import { ButtonGroup, Button } from "react-bootstrap"; + +interface FilterInfo { + slot: number; + filter_name: string; +} + +const DeviceFilterSimpleControlPanel: React.FC = () => { + const [current_filter, set_current_filter] = React.useState(0); + const [max_filter, set_max_filter] = React.useState(8); + const [rotate_flag, set_rotate_flag] = React.useState(true); + // 全体名称 + const initialFilterNames = + GlobalStore.useAppState( + (state) => state.GlobalParameterStore.get_filter_names_full + ) || []; + const GlobalInfo = GlobalStore.useAppState( + (state) => state.GlobalParameterStore + ).global_parameter; + const [all_filters, setAllFilters] = React.useState(initialFilterNames); + + // ws数据传递 + const process_ws_message = (msg: any): void => { + let response_data = msg; + console.log(response_data); + if (response_data.device_name == "filter") { + switch (response_data.instruction) { + // 获取信息 + case "get_filter_slot": { + // 设置最大滤镜轮数量 + set_current_filter(response_data.data.filter_slot); + set_rotate_flag(response_data.data.idle); + break; + } + case "get_all_params": { + set_current_filter(response_data.data.filter_slot); + updateFiltersInfo(response_data.data.filters_info); + break; + } + case "update_all_slot_data": { + // 没有错误返回 + } + case "set_one_filter_solt_value": { + // 没有错误返回 + } + case "switch_filter": { + if (response_data.message == "Filter wheel in rotating!") { + // report error + } + if ( + response_data.message == "filter slot index value out of range!" + ) { + // report error + } + } + default: { + } + } + } + }; + + const { sendMessage, removeListener } = useEchoWebSocket(process_ws_message); + + // 更新 + const updateFiltersInfo = (filtersInfo: FilterInfo[]) => { + const updatedFilterNames = filtersInfo.map((filterInfo) => ({ + label: filterInfo.filter_name, + value: filterInfo.slot, + })); + setAllFilters(updatedFilterNames); + set_max_filter(filtersInfo.length); + }; + const OnFilterClicked = (slot_number: number) => { + if (slot_number != current_filter) { + switch_filter(slot_number, true); + } + }; + // 旋转当前滤镜轮 + const switch_filter = (slot_index: number, need_focuser: boolean) => { + sendMessage( + JSON.stringify({ + device_name: "filter", + instruction: "switch_filter", + params: [slot_index], + }) + ); + // 需要focuser + if (need_focuser) { + if (GlobalInfo.autofocus?.use_filter_offset) { + if ( + GlobalInfo.filter_setting?.filter_info[slot_index - 1] && + GlobalInfo.filter_setting?.filter_info[current_filter - 1] + ) { + let to_set_move_rel_step = + GlobalInfo.filter_setting.filter_info[slot_index - 1].focus_offset - + GlobalInfo.filter_setting.filter_info[current_filter - 1] + .focus_offset; + sendMessage( + JSON.stringify({ + device_name: "focus", + instruction: "move_relative_step", + params: [to_set_move_rel_step], + }) + ); + } else { + alert("filter not found"); + } + } else { + alert("auto focus prohibited!"); + } + } + // set_current_filter(slot_index); + }; + const MINUTE_MS = 1000; + React.useEffect(() => { + // This will fire only on mount. + sendMessage( + JSON.stringify({ + device_name: "filter", + instruction: "get_all_params", + params: [], + }) + ); + const interval = setInterval(() => { + console.log("Logs every second"); + sendMessage( + JSON.stringify({ + device_name: "filter", + instruction: "get_filter_slot", + params: [], + }) + ); + }, MINUTE_MS); + + return () => { + console.log("clear interval"); + clearInterval(interval); + removeListener(process_ws_message); + }; // This represents the unmount function, in which you need to clear your interval to prevent memory leaks. + }, []); + + return ( + + {all_filters.length > 0 ? ( + + {all_filters.map((one_filter, index) => { + return ( + + ); + })} + + ) : ( +

全局变量中没有检测到滤镜配置!

+ )} +
+ ); +}; + +DeviceFilterSimpleControlPanel.displayName = "滤镜轮控制"; + +export default DeviceFilterSimpleControlPanel; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/controls/FocuserControl.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/controls/FocuserControl.tsx new file mode 100644 index 00000000..520f421a --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/controls/FocuserControl.tsx @@ -0,0 +1,248 @@ +import * as React from "react"; +import { useEchoWebSocket } from "../../../../utils/websocketProvider"; +import { Card, Button, ButtonGroup, Form, InputGroup } from "react-bootstrap"; +import { + ArrowLeftCircleFill, + ArrowRightCircleFill, + ArrowLeftSquareFill, + ArrowRightSquareFill, + CursorFill, +} from "react-bootstrap-icons"; +import HelperSnackbar from '../../../device/description/helper_snackbar' + +const DeviceFocuserSimpleControlPanel: React.FC = () => { + const [current_step, set_current_step] = React.useState(0); + const [max_step, set_max_step] = React.useState(65000); + const [work_status, set_work_status] = React.useState(false); + const [help_text, set_help_text] = React.useState(""); + const helper_ref = React.useRef(null); + + const [to_set_move_step, set_to_set_move_step] = React.useState(0); + const [to_set_move_rel_step, set_to_set_move_rel_step] = React.useState(0); + + const process_ws_message = (msg: any): void => { + let response_data = msg; + if (response_data.device_name == "focus") { + switch (response_data.instruction) { + case "get_params": { + set_current_step(response_data.data.focus_position); + set_max_step(response_data.data.focus_max); + if ( + response_data.data.focus_state == "Idle" || + response_data.data.focus_state == "Ok" + ) { + set_work_status(false); + } else { + set_work_status(true); + } + break; + } + } + } + }; + + const { sendMessage, removeListener } = useEchoWebSocket(process_ws_message); + + const MINUTE_MS = 1000; + React.useEffect(() => { + const interval = setInterval(() => { + console.log("Logs every second"); + sendMessage( + JSON.stringify({ + device_name: "focus", + instruction: "get_params", + params: [], + }) + ); + }, MINUTE_MS); + + return () => { + console.log("clear interval"); + clearInterval(interval); + removeListener(process_ws_message); + }; + }, []); + + return ( + + + +

当前位置 {current_step}

+ + + + + + + + 移动到指定步长 + + + + + + set_to_set_move_step(parseInt(event.target.value)) + } + /> + + + + + 移动相对步长 + + + + + + set_to_set_move_rel_step(parseInt(event.target.value)) + } + /> + + + +
+
+ ); +}; + +DeviceFocuserSimpleControlPanel.displayName = "调焦器控制"; + +export default DeviceFocuserSimpleControlPanel; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/controls/GuiderControl.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/controls/GuiderControl.tsx new file mode 100644 index 00000000..946d743f --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/controls/GuiderControl.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; + + +const DeviceGuiderSimpleControlPanel: React.FC = () => { + return ( +
+ ) +} + +DeviceGuiderSimpleControlPanel.displayName = '导星相机控制' + +export default DeviceGuiderSimpleControlPanel; \ No newline at end of file diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/controls/Phd2Control.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/controls/Phd2Control.tsx new file mode 100644 index 00000000..83a955e3 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/controls/Phd2Control.tsx @@ -0,0 +1,138 @@ +import * as React from "react"; +import { + Container, + Button, + ButtonGroup, + Alert, + Accordion, + Card, + CardBody, + CardHeader, +} from "react-bootstrap"; +import { useEchoWebSocket } from "../../../../utils/websocketProvider"; +import { GlobalStore } from "../../../../store/globalStore"; + +import { ReactComponent as GuideSVG } from "../../../../../icons/console/guide.svg"; +import { ReactComponent as CameraShootSVG } from "../../../../../icons/console/camera_shoot.svg"; +import PHD2GuideResultGraph from "../../comps/graph/PHD2_guide_graph"; + +const DevicePHD2SimpleControlPanel: React.FC = () => { + // ui control + const [guide_graph, set_guide_graph] = React.useState(false); + const [calibration_result, set_calibration_result] = React.useState(false); + + const close_guide_graph = () => { + set_guide_graph(false); + }; + const open_guide_graph = () => { + set_guide_graph(!guide_graph); + }; + const open_calibration_result = () => { + set_calibration_result(!calibration_result); + }; + const close_calibration_result = () => { + set_calibration_result(false); + }; + const check_phd2_status = () => {}; + const process_ws_message = (msg: any): void => { + if (msg.device_name == "phd2_event") { + } else if (msg.device_name == "PHD2_response") { + } + }; + const start_exposure = () => { + sendMessage( + JSON.stringify({ + device_name: "phd2", + instruction: "loop_exposure", + params: [], + }) + ); + }; + const start_calibration = () => { + sendMessage( + JSON.stringify({ + device_name: "phd2", + instruction: "start_guide", + params: [true], + }) + ); + }; + const start_guide = () => { + sendMessage( + JSON.stringify({ + device_name: "phd2", + instruction: "start_guide", + params: [false], + }) + ); + }; + const stop_guide = () => { + sendMessage( + JSON.stringify({ + device_name: "phd2", + instruction: "stop_guide", + params: [], + }) + ); + }; + const { sendMessage, removeListener } = useEchoWebSocket(process_ws_message); + React.useEffect(() => { + check_phd2_status(); + return () => { + removeListener(process_ws_message); + }; + }, []); + + return ( +
+ {/* control of the graph */} +
+ + {/* control of the graph */} + + + + + {/* main control */} + + + + 主控制面板 + + + + + + + + + + + + + + + 导星数据 + + + 校准结果 + + +
+
+ ); +}; + +DevicePHD2SimpleControlPanel.displayName = "PHD2控制"; + +export default DevicePHD2SimpleControlPanel; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/controls/TelescopeControl.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/controls/TelescopeControl.tsx new file mode 100644 index 00000000..c42af8a5 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/controls/TelescopeControl.tsx @@ -0,0 +1,391 @@ +import * as React from "react"; +import { + Container, + Row, + Col, + Card, + Form, + Button, + Modal, +} from "react-bootstrap"; +import { Gear, ArrowClockwise, XLg } from "react-bootstrap-icons"; +import { useEchoWebSocket } from "../../../../utils/websocketProvider"; + +import SquareButton from "../../../device/SquareButton"; + +const DeviceTelescopeSimpleControlPanel: React.FC = () => { + const [tracking_status, set_tracking_status] = React.useState(false); + const [moving_time, set_moving_time] = React.useState("1"); // in seconds + // ui data + const [fix_time_selection, set_fix_time_selection] = React.useState(true); + const [slew_speed_selections, set_slew_speed_selections] = React.useState< + Array<{ label: String; value: String }> + >([{ label: "1x", value: "1x" }]); + const [selected_slew_speed, set_selected_slew_speed] = + React.useState("0(0.25x)"); + const [setting_open, set_setting_open] = React.useState(false); + + const time_selections = [ + { label: "0.001", value: "0.001" }, + { label: "0.01", value: "0.01" }, + { label: "0.05", value: "0.05" }, + { label: "0.1", value: "0.1" }, + { label: "0.5", value: "0.5" }, + { label: "1", value: "1" }, + { label: "2", value: "2" }, + ]; + const process_ws_message = (msg: any): void => { + if (msg.device_name == "telescope") { + switch (msg.instruction) { + case "get_set_params": { + // update all selections to selects. + let new_selections = []; + for (let i = 0; i < msg.data.slew_rate.selections.length; i++) { + new_selections.push({ + label: msg.data.slew_rate.selections[i], + value: msg.data.slew_rate.selections[i], + }); + } + set_slew_speed_selections(new_selections); + // set current slection. + set_selected_slew_speed(msg.data.slew_rate.value); + break; + } + case "get_real_time_info": { + set_tracking_status(msg.data.tracking); + break; + } + } + } + }; + const { sendMessage, removeListener } = useEchoWebSocket(process_ws_message); + const MINUTE_MS = 1000; + React.useEffect(() => { + sendMessage( + JSON.stringify({ + device_name: "telescope", + instruction: "get_set_params", + params: [], + }) + ); + // This will fire only on mount. + const interval = setInterval(() => { + // console.log('Logs every second'); + sendMessage( + JSON.stringify({ + device_name: "telescope", + instruction: "get_real_time_info", + params: [], + }) + ); + }, MINUTE_MS); + + return () => { + console.log("clear interval"); + clearInterval(interval); + removeListener(process_ws_message); + // if arrow in action, send abort + if (arrow_in_action) { + sendMessage( + JSON.stringify({ + device_name: "telescope", + instruction: "abort", + params: [], + }) + ); + } + }; // This represents the unmount function, in which you need to clear your interval to prevent memory leaks. + }, []); + React.useEffect(() => { + sendMessage( + JSON.stringify({ + device_name: "telescope", + instruction: "set_slew_rate", + params: [selected_slew_speed], + }) + ); + }, [selected_slew_speed]); + const [arrow_in_action, set_arrow_in_action] = React.useState(false); + const arrow_action_func = (btn_type: string, action: string) => { + // console.log('!!!arrow', btn_type, action); + + if (fix_time_selection) { + if (action == "press") { + if (arrow_in_action) { + // console.log('in action, no effect'); + return; + } + set_arrow_in_action(true); + let wait_time = 0; + if (moving_time[0] == null) { + wait_time = 1000; + } else { + wait_time = parseFloat(moving_time[0]); + } + // set time out + // console.log('set timeout'); + sendMessage( + JSON.stringify({ + device_name: "telescope", + instruction: "handle_move_button", + params: [btn_type, true], + }) + ); + setTimeout(() => { + // console.log('timeout finished'); + sendMessage( + JSON.stringify({ + device_name: "telescope", + instruction: "handle_move_button", + params: [btn_type, false], + }) + ); + set_arrow_in_action(false); + }, wait_time); + } + } else { + if (action == "press") { + if (arrow_in_action) { + console.log("in action, no effect"); + return; + } + // console.log('press down'); + sendMessage( + JSON.stringify({ + device_name: "telescope", + instruction: "handle_move_button", + params: [btn_type, true], + }) + ); + set_arrow_in_action(true); + } else if (action == "release") { + // console.log('released'); + sendMessage( + JSON.stringify({ + device_name: "telescope", + instruction: "handle_move_button", + params: [btn_type, false], + }) + ); + set_arrow_in_action(false); + } else { + console.log("error!"); + } + } + }; + + return ( + + + + + {fix_time_selection ? ( +

+ 固定移动时间 +

+ ) : ( +

+ 长按手柄模式 +

+ )} + +
+ + + set_setting_open(true)} + > + 设置 + + + + arrow_action_func("up", "press")} + onMouseUp={() => arrow_action_func("up", "release")} + > + 上 + + + + + {null} + + + + + + arrow_action_func("left", "press")} + onMouseUp={() => arrow_action_func("left", "release")} + > + 左 + + + + { + sendMessage( + JSON.stringify({ + device_name: "telescope", + instruction: "abort", + params: [], + }) + ); + }} + > + 停 + + + + arrow_action_func("right", "press")} + onMouseUp={() => arrow_action_func("right", "release")} + > + 右 + + + + + + {tracking_status ? ( + + 跟踪中 + + ) : ( + + 未跟踪 + + )} + + + arrow_action_func("down", "press")} + onMouseUp={() => arrow_action_func("down", "release")} + > + 下 + + + + { + if (tracking_status) { + sendMessage( + JSON.stringify({ + device_name: "telescope", + instruction: "stop_track", + params: [], + }) + ); + } else { + sendMessage( + JSON.stringify({ + device_name: "telescope", + instruction: "start_track", + params: [], + }) + ); + } + }} + > + 开始跟踪/停止跟踪 + + + + + set_setting_open(false)}> + + 赤道仪手控器参数设置 + + + + 设置赤道仪移动速率 + + set_selected_slew_speed(event.target.value) + } + > + {slew_speed_selections.map((one_slew_selection, index) => ( + + ))} + + + + { + if (!arrow_in_action) { + set_fix_time_selection(event.target.checked); + } + }} + label={fix_time_selection ? "固定时间模式" : "模拟手柄模式"} + /> + + {fix_time_selection && ( + + 选择手动移动时长(s) + set_moving_time(event.target.value)} + > + {time_selections.map((one_time_select, index) => ( + + ))} + + + )} + + + + + +
+
+ ); +}; + +DeviceTelescopeSimpleControlPanel.displayName = "赤道仪控制"; + +export default DeviceTelescopeSimpleControlPanel; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/light/ConfirmDialog.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/light/ConfirmDialog.tsx new file mode 100644 index 00000000..f0c5c2ac --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/light/ConfirmDialog.tsx @@ -0,0 +1,49 @@ +import * as React from "react"; +import Button from "react-bootstrap/Button"; +import Modal from "react-bootstrap/Modal"; + +interface ConfirmDialogProps { + open: number; + show_text: string; + show_title: string; + on_confirm_clicked: () => void; + on_cancel_clicked: () => void; +} + +const ConfirmDialog: React.FC = (props) => { + const [show, setShow] = React.useState(false); + + const handleClose = (confirm_flag: boolean) => { + setShow(false); + if (confirm_flag) { + props.on_confirm_clicked(); + } else { + props.on_cancel_clicked(); + } + }; + + React.useEffect(() => { + if (props.open > 0) { + setShow(true); + } + }, [props.open]); + + return ( + handleClose(false)}> + + {props.show_title} + + {props.show_text} + + + + + + ); +}; + +export default ConfirmDialog; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/light/LightStarSelection.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/light/LightStarSelection.tsx new file mode 100644 index 00000000..bbb99164 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/light/LightStarSelection.tsx @@ -0,0 +1,58 @@ +import * as React from "react"; +import { ListGroup, Card, Container, Row, Col } from "react-bootstrap"; +import { Modal } from "react-bootstrap"; + +interface LightStarSelectionProps { + open: boolean; + handleClose: () => void; + all_star_lists: [ + Array, + Array, + Array, + Array + ]; + on_star_selected: (star_area_index: number, star_array_index: number) => void; +} + +const LightStartSelection: React.FC = (props) => { + const area_categoary = ["东天区", "西天区", "南天区", "北天区"]; + + return ( + + + 亮星选择 + + + + + {area_categoary.map((categoryName, categoryIndex) => ( + + + {categoryName} + + {props.all_star_lists[categoryIndex].map( + (star_info, index) => ( + { + props.on_star_selected(categoryIndex, index); + }} + > + {star_info.show_name}: 高度角{" "} + {star_info.alt.toFixed(1)}° + + ) + )} + + + + ))} + + + + + ); +}; + +export default LightStartSelection; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/light/index.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/light/index.tsx new file mode 100644 index 00000000..94a9a1b7 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/light/index.tsx @@ -0,0 +1,325 @@ +import * as React from "react"; +import { + Card, + Button, + ButtonGroup, + Modal, + Accordion, + AccordionHeader, + AccordionBody, +} from "react-bootstrap"; +import { GlobalStore } from "../../../../store/globalStore"; +import * as AXIOSOF from "../../../../services/object_finding_api"; +import * as AXIOSPAAF from "../../../../services/paa_fixed_procedure_api"; + +import LightStartSelection from "./LightStarSelection"; +import ConfirmDialog from "./ConfirmDialog"; + +const Star = () => { + const [east_selected, set_east_selected] = React.useState(true); + const [west_selected, set_west_selected] = React.useState(true); + const [south_selected, set_south_selected] = React.useState(true); + const [north_selected, set_north_selected] = React.useState(true); + const [all_star_lists, set_all_star_lists] = React.useState< + [ + Array, + Array, + Array, + Array + ] + >([[], [], [], []]); + const [selected_star_info, set_selected_star_info] = + React.useState(null); + const [can_start_paa, set_can_start_paa] = React.useState(false); + const [dialog_open, set_dialog_open] = React.useState(0); + const [select_star_open, set_select_star_open] = React.useState(false); + // store + const set_console_state = GlobalStore.actions.console.setState; + + const on_filter_button_clicked = (clicked_type: string) => { + switch (clicked_type) { + case "east": { + set_east_selected(!east_selected); + get_light_star_data( + !east_selected, + west_selected, + south_selected, + north_selected + ); + break; + } + case "west": { + set_west_selected(!west_selected); + get_light_star_data( + east_selected, + !west_selected, + south_selected, + north_selected + ); + break; + } + case "north": { + set_north_selected(!north_selected); + get_light_star_data( + east_selected, + west_selected, + south_selected, + !north_selected + ); + break; + } + case "south": { + set_south_selected(!south_selected); + get_light_star_data( + east_selected, + west_selected, + !south_selected, + north_selected + ); + break; + } + default: { + break; + } + } + }; + const get_light_star_data = async ( + east: boolean, + west: boolean, + south: boolean, + north: boolean + ) => { + let sky_range = []; + if (east) { + sky_range.push("east"); + } + if (west) { + sky_range.push("west"); + } + if (south) { + sky_range.push("north"); + } + if (north) { + sky_range.push("south"); + } + let to_seach_star_request: IOFRequestLightStar = { + sky_range: sky_range, + max_mag: 2, + }; + try { + const get_light_star_data = await AXIOSOF.getLightStars( + to_seach_star_request + ); + /* + self.name = name + self.show_name = show_name + self.ra = ra + self.dec = dec + self.Const = Const + self.Const_Zh = Const_Zh + self.magnitude = magnitude + self.alt = None + self.az = None + self.sky = '' + */ + let west_list = []; + let east_list = []; + let north_list = []; + let south_list = []; + for (let i = 0; i < get_light_star_data.data.length; i++) { + if (get_light_star_data.data[i].sky == "west") { + west_list.push(get_light_star_data.data[i]); + } + if (get_light_star_data.data[i].sky == "east") { + east_list.push(get_light_star_data.data[i]); + } + if (get_light_star_data.data[i].sky == "north") { + north_list.push(get_light_star_data.data[i]); + } + if (get_light_star_data.data[i].sky == "south") { + south_list.push(get_light_star_data.data[i]); + } + } + set_all_star_lists([east_list, west_list, south_list, north_list]); + } catch (err) { + return null; + } + }; + const on_star_selected = ( + star_area_index: number, + star_array_index: number + ) => { + set_selected_star_info(all_star_lists[star_area_index][star_array_index]); + set_select_star_open(false); + }; + const update_paa_status_and_clean = async () => { + set_can_start_paa(false); + try { + let paa_running_status = await AXIOSPAAF.get_update_paa_running_status(); + if (paa_running_status.data) { + // true means in running + set_can_start_paa(false); + } else { + set_can_start_paa(true); + } + } catch (err) { + return null; + } + }; + const get_paa_status_after_start = async () => { + set_can_start_paa(false); + try { + let paa_status_check = await AXIOSPAAF.get_paa_status(); + if (paa_status_check.data.flag) { + // true meas in running + set_can_start_paa(false); + } else { + set_can_start_paa(true); + } + } catch (err) { + return null; + } + }; + const on_start_goto_clicked = async () => { + if (selected_star_info != null) { + try { + let start_result = await AXIOSPAAF.start_fixed_Goto({ + ra: selected_star_info.ra, + dec: selected_star_info.dec, + }); + if (start_result.success) { + // successfully started + set_can_start_paa(false); + set_console_state({ + alert_type: "success", + alert_message: "亮星Goto,执行中", + }); + } else { + console.log(start_result.message); + get_paa_status_after_start(); + } + } catch (err) { + return null; + } + } else { + set_console_state({ + alert_type: "danger", + alert_message: "亮星Goto,必须选择一个目标才能执行!", + }); + } + }; + const on_start_goto_center_clicked = async () => { + if (selected_star_info != null) { + try { + let start_result = await AXIOSPAAF.start_fixed_Goto_Center({ + ra: selected_star_info.ra, + dec: selected_star_info.dec, + }); + if (start_result.success) { + // successfully started + set_can_start_paa(false); + set_console_state({ + alert_type: "success", + alert_message: "亮星Goto居中,执行中", + }); + } else { + console.log(start_result.message); + get_paa_status_after_start(); + } + } catch (err) { + return null; + } + } else { + set_console_state({ + alert_type: "danger", + alert_message: "亮星Goto居中,必须选择一个目标才能执行!", + }); + } + }; + const post_stop_paa = async () => { + try { + let stop_result = await AXIOSPAAF.post_stop_paa(); + if (stop_result.success) { + set_can_start_paa(true); + update_paa_status_and_clean(); + } else { + set_can_start_paa(true); + console.log(stop_result.message); + } + } catch (err) { + return null; + } + }; + const on_stop_paa_clicked = () => { + set_dialog_open(dialog_open + 1); + }; + + React.useEffect(() => { + set_east_selected(true); + set_west_selected(true); + set_north_selected(true); + set_south_selected(true); + get_light_star_data(true, true, true, true); + update_paa_status_and_clean(); + }, []); + + return ( +
+ + + + {selected_star_info?.show_name || "未选择"} + + + {selected_star_info + ? `${selected_star_info.alt.toFixed(1)}°, ${ + selected_star_info.Const_Zh + }` + : "~"} + + + + + + +
+ set_select_star_open(false)} + /> +
+ {can_start_paa ? ( + + + + + ) : ( + + )} + {}} + /> +
+ ); +}; +// 加一个selected star。 + +Star.displayName = "亮星"; + +export default Star; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/shoot/HFRHistoryChart.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/shoot/HFRHistoryChart.tsx new file mode 100644 index 00000000..e69de29b diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/shoot/index.tsx b/websrc/cobalt-web/src/pages/dashboard2/actions/shoot/index.tsx new file mode 100644 index 00000000..fffc8d8f --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/shoot/index.tsx @@ -0,0 +1,421 @@ +import * as React from "react"; +import { Button, ButtonGroup } from "react-bootstrap"; +import { + ClockFill, + Radioactive, + GraphUp, + Gear, + StopFill, + XLg, + Camera, +} from "react-bootstrap-icons"; +import { Modal, FormControl, FormLabel, FormText } from "react-bootstrap"; +import * as AXIOSPAAF from "../../../../services/paa_fixed_procedure_api"; +import { OverlayTrigger, Tooltip } from "react-bootstrap"; +import { Stack } from "react-bootstrap"; +import { ListGroup } from "react-bootstrap"; +import { Toast, ToastContainer } from "react-bootstrap"; +import "./style.less"; + +import { ReactSVG } from "react-svg"; + +import { useEchoWebSocket } from "../../../../utils/websocketProvider"; + +import { CaptureButton } from "../../CaptureButton"; + +const Shoot = () => { + const [can_shoot, set_can_shoot] = React.useState(false); + const [exposure_time_select_open, set_exposure_time_select_open] = + React.useState(false); + const [loop, set_loop] = React.useState(false); + const [save, set_save] = React.useState(false); + const [exposure_time, set_exposure_time] = React.useState(1); + const [in_exposure, set_in_exposure] = React.useState(false); + const [input_exposure_time, set_input_exposure_time] = React.useState("1"); + const [exposure_finished, set_exposure_finished] = React.useState(false); + const [abort_signal, set_abort_signal] = React.useState(false); + // extra ui control + const [show_hfr_snackbar, set_show_hfr_snackbar] = React.useState(false); + const [show_chart_snackbar, set_show_chart_snackbar] = React.useState(false); + const [show_info_snackbar, set_show_info_snackbar] = React.useState(false); + const [info_show_message, set_info_show_message] = React.useState(""); + const [newest_hf_data, set_newest_hfr_data] = + React.useState(null); + // functions + const check_can_shoot = async () => { + // check paa in progress? + set_can_shoot(false); + try { + let paa_running_status = await AXIOSPAAF.get_update_paa_running_status(); + if (paa_running_status.data) { + // true means in running + set_can_shoot(false); + } else { + set_can_shoot(true); + } + } catch (err) { + return null; + } + // check camera in exposure? + sendMessage( + JSON.stringify({ + device_name: "camera", + instruction: "get_real_time_info", + params: [], + }) + ); + }; + const on_loop_clicked = () => { + set_loop(!loop); + }; + const on_save_clicked = () => { + set_save(!save); + }; + const on_exposure_time_clicked = () => { + set_exposure_time_select_open(true); + }; + const on_exposure_time_changed = (target_value: number) => { + if (target_value < 0) { + set_exposure_time(1); + } else { + set_exposure_time(target_value); + } + }; + const handleClose = () => { + let target_value = parseFloat(input_exposure_time); + on_exposure_time_changed(target_value); + set_exposure_time_select_open(false); + }; + const start_exposure = () => { + // check can exposure + // console.log('start exposure clicked!'); + set_abort_signal(false); + if (can_shoot) { + sendMessage( + JSON.stringify({ + device_name: "camera", + instruction: "start_single_exposure", + params: [exposure_time, save], + }) + ); + set_in_exposure(true); + } + }; + const stop_exposure = () => { + sendMessage( + JSON.stringify({ + device_name: "camera", + instruction: "abort_exposure", + params: [], + }) + ); + set_in_exposure(false); + set_abort_signal(true); + }; + const process_ws_message = (message: any) => { + if (message.device_name == "camera") { + if (message.instruction == "get_real_time_info") { + set_in_exposure(message.data.in_exposure); + } + } else if (message.device_name == "Signal") { + if ( + (message.instruction == "Image Stretch" || + message.instruction == "Image Process Failed!") && + message.data.camera_type == "camera" + ) { + set_exposure_finished(true); + } + if ( + message.instruction == "Image Stretch" && + message.data.camera_type == "camera" + ) { + set_newest_hfr_data(message.data.hfr_info); + } + } + }; + // websocket handler + const { sendMessage, removeListener } = useEchoWebSocket(process_ws_message); + // useEffect + const MINUTE_MS = 1000; + React.useEffect(() => { + check_can_shoot(); + const interval = setInterval(() => { + // console.log('Logs every second'); + sendMessage( + JSON.stringify({ + device_name: "camera", + instruction: "get_real_time_info", + params: [], + }) + ); + }, MINUTE_MS); + return () => { + clearInterval(interval); + removeListener(process_ws_message); + }; + }, []); + React.useEffect(() => { + if (abort_signal) { + set_abort_signal(false); + set_in_exposure(false); + return; + } + if (exposure_finished) { + // todo, can update some status + // console.log('got finish exposure signal', loop); + if (loop) { + sendMessage( + JSON.stringify({ + device_name: "camera", + instruction: "start_single_exposure", + params: [exposure_time, save], + }) + ); + set_in_exposure(true); + } else { + set_in_exposure(false); + } + set_exposure_finished(false); + } + }, [exposure_finished]); + // functions for ui control + const on_hfr_show_clicked = () => { + set_show_chart_snackbar(false); + set_show_hfr_snackbar(true); + }; + const on_hfr_graph_show_clicked = () => { + set_show_hfr_snackbar(false); + set_show_chart_snackbar(true); + }; + + return ( + <> +
+
+
+ {can_shoot ? ( + in_exposure ? ( + + ) : ( + + { + svg.classList.add("svg-class-name"); + svg.setAttribute("style", "width: 24px"); + svg.setAttribute("style", "height: 24px"); + }} + src="../../../../icons/console/camera_shoot.svg" + /> + + ) + ) : ( + { + set_info_show_message("PAA流程正在进行中,请终止PAA流程再试"); + set_show_info_snackbar(true); + }} + > + + + )} +
+ +
+ {loop ? ( + + ) : ( + + )} + {save ? ( + + ) : ( + + )} +
+
+ + 星点数据} + > + + + 星点数据图表} + > + + + +
+
{/* 用来显示拍摄状态 */}
+ {/* 用来设置曝光时间的Modal */} + + + 曝光时间输入 + + +

修改手动曝光时间,可以选择一个常用曝光时间或者手动输入时间。

+
+ + + + + + + + + + + + + 设置曝光时间 + + set_input_exposure_time(event.target.value) + } + /> + + 如果需要拍摄bias,设置0.001秒; +
+ 一般曝光设置10秒以内,可以用来进行手动对焦,或光轴调试;
+ 也可以设置长曝光,用作测试拍摄。 +
+
+
+
+ + + +
+ {/* 用来显示最新的HFR数据的Toast */} +
+ + + + + 最新曝光星点数据 + + + + + 星点数据量 + 平均HFR + + + + + + {/* 显示HFR历史记录图标的Toast */} + + + + 最新曝光星点数据 + + + + + + set_show_info_snackbar(false)} + delay={3000} + autohide + > + {info_show_message} + + + + ); +}; + +Shoot.displayName = "拍摄"; + +export default Shoot; diff --git a/websrc/cobalt-web/src/pages/dashboard2/actions/shoot/style.less b/websrc/cobalt-web/src/pages/dashboard2/actions/shoot/style.less new file mode 100644 index 00000000..dee20930 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/actions/shoot/style.less @@ -0,0 +1,90 @@ +.app-console-shoot-actions{ + width: 150px; + height: 400px; + position: absolute; + top: 50%; + right: 10px; + transform: translateY(-50%); + z-index: 20; +} + +.shoot-button-start{ + width: 60px; + height: 60px; + text-align: center; + color:#ffffff; + line-height: 60px; + border-radius: 50%; + background-color: rgba(0,0,0,0.85); + border:6px solid #ffffff; + position: absolute; + top: 50%; + // bottom:0px; + right:0px; +} +.shoot-button-stop{ + width: 60px; + height: 60px; + text-align: center; + color:#FF0000; + line-height: 60px; + border-radius: 50%; + background-color: rgba(0,0,0,0.85); + border:6px solid #FF0000; + position: absolute; + top: 50%; + // bottom:0px; + right:0px; +} + +.shoot-button-config{ + width: 32px; + height: 32px; + color:#ffffff; + line-height: 32px; + text-align: center; + font-size:12px; + border:1px solid #ffffff; + border-radius: 50%; + position: absolute; +} + +.shoot-button-exposure-time{ + position: absolute; + top: 10%; + right: 0px; +} +.shoot-button-config[role='save_checked']{ + border:3px solid #008000; + top: 30%; + right:0px; +} +.shoot-button-config[role='save_uncheck']{ + border:3px solid #ffffff; + top: 30%; + right:0px; +} +.shoot-button-config[role='loop_checked']{ + border:3px solid #008000; + top: 80%; + right:0px; +} +.shoot-button-config[role='loop_uncheck']{ + border:3px solid #ffffff; + top: 80%; + right:0px; +} + +// .shoot-button-config:last-child{ +// top:0; +// right:0; +// } + +.shoot-pic-process-config{ + position: absolute; + left: 0; + top: 0px; +} +.app-console-shoot-status{ + position: absolute; +} \ No newline at end of file diff --git a/websrc/cobalt-web/src/pages/dashboard2/comps/graph/AF_figure.tsx b/websrc/cobalt-web/src/pages/dashboard2/comps/graph/AF_figure.tsx new file mode 100644 index 00000000..b5198dd2 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/comps/graph/AF_figure.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import ReactECharts from 'echarts-for-react'; + +interface AF_figure_Props { + focus_measures: Array<[number, number]> + focus_model: any; + focus_point: [number, number]; + focus_find: boolean; +} + +const AF_figure: React.FC = (props) => { + const [focus_figure, set_focus_figure] = React.useState({ + + }) + return ( + <> + ) +} +export default AF_figure; \ No newline at end of file diff --git a/websrc/cobalt-web/src/pages/dashboard2/comps/graph/HFR_history_result.tsx b/websrc/cobalt-web/src/pages/dashboard2/comps/graph/HFR_history_result.tsx new file mode 100644 index 00000000..7c441802 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/comps/graph/HFR_history_result.tsx @@ -0,0 +1,8 @@ +import * as React from 'react'; + +const HFRHistoryResult: React.FC = () => { + return ( + <> + ) +} +export default HFRHistoryResult; \ No newline at end of file diff --git a/websrc/cobalt-web/src/pages/dashboard2/comps/graph/PHD2_calibration_result.tsx b/websrc/cobalt-web/src/pages/dashboard2/comps/graph/PHD2_calibration_result.tsx new file mode 100644 index 00000000..3b714fdd --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/comps/graph/PHD2_calibration_result.tsx @@ -0,0 +1,8 @@ +import * as React from 'react'; + +const PHD2CalibrationResult: React.FC = () => { + return ( + <> + ) +} +export default PHD2CalibrationResult; \ No newline at end of file diff --git a/websrc/cobalt-web/src/pages/dashboard2/comps/graph/PHD2_guide_graph.tsx b/websrc/cobalt-web/src/pages/dashboard2/comps/graph/PHD2_guide_graph.tsx new file mode 100644 index 00000000..45816f0e --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/comps/graph/PHD2_guide_graph.tsx @@ -0,0 +1,104 @@ +import * as React from 'react'; +import ReactECharts from 'echarts-for-react'; +import { GlobalStore } from '../../../../store/globalStore'; + + +const PHD2GuideResultGraph: React.FC = () => { + const process_data = GlobalStore.useAppState((state) => state.ProcessDataSaveStore); + const eChartsRef = React.useRef(null as any); + const [guide_result_graph, set_guide_result_graph] = React.useState( + { + tooltip: { + trigger: 'axis' + }, + xAxis: { + type: 'category', + }, + yAxis: [{ + name: 'error', + type: 'value' + }, + { + name: 'guide', + type: 'value', + } + ], + series: [{ + data: process_data.PHD2_guide_data_list.RaDistance, + type: 'line', + yAxisIndex: 0, + markLine: { + silent: true, + data: [] + }, + lineStyle: { + color: '#fc0303' + } + }, { + data: process_data.PHD2_guide_data_list.DecDistance, + type: 'line', + yAxisIndex: 0, + markLine: { + silent: true, + data: [] + }, + lineStyle: { + color: '#031cfc' + } + }, { + data: process_data.PHD2_guide_data_list.RaControl, + type: 'bar', + yAxisIndex: 1, + stack: 'Total', + markLine: { + silent: true, + data: [] + }, + itemStyle: { + borderColor: '#fc0303', + color: 'rgba(255, 255, 255, 0)' + } + }, { + data: process_data.PHD2_guide_data_list.DecControl, + type: 'bar', + yAxisIndex: 1, + stack: 'Total', + markLine: { + silent: true, + data: [] + }, + itemStyle: { + borderColor: '#031cfc', + color: 'rgba(255, 255, 255, 0)' + } + }] + } + + ); + + React.useEffect(() => { + let new_series = guide_result_graph.series; + new_series[0].data = process_data.PHD2_guide_data_list.RaDistance; + new_series[1].data = process_data.PHD2_guide_data_list.DecDistance; + new_series[2].data = process_data.PHD2_guide_data_list.RaControl; + new_series[3].data = process_data.PHD2_guide_data_list.DecControl; + set_guide_result_graph({ + ...guide_result_graph, + series: new_series, + }) + if (eChartsRef && eChartsRef.current) + eChartsRef.current?.getEchartsInstance().setOption(guide_result_graph); + }, [process_data.PHD2_guide_data_list.RaDistance]) + + return ( + + ) +} +export default PHD2GuideResultGraph; \ No newline at end of file diff --git a/websrc/cobalt-web/src/pages/dashboard2/comps/ui/ConsoleSelectComp.tsx b/websrc/cobalt-web/src/pages/dashboard2/comps/ui/ConsoleSelectComp.tsx new file mode 100644 index 00000000..e9fb8eae --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/comps/ui/ConsoleSelectComp.tsx @@ -0,0 +1,125 @@ +import * as React from "react"; +import { Button, Alert } from "react-bootstrap"; +import Modal from "react-bootstrap/Modal"; +import Card from "react-bootstrap/Card"; +import Form from "react-bootstrap/Form"; +import { Offcanvas } from "react-bootstrap"; +import { GlobalStore } from "../../../../store/globalStore"; +import Registry from "../../registry"; + +const ConsoleSelectComp: React.FC = () => { + const state = GlobalStore.useAppState((state) => state.console); + + const ICONHandler = Registry._registry_icon.get(state.menu) as React.FC; + + return ( + <> + + + {/* drawer of the functions */} + { + GlobalStore.actions.console.setState({ + selec_console_drawer_open: false, + }); + }} + placement="start" + style={{ + backgroundColor: "transparent", + padding: "24px", + boxShadow: "none", + }} + > + + 中控台 + + +
+ + 功能选择 + +
+ {Registry.getMenus().map((item) => ( + { + GlobalStore.actions.console.setState({ + menu: item.id as any, + drawerVisible: true, + selec_console_drawer_open: false, + }); + if (item.id == "phd2") { + GlobalStore.actions.ProcessDataSaveStore.switch_camera_display( + 1 + ); + } else { + GlobalStore.actions.ProcessDataSaveStore.switch_camera_display( + 0 + ); + } + }} + > + + + {item.displayName} + + + + ))} +
+
+
+
+ {/* end of the drawer */} + + ); +}; + +export default ConsoleSelectComp; diff --git a/websrc/cobalt-web/src/pages/dashboard2/comps/ui/InfoUpdateComp.tsx b/websrc/cobalt-web/src/pages/dashboard2/comps/ui/InfoUpdateComp.tsx new file mode 100644 index 00000000..ec9014a8 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/comps/ui/InfoUpdateComp.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import { Alert } from 'react-bootstrap'; +import { GlobalStore } from '../../../../store/globalStore'; + +const InfoUpdateComp: React.FC = () => { + const state = GlobalStore.useAppState(state=>state.console); + + return ( + {state.alert_message} + ) +} + +export default InfoUpdateComp; \ No newline at end of file diff --git a/websrc/cobalt-web/src/pages/dashboard2/index.tsx b/websrc/cobalt-web/src/pages/dashboard2/index.tsx new file mode 100644 index 00000000..f49aaf59 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/index.tsx @@ -0,0 +1,116 @@ +import * as React from "react"; +import Offcanvas from "react-bootstrap/Offcanvas"; +import { GlobalStore } from "../../store/globalStore"; +import Registry from "./registry"; +import Align from "./actions/align"; +import Light from "./actions/light"; +import Shoot from "./actions/shoot"; +import { useEchoWebSocket } from "../../utils/websocketProvider"; +import "./style.less"; +import Autofocus from "./actions/autofocus"; +import InfoUpdateComp from "./comps/ui/InfoUpdateComp"; +import ConsoleSelectComp from "./comps/ui/ConsoleSelectComp"; +import { + Camera, + Magic, + ArrowsMove, + Bullseye, + Map, + Star, + PaintBucket, + Rulers, +} from "react-bootstrap-icons"; + +import { ReactSVG } from "react-svg"; + +// devices +import DeviceCameraSimpleControlPanel from "./actions/controls/CameraControl"; +import DeviceTelescopeSimpleControlPanel from "./actions/controls/TelescopeControl"; +import DeviceFocuserSimpleControlPanel from "./actions/controls/FocuserControl"; +import DeviceFilterSimpleControlPanel from "./actions/controls/FilterControl"; +import DeviceGuiderSimpleControlPanel from "./actions/controls/GuiderControl"; +import DevicePHD2SimpleControlPanel from "./actions/controls/Phd2Control"; + +Registry.addComponent("shoot", Shoot, Camera); +Registry.addComponent("star", Light, Magic); +Registry.addComponent("align", Align, Map); +Registry.addComponent("autofocus", Autofocus, Bullseye); + +Registry.addComponent("camera", DeviceCameraSimpleControlPanel); +Registry.addComponent("telescope", DeviceTelescopeSimpleControlPanel, Star); +Registry.addComponent("focuser", DeviceFocuserSimpleControlPanel); +Registry.addComponent("filter", DeviceFilterSimpleControlPanel, PaintBucket); +Registry.addComponent("phd2", DevicePHD2SimpleControlPanel, Rulers); + +const Console = () => { + // state + const state = GlobalStore.useAppState((state) => state.console); + + const graph = React.useRef(null); + + const container = React.useRef(null); + + // store + const process_data = GlobalStore.useAppState( + (state) => state.ProcessDataSaveStore + ); + + const handleMsg = React.useCallback((msg: any) => { + // console.log(msg); + }, []); + + const { removeListener } = useEchoWebSocket(handleMsg); + + React.useEffect(() => { + return () => removeListener(handleMsg); + }, []); + + const toggleDrawer = () => { + GlobalStore.actions.console.setState({ + drawerVisible: false, + }); + }; + + const Handler = Registry._registry.get(state.menu) as React.FC; + const ICONHandler = Registry._registry_icon.get(state.menu) as React.FC; + + return ( + <> + {process_data.show_camera == 0 ? ( + + ) : ( + + )} +
+ + + + +
+ +
+
+
+
+ + ); +}; + +export default Console; diff --git a/websrc/cobalt-web/src/pages/dashboard2/registry/index.tsx b/websrc/cobalt-web/src/pages/dashboard2/registry/index.tsx new file mode 100644 index 00000000..42045aa9 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/registry/index.tsx @@ -0,0 +1,40 @@ +import * as React from "react"; +import { Android } from "react-bootstrap-icons"; +import { IconProps } from "react-bootstrap-icons"; + +class Registry { + static _registry: Map = new Map(); + static _registry_icon: Map> = new Map(); + + static addComponent = ( + id: string, + component: React.FC, + Icon: React.FC | null = null + ) => { + this._registry.set(id, component); + if (Icon === null) { + this._registry_icon.set(id, (props: IconProps) => ); + } else { + this._registry_icon.set(id, Icon); + } + }; + + static getComponent = (id: string) => this._registry.get(id); + + static getMenus = () => { + return Array.from(this._registry.keys()).map((id) => ({ + id, + displayName: this._registry.get(id)?.displayName, + icon: (props: IconProps) => { + const Icon = this._registry_icon.get(id); + return ( +
+ +
+ ); + }, + })); + }; +} + +export default Registry; diff --git a/websrc/cobalt-web/src/pages/dashboard2/style.less b/websrc/cobalt-web/src/pages/dashboard2/style.less new file mode 100644 index 00000000..1fee1d01 --- /dev/null +++ b/websrc/cobalt-web/src/pages/dashboard2/style.less @@ -0,0 +1,89 @@ +.app-console{ + width: 100vw; + position: absolute; + left: 0; + top: 0; + height: calc(~"100vh - 32px"); // calc(~"100vh- 60px") + background-image: url('../../assets/imgs//bg.jpg'); + background-size: cover; + z-index: 0; +} + +.app-console-menu{ + position: absolute; + bottom: 10px; + width:150px; + height: 150px; + +} + +.circle-button{ + width: 48px; + height: 48px; + background-color: rgba(0,0,0,0.85); + border-radius: 50%; + border-color:transparent; + border-width: 1px; + text-align: center; + line-height: 48px; + color:#ffffff; + padding: 0px; + border-style: solid; + transition: all 0.4s ease-in; + &-selected{ + border-color: #ffffff; + } +} + + + +.circle-button:first-child{ + position: absolute; + left: 67px; + bottom:67px; +} + +.circle-button:nth-child(2){ + position: absolute; + top: 0; + left:10px; +} +.circle-button:last-child{ + position: absolute; + right: 0; + bottom:0px; +} + +.app-console-handler{ + height: calc(~"100vh - 90px"); + width: 320px; + position: relative; +} + +.console-drawer{ + top: 32px !important;; + + .MuiModal-backdrop{ + background-color: transparent !important; + top: 32px !important;; + } + + .MuiDrawer-paper{ + top: 32px !important; + height: calc(~"100vh - 66px") !important; + background-color: transparent !important;//rgba(0, 0, 0, 0.5) !important; + } +} + +.newest_image { + position: fixed; + z-index: 1; + // top: 100px; + left: 0; + right: 0; + margin: 0; + padding: 0; + width: 100%; + height: calc("100vh"); + object-fit: contain; +} \ No newline at end of file diff --git a/websrc/cobalt-web/src/pages/device/SquareButton.tsx b/websrc/cobalt-web/src/pages/device/SquareButton.tsx new file mode 100644 index 00000000..b04aba44 --- /dev/null +++ b/websrc/cobalt-web/src/pages/device/SquareButton.tsx @@ -0,0 +1,34 @@ +import * as React from "react"; +import { Button, ButtonProps } from "react-bootstrap"; + +interface SquareButtonProps extends ButtonProps { + children: React.ReactNode; +} + +const SquareButton: React.FC = (props) => { + const { children, ...other } = props; + return ( +
+ +
+ ); +}; + +export default SquareButton; diff --git a/websrc/cobalt-web/src/pages/device/camera.tsx b/websrc/cobalt-web/src/pages/device/camera.tsx index 60b5ef5e..818eacf9 100644 --- a/websrc/cobalt-web/src/pages/device/camera.tsx +++ b/websrc/cobalt-web/src/pages/device/camera.tsx @@ -288,10 +288,10 @@ const DeviceCameraGeneralControlPanel: React.FC = () => { }, []); return ( - + - + 主相机信息
设备型号: {device_name}
@@ -300,32 +300,41 @@ const DeviceCameraGeneralControlPanel: React.FC = () => {

y像素数量: {camera_info.y_pixels}

x像元大小: {camera_info.x_pixel_size.toFixed(2)} um

y像元大小: {camera_info.y_pixel_size.toFixed(2)} um

-
- {exposure_status ? ( - - ) : ( - - )} -
-
- {cool_status ? ( - - ) : ( - - )} -
+ + +
+ {exposure_status ? ( + + ) : ( + + )} +
+ + +
+ {cool_status ? ( + + ) : ( + + )} +
+ +
- - 设备控制 + 开关冷冻 diff --git a/websrc/cobalt-web/src/pages/device/description/helper_snackbar.tsx b/websrc/cobalt-web/src/pages/device/description/helper_snackbar.tsx index ec76e70a..7edfd7a5 100644 --- a/websrc/cobalt-web/src/pages/device/description/helper_snackbar.tsx +++ b/websrc/cobalt-web/src/pages/device/description/helper_snackbar.tsx @@ -1,39 +1,49 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { Container, Row, Col, Button, Toast } from 'react-bootstrap'; -import { Link, Line } from 'react-bootstrap-icons'; +import * as React from "react"; +import { Toast, ToastContainer } from "react-bootstrap"; +import { XCircle } from "react-bootstrap-icons"; interface HelperSnackbarProps { help_text: string; close_signal: boolean; } -const HelperSnackbar = React.forwardRef(function HelperSnackbar(props: HelperSnackbarProps, ref) { - const { t } = useTranslation(); - const [show, setShow] = React.useState(false); +const HelperSnackbar: React.ForwardRefRenderFunction< + HelperHandle, + HelperSnackbarProps +> = (props, ref) => { + const [open_snackbar, setOpen_snackbar] = React.useState(false); - const handleClose = () => setShow(false); + const handleSnackbarClose = () => { + setOpen_snackbar(false); + }; React.useImperativeHandle(ref, () => ({ - openSnackbar() { - setShow(true); - } + open_snackbar() { + setOpen_snackbar(true); + }, })); React.useEffect(() => { if (props.close_signal) { - setShow(false); + setOpen_snackbar(false); } }, [props.close_signal]); return ( - - - {t('title')} - - {props.help_text} - + + + + {props.help_text} + + + + ); -}); +}; -export default HelperSnackbar; +export default React.forwardRef(HelperSnackbar); diff --git a/websrc/cobalt-web/src/pages/device/guider.tsx b/websrc/cobalt-web/src/pages/device/guider.tsx index 40596020..4772ae2a 100644 --- a/websrc/cobalt-web/src/pages/device/guider.tsx +++ b/websrc/cobalt-web/src/pages/device/guider.tsx @@ -6,6 +6,7 @@ import { InputGroup, Col, Row, + Container, } from "react-bootstrap"; import { ArrowLeft, @@ -192,7 +193,7 @@ const DeviceGuiderGeneralControlPanel = () => { }, []); return ( -
+
@@ -597,7 +598,7 @@ const DeviceGuiderGeneralControlPanel = () => {
-
+
); }; diff --git a/websrc/cobalt-web/src/pages/device/index.tsx b/websrc/cobalt-web/src/pages/device/index.tsx index 34c37f8f..30f82a1e 100644 --- a/websrc/cobalt-web/src/pages/device/index.tsx +++ b/websrc/cobalt-web/src/pages/device/index.tsx @@ -23,6 +23,7 @@ import DeviceFocuserGeneralControlPanel from "./focuser"; import DeviceGuiderGeneralControlPanel from "./guider"; import DevicePHD2GeneralControlPanel from "./phd2"; import DeviceTelescopeGeneralControlPanel from "./telescope"; +import DeviceSolverGeneralControlPanel from "./solver"; import DeviceCameraDetailedControlPanel from "./detail/camera"; import DeviceTelescopeDetailedControlPanel from "./detail/telescope"; @@ -67,6 +68,8 @@ const DeviceControlPanelPage = () => { return true; } else if (device_key === "phd2") { return true; + } else if (device_key == "solver") { + return true; } }; @@ -144,6 +147,18 @@ const DeviceControlPanelPage = () => {
); + } else if (device_key == "solver") { + return ( + + + + + Solver未连接 + +

Solver未连接

+
+
+ ); } }; @@ -152,29 +167,32 @@ const DeviceControlPanelPage = () => { }, []); return ( - - - - - set_show_detail(false)} - /> - setShowAdvancedMode(true)} - size={32} - /> - + + + + +
+ +
+
+ +
+ +
+
+ { /> - + { )} + + { + //show_device_icon_if_not_connected("solver") + } + {!show_detail ? ( + + ) : ( + + )} +
diff --git a/websrc/cobalt-web/src/pages/device/solver.tsx b/websrc/cobalt-web/src/pages/device/solver.tsx new file mode 100644 index 00000000..139e98b1 --- /dev/null +++ b/websrc/cobalt-web/src/pages/device/solver.tsx @@ -0,0 +1,195 @@ +import React, { useState, useEffect } from "react"; +import Container from "react-bootstrap/Container"; +import Row from "react-bootstrap/Row"; +import Col from "react-bootstrap/Col"; +import Form from "react-bootstrap/Form"; +import Button from "react-bootstrap/Button"; +import ButtonGroup from "react-bootstrap/ButtonGroup"; +import { Check, XCircle } from "react-bootstrap-icons"; + +import { useEchoWebSocket } from "../../utils/websocketProvider"; + +const DeviceSolverGeneralControlPanel = () => { + const [connected, setConnected] = useState(false); + const [solver, setSolver] = useState("astrometry"); // Default solver + const [solveTimeout, setSolveTimeout] = useState(60); // Default solve timeout in seconds + const [downsample, setDownsample] = useState(2); // Default downsample value + const [showAdvancedOptions, setShowAdvancedOptions] = useState(false); + + const checkSolverStatus = () => { + // Placeholder for checking the status of selected solver + }; + + const saveSettings = () => { + // Placeholder for saving solver settings + }; + + const processWsMessage = (msg) => { + // Placeholder for processing WebSocket messages + }; + + const { sendMessage, removeListener } = useEchoWebSocket(processWsMessage); + + useEffect(() => { + checkSolverStatus(); + return () => { + removeListener(processWsMessage); + }; + }, []); + + return ( + + + + +
+
解析器设置
+ {connected ? ( + + ) : ( + + )} +
+
+ + + 解析器选择 + setSolver(e.target.value)} + > + + + + + + + + + 超时设置 (秒) + setSolveTimeout(event.target.value)} + min={0} + /> + + + 降采样 + + + + + + + + + {/* + + + + + {showAdvancedOptions && ( +
+ {solver === "astrometry" && ( + + + Astrometry.net API Key + + + + )} +
+ )} + */} +
+ + + {/* Right side for specific solver parameters */} +
+ {/* Specific solver parameters */} + + {solver === "astrometry" && ( +
+ {/* Astrometry.net parameters */} +

Astrometry.net特有参数

+ + 位深 + + + + + + + + 拉伸 + + + {/* Add more Astrometry.net parameters as needed */} +
+ )} + {solver === "astap" && ( +
+ {/* Astap parameters */} + + Specific parameter A + + + + Specific parameter B + + + {/* Add more Astap parameters as needed */} +
+ )} + {solver === "stellarsolver" && ( +
+ {/* StellarSolver parameters */} + + Specific parameter X + + + + Specific parameter Y + + + {/* Add more StellarSolver parameters as needed */} +
+ )} +
+ +
+
+ ); +}; + +export default DeviceSolverGeneralControlPanel; diff --git a/websrc/cobalt-web/src/pages/device/telescope.tsx b/websrc/cobalt-web/src/pages/device/telescope.tsx index 1694ed7c..ffc4a49a 100644 --- a/websrc/cobalt-web/src/pages/device/telescope.tsx +++ b/websrc/cobalt-web/src/pages/device/telescope.tsx @@ -1,5 +1,13 @@ import React from "react"; -import { Container, Row, Col, Form, Button, Card } from "react-bootstrap"; +import { + Container, + Row, + Col, + Form, + Button, + Card, + ButtonGroup, +} from "react-bootstrap"; import { ArrowUp, ArrowDown, @@ -33,7 +41,19 @@ const DeviceTelescopeGeneralControlPanel: React.FC = () => { const [fix_time_selection, set_fix_time_selection] = React.useState(true); const [slew_speed_selections, set_slew_speed_selections] = React.useState< Array<{ label: String; value: String }> - >([{ label: "1x", value: "1x" }]); + >([ + { label: "1x", value: "1x" }, + { label: "2x", value: "2x" }, + { label: "4x", value: "4x" }, + { label: "8x", value: "8x" }, + { label: "16x", value: "16x" }, + { label: "32x", value: "32x" }, + { label: "64x", value: "64x" }, + { label: "128x", value: "128x" }, + { label: "256x", value: "256x" }, + { label: "512x", value: "512x" }, + { label: "1024x", value: "1024x" }, + ]); const [selected_slew_speed, set_selected_slew_speed] = React.useState("0(0.25x)"); @@ -290,11 +310,10 @@ const DeviceTelescopeGeneralControlPanel: React.FC = () => { }; return ( - + - - 赤道仪信息 + 设备记录经度: {geo_location.longitude} ° 设备记录纬度: {geo_location.latitude} ° @@ -316,10 +335,116 @@ const DeviceTelescopeGeneralControlPanel: React.FC = () => { ra: {heading_degree.ra} ° dec: {heading_degree.dec} ° + + + +
+ {tracking_status ? ( + + ) : ( + + )} +
+ + +
+ +
+ +
+ + + + + + + + + + + + +
- + 设置赤道仪移动速率 { - + { - +
+ +
- + - +
+ +
- +
+ +
- +
+ +
- + - +
+ +
- - {tracking_status ? ( - - ) : ( - - )} - - - - -
diff --git a/websrc/cobalt-web/src/pages/home/animation.tsx b/websrc/cobalt-web/src/pages/home/animation.tsx index 67ad057c..7ca9e12d 100644 --- a/websrc/cobalt-web/src/pages/home/animation.tsx +++ b/websrc/cobalt-web/src/pages/home/animation.tsx @@ -71,8 +71,8 @@ function Welcome() { }} > - Welcome + />*/} Cobalt WebClient diff --git a/websrc/cobalt-web/src/pages/home/init_ws_listener.tsx b/websrc/cobalt-web/src/pages/home/init_ws_listener.tsx index 55755214..c48904ea 100644 --- a/websrc/cobalt-web/src/pages/home/init_ws_listener.tsx +++ b/websrc/cobalt-web/src/pages/home/init_ws_listener.tsx @@ -1,70 +1,109 @@ -import * as React from 'react'; -import { useEchoWebSocket } from '../..//utils/websocketProvider'; -import { GlobalStore } from '../../store/globalStore'; +import * as React from "react"; +import { useEchoWebSocket } from "../../utils/websocketProvider"; +import { GlobalStore } from "../../store/globalStore"; +import { setInterval } from "timers/promises"; const WS_LISTENER_COMP: React.FC = () => { // store taker - const register_send_message = GlobalStore.actions.ProcessDataSaveStore.at_start_add_ws_listener; + const register_send_message = + GlobalStore.actions.ProcessDataSaveStore.at_start_add_ws_listener; // const send_ws_message = GlobalStore.actions.ProcessDataSaveStore.send_message_to_ws; - const process_data = GlobalStore.useAppState((state) => state.ProcessDataSaveStore); - const append_HFR = GlobalStore.actions.ProcessDataSaveStore.append_newest_history_HFR_data; - const append_guide_step = GlobalStore.actions.ProcessDataSaveStore.append_newest_PHD2_guide_data; - const get_newest_jpg = GlobalStore.actions.ProcessDataSaveStore.get_newest_jpg; + const process_data = GlobalStore.useAppState( + (state) => state.ProcessDataSaveStore + ); + const append_HFR = + GlobalStore.actions.ProcessDataSaveStore.append_newest_history_HFR_data; + const append_guide_step = + GlobalStore.actions.ProcessDataSaveStore.append_newest_PHD2_guide_data; + const get_newest_jpg = + GlobalStore.actions.ProcessDataSaveStore.get_newest_jpg; + const load_saved_targets = + GlobalStore.actions.TargetListStore.load_all_targets; + const initial_ui_setting = + GlobalStore.actions.GlobalParameterStore.initial_ui_setting; + const load_ui_setting = + GlobalStore.actions.GlobalParameterStore.load_ui_setting; + const get_all_global_parameters = + GlobalStore.actions.GlobalParameterStore.get_all_paramters; - const sendMessageRef = React.useRef<((message: string) => void)|null>(null); - const process_ws_message = (message: any) => { - if (message instanceof Blob){ - // process_blob_data(message); - }else{ - // listening signals and other important data - if (message.device_name == 'Signal'){ - if (message.instruction == 'Image Stretch'){ - if (message.data.camera_type == 'camera'){ - get_newest_jpg('camera'); - // console.log('camera got stretched!', typeof sendMessageRef.current); - // if (sendMessageRef.current != null){ - // sendMessageRef.current(JSON.stringify({ - // device_name: 'Signal', - // instruction: 'camera_newest', - // params: [], - // })); - // } - append_HFR(message.data.hfr_info); - }else if (message.data.camera_type == 'guider'){ - get_newest_jpg('guider'); - // if (sendMessageRef.current != null){ - // sendMessageRef.current(JSON.stringify({ - // device_name: 'Signal', - // instruction: 'guider_newest', - // params: [], - // })); - // } - } - }else if (message.instruction == 'Image Process Failed!'){ - + // listening signals and other important data + if (message.device_name == "Signal") { + if (message.instruction == "Image Stretch") { + if (message.data.camera_type == "camera") { + get_newest_jpg("camera"); + // console.log('camera got stretched!', typeof sendMessageRef.current); + append_HFR(message.data.hfr_info); + } else if (message.data.camera_type == "guider") { + get_newest_jpg("guider"); + } + } else if (message.instruction == "Image Process Failed!") { + } + } else if (message.device_name == "phd2") { + // message from server + switch (message.instruction) { + case "get_guiding_data": { + GlobalStore.actions.ProcessDataSaveStore.setState({ + PHD2_guide_data_list: { + dx: message.data.dx, + dy: message.data.dy, + RaControl: message.data.RaControl, + DecControl: message.data.DecControl, + RaDistance: message.data.RaDistance, + DecDistance: message.data.DecDistance, + }, + }); + break; } - }else if (message.device_name == 'PHD2_event'){ - if (message.type == 'GuideStep'){ - // process newest guide step data - append_guide_step(message.data); + case "get_record_guide_rms": { + break; } - }else if (message.device_name == 'PHD2_response'){ - + } + } else if (message.device_name == "PHD2_event") { + // message from phd2 + console.log("phd2 event", message.message); + if (message.message == "GuideStep") { + // process newest guide step data + append_guide_step(message.data); + } else if (message.message == "get_calibration_data") { + GlobalStore.actions.ProcessDataSaveStore.setState({ + PHD2_calibration_result: message.data, + }); + } + } else if (message.device_name == "PHD2_response") { + } else if (message.device_name == "PAA") { + if (message.type == "step") { + GlobalStore.actions.PAA.paa_new_step_info_in(message); + } else if (message.instruction == "log") { + GlobalStore.actions.PAA.paa_new_log_info_in(message); } } }; - const {sendMessage, removeListener} = useEchoWebSocket(process_ws_message); + const { sendMessage, removeListener } = useEchoWebSocket(process_ws_message); // useEffect React.useEffect(() => { + load_saved_targets(); + initial_ui_setting(); + load_ui_setting(); register_send_message(sendMessage); - sendMessageRef.current = sendMessage; - }, []) + get_all_global_parameters(); + // get history guiding data + // todo fatal bug, this sendMessage is not available at this step. + setTimeout(() => { + // console.log(sendMessage); + sendMessage( + JSON.stringify({ + device_name: "phd2", + instruction: "get_guiding_data", + params: [], + }) + ); + }, 500); + // get history hfr data, todo + }, []); // 这个listener只添加一次,不能删除 - return ( - <> - ) -} + return <>; +}; /* 现在的问题是,我需要有在全局有且仅有一次注册一个websocket的listener,去对一些特殊信号进行处理。 同时,有一部分信号要去ws接口里去send message。 @@ -74,5 +113,4 @@ const WS_LISTENER_COMP: React.FC = () => { 在一个欢迎界面下写组件,别的问题没有,怎么解决仅注册这一次? */ - -export default WS_LISTENER_COMP; \ No newline at end of file +export default WS_LISTENER_COMP; diff --git a/websrc/cobalt-web/src/pages/search.tsx b/websrc/cobalt-web/src/pages/search.tsx deleted file mode 100644 index bcbd41f4..00000000 --- a/websrc/cobalt-web/src/pages/search.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React, { useState } from 'react'; -import { Container, Form, Button, Row, Col, Card } from 'react-bootstrap'; - -const SearchPage = () => { - const [searchTerm, setSearchTerm] = useState(''); - const [searchResults, setSearchResults] = useState([]); - - const handleSearch = (e) => { - e.preventDefault(); - // 发送搜索请求 - fetch(`https://jsonplaceholder.typicode.com/posts?q=${searchTerm}`) - .then((response) => response.json()) - .then((data) => setSearchResults(data)) - .catch((error) => console.log(error)); - }; - - return ( - -

Search Page

-
- - setSearchTerm(e.target.value)} - /> - - -
-
- {searchResults.length > 0 ? ( - - {searchResults.map((result) => ( - - - - {result.title} - {result.body} - - - - ))} - - ) : ( -

No results found

- )} -
- ); -}; - -export default SearchPage; diff --git a/websrc/cobalt-web/src/pages/settings.tsx b/websrc/cobalt-web/src/pages/settings.tsx deleted file mode 100644 index 4eb6cc85..00000000 --- a/websrc/cobalt-web/src/pages/settings.tsx +++ /dev/null @@ -1,299 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { Card, Button, Form } from "react-bootstrap"; -import { GearFill, ArrowUpCircleFill, Search } from "react-bootstrap-icons"; -import axios from "axios"; - -import styled from "styled-components"; - -const ScrollToTopButton = styled.div` - position: fixed; - bottom: 20px; - right: 20px; - cursor: pointer; -`; - -const ScrollToTopIcon = styled(ArrowUpCircleFill)` - font-size: 32px; -`; - -const SearchBar = styled.div` - display: flex; - align-items: center; - margin-bottom: 20px; - - & > *:not(:last-child) { - margin-right: 10px; - } -`; - -const SearchInput = styled(Form.Control)` - width: 200px; -`; - -const SearchResult = styled.div` - margin-top: 10px; -`; - -interface ConfigData { - [key: string]: { - [key: string]: { - value: string; - type: string; - validation?: { - required?: boolean; - minLength?: number; - maxLength?: number; - minValue?: number; - maxValue?: number; - pattern?: string; - errorMessage?: string; - }; - }; - }; -} - -const ConfigManager = () => { - const [configData, setConfigData] = useState({ - category1: { - key1: { - value: "default value 1", - type: "text", - }, - key2: { - value: "default value 2", - type: "number", - }, - }, - category2: { - key3: { - value: "default value 3", - type: "text", - }, - key4: { - value: "default value 4", - type: "text", - }, - }, - }); - const [showScrollToTop, setShowScrollToTop] = useState(false); - const [searchTerm, setSearchTerm] = useState(""); - const [searchResults, setSearchResults] = useState([]); - - // 从后端获取配置数据 - useEffect(() => { - axios - .get("/api/config") - .then((response) => { - setConfigData(response.data); - }) - .catch((error) => { - console.error("获取配置数据出错:", error); - }); - - // 监听页面滚动事件 - const handleScroll = () => { - if (window.scrollY > 200) { - setShowScrollToTop(true); - } else { - setShowScrollToTop(false); - } - }; - - window.addEventListener("scroll", handleScroll); - return () => { - window.removeEventListener("scroll", handleScroll); - }; - }, []); - - const handleSetConfig = (category: string, name: string, value: string) => { - axios - .post("/api/config", { - category, - name, - value, - }) - .then((response) => { - console.log(`设置配置成功: ${response.data}`); - }) - .catch((error) => { - console.error(`设置配置失败: ${error}`); - }); - }; - - const handleInputChange = ( - category: string, - name: string, - e: React.ChangeEvent - ) => { - const value = e.target.value; - setConfigData((prevData) => ({ - ...prevData, - [category]: { - ...prevData[category], - [name]: { - ...prevData[category][name], - value, - }, - }, - })); - }; - - const validateInput = ( - value: string, - validation?: ConfigData[keyof ConfigData][keyof ConfigData[keyof ConfigData]]["validation"] - ) => { - if (validation) { - if (validation.required && value.trim() === "") { - return validation.errorMessage || "该字段不能为空"; - } - if (validation.minLength && value.length < validation.minLength) { - return ( - validation.errorMessage || - `该字段长度不能小于 ${validation.minLength}` - ); - } - if (validation.maxLength && value.length > validation.maxLength) { - return ( - validation.errorMessage || - `该字段长度不能大于 ${validation.maxLength}` - ); - } - if (validation.minValue && Number(value) < validation.minValue) { - return ( - validation.errorMessage || `该字段值不能小于 ${validation.minValue}` - ); - } - if (validation.maxValue && Number(value) > validation.maxValue) { - return ( - validation.errorMessage || `该字段值不能大于 ${validation.maxValue}` - ); - } - if (validation.pattern && !new RegExp(validation.pattern).test(value)) { - return validation.errorMessage || "该字段格式不正确"; - } - } - return ""; - }; - - const renderInputField = ( - category: string, - name: string, - config: ConfigData[keyof ConfigData][keyof ConfigData[keyof ConfigData]] - ) => { - const { value, type, validation } = config; - const error = validateInput(value, validation); - - return ( -
{ - if (searchTerm && el && el.textContent?.includes(searchTerm)) { - setSearchResults((prevResults) => [ - ...prevResults, - el as HTMLDivElement, - ]); - } - }} - > -
{name}
-
-
{value}
- handleInputChange(category, name, e)} - /> - {error &&
{error}
} - -
-
- ); - }; - - const handleScrollToTop = () => { - window.scrollTo({ - top: 0, - behavior: "smooth", - }); - }; - - const handleSearch = (e: React.ChangeEvent) => { - setSearchTerm(e.target.value); - setSearchResults([]); - }; - - useEffect(() => { - if (searchTerm) { - setSearchResults([]); - // 搜索匹配结果 - const matchingResults = Array.from( - document.querySelectorAll(".config-name") - ).filter((el) => el.textContent?.includes(searchTerm)); - setSearchResults(matchingResults); - // 将第一个匹配项滚动到可视区域内 - if (matchingResults.length > 0) { - matchingResults[0].scrollIntoView({ - behavior: "smooth", - block: "center", - }); - } - } - }, [searchTerm]); - - return ( - <> -

配置管理

- - - - - {searchResults.length > 0 && ( - - 共找到 {searchResults.length} 个匹配结果: - {searchResults.map((el, index) => ( -
- el.scrollIntoView({ behavior: "smooth", block: "center" }) - } - > - {el.textContent} -
- ))} -
- )} - {Object.entries(configData).map(([category, configs]) => ( - - - {category} - - - {Object.entries(configs).map(([name, config]) => - renderInputField(category, name, config) - )} - - - ))} - {showScrollToTop && ( - - - - )} - - ); -}; - -export default ConfigManager; diff --git a/websrc/cobalt-web/src/pages/skymap/framing.css b/websrc/cobalt-web/src/pages/skymap/framing.css new file mode 100644 index 00000000..82a2d0bc --- /dev/null +++ b/websrc/cobalt-web/src/pages/skymap/framing.css @@ -0,0 +1,7 @@ +.transparent_paper { + background-color: transparent; +} +.framing-root { + width: 100vw; + height: calc(~"100vh - 32px"); +} diff --git a/websrc/cobalt-web/src/pages/skymap/image_framing.tsx b/websrc/cobalt-web/src/pages/skymap/image_framing.tsx index 245934da..a151e77f 100644 --- a/websrc/cobalt-web/src/pages/skymap/image_framing.tsx +++ b/websrc/cobalt-web/src/pages/skymap/image_framing.tsx @@ -1,31 +1,26 @@ -import React, { useState, useEffect } from "react"; -import { - Card, - Button, - Container, - Row, - Col, - InputGroup, - Form, -} from "react-bootstrap"; -import { Search } from "react-bootstrap-icons"; +import * as React from "react"; +import { Alert, Button, Stack } from "react-bootstrap"; +import { Card } from "react-bootstrap"; +import "./framing.css"; +import { GlobalStore } from "../../store/globalStore"; +import { Search, Gear } from "react-bootstrap-icons"; +import AladinLiteView from "../../components/skymap/aladin"; import { useImmer } from "use-immer"; +import * as AXIOSOF from "../..//services/object_finding_api"; -import AladinLiteView from "../../components/skymap/aladin_wrapper"; -import * as AXIOSOF from "../../services/object_finding_api"; import FOVSettingDialog from "../../components/skymap/fov_dialog"; -import ObjectManagementDialog from "./object_manager_dialog"; +import ObjectManagementDialog from "./object_manager"; import ObjectSearchDialog from "./object_search_dialog"; -import { GlobalStore } from "../../store/globalStore"; -import { TransparentPaper } from "./style"; +const ImageFraming: React.FC = () => { + // const theme = useTheme(); -const ImageFraming = () => { - const [target_ra, set_target_ra] = useState(0); - const [target_dec, set_target_dec] = useState(0); - const [screen_ra, set_screen_ra] = useState(0); - const [screen_dec, set_screen_dec] = useState(0); - const [camera_rotation, set_camera_rotation] = useState(0); + // comp data + const [target_ra, set_target_ra] = React.useState(0); + const [target_dec, set_target_dec] = React.useState(0); + const [screen_ra, set_screen_ra] = React.useState(0); + const [screen_dec, set_screen_dec] = React.useState(0); + const [camera_rotation, set_camera_rotation] = React.useState(0); const [fov_data, update_fov_data] = useImmer({ x_pixels: 0, x_pixel_size: 0, @@ -33,15 +28,20 @@ const ImageFraming = () => { y_pixel_size: 0, focal_length: 0, }); - const [show_span, set_show_span] = useState(false); - const [open_fov_dialog, set_open_fov_dialog] = useState(0); - const [open_search_dialog, set_open_search_dialog] = useState(0); - const [open_manage_dialog, set_open_manage_dialog] = useState(0); + const [show_span, set_show_span] = React.useState(false); + const [open_fov_dialog, set_open_fov_dialog] = React.useState(0); + const [open_search_dialog, set_open_search_dialog] = React.useState(0); + const [open_manage_dialog, set_open_manage_dialog] = React.useState(0); - const [fov_points, set_fov_points] = useState([]); - const [fov_x, set_fov_x] = useState(0.25); - const [fov_y, set_fov_y] = useState(0.25); - const [aladin_show_fov, set_aladin_show_fov] = useState(0.5); + // other data + const [fov_points, set_fov_points] = React.useState< + Array< + [[number, number], [number, number], [number, number], [number, number]] + > + >([]); + const [fov_x, set_fov_x] = React.useState(0.25); + const [fov_y, set_fov_y] = React.useState(0.25); + const [aladin_show_fov, set_aladin_show_fov] = React.useState(0.5); // store related const target_store = GlobalStore.useAppState( @@ -56,11 +56,11 @@ const ImageFraming = () => { GlobalStore.actions.TargetListStore.fetch_twilight_data; // todo actions need to be initialed if necessary - const on_new_ra_dec_input = (new_ra, new_dec) => { + const on_new_ra_dec_input = (new_ra: number, new_dec: number) => { + // console.log('got new radec', new_ra, new_dec); set_screen_ra(new_ra); set_screen_dec(new_dec); }; - const refresh_camera_telescope_data = () => { let camera_info = global_parameter.global_parameter.camera_info; let telescope_info = global_parameter.global_parameter.telescope_info; @@ -80,15 +80,13 @@ const ImageFraming = () => { } }); }; - const on_click_reset_with_current_center = () => { set_target_ra(screen_ra); set_target_dec(screen_dec); calculate_fov_points(); }; - - const post_for_one_single_fov_rect = async (ra, dec) => { - let fov_request = { + const post_for_one_single_fov_rect = async (ra: number, dec: number) => { + let fov_request: IOFRequestFOVpoints = { x_pixels: fov_data.x_pixels, x_pixel_size: fov_data.x_pixel_size, y_pixels: fov_data.y_pixels, @@ -101,6 +99,7 @@ const ImageFraming = () => { try { const fov_response = await AXIOSOF.getFovPointsOfRect(fov_request); if (fov_response.success) { + // console.log('successfully got fov points', fov_response.data); set_fov_points([fov_response.data]); } else { console.log(fov_response.message); @@ -110,9 +109,7 @@ const ImageFraming = () => { return null; } }; - const calculate_tile_fov_points = () => {}; - const calculate_fov_points = () => { set_fov_points([]); if (fov_data.focal_length == 0) { @@ -124,7 +121,6 @@ const ImageFraming = () => { post_for_one_single_fov_rect(target_ra, target_dec); } }; - const update_target_center_points = () => { if (target_store.current_focus_index != null) { target_store.all_saved_targets[target_store.current_focus_index].ra = @@ -136,9 +132,8 @@ const ImageFraming = () => { ].rotation = camera_rotation; } }; - const add_current_as_new_target = () => { - let to_add_object = { + let to_add_object: IDSOFramingObjectInfo = { name: "-", ra: target_ra, dec: target_dec, @@ -146,184 +141,245 @@ const ImageFraming = () => { flag: "", tag: "", target_type: "", - bmag: 0, - vmag: 0, size: 0, checked: false, }; set_focus_target_to_store(to_add_object); }; - const start_goto_and_focus_target = () => { // TODO, write this code later! }; - useEffect(() => { + // on mount + React.useEffect(() => { + // read x pixel and y pixel data from global parameter + // console.log('image framing got ', props.tab_value); update_twilight_data(); refresh_camera_telescope_data(); }, []); - - useEffect(() => { + // camera and telescope info update to string + React.useEffect(() => { let fov_x = ((57.3 / fov_data.focal_length) * fov_data.x_pixels * fov_data.x_pixel_size) / - 3600; + 1000; let fov_y = ((57.3 / fov_data.focal_length) * fov_data.y_pixels * fov_data.y_pixel_size) / - 3600; + 1000; set_fov_x(fov_x); set_fov_y(fov_y); + if (fov_x > fov_y) { + if (2 * fov_x < 4) set_aladin_show_fov(4); + else set_aladin_show_fov(2 * fov_x); + } else { + if (2 * fov_y < 4) set_aladin_show_fov(4); + else set_aladin_show_fov(2 * fov_y); + } + calculate_fov_points(); }, [fov_data]); + // on target ra dec change + React.useEffect(() => { + calculate_fov_points(); + }, [target_ra, target_dec, camera_rotation]); + + React.useEffect(() => { + // console.log('need focus change', target_store.need_focus); + if (target_store.need_focus) { + set_target_ra(target_store.current_focus_target.ra); + set_target_dec(target_store.current_focus_target.dec); + set_screen_ra(target_store.current_focus_target.ra); + set_screen_dec(target_store.current_focus_target.dec); + } + target_store.need_focus = false; + }, [target_store.need_focus]); return ( - - Image Framing +
+
+ +
+
+ + + + 当前目标: {target_store.current_focus_target.name} + + + Ra: {target_ra.toFixed(7)} + + + Dec: {target_dec.toFixed(7)} + + + + + + +
+
+ + + + + + +

+ +
+
- - - - - - - - - - -
-
-
- Target Center -
- - RA: - set_target_ra(e.target.value)} - /> - - - Dec: - set_target_dec(e.target.value)} - /> - -
- -
-
- Field of View (FOV) -
- - X: - set_fov_x(e.target.value)} - /> - - - Y: - set_fov_y(e.target.value)} - /> - -
-
- - -
- {open_fov_dialog && ( - { - update_fov_data((draft) => { - console.log( - "in framing, got fov change", - new_fov_data - ); - draft.focal_length = new_fov_data.focal_length; - draft.x_pixels = new_fov_data.x_pixels; - draft.x_pixel_size = new_fov_data.x_pixel_size; - draft.y_pixels = new_fov_data.y_pixels; - draft.y_pixel_size = new_fov_data.y_pixel_size; - }); - }} - on_rotation_change={function (rotation: number): void { - throw new Error("Function not implemented."); - }} - /> - )} -
-
- Objects - - - {open_search_dialog && ( - - )} - {open_manage_dialog && ( - - )} -
-
-
- -
-
- + + 视场中心点坐标: Ra: {screen_ra.toFixed(7)};Dec: {screen_dec.toFixed(7)} + + {/* 修正左上角aladin关不掉的中心点坐标 */} +
+ { + update_fov_data((draft) => { + console.log("in framing, got fov change", new_fov_data); + draft.focal_length = new_fov_data.focal_length; + draft.x_pixels = new_fov_data.x_pixels; + draft.x_pixel_size = new_fov_data.x_pixel_size; + draft.y_pixels = new_fov_data.y_pixels; + draft.y_pixel_size = new_fov_data.y_pixel_size; + }); + }} + on_rotation_change={(new_rotation) => { + set_camera_rotation(new_rotation); + }} + /> + + +
); }; diff --git a/websrc/cobalt-web/src/pages/skymap/index.tsx b/websrc/cobalt-web/src/pages/skymap/index.tsx index c2155206..0fd0d6f8 100644 --- a/websrc/cobalt-web/src/pages/skymap/index.tsx +++ b/websrc/cobalt-web/src/pages/skymap/index.tsx @@ -1,50 +1,39 @@ -import React from "react"; +import * as React from "react"; +import { Card } from "react-bootstrap"; +import { GlobalStore } from "../..//store/globalStore"; import { Tabs, Tab } from "react-bootstrap"; -import { - Search, - Grid3x3, - ClipboardData, - Code -} from "react-bootstrap-icons"; -import ObjectSearch from "./object_search"; + +//import ObjectSearch from './ObjectSearch'; import ImageFraming from "./image_framing"; -import ObjectManagement from "./object_manager"; -//import TargetTestPage from './TargetTestPage'; +//import ObjectManagement from './ObjectManagement'; +///import TargetTestPage from './TargetTestPage'; + +const ObjectFinding: React.FC = () => { + // reload all global parameter first. + const get_all_global_parameters = + GlobalStore.actions.GlobalParameterStore.get_all_paramters; + React.useEffect(() => { + get_all_global_parameters(); + }, []); -const ObjectFinding = () => { return ( - - - 目标搜索 - - } - > - - - - 构图助手 - - } - > - - - - 拍摄目标管理 - - } - > - - - + + + + // + // + // + // + // + // + // + // + // + // + // + // + // + // ); }; diff --git a/websrc/cobalt-web/src/pages/skymap/object_manager.tsx b/websrc/cobalt-web/src/pages/skymap/object_manager.tsx index 27dba993..afb585ee 100644 --- a/websrc/cobalt-web/src/pages/skymap/object_manager.tsx +++ b/websrc/cobalt-web/src/pages/skymap/object_manager.tsx @@ -1,22 +1,19 @@ -import React from "react"; - +import * as React from "react"; +import { GlobalStore } from "../../store/globalStore"; import { - Container, - Row, - Col, Card, Button, Alert, - Form, + Container, + Row, + Col, + FormControl, InputGroup, - Modal, } from "react-bootstrap"; -import { Trash, PencilSquare } from "react-bootstrap-icons"; - -import { useImmer } from "use-immer"; - -import { GlobalStore } from "../../store/globalStore"; import * as AXIOSOF from "../../services/object_finding_api"; +import Modal from "react-bootstrap/Modal"; +import Form from "react-bootstrap/Form"; + import TargetSmallCardHori from "../../components/skymap/target_smallv2"; import TargetSmallCard from "../../components/skymap/target_small"; @@ -32,6 +29,7 @@ const ObjectManagement: React.FC = (props) => { const clear_all_checked = GlobalStore.actions.TargetListStore.clear_all_checked; const remove_one_target = GlobalStore.actions.TargetListStore.remove_target; + const save_all_targets = GlobalStore.actions.TargetListStore.save_all_targets; const change_saved_focus_target = GlobalStore.actions.TargetListStore.change_saved_focus_target; const store_target_set_flag = @@ -45,8 +43,6 @@ const ObjectManagement: React.FC = (props) => { const [current_checked, set_current_checked] = React.useState( null ); - // const [dso_detailed_list, update_dso_detailed_list] = useImmer>([]); - // const [dso_detail_updated, update_dso_detail_updated] = useImmer>([]); // extra input data const [rename_text_dialog, set_rename_text_dialog] = React.useState(false); const [rename_text, set_rename_text] = React.useState(""); @@ -82,33 +78,6 @@ const ObjectManagement: React.FC = (props) => { } } }; - // const one_dso_card_need_update = () => { - // if (current_checked != null){ - // let new_card_info = dso_detailed_list[current_checked]; - // if (new_card_info != null){ - // let new_name = target_store.all_saved_targets[current_checked].name; - // update_dso_detailed_list((draft) => { - // draft[current_checked].name = new_name; - // }) - // } - // } - // }; - // const construct_all_dso_card_data = () => { - // let new_dso_detail_list = Array(target_store.all_saved_target_store.all_saved_targets.length); - // let new_dos_update_list = Array(target_store.all_saved_target_store.all_saved_targets.length); - // for (let i = 0; i < new_dos_update_list.length; i++) { - // new_dos_update_list[i] = false; - // } - // update_dso_detailed_list((draft) => { - // draft=new_dso_detail_list - // }); - // update_dso_detail_updated((draft) => { - // draft=new_dos_update_list; - // }); - // for( let i = 0; i < target_store.all_saved_target_store.all_saved_targets.length; i++){ - // construct_framing_info2card_info(target_store.all_saved_targets[i], i); - // } - // }; const rename_selected_target = () => { if (current_checked != null) { set_rename_text(""); @@ -121,10 +90,9 @@ const ObjectManagement: React.FC = (props) => { }; const on_add_tag_clicked = () => { if (current_checked != null) { + set_tag_dialog(true); } else { // pop up a warning - set_popup_text("没有选中任何待拍摄目标!"); - set_popup_dialog(true); } }; const on_add_flag_clicked = () => { @@ -141,6 +109,7 @@ const ObjectManagement: React.FC = (props) => { if (current_checked != null) { if (target_store.all_saved_targets[current_checked].checked) { remove_one_target(current_checked); + save_all_targets(); } else { console.log( current_checked, @@ -154,44 +123,12 @@ const ObjectManagement: React.FC = (props) => { set_popup_dialog(true); } }; - // const construct_framing_info2card_info = async (card_info: IDSOFramingObjectInfo, index: number) => { - // try{ - // const new_curve_data = await AXIOSOF.getTargetALtCurveOnly(card_info.ra, card_info.dec); - // if (new_curve_data.success){ - // let new_target_frame_info: IDSOObjectDetailedInfo = { - // name: card_info.name, - // ra: card_info.ra, - // dec: card_info.dec, - // target_type: card_info.target_type, - // bmag: card_info.bmag, - // vmag: card_info.vmag, - // size: card_info.size, - // moon_distance: new_curve_data.data.moon_distance, - // altitude: new_curve_data.data.altitude, - // } - // update_dso_detail_updated((draft) => { - // draft[index] = true; - // }); - // update_dso_detailed_list((draft) => { - // draft[index] = new_target_frame_info; - // }) - // }else{ - // return null; - // } - // } - // catch (err) { - // return null; - // } - // } const handle_rename_close = (save: boolean) => { if (rename_text != "" && current_checked != null && save) { store_target_rename({ index: current_checked, update_string: rename_text, }); - // update_dso_detailed_list((draft) => { - // draft[current_checked].name = rename_text; - // }) } set_rename_text(""); set_rename_text_dialog(false); @@ -206,7 +143,9 @@ const ObjectManagement: React.FC = (props) => { set_flag_text(""); set_flag_dialog(false); }; - const handle_tag_selection = (event: SelectChangeEvent) => { + const handle_tag_selection = ( + event: React.ChangeEvent + ) => { set_tag_value(event.target.value); }; const handle_tag_close = () => { @@ -221,100 +160,92 @@ const ObjectManagement: React.FC = (props) => { }; return ( - - - - {/* objects card */} - - {target_store.all_saved_targets.length === 0 ? ( - 拍摄列表中还没有目标 - ) : ( - target_store.all_saved_targets.map((one_target_info, index) => ( - - - - - - )) - )} - - - - {/* controls */} - - - 操作选中的目标 - - - - - - - - - - - + <> + + + + {/* objects card */} + + {target_store.all_saved_targets.length == 0 ? ( + + 拍摄列表中还没有目标 + + ) : ( + target_store.all_saved_targets.map((one_target_info, index) => { + return ( + + + + ); + }) + )} + + + + {/* controls */} + + +
操作选中的目标
+
+ + {" "} + +
+
+ {" "} + +
+
+ +
+
+
+ + + {" "} + + + + +
+
- - -
- - 根据tag筛选 - - - {/* Render tag options */} - - - - 根据flag筛选 - - - - - -
-
-
- -
- - {/* Rename Text Dialog */} handle_rename_close(false)} @@ -323,87 +254,75 @@ const ObjectManagement: React.FC = (props) => { 重命名该目标 -
- - 重命名为 - set_rename_text(event.target.value)} - /> - -
+ { + set_rename_text(event.target.value); + }} + />
- +
- {/* Flag Dialog */} handle_flag_close(false)}> 修改flag -
- - Flag - set_flag_text(event.target.value)} - /> - -
+ { + set_flag_text(event.target.value); + }} + />
- +
- {/* Tag Dialog */} - + handle_tag_close()}> 修改目标tag -
- - 目标tag - set_tag_value(event.target.value)} - > - - {/* Render tag options */} - - -
+ + + {target_store.all_tags.map((one_tag, index) => { + return ( + + ); + })} +
-
- {/* Popup Dialog */} { @@ -413,20 +332,19 @@ const ObjectManagement: React.FC = (props) => { 提示 - {/* Popup message content */} + {popup_text} -
+ ); }; diff --git a/websrc/cobalt-web/src/pages/skymap/object_manager_dialog.tsx b/websrc/cobalt-web/src/pages/skymap/object_manager_dialog.tsx index 8affe652..45644905 100644 --- a/websrc/cobalt-web/src/pages/skymap/object_manager_dialog.tsx +++ b/websrc/cobalt-web/src/pages/skymap/object_manager_dialog.tsx @@ -1,10 +1,16 @@ import React, { useState, useEffect } from "react"; import { Modal, Button } from "react-bootstrap"; import ObjectManagement from "./object_manager"; +import { XLg } from "react-bootstrap-icons"; import { GlobalStore } from "../../store/globalStore"; -import { XCircle } from "react-bootstrap-icons"; -const ObjectManagementDialog = (props) => { +interface ObjectManagementDialogProps { + open_dialog: number; +} + +const ObjectManagementDialog: React.FC = ( + props +) => { const [open, set_open] = useState(false); const clear_all_checked = GlobalStore.actions.TargetListStore.clear_all_checked; @@ -23,12 +29,16 @@ const ObjectManagementDialog = (props) => { return ( <> handleClose()} size="xl" fullscreen> + + 待拍摄目标管理 + - diff --git a/websrc/cobalt-web/src/pages/skymap/object_search.tsx b/websrc/cobalt-web/src/pages/skymap/object_search.tsx index 6a81389c..df894579 100644 --- a/websrc/cobalt-web/src/pages/skymap/object_search.tsx +++ b/websrc/cobalt-web/src/pages/skymap/object_search.tsx @@ -1,71 +1,65 @@ -import React, { useState } from "react"; -import { Form, Alert, Card } from "react-bootstrap"; -import { FilesAlt, ChevronDown, ChevronUp } from "react-bootstrap-icons"; -import styled from "styled-components"; +import * as React from "react"; +import { + Container, + Row, + Col, + Form, + Button, + Collapse, + ListGroup, + Alert, +} from "react-bootstrap"; +import { useImmer } from "use-immer"; +import { Filter } from "react-bootstrap-icons"; +import * as AXIOSOF from "../../services/object_finding_api"; import TargetSmallCardHori from "../../components/skymap/target_smallv2"; import TargetSmallCard from "../../components/skymap/target_small"; -import * as AXIOSOF from "../../services/object_finding_api"; - interface ObjectSearchProps { on_choice_maken: (() => void) | null; } -const SearchContainer = styled.div` - display: flex; - flex-direction: row; -`; - -const SearchInputContainer = styled.div` - flex: 1; - margin-right: 10px; -`; - -const FilterContainer = styled(Card)` - flex: 1; -`; - -const FilterHeader = styled(Card.Header)` - cursor: pointer; -`; - const ObjectSearch: React.FC = (props) => { // ui control - const [expand_filter, set_expand_filter] = useState(false); - const [alert_type, set_alert_type] = useState("info"); + const [expand_filter, set_expand_filter] = React.useState(false); + const [alert_variant, set_alert_variant] = React.useState< + "info" | "warning" | "danger" + >("info"); const [alert_text, set_alert_text] = - useState("请在左侧输入框输入需要搜索的信息"); + React.useState("请在左侧输入框输入需要搜索的信息"); // data - const [to_search_text, set_to_search_text] = useState("-"); - const [found_target_result, set_found_target_result] = useState< + const [to_search_text, set_to_search_text] = React.useState("-"); + const [found_target_result, set_found_target_result] = React.useState< Array >([]); - const [filter_settings, update_filter_settings] = useState({ + const [filter_settings, update_filter_settings] = useImmer({ angular_size: 1, // in arc degree }); const handleFilterExpandClick = () => { set_expand_filter(!expand_filter); }; + const handleSearchTextOnBlur = async () => { if (to_search_text === "-") { return null; } set_found_target_result([]); - set_alert_type("info"); + set_alert_variant("info"); set_alert_text("查询中"); try { const found_targets = await AXIOSOF.findTargetByName(to_search_text); + console.log(found_targets); if (found_targets.success) { if (found_targets.data.length > 0) { set_found_target_result(found_targets.data); } else { - set_alert_type("warning"); + set_alert_variant("warning"); set_alert_text("没有找到相关目标"); } } else { - set_alert_type("error"); + set_alert_variant("danger"); set_alert_text("没有找到相关目标!!!"); } } catch (err) { @@ -74,53 +68,61 @@ const ObjectSearch: React.FC = (props) => { }; return ( - - - {/* search and filter part */} - - { - set_to_search_text(event.target.value); - }} - className="mt-1" - /> - - - - - - 目标搜索筛选 - {expand_filter ? : } - - {expand_filter &&
开发中
}
-
-
- {/* display cards part */} - {found_target_result.length === 0 ? ( - {alert_text} - ) : ( -
- {found_target_result.map((one_dso_target_info, index) => { - return ( -
- -
- ); - })} + + + + {/* search and filter part */} + + set_to_search_text(event.target.value)} + className="mt-3" + /> + + + + + 高级搜索 + {expand_filter ? ( + + ) : ( + + )} + + + +
开发中
+
+
+
+ + + {/* display cards part */} +
+ {found_target_result.length === 0 ? ( + {alert_text} + ) : ( + + {found_target_result.map((one_dso_target_info, index) => { + return ( + + + + ); + })} + + )}
- )} -
- + + + ); }; diff --git a/websrc/cobalt-web/src/pages/skymap/object_search_dialog.tsx b/websrc/cobalt-web/src/pages/skymap/object_search_dialog.tsx index 65ee511d..b6bdc86e 100644 --- a/websrc/cobalt-web/src/pages/skymap/object_search_dialog.tsx +++ b/websrc/cobalt-web/src/pages/skymap/object_search_dialog.tsx @@ -1,41 +1,58 @@ -import React from "react"; +import React, { useState, useEffect, useRef } from "react"; import { Modal, Button } from "react-bootstrap"; import ObjectSearch from "./object_search"; import { XCircle } from "react-bootstrap-icons"; +import { ArrowUpCircle } from "react-bootstrap-icons"; interface ObjectSearchDialogProps { open_dialog: number; } const ObjectSearchDialog: React.FC = (props) => { - const [open, set_open] = React.useState(false); + const [open, setOpen] = useState(false); + const dialogRef = useRef(null); - React.useEffect(() => { + useEffect(() => { if (props.open_dialog > 0) { - set_open(true); + setOpen(true); } }, [props.open_dialog]); const handleClose = () => { - set_open(false); + setOpen(false); }; return ( <> - + handleClose()} fullscreen> - 标题 + 目标搜索 - + - + {open && ( + { + if (dialogRef.current) dialogRef.current.scrollTop = 0; + }} + /> + )} ); }; diff --git a/websrc/cobalt-web/src/pages/websocket.tsx b/websrc/cobalt-web/src/pages/websocket.tsx deleted file mode 100644 index 3685d035..00000000 --- a/websrc/cobalt-web/src/pages/websocket.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import React, { - useState, - useEffect, - useRef, - useCallback, - createContext, - useContext, -} from "react"; -import WebSocket from "isomorphic-ws"; -import { - Container, - Row, - Col, - Form, - Button, - ListGroup, - Alert, -} from "react-bootstrap"; - -// 创建 WebSocketContext 作为全局的 WebSocket 连接上下文 -const WebSocketContext = createContext(""); - -function WebSocketClient() { - const [serverUrl, setServerUrl] = useState("ws://localhost:3000"); - const [messages, setMessages] = useState([]); - const [inputMessage, setInputMessage] = useState(""); - const [connected, setConnected] = useState(false); - const [socket, setSocket] = useState(null); - const [error, setError] = useState(""); - - // 使用 useRef 来保存 socket 实例 - const socketRef = useRef(null); - - // 使用 useCallback 对 connectToServer 和 disconnectFromServer 进行优化 - const connectToServer = useCallback(() => { - const newSocket = new WebSocket(serverUrl); - // 设置超时时间为10秒钟 - const timeout = setTimeout(() => { - console.error("Connection timeout"); - setError("Connection timeout"); - newSocket.close(); - }, 10000); - newSocket.addEventListener("open", () => { - clearTimeout(timeout); // 连接建立后清除超时定时器 - }); - socketRef.current = newSocket; - }, [serverUrl]); - - const disconnectFromServer = useCallback(() => { - if (socketRef.current) { - socketRef.current.close(); - socketRef.current = null; - } - }, []); - - // 使用 useEffect 监听 socket 状态变化 - useEffect(() => { - const socket = socketRef.current; - if (socket) { - socket.addEventListener("open", () => { - console.log("Connected"); - setError(null); - }); - socket.addEventListener("close", () => { - console.log("Disconnected"); - setError(null); - // 断线重连 - setTimeout(connectToServer, 5000); - }); - socket.addEventListener("message", (event) => { - const message = event.data; - setMessages((prevMessages) => [...prevMessages, message]); - // 进行消息过滤 - if (message.includes("important")) { - // 发送事件通知 - sendNotification("Important message received"); - } - }); - socket.addEventListener("error", (event) => { - console.error("WebSocket error:", event); - setError("WebSocket error"); - disconnectFromServer(); - }); - } - }, [connectToServer, disconnectFromServer]); - - const sendMessage = (event) => { - event.preventDefault(); - if (socketRef.current) { - socketRef.current.send(inputMessage); - setInputMessage(""); - } - }; - - function sendNotification(message) { - // 发送事件通知给用户 - console.log("Notification:", message); - } - - // ... 其他代码保持不变 - - return ( - - - - -
- - setServerUrl(e.target.value)} - /> - - - -
- -
- {error && ( - - - {error} - - - )} - - - - {messages.map((message, index) => ( - {message} - ))} - - - - - -
- - setInputMessage(e.target.value)} - disabled={!connected} - /> - - -
- -
-
-
- ); -} - -// 在其他组件中使用全局 WebSocket 连接状态 -function SomeOtherComponent() { - const { connectToServer, disconnectFromServer } = - useContext(WebSocketContext); - // ... -} - -export default WebSocketClient; diff --git a/websrc/cobalt-web/src/routes/root.tsx b/websrc/cobalt-web/src/routes/root.tsx index 8604392b..6f47d573 100644 --- a/websrc/cobalt-web/src/routes/root.tsx +++ b/websrc/cobalt-web/src/routes/root.tsx @@ -17,9 +17,7 @@ import { import Welcome from "../pages/home/animation"; import Home from "../pages/home"; import ServerSearch from "../pages/server"; -import DeviceConnection from "../pages/connect"; import DeviceControlPanelPage from "../pages/device"; -import ConfigManager from "../pages/settings"; import ModuleManager from "../pages/module"; import Help from "../pages/about/help"; import AboutPage from "../pages/about"; @@ -30,9 +28,9 @@ import Connect from "../pages/connection/index"; import Helper from "../pages/helper"; import ErrorPage from "../pages/error"; import ObjectFinding from "../pages/skymap"; -import Dashboard from "../pages/dashboard/index"; import PAAIndexPage from "../pages/sequence" -import Test from "../pages/test/test"; +import Console from "../pages/dashboard2/index"; +//import Test from "../pages/test/test"; interface IndexRouteObjectPlus extends IndexRouteObject { title?: string; @@ -61,25 +59,12 @@ export const routesConfig: RouteObjectPlus[] = [ icon: , errorElement: }, - { - path: "/dashboard", - element: , - title: "Dashboard", - icon: , - errorElement: - }, { path: "/server", element: , title: "服务器连接", icon: , }, - { - path: "/device_connect", - element: , - title: "设备连接", - icon: , - }, { path: "/device", element: , @@ -92,12 +77,6 @@ export const routesConfig: RouteObjectPlus[] = [ title: "连接", icon: , }, - { - path: "/settings", - element: , - title: "配置管理", - icon: , - }, { path : "/config", element: , @@ -153,11 +132,17 @@ export const routesConfig: RouteObjectPlus[] = [ icon: , }, { - path: "/test", - element: , - title: "测试", - icon: , - } + path: "/console", + element: , + title: "控制台", + icon: , + }, + //{ + // path: "/test", + // element: , + // title: "测试", + // icon: , + //} ]; const Route = () => { diff --git a/websrc/cobalt-web/src/store/GlobalParameterStore.tsx b/websrc/cobalt-web/src/store/GlobalParameterStore.tsx index 856356b2..41605b09 100644 --- a/websrc/cobalt-web/src/store/GlobalParameterStore.tsx +++ b/websrc/cobalt-web/src/store/GlobalParameterStore.tsx @@ -22,6 +22,16 @@ export interface IGLobalParametersModels { IGLobalParametersModels, IGPFilterSelection[] >; + + // for phone side local storage + ui_setting: IGPManualHelpSetting; + initial_ui_setting: Action; + load_ui_setting: Action; + save_ui_setting: Action; + set_ui_setting: Action< + IGLobalParametersModels, + Partial + >; } export const GlobalParameterStore = (): IGLobalParametersModels => ({ @@ -136,4 +146,32 @@ export const GlobalParameterStore = (): IGLobalParametersModels => ({ return ret_struct; } }), + + // ui setting part + ui_setting: { + show_three_point_help: true, + }, + initial_ui_setting: action((state) => { + state.ui_setting = { + show_three_point_help: true, + }; + }), + load_ui_setting: action((state) => { + let loaded_ui_setting_string = localStorage.getItem("UISetting"); + if (loaded_ui_setting_string != null) { + let loaded_ui_setting = JSON.parse( + loaded_ui_setting_string + ) as Partial; + state.ui_setting = Object.assign({}, state.ui_setting, { + ...loaded_ui_setting, + }); + } + }), + save_ui_setting: action((state) => { + localStorage.setItem("UISetting", JSON.stringify(state.ui_setting)); + }), + set_ui_setting: action((state, payload) => { + state.ui_setting = Object.assign({}, state.ui_setting, { ...payload }); + localStorage.setItem("UISetting", JSON.stringify(state.ui_setting)); + }), }); diff --git a/websrc/cobalt-web/src/store/PAAModel.ts b/websrc/cobalt-web/src/store/PAAModel.ts index cbcbd1c9..5bf5aff7 100644 --- a/websrc/cobalt-web/src/store/PAAModel.ts +++ b/websrc/cobalt-web/src/store/PAAModel.ts @@ -13,27 +13,51 @@ import { } from "../services/PAA"; import { Action, action, thunk, Thunk } from "easy-peasy"; -interface script_info { - file_name: string; - modified_time: string; -} +// TODO very big one +// 这里要对store的功能做个总管理。整个paa执行数据都在store里,那么前段这里只做调用和获取store的信息 + +// interface step_info{ +// device: string, +// id: number, +// params: { +// count?: number, +// repeat?: number, +// } +// instruction: string, +// children?: (step_info)[], +// after_loop?: [], +// name?: string, +// ra?: number, +// dec?: number, +// } + export interface PAAModel { // 变量 setting_mode: number; - get_object: boolean; // 脚本信息 - all_script_info: (never | script_info)[]; - + all_script_info: PAAScriptInfo[]; // 内容信息 - current_script_data: []; + all_step_data: PAABaseStepsInfo[]; + this_script_type: string; + this_script_setting: any; + + // 目前在执行的脚本 + current_step: null | PAAlog_info; + // 目前执行的脚本编号 + current_step_index: number; + current_step_children_index: number; + current_step_children_max_index: number; + // 对应的script_name等信息 - current_script_info: script_info; + current_script_info: PAAScriptInfo; + + all_logs_info: PAAlog_info[]; // 所有Post请求 post_start_PAA: Thunk; post_stop_PAA: Thunk; - post_load_script: Thunk; + post_load_script: Thunk; post_save_script: Thunk; post_update_script: Thunk; post_delete_script: Thunk; @@ -45,116 +69,283 @@ export interface PAAModel { get_current_script: Thunk; setState: Action>; + + add_paa_logs: Action; + change_paa_step: Action; + + // modified by gao + can_start_paa: boolean; // 表示是否具备启动paa的状态。 + paa_in_progress: boolean; // 表示是否有一个paa进程在运行 + + // 如果有相机在单独拍摄,但是没有paa在运行,can start paa也会是false。也就是说,即使没有paa在运行,也存在可能没法启动paa。 + check_PAA_start_status: Thunk; // 这个函数就是用来专门刷新上面两个边练改的。 + change_paa_loop_step: Action; + change_paa_step_async: Thunk; + // about paa step change + current_step_id: string; + paa_new_step_info_in: Action; + paa_new_log_info_in: Action; } export const getPAAModel = (): PAAModel => ({ setting_mode: 0, - get_object: false, all_script_info: [], - current_script_data: [], + all_step_data: [], + this_script_type: "", + this_script_setting: {}, current_script_info: { file_name: "", modified_time: "", }, + current_step: null, + current_step_index: -1, + current_step_children_index: -1, + current_step_children_max_index: 0, + + all_logs_info: [], post_start_PAA: thunk(async (state, payload, { getState }) => { const res = await postPAAStart(); - console.log(res); + state.setState({ + current_step_id: "", + }); + if (res.success) { + alert("PAA启动成功"); + } else { + alert("PAA启动失败"); + } }), post_stop_PAA: thunk(async (state, payload, { getState }) => { const res = await postPAAStop(); - console.log(res); + if (res.success) { + alert("PAA停止成功"); + state.setState({ + current_step_index: -1, + current_step: null, + all_logs_info: [], + }); + } }), - + // todo modified need test post_delete_script: thunk(async (state, payload, { getState }) => { let props = { script_name: payload, }; const res = await postPAADeleteScript(props); + state.get_saved_scripts(); return res; }), - + // todo modified need test post_generate_script: thunk(async (state, payload, { getState }) => { let props = { - script_type: "dark", - script_setting: payload, + script_type: payload.name, + script_setting: payload.data, }; const res = await postPAAGenerate(props); - console.log(res); + // todo, 需要修改成当前脚本就是刚刚生成的 + // 记得后续修改回原来的版本, all_step_data = res.data.script + state.setState({ + all_step_data: res.data, + this_script_type: res.data.type, + this_script_setting: res.data.setting, + }); + return res; }), // 更换当前运行的脚本 + // todo modified need test post_update_script: thunk(async (state, payload, { getState }) => { + // 把脚本替换成待运行的。这个理论上是不需要用的,在手机版上 let props = { script: payload.data, }; const res = await postPAAUpdate(props); if (res.success) { - alert("success"); + console.log("更换运行脚本成功"); state.setState({ - setting_mode: 2, - current_script_info: getState().all_script_info.find( - (item) => item.file_name == payload.file_name - ), + all_step_data: res.data.script, + this_script_type: res.data.type, + this_script_setting: res.data.setting, }); } }), - + // todo modifed need test post_save_script: thunk(async (state, payload, { getState }) => { let props = { script_name: payload, }; const res = await postPAASaveScript(props); + state.get_saved_scripts(); + state.setState({ + setting_mode: 1, + current_script_info: { + file_name: payload + ".json", + modified_time: "Now", + }, + }); }), // 获得脚本名字对应的脚本内容 + // todo 大改逻辑 + // todo modified need test post_load_script: thunk(async (state, payload, { getState }) => { const props = { - script_name: payload, + script_name: payload.file_name, }; const res = await postPAALoadSavedScript(props); - return res; + if (res.success) { + state.setState({ + current_script_info: payload, + setting_mode: 1, + all_step_data: res.data, + this_script_type: res.data.type, + this_script_setting: res.data.setting, + }); + } }), // 获得当前正在运行的脚本内容 get_current_script: thunk(async (state, payload, { getState }) => { const res = await getCurrentScript(); - // 如果current_script不在这个队列中怎么办? - state.setState({ - current_script_data: res.data, - }); + if (res.success) { + if (res.data.script.length > 0) { + state.setState({ + setting_mode: 1, + all_step_data: res.data.script, + this_script_type: res.data.type, + this_script_setting: res.data.setting, + }); + } else { + state.setState({ + setting_mode: 0, + all_step_data: [], + }); + } + } else { + state.setState({ + setting_mode: 0, + }); + } }), get_PAA_status: thunk(async (state, payload, { getState }) => { const res = await getPAAStatus(); - console.log(res); + if (res.data.flag) { + alert("当前PAA处于运行状态"); + } else { + alert("当前PAA处于关闭状态"); + } }), // 获得所有脚本 + // todo, modified, need test. get_saved_scripts: thunk(async (state, payload, { getState }) => { const res = await getSavedScripts(); if (res.success) { - const promises = res.data.map((item: any) => { - return state.post_load_script(item.file_name); + state.setState({ + all_script_info: res.data, }); + // todo, 这里不需要更新所有的脚本信息。无效的请求。 + // const promises = res.data.map((item: any) => { + // return state.post_load_script(item.file_name); + // }); - // Wait for all promises to resolve - const scriptInfos = await Promise.all(promises); + // // Wait for all promises to resolve + // const scriptInfos = await Promise.all(promises); - const updatedScriptInfo = res.data.map((item: any, index: number) => ({ - file_name: item.file_name, - modified_time: item.modified_time, - script_info: scriptInfos[index].data, - })); + // const updatedScriptInfo = res.data.map((item: any, index: number) => ({ + // file_name: item.file_name, + // modified_time: item.modified_time, + // script_info: scriptInfos[index].data, + // })); - state.setState({ - all_script_info: updatedScriptInfo, - }); + // state.setState({ + // all_script_info: updatedScriptInfo, + // }); + } + }), + + add_paa_logs: action((state, payload) => { + state.all_logs_info.unshift(payload); + const regex = /!LOOP STEP! Loop step start (\d+)\/(\d+)/; + const match = payload.message.match(regex); + + if (match && match.length === 3) { + const current = parseInt(match[1], 10); + // 获取 all_step_data 的副本 + const updatedAllStepData = [...state.all_step_data]; + + // 获取要修改的项的索引 + const indexToModify = state.current_step_index; + const subIndexToModify = state.current_step_children_index; + + // 确保索引在有效范围内 + if (indexToModify >= 0 && indexToModify < updatedAllStepData.length) { + // 修改 count 值 + if (subIndexToModify != -1) { + updatedAllStepData[indexToModify].children[ + subIndexToModify + ].params.count = current; + } else { + updatedAllStepData[indexToModify].params.count = current; + } + } + + // 更新状态 + state.all_step_data = updatedAllStepData; + } + }), + + // 1st paa change + change_paa_step: action((state, payload) => { + state.current_step_index += 1; + state.current_step_children_index = -1; + state.current_step_children_max_index = payload.data.children + ? payload.data.children.length + : 0; + state.current_step = payload; + }), + + // 2nd paa change (children) + change_paa_loop_step: action((state, payload) => { + state.current_step_children_index += 1; + state.current_step = payload; + }), + + change_paa_step_async: thunk(async (state, payload, { getState }) => { + if ( + getState().current_step_children_index + 1 === + getState().current_step_children_max_index + ) { + state.change_paa_step(payload); + } else { + state.change_paa_loop_step(payload); } }), setState: action((state, payload) => { state = Object.assign(state, payload); }), + + // modified by gao + can_start_paa: true, + paa_in_progress: false, + check_PAA_start_status: thunk(async (state, payload, { getState }) => { + const res = await getPAAUpdateStatus(); + // true meas in running + state.setState({ + paa_in_progress: res.data, + can_start_paa: !res.data, + }); + // todo, extra check camera status + }), + current_step_id: "", + paa_new_step_info_in: action((state, payload) => { + if (payload.message == "step_start") + state.current_step_id = payload.data.id; + }), + paa_new_log_info_in: action((state, payload) => { + state.all_logs_info.unshift(payload); + }), }); diff --git a/websrc/cobalt-web/src/store/ProcessDataSave.ts b/websrc/cobalt-web/src/store/ProcessDataSave.ts index 0903f389..27323f13 100644 --- a/websrc/cobalt-web/src/store/ProcessDataSave.ts +++ b/websrc/cobalt-web/src/store/ProcessDataSave.ts @@ -3,20 +3,20 @@ import { Thunk, Action, Computed } from "easy-peasy"; import * as AXIOSPAAF from "../services/paa_fixed_procedure_api"; export interface IProcessDataSaveModels { - // process_ws_message: (message: any) => void; - // - // newest_camera_jpg: string |null; camera_jpg_updated: number; newest_camera_jpg_url: string; guider_jpg_updates: number; - // newest_guider_jpg: string |null; newest_guider_jpg_url: string; show_camera: number; // 0 means camera, 1 means guider HFR_points_list: ICHFRDataPointList; newest_HFR_info: ICSingleHFRPointData; PHD2_guide_data_list: ICPHD2GuideDataPointList; + PHD2_calibration_result: ICPHD2InterfaceCalibrationResult; + PHD2_calibrated: boolean; send_ws_message_handler: ((message: any) => void) | null; registered: Computed; + // + setState: Action>; at_start_add_ws_listener: Action< IProcessDataSaveModels, (message: any) => void @@ -27,7 +27,7 @@ export interface IProcessDataSaveModels { IProcessDataSaveModels, ICSingleHFRPointData >; - fetch_history_PHD2_guide_data: Thunk; + fetch_history_PHD2_guide_data: Action; append_newest_PHD2_guide_data: Action< IProcessDataSaveModels, ICPHD2InterfaceGuideStep @@ -69,6 +69,17 @@ export const ProcessDataSaveModel = (): IProcessDataSaveModels => ({ RaControl: [], DecControl: [], }, + PHD2_calibration_result: { + calibrated: false, + xAngle: 0, + xRate: 0, + xParity: "+", + yAngle: 0, + yRate: 0, + yParity: "+", + declination: 0, + }, + PHD2_calibrated: false, send_ws_message_handler: null, registered: computed((state) => { if (state.send_ws_message_handler != null) { @@ -77,6 +88,9 @@ export const ProcessDataSaveModel = (): IProcessDataSaveModels => ({ return false; } }), + setState: action((state, payload) => { + state = Object.assign(state, payload); + }), at_start_add_ws_listener: action((state, payload) => { // const {sendMessage, removeListener} = useEchoWebSocket(process_ws_message); state.send_ws_message_handler = payload; @@ -94,21 +108,29 @@ export const ProcessDataSaveModel = (): IProcessDataSaveModels => ({ state.HFR_points_list.star_count.push(payload.star_count); state.newest_HFR_info = payload; }), - fetch_history_PHD2_guide_data: thunk(async (actions) => {}), + fetch_history_PHD2_guide_data: action((state, pyaload) => {}), append_newest_PHD2_guide_data: action((state, payload) => { state.PHD2_guide_data_list.dx.push(payload.dx); state.PHD2_guide_data_list.dy.push(payload.dy); state.PHD2_guide_data_list.RaDistance.push(payload.RADistanceRaw); state.PHD2_guide_data_list.DecDistance.push(payload.DECDistanceRaw); if (payload.RADirection == "East") { - state.PHD2_guide_data_list.RaControl.push(payload.RADuration); + state.PHD2_guide_data_list.RaControl.push(payload.RADistanceGuide); } else { - state.PHD2_guide_data_list.RaControl.push(-payload.RADuration); + state.PHD2_guide_data_list.RaControl.push(-payload.RADistanceGuide); } if (payload.DECDirection == "North") { - state.PHD2_guide_data_list.DecControl.push(payload.DECDuration); + state.PHD2_guide_data_list.DecControl.push(payload.DECDistanceGuide); } else { - state.PHD2_guide_data_list.DecControl.push(-payload.DECDuration); + state.PHD2_guide_data_list.DecControl.push(-payload.DECDistanceGuide); + } + if (state.PHD2_guide_data_list.dx.length > 300) { + state.PHD2_guide_data_list.dx.shift(); + state.PHD2_guide_data_list.dy.shift(); + state.PHD2_guide_data_list.RaDistance.shift(); + state.PHD2_guide_data_list.DecDistance.shift(); + state.PHD2_guide_data_list.RaControl.shift(); + state.PHD2_guide_data_list.DecControl.shift(); } }), // update_jpg_data: action((state, payload) => { @@ -142,7 +164,7 @@ export const ProcessDataSaveModel = (): IProcessDataSaveModels => ({ if (payload.device.includes("camera")) { state.newest_camera_jpg_url = payload.data; state.camera_jpg_updated = state.camera_jpg_updated + 1; - console.log("before generating url", typeof payload.data); + // console.log('before generating url', typeof(payload.data)); // let new_url = URL.createObjectURL(payload.data); // console.log(new_url); // if (state.newest_camera_jpg_url !== null){ diff --git a/websrc/cobalt-web/src/store/ScheduledTargetList.ts b/websrc/cobalt-web/src/store/ScheduledTargetList.ts index 07d9ee5f..6594405c 100644 --- a/websrc/cobalt-web/src/store/ScheduledTargetList.ts +++ b/websrc/cobalt-web/src/store/ScheduledTargetList.ts @@ -1,194 +1,253 @@ -import { createStore, action, thunk, computed } from 'easy-peasy'; -import {Thunk, Action, Computed } from 'easy-peasy'; +import { createStore, action, thunk, computed } from "easy-peasy"; +import { Thunk, Action, Computed } from "easy-peasy"; import { DateTime } from "luxon"; -import * as AXIOSOF from '../services/object_finding_api'; +import * as AXIOSOF from "../services/object_finding_api"; interface update_string { - index: number, - update_string: string, + index: number; + update_string: string; } const all_available_tags = [ - '星云', '星系', '黑白目标', '彩机目标', 'LRGB', 'HSO', '双窄' -] + "星云", + "星系", + "黑白目标", + "彩机目标", + "LRGB", + "HSO", + "双窄", +]; - -function isFramingObject(target_info: IDSOFramingObjectInfo | IDSOObjectInfo): target_info is IDSOFramingObjectInfo { - return 'rotation' in target_info; +function isFramingObject( + target_info: IDSOFramingObjectInfo | IDSOObjectInfo +): target_info is IDSOFramingObjectInfo { + return "rotation" in target_info; } - export interface ISTargetListModels { - current_focus_target: IDSOFramingObjectInfo; - current_focus_index: number | null; - need_focus: boolean; - all_tags: Array; - all_saved_targets: Array; - twilight_data: ITwilightData; - //-------------------------------- - add_target_and_focus: Action; - change_focus_target: Action; - change_saved_focus_target: Action; - update_focus_target_by_framing: Action; - remove_target: Action; - target_rename: Action; - target_set_flag: Action; - target_set_tag: Action; - clear_focus_target: Action; - check_one_target: Action; - clear_all_checked: Action; - fetch_twilight_data: Thunk; - update_twilight_data: Action; - // get_targets_by_flag: Action; - // get_targets_by_tag: Action; - // load_user_local_data + current_focus_target: IDSOFramingObjectInfo; + current_focus_index: number | null; + need_focus: boolean; + all_tags: Array; + all_saved_targets: Array; + twilight_data: ITwilightData; + //-------------------------------- + add_target_and_focus: Action; + change_focus_target: Action; + change_saved_focus_target: Action; + update_focus_target_by_framing: Action< + ISTargetListModels, + IDSOFramingObjectInfo + >; + remove_target: Action; + target_rename: Action; + target_set_flag: Action; + target_set_tag: Action; + clear_focus_target: Action; + check_one_target: Action; + clear_all_checked: Action; + fetch_twilight_data: Thunk; + update_twilight_data: Action; + // get_targets_by_flag: Action; + // get_targets_by_tag: Action; + // load_user_local_data + save_all_targets: Action; + load_all_targets: Action; } // todo, load user local saved data. -export const ScheduledTargetListStore = ():ISTargetListModels =>({ - current_focus_target: { - name: '', ra: 0, dec: 0, - rotation: undefined, - flag: '', - tag: '', - target_type: '', - bmag: undefined, - vmag: undefined, - size: undefined, - checked: false, +export const ScheduledTargetListStore = (): ISTargetListModels => ({ + current_focus_target: { + name: "", + ra: 0, + dec: 0, + rotation: undefined, + flag: "", + tag: "", + target_type: "", + size: undefined, + checked: false, + }, + current_focus_index: null, + need_focus: false, + all_saved_targets: [], + all_tags: all_available_tags, + twilight_data: { + evening: { + sun_set_time: new Date(2023, 1, 1, 18, 0, 0), + evening_civil_time: new Date(2023, 1, 1, 18, 0, 0), + evening_nautical_time: new Date(2023, 1, 1, 18, 0, 0), + evening_astro_time: new Date(2023, 1, 1, 18, 0, 0), }, - current_focus_index: null, - need_focus: false, - all_saved_targets: [], - all_tags: all_available_tags, - twilight_data: { - evening: { - sun_set_time: new Date(2023, 1, 1, 18, 0, 0), - evening_civil_time: new Date(2023, 1, 1, 18, 0, 0), - evening_nautical_time: new Date(2023, 1, 1, 18, 0, 0), - evening_astro_time: new Date(2023, 1, 1, 18, 0, 0) - }, - morning: { - sun_rise_time: new Date(2023, 1, 1, 18, 0, 0), - morning_civil_time: new Date(2023, 1, 1, 18, 0, 0), - morning_nautical_time: new Date(2023, 1, 1, 18, 0, 0), - morning_astro_time: new Date(2023, 1, 1, 18, 0, 0) - } + morning: { + sun_rise_time: new Date(2023, 1, 1, 18, 0, 0), + morning_civil_time: new Date(2023, 1, 1, 18, 0, 0), + morning_nautical_time: new Date(2023, 1, 1, 18, 0, 0), + morning_astro_time: new Date(2023, 1, 1, 18, 0, 0), }, - add_target_and_focus: action((state, payload) => { - state.all_saved_targets.push(payload); - state.current_focus_target = payload; - state.current_focus_index = state.all_saved_targets.length - 1; - state.need_focus = true; - }), - change_focus_target: action((state, payload) => { - state.current_focus_target = payload; - state.current_focus_index = null; - state.need_focus = true; - }), - change_saved_focus_target: action((state, payload) => { - state.current_focus_target = state.all_saved_targets[payload]; - state.current_focus_index = payload; - state.need_focus = true; - }), - update_focus_target_by_framing: action((state, payload) => { // note do not use, it is not tested and verified. - state.current_focus_target = payload; - state.need_focus = true; - }), - remove_target: action((state, payload) => { - if (payload < state.all_saved_targets.length){ - state.all_saved_targets.splice(payload, 1); - state.current_focus_index = null; - } - }), - target_rename: action((state, payload) => { - // state.all_saved_targets.map((one_target, index) => { - // if (index == payload.index){ - // one_target.name = payload.update_string; - // } - // }) - if (payload.index < state.all_saved_targets.length){ - state.all_saved_targets[payload.index].name = payload.update_string; - } - }), - target_set_flag: action((state, payload) => { - // state.all_saved_targets.map((one_target, index) => { - // if (index == payload.index){ - // one_target.flag = payload.update_string; - // } - // }) - if (payload.index < state.all_saved_targets.length){ - state.all_saved_targets[payload.index].flag = payload.update_string; - } - }), - target_set_tag: action((state, payload) => { - // if (payload.update_string in all_available_tags){ - // state.all_saved_targets.map((one_target, index) => { - // if (index == payload.index){ - // one_target.tag = payload.update_string; - // } - // }) - // } - if (payload.update_string in all_available_tags){ - if (payload.index < state.all_saved_targets.length){ - state.all_saved_targets[payload.index].tag = payload.update_string; - } - } - }), - clear_focus_target: action((state)=>{ - state.current_focus_target = { - name: '-', ra: 0, dec: 0, - rotation: undefined, - flag: '', - tag: '', - target_type: '', - bmag: undefined, - vmag: undefined, - size: undefined, - checked: false, - } - state.current_focus_index = null; - }), - check_one_target: action((state, payload) => { - state.all_saved_targets[payload].checked = true; - }), - clear_all_checked: action((state) => { - for (let i = 0; i { - let res_data = await AXIOSOF.getTwilightData(); - if (res_data.success){ - actions.update_twilight_data(res_data.data); - } - }), - update_twilight_data: action((state, payload) => { - state.twilight_data = { - evening: { - sun_set_time: DateTime.fromFormat(payload.evening.sun_set_time, 'yyyy-MM-dd HH:mm:ss').toJSDate(), - evening_civil_time: DateTime.fromFormat(payload.evening.evening_civil_time, 'yyyy-MM-dd HH:mm:ss').toJSDate(), - evening_nautical_time: DateTime.fromFormat(payload.evening.evening_nautical_time, 'yyyy-MM-dd HH:mm:ss').toJSDate(), - evening_astro_time: DateTime.fromFormat(payload.evening.evening_astro_time, 'yyyy-MM-dd HH:mm:ss').toJSDate(), - }, - morning: { - sun_rise_time: DateTime.fromFormat(payload.morning.sun_rise_time, 'yyyy-MM-dd HH:mm:ss').toJSDate(), - morning_civil_time: DateTime.fromFormat(payload.morning.morning_civil_time, 'yyyy-MM-dd HH:mm:ss').toJSDate(), - morning_nautical_time: DateTime.fromFormat(payload.morning.morning_nautical_time, 'yyyy-MM-dd HH:mm:ss').toJSDate(), - morning_astro_time: DateTime.fromFormat(payload.morning.morning_astro_time, 'yyyy-MM-dd HH:mm:ss').toJSDate(), - } - } - }), - // get_targets_by_flag: action((state, payload) => { - // let result = []; - // for(let i=0;i { + state.all_saved_targets.push(payload); + state.current_focus_target = payload; + state.current_focus_index = state.all_saved_targets.length - 1; + state.need_focus = true; + }), + change_focus_target: action((state, payload) => { + state.current_focus_target = payload; + state.current_focus_index = null; + state.need_focus = true; + }), + change_saved_focus_target: action((state, payload) => { + state.current_focus_target = state.all_saved_targets[payload]; + state.current_focus_index = payload; + state.need_focus = true; + }), + update_focus_target_by_framing: action((state, payload) => { + // note do not use, it is not tested and verified. + state.current_focus_target = payload; + state.need_focus = true; + }), + remove_target: action((state, payload) => { + if (payload < state.all_saved_targets.length) { + state.all_saved_targets.splice(payload, 1); + state.current_focus_index = null; + } + }), + target_rename: action((state, payload) => { + // state.all_saved_targets.map((one_target, index) => { + // if (index == payload.index){ + // one_target.name = payload.update_string; // } - // return result; - // }), - // get_targets_by_tag: action((state, payload) => { - - // }), -}) \ No newline at end of file + // }) + if (payload.index < state.all_saved_targets.length) { + state.all_saved_targets[payload.index].name = payload.update_string; + } + }), + target_set_flag: action((state, payload) => { + // state.all_saved_targets.map((one_target, index) => { + // if (index == payload.index){ + // one_target.flag = payload.update_string; + // } + // }) + if (payload.index < state.all_saved_targets.length) { + state.all_saved_targets[payload.index].flag = payload.update_string; + } + }), + target_set_tag: action((state, payload) => { + // if (payload.update_string in all_available_tags){ + // state.all_saved_targets.map((one_target, index) => { + // if (index == payload.index){ + // one_target.tag = payload.update_string; + // } + // }) + // } + if (payload.update_string in all_available_tags) { + if (payload.index < state.all_saved_targets.length) { + state.all_saved_targets[payload.index].tag = payload.update_string; + } + } + }), + clear_focus_target: action((state) => { + state.current_focus_target = { + name: "-", + ra: 0, + dec: 0, + rotation: undefined, + flag: "", + tag: "", + target_type: "", + size: undefined, + checked: false, + }; + state.current_focus_index = null; + }), + check_one_target: action((state, payload) => { + state.all_saved_targets[payload].checked = true; + }), + clear_all_checked: action((state) => { + for (let i = 0; i < state.all_saved_targets.length; i++) { + state.all_saved_targets[i].checked = false; + } + }), + fetch_twilight_data: thunk(async (actions) => { + let res_data = await AXIOSOF.getTwilightData(); + if (res_data.success) { + actions.update_twilight_data(res_data.data); + } + }), + update_twilight_data: action((state, payload) => { + state.twilight_data = { + evening: { + sun_set_time: DateTime.fromFormat( + payload.evening.sun_set_time, + "yyyy-MM-dd HH:mm:ss" + ).toJSDate(), + evening_civil_time: DateTime.fromFormat( + payload.evening.evening_civil_time, + "yyyy-MM-dd HH:mm:ss" + ).toJSDate(), + evening_nautical_time: DateTime.fromFormat( + payload.evening.evening_nautical_time, + "yyyy-MM-dd HH:mm:ss" + ).toJSDate(), + evening_astro_time: DateTime.fromFormat( + payload.evening.evening_astro_time, + "yyyy-MM-dd HH:mm:ss" + ).toJSDate(), + }, + morning: { + sun_rise_time: DateTime.fromFormat( + payload.morning.sun_rise_time, + "yyyy-MM-dd HH:mm:ss" + ).toJSDate(), + morning_civil_time: DateTime.fromFormat( + payload.morning.morning_civil_time, + "yyyy-MM-dd HH:mm:ss" + ).toJSDate(), + morning_nautical_time: DateTime.fromFormat( + payload.morning.morning_nautical_time, + "yyyy-MM-dd HH:mm:ss" + ).toJSDate(), + morning_astro_time: DateTime.fromFormat( + payload.morning.morning_astro_time, + "yyyy-MM-dd HH:mm:ss" + ).toJSDate(), + }, + }; + }), + // get_targets_by_flag: action((state, payload) => { + // let result = []; + // for(let i=0;i { + + // }), + save_all_targets: action((state) => { + let to_save_array = state.all_saved_targets.map((obj) => ({ + name: obj.name, + ra: obj.ra, + dec: obj.dec, + rotation: obj.rotation, + flag: obj.flag, + tag: obj.tag, + target_type: obj.target_type, + size: obj.size, + checked: false, + })); + // console.log('saving', to_save_array); + localStorage.setItem("TargetList", JSON.stringify(to_save_array)); + }), + load_all_targets: action((state) => { + let data = localStorage.getItem("TargetList"); + if (data != null) { + let list_data = JSON.parse(data) as IDSOFramingObjectInfo[]; + // console.log('loaded list', list_data); + state.all_saved_targets = list_data; + } + }), +}); diff --git a/websrc/cobalt-web/src/store/connectModel.ts b/websrc/cobalt-web/src/store/connectModel.ts index aec58fab..c8e80c05 100644 --- a/websrc/cobalt-web/src/store/connectModel.ts +++ b/websrc/cobalt-web/src/store/connectModel.ts @@ -4,10 +4,19 @@ import { GetCurrentDeviceProfile, getDeviceBrand, getDeviceList, + getDriverServerStatus, + postCheckPhd2, + postCloseDeviceServer, + postConnectPhd2, postDeviceStatus, postStartDeviceServer, + postStartPhd2, } from "../services/api"; -import { postGLobalParameterOnStart } from "../services/global_parameter_api"; +import { + postGlobalLoadProfile, + postGLobalParameterOnStart, + postGLobalParameterProfileDelete, +} from "../services/global_parameter_api"; interface DeviceSelection { device_name: string; @@ -28,7 +37,12 @@ interface Phd2Config { calibration_distance: number; } -type AllowedDeviceTypes = "telescope" | "camera" | "focus" | "filter" | "polar"; +type AllowedDeviceTypes = + | "telescope" + | "camera" + | "focus" + | "filter" + | "guider"; let deviceType: AllowedDeviceTypes = "telescope"; export interface ConnectModel { @@ -42,11 +56,11 @@ export interface ConnectModel { // 所有的device device_list: any | null; // 所有的brand类别 - brand_type_en: any; + brand_type_en: Array; brand_type_cn: any; brand_connection: any; // 所有的用户配置 - user_config_list: any | null; + user_config_list: Array; // panel选择 setting_mode: number; // 是否打开dialog @@ -62,17 +76,18 @@ export interface ConnectModel { phd2_connect_ready: boolean; already_connect: boolean; + is_connect: boolean; all_profiles: []; // 用户选择的brand - brand_selections: any | null; + brand_selections: IConnectSelectedBrandList; device_selections: any | null; setState: Action>; setSelectBrand: Action< ConnectModel, - { type: string; brand_name: string | null } + { type: AllowedDeviceTypes; brand_name: IConnectBrandSelection } >; setSelectDevice: Action; resetDeviceSelections: Action; @@ -83,27 +98,35 @@ export interface ConnectModel { getDeviceList: Thunk; setDeviceList: Action; getProfileList: Thunk; + setProfileList: Action; getProfileDevice: Thunk; + + // 新建和加载合用 setProfile: Thunk; deleteProfile: Thunk; checkPhd2Profile: Thunk; startPhd2: Thunk; - // 注意,前端维护用户userConfigList,但是这个应该要用到localStorage等方法 connectDeviceServer: Thunk; + closeDeviceServer: Thunk; connectDevice: Thunk; + checkDevicesConnection: Thunk; + checkPhd2Status: Thunk; + + // 用于一口气刷新所有device的connect状态的 + refreshDeviceConnectionStatus: Thunk; + setBrandInLoading: Action; } export const getConnectModel = (): ConnectModel => ({ brand_list: null, brand_selections: { - camera: "", - telescope: "", - guider: "", - focus: "", - filter: "", - polar: "", + camera: { zh: "", en: "", driver: "" }, + telescope: { zh: "", en: "", driver: "" }, + guider: { zh: "", en: "", driver: "" }, + focus: { zh: "", en: "", driver: "" }, + filter: { zh: "", en: "", driver: "" }, }, allDrivers: [], device_list: null, @@ -113,7 +136,6 @@ export const getConnectModel = (): ConnectModel => ({ guider: "", focus: "", filter: "", - polar: "", }, all_profiles: [], user_config_list: [], @@ -121,10 +143,10 @@ export const getConnectModel = (): ConnectModel => ({ brand_selection_over: false, setting_mode: 1, alreadyHaveProfile: false, - config_name: null, + config_name: "", already_connect: false, - brand_type_cn: ["赤道仪", "相机", "导星相机", "电调焦", "滤镜轮", "极轴镜"], - brand_type_en: ["telescope", "camera", "guider", "focus", "filter", "polar"], + brand_type_cn: ["赤道仪", "相机", "导星相机", "电调焦", "滤镜轮"], + brand_type_en: ["telescope", "camera", "guider", "focus", "filter"], current_profile: "", phd2_config: { name: "", @@ -138,16 +160,16 @@ export const getConnectModel = (): ConnectModel => ({ calibration_duration: 0, calibration_distance: 0, }, + is_connect: false, phd2_connect_ready: false, - // 0:未选中,1:连接失败, 2:连接成功 + // 0:未选中,1:连接失败, 2:连接成功,-1更新中 brand_connection: { telescope: 0, camera: 0, guider: 0, focus: 0, filter: 0, - polar: 0, }, setState: action((state, payload) => { @@ -165,13 +187,16 @@ export const getConnectModel = (): ConnectModel => ({ const { type, device_infor } = payload; if (state.device_selections !== null) { if (device_infor == null) { - state.device_selections[type] = { - device_name: "", - device_driver_name: "", - device_driver_exec: "", - }; + state.device_selections[type] = ""; } else { - state.device_selections[type] = device_infor; + const deviceNames: string[] = state.device_list.map( + (device: { device_name: string }) => device.device_name + ); + if (device_infor in state.device_list) { + state.device_selections[type] = device_infor; + } else { + state.device_selections[type] = ""; + } if (type === "guider") { state.phd2_config["camera"] = device_infor.device_driver_exec; } @@ -180,19 +205,26 @@ export const getConnectModel = (): ConnectModel => ({ } } } - state.brand_connection = { ...state.brand_connection, [type]: 0 }; }), - getProfileList: thunk(async (state) => { + getProfileList: thunk(async (actions) => { const res = await postGLobalParameterOnStart(); if (res) { - state.setState({ - user_config_list: res.data.all_profiles, - config_name: res.data.current_profile, + actions.setProfileList({ + userConfigList: res.data.all_profiles, + configName: res.data.current_profile, }); } }), + setProfileList: action((state, payload) => { + state.user_config_list = payload.userConfigList; + state.config_name = payload.configName; + if (payload.userConfigList.length == 0) { + state.setting_mode = 0; + } + }), + getProfileDevice: thunk(async (state, payload, { getState }) => { const res = await GetCurrentDeviceProfile(); if (res.data["all_drivers"] != null) { @@ -205,6 +237,7 @@ export const getConnectModel = (): ConnectModel => ({ } await state.getBrandList(); await state.getDeviceList(); + state.setState({ brand_selection_over: true, }); @@ -233,32 +266,43 @@ export const getConnectModel = (): ConnectModel => ({ }), setProfile: thunk(async (state, payload, { getState }) => { - await postGlobalLoadProfile(payload); - - // 调用一次重新获取 - await state.getProfileList(); - - // 更新当前页面 - await state.getProfileDevice(); + if (payload === "") { + state.resetDeviceSelections(); + } else { + const res = await postGlobalLoadProfile(payload); + if (res) { + state.setState({ + config_name: payload, + }); + // 新建 + if (!res.success) { + const updatedList = [...getState().user_config_list, payload]; + state.setState({ + user_config_list: updatedList, + }); + } + // 更新当前页面 + await state.getProfileDevice(); + } + } }), deleteProfile: thunk(async (state, payload, { getState }) => { - await postGLobalParameterProfileDelete(payload); + const res = await postGLobalParameterProfileDelete(payload); + // todo modify later: change res to res.success + if (res) { + // 删除对应的元素 + const updatedList = getState().user_config_list.filter( + (item) => item !== payload + ); - // 调用一次重新获取 - await state.getProfileList(); + state.setState({ + user_config_list: updatedList, + }); - state.setState({ - config_name: null, - device_selections: { - camera: "", - telescope: "", - guider: "", - focus: "", - filter: "", - polar: "", - }, - }); + // 自动更新到新的第一个 + await state.setProfile(updatedList[0]); + } }), getBrandList: thunk(async (state, payload, { getState }) => { @@ -280,6 +324,7 @@ export const getConnectModel = (): ConnectModel => ({ const filteredData = res.data.filter( (item: { device_name: string }) => item.device_name !== "" ); + // 加载失败 if (filteredData.length == 0) { return false; @@ -304,7 +349,6 @@ export const getConnectModel = (): ConnectModel => ({ telescope: [], focus: [], filter: [], - polar: [], guider: [], }; @@ -348,7 +392,6 @@ export const getConnectModel = (): ConnectModel => ({ guider: "", focus: "", filter: "", - polar: "", }; state.brand_connection = { telescope: 0, @@ -356,7 +399,6 @@ export const getConnectModel = (): ConnectModel => ({ guider: 0, focus: 0, filter: 0, - polar: 0, }; state.config_name = null; }), @@ -376,20 +418,25 @@ export const getConnectModel = (): ConnectModel => ({ connectDeviceServer: thunk(async (state, payload, { getState }) => { let postBrandData: string[] = []; - if (getState().brand_selections === null) { - alert("当前尚未选择品牌,请选择!"); - } else { - state.resetDeviceSelections(); - Object.entries( - getState().brand_selections as Record - ).forEach(async ([device, values]) => { - if (values != "") { + state.resetDeviceSelections(); + Object.entries(getState().brand_selections).forEach( + async ([device, values]) => { + if (values.driver != "") { postBrandData.push(values); } - }); - const res = await postStartDeviceServer(postBrandData); - return res; + } + ); + if (postBrandData.length == 0) { + return false; } + const res_stop = await postCloseDeviceServer(); + const res = await postStartDeviceServer(postBrandData); + return res; + }), + + closeDeviceServer: thunk(async (state, payload, { getState }) => { + const res = await postCloseDeviceServer(); + return res; }), // 正式连接,一个一个连,生成连接状态并且即时返回给用户 @@ -397,58 +444,93 @@ export const getConnectModel = (): ConnectModel => ({ if (getState().device_selections === null) { alert("当前尚未选择设备"); } else { - Object.entries( + state.setState({ + brand_connection: { + telescope: 0, + camera: 0, + guider: 0, + focus: 0, + filter: 0, + }, + }); + // 禁用button + state.setState({ + is_connect: true, + }); + // 创建一个 Promise 数组,用于存放每个设备连接操作的 Promise + const connectPromises = Object.entries( getState().device_selections as Record - ).forEach(async ([device, values]) => { - if (values.device_name && values.device_name != "") { + ).map(async ([device, values]) => { + if (values.device_name && values.device_name !== "") { // 等待设备连接完毕 await ConnectDevice( "start", device as AllowedDeviceTypes, values.device_name ); - // 查询连接情况 - try { - const res = await postDeviceStatus({ - device: device, - }); - // 回显 - if (res && res.data === "Connected") { - state.setState({ - brand_connection: { - ...getState().brand_connection, - [device]: 2, - }, - }); - } else { - state.setState({ - brand_connection: { - ...getState().brand_connection, - [device]: 1, - }, - }); - } - } catch (error) { - state.setState({ - brand_connection: { ...getState().brand_connection, [device]: 1 }, - }); - } } else { state.setState({ brand_connection: { ...getState().brand_connection, [device]: 1 }, }); } - // 避免用户连续点击 - state.setState({ - already_connect: true, - }); + }); + + // 等待所有设备连接操作的 Promise 完成 + await Promise.all(connectPromises); + + state.setState({ + is_connect: false, }); } + await state.checkDevicesConnection(); // 检查是否可以启动phd2 - let selections = getState().device_selections; + state.checkPhd2Status(); + }), + + checkDevicesConnection: thunk(async (state, payload, { getState }) => { + let check_connect = false; + const connectPromises = Object.entries( + getState().device_selections as Record + ).map(async ([device, values]) => { + try { + const res = await postDeviceStatus({ + device: device, + }); + + if (res && res.data === "Connected") { + check_connect = true; + state.setState({ + brand_connection: { ...getState().brand_connection, [device]: 2 }, + }); + } else { + state.setState({ + brand_connection: { ...getState().brand_connection, [device]: 1 }, + }); + } + } catch (error) { + state.setState({ + brand_connection: { ...getState().brand_connection, [device]: 1 }, + }); + } + }); + + // 等待所有设备连接操作的 Promise 完成 + await Promise.all(connectPromises); + // 如果存在一个连接了, 说明已经有连接过 + state.setState({ + already_connect: check_connect, + }); + }), + + checkPhd2Status: thunk(async (state, payload, { getState }) => { + const guiderDeviceName = + getState().device_selections["guider"].device_name || ""; + const telescopeDeviceName = + getState().device_selections["telescope"].device_name || ""; + const phd2_check_ok = await postCheckPhd2({ - camera: selections["guider"].device_name, - telescope: selections["telescope"].device_name, + camera: guiderDeviceName, + telescope: telescopeDeviceName, }); if (phd2_check_ok) { state.setState({ @@ -460,10 +542,10 @@ export const getConnectModel = (): ConnectModel => ({ startPhd2: thunk(async (state) => { try { const res = await postStartPhd2(); - if (res.data == true) { + if (res.data.flag == true) { await Sleep(1000); const res2 = await postConnectPhd2(); - if (res2.data === true) { + if (res2.data.flag === true) { return true; } } @@ -472,6 +554,45 @@ export const getConnectModel = (): ConnectModel => ({ return false; } }), + + refreshDeviceConnectionStatus: thunk(async (state, payload, { getState }) => { + let to_update_devices = [ + "camera", + "telescope", + "focus", + "guider", + "filter", + ]; + to_update_devices.map(async (device, index) => { + state.setBrandInLoading({ device: device, flag: true }); + try { + const res = await postDeviceStatus({ + device: device, + }); + // 回显 + if (res && res.data === "Connected") { + state.setState({ + brand_connection: { ...getState().brand_connection, [device]: 2 }, + }); + } else { + state.setState({ + brand_connection: { ...getState().brand_connection, [device]: 1 }, + }); + } + } catch (error) { + state.setState({ + brand_connection: { ...getState().brand_connection, [device]: 1 }, + }); + } + }); + }), + setBrandInLoading: action((state, payload) => { + if (payload.flag) { + state.brand_connection[payload.device] = -1; + } else { + state.brand_connection[payload.device] = 0; + } + }), }); const Sleep = (ms: number) => { diff --git a/websrc/cobalt-web/src/store/globalStore.tsx b/websrc/cobalt-web/src/store/globalStore.tsx index d756b4dc..514ecce5 100644 --- a/websrc/cobalt-web/src/store/globalStore.tsx +++ b/websrc/cobalt-web/src/store/globalStore.tsx @@ -1,37 +1,34 @@ -import * as React from 'react'; -import {createStore,createTypedHooks,StoreProvider} from 'easy-peasy'; -import {Middleware} from 'redux'; -import {getGLobalStoreModel,GlobalStoreModel} from './globalStoreModel'; - -function createGlobalStore(){ - const middleware:Middleware[] = []; - const store = createStore(getGLobalStoreModel(),{middleware}); - return store; +import * as React from "react"; +import { createStore, createTypedHooks, StoreProvider } from "easy-peasy"; +import { Middleware } from "redux"; +import { getGLobalStoreModel, GlobalStoreModel } from "./globalStoreModel"; + +function createGlobalStore() { + const middleware: Middleware[] = []; + const store = createStore(getGLobalStoreModel(), { middleware }); + return store; } const hooks = createTypedHooks(); +let _globalStoreInstance: ReturnType | undefined; -let _globalStoreInstance:ReturnType |undefined; - -const globalStoreInstance = ():ReturnType=>{ - if(_globalStoreInstance===undefined){ - _globalStoreInstance = createGlobalStore(); - } - return _globalStoreInstance; -} +const globalStoreInstance = (): ReturnType => { + if (_globalStoreInstance === undefined) { + _globalStoreInstance = createGlobalStore(); + } + return _globalStoreInstance; +}; export const GlobalStore = { - useAppState:hooks.useStoreState, - get actions(){ - return globalStoreInstance().getActions(); - } -} - -export const GlobalStoreProvider:React.FC=({children})=>{ - return( - {children} - ) -} - - + useAppState: hooks.useStoreState, + get actions() { + return globalStoreInstance().getActions(); + }, +}; + +export const GlobalStoreProvider: React.FC = ({ children }) => { + return ( + {children} + ); +}; diff --git a/websrc/cobalt-web/src/store/globalStoreModel.ts b/websrc/cobalt-web/src/store/globalStoreModel.ts index 7fe3f1b4..7ce7fbc6 100644 --- a/websrc/cobalt-web/src/store/globalStoreModel.ts +++ b/websrc/cobalt-web/src/store/globalStoreModel.ts @@ -16,7 +16,6 @@ import { ProcessDataSaveModel, IProcessDataSaveModels, } from "./ProcessDataSave"; -import { DeepPartial } from "redux"; interface GlobalStoreStateModel { visible: VisibleModel; diff --git a/websrc/cobalt-web/src/theme.tsx b/websrc/cobalt-web/src/theme.tsx new file mode 100644 index 00000000..744cc10a --- /dev/null +++ b/websrc/cobalt-web/src/theme.tsx @@ -0,0 +1,52 @@ +import React from "react"; +import { ThemeProvider } from "styled-components"; +import { createGlobalStyle } from "styled-components"; +import { Container } from "react-bootstrap"; + +const darkTheme = { + palette: { + mode: "dark", + primary: { + main: "#3f51b5", + }, + secondary: { + main: "#f50057", + }, + success: { + main: "#388e3c", + }, + }, +}; + +const GlobalStyle = createGlobalStyle` + body { + background-color: ${({ theme }) => + theme.palette.mode === "dark" ? "#121212" : "#fff"}; + color: ${({ theme }) => (theme.palette.mode === "dark" ? "#fff" : "#000")}; + } + + .btn { + border-radius: 8px; + } + + .btn-link { + border-radius: 8px; + } + + .form-control { + background-color: rgba(255, 255, 255, 0.5); + } + + .form-control:focus { + background-color: rgba(255, 255, 255, 0.5); + } +`; + +export const CustomThemeProvider: React.FC = ({ children }) => { + return ( + + + {children} + + ); +}; diff --git a/websrc/cobalt-web/src/utils/combineProviders.tsx b/websrc/cobalt-web/src/utils/combineProviders.tsx index 516f2139..75909bd7 100644 --- a/websrc/cobalt-web/src/utils/combineProviders.tsx +++ b/websrc/cobalt-web/src/utils/combineProviders.tsx @@ -39,15 +39,9 @@ type ProviderList = Array; export const combineProviders = ( list: ProviderList, children: React.ReactNode -) => { - const filteredList = list.filter(Boolean) as Array; - - if (filteredList.length === 0) { - return <>{children}; - } - - return filteredList.reduceRight( - (acc, Provider) => {acc}, - <>{children} - ); -}; +) => + ( + list + // filter out falsy items + .filter(Boolean) as Array + ).reduceRight((acc, Provider) => {acc}, <>{children}); diff --git a/websrc/cobalt-web/src/utils/websocket.tsx b/websrc/cobalt-web/src/utils/websocket.tsx index 578e28ac..dda34b4b 100644 --- a/websrc/cobalt-web/src/utils/websocket.tsx +++ b/websrc/cobalt-web/src/utils/websocket.tsx @@ -1,8 +1,8 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState } from "react"; export interface WebsocketProps { url: string; - onMessage: (data:any) => void; + onMessage: (data: any) => void; onOpen: (event: Event, websocket: WebSocket) => void; keepAliveInterval?: number; onClose?: (event: Event, websocket: WebSocket) => void; @@ -12,8 +12,8 @@ const keepAlive = (websocket: WebSocket, interval: number) => { setTimeout(() => { websocket.send( JSON.stringify({ - method: 'ping', - }), + method: "ping", + }) ); keepAlive(websocket, interval); }, interval); @@ -59,4 +59,4 @@ export const Websocket = ({ }, [onMessage]); return null; -}; \ No newline at end of file +}; diff --git a/websrc/cobalt-web/src/utils/websocketProvider.tsx b/websrc/cobalt-web/src/utils/websocketProvider.tsx index 1a283311..4d742fce 100644 --- a/websrc/cobalt-web/src/utils/websocketProvider.tsx +++ b/websrc/cobalt-web/src/utils/websocketProvider.tsx @@ -1,11 +1,4 @@ -import React, { - createContext, - FC, - useContext, - useEffect, - useMemo, - useState, -} from "react"; +import React, { ReactNode, useContext, useEffect, useState } from "react"; import { Websocket } from "./websocket"; let ws_url = ""; @@ -16,20 +9,15 @@ if (process.env.NODE_ENV === "development") { ws_url += "/ws/indi_client/"; } -type EchoWebSocketContextType = [ - addListener: (event: any, filter?: string) => void, - sendMessage: (event: any) => void, - removeListener: (fn: any) => void +export type EchoWebSocketContextType = [ + (event: any, filter?: string) => void, + (event: any) => void, + (fn: any) => void ]; -const EchoWebSocketContext = createContext([ - () => {}, - () => {}, - () => {}, -]); +export const EchoWebSocketContext = + React.createContext([() => {}, () => {}, () => {}]); -interface IProps {} - -export const EchoWebSocketProvider: FC = ({ children }) => { +export const EchoWebSocketProvider: React.FC = ({ children }) => { const [webSocket, setWebSocket] = useState(null); const [listeners, setListeners] = useState<{ [key: string]: ((event: any) => void)[]; @@ -48,7 +36,7 @@ export const EchoWebSocketProvider: FC = ({ children }) => { setListeners((currentListeners) => { return { ...currentListeners, - any: currentListeners.any.filter((listener) => listener !== fn), + any: currentListeners["any"].filter((listener) => listener !== fn), }; }); }; @@ -69,22 +57,10 @@ export const EchoWebSocketProvider: FC = ({ children }) => { listeners.any.forEach((listener) => listener(event)); } }; - - const webSocketValue = useMemo( - () => [addListener, sendMessage, removeListener], - [addListener, sendMessage, removeListener] - ); - - useEffect(() => { - if (webSocket) { - return () => { - webSocket.close(); - }; - } - }, [webSocket]); - return ( - + { @@ -104,16 +80,10 @@ export const useEchoWebSocket = ( ) => { const [addListener, sendMessage, removeListener] = useContext(EchoWebSocketContext); - useEffect(() => { if (listener) { addListener(listener, filter); } - return () => { - if (listener) { - removeListener(listener); - } - }; }, []); return { sendMessage, removeListener }; diff --git a/websrc/cobalt-web/tsconfig.json b/websrc/cobalt-web/tsconfig.json new file mode 100644 index 00000000..56f22c22 --- /dev/null +++ b/websrc/cobalt-web/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": false, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src", "./src/interfaces/*.d.ts"], + "references": [ + { + "path": "./tsconfig.node.json" + } + ] +} diff --git a/websrc/cobalt-web/tsconfig.node.json b/websrc/cobalt-web/tsconfig.node.json new file mode 100644 index 00000000..9d31e2ae --- /dev/null +++ b/websrc/cobalt-web/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/websrc/cobalt-web/vite.config.js b/websrc/cobalt-web/vite.config.js deleted file mode 100644 index 5a33944a..00000000 --- a/websrc/cobalt-web/vite.config.js +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], -}) diff --git a/websrc/cobalt-web/vite.config.ts b/websrc/cobalt-web/vite.config.ts new file mode 100644 index 00000000..f10c29ed --- /dev/null +++ b/websrc/cobalt-web/vite.config.ts @@ -0,0 +1,52 @@ +import { ConfigEnv, UserConfigExport } from "vite"; +import react from "@vitejs/plugin-react"; +import svgr from "vite-plugin-svgr"; +import path from "path"; +// import { viteMockServe } from 'vite-plugin-mock' + +const resolve = (dir: string) => path.join(__dirname, ".", dir); + +const isDev = process.env.NODE_ENV === "development"; + +export default ({ command }: ConfigEnv): UserConfigExport => { + return { + publicDir: "public", + plugins: [ + react(), + svgr(), + /*viteMockServe({ + mockPath: 'mock', + localEnabled: false, + supportTs: true, + watchFiles: true, + }),*/ + ], + resolve: { + alias: { + "@": resolve("src"), + }, + }, + css: { + preprocessorOptions: { + less: { + javascriptEnabled: true, + }, + }, + }, + server: { + cors: true, + watch: { + usePolling: true, + }, + // http://192.168.31.193:7999 + proxy: { + "^/api/.*": { + target: "http://192.168.31.193:7999", + changeOrigin: true, + ws: true, + rewrite: (path) => path.replace(/^\/api/, ""), + }, + }, + }, + }; +}; diff --git a/websrc/heal-web/.gitignore b/websrc/heal-web/.gitignore deleted file mode 100644 index 8296128d..00000000 --- a/websrc/heal-web/.gitignore +++ /dev/null @@ -1,92 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock -.DS_Store - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# next.js build output -.next - -# nuxt.js build output -.nuxt - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# Webpack -.webpack/ - -# Vite -.vite/ - -# Electron-Forge -out/ diff --git a/websrc/heal-web/forge.config.js b/websrc/heal-web/forge.config.js deleted file mode 100644 index d6aa49c1..00000000 --- a/websrc/heal-web/forge.config.js +++ /dev/null @@ -1,63 +0,0 @@ -const { FusesPlugin } = require('@electron-forge/plugin-fuses'); -const { FuseV1Options, FuseVersion } = require('@electron/fuses'); - -module.exports = { - packagerConfig: { - asar: true, - }, - rebuildConfig: {}, - makers: [ - { - name: '@electron-forge/maker-squirrel', - config: {}, - }, - { - name: '@electron-forge/maker-zip', - platforms: ['darwin'], - }, - { - name: '@electron-forge/maker-deb', - config: {}, - }, - { - name: '@electron-forge/maker-rpm', - config: {}, - }, - ], - plugins: [ - { - name: '@electron-forge/plugin-auto-unpack-natives', - config: {}, - }, - { - name: '@electron-forge/plugin-webpack', - config: { - mainConfig: './webpack.main.config.js', - renderer: { - config: './webpack.renderer.config.js', - entryPoints: [ - { - html: './src/index.html', - js: './src/renderer.js', - name: 'main_window', - preload: { - js: './src/preload.js', - }, - }, - ], - }, - }, - }, - // Fuses are used to enable/disable various Electron functionality - // at package time, before code signing the application - new FusesPlugin({ - version: FuseVersion.V1, - [FuseV1Options.RunAsNode]: false, - [FuseV1Options.EnableCookieEncryption]: true, - [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false, - [FuseV1Options.EnableNodeCliInspectArguments]: false, - [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true, - [FuseV1Options.OnlyLoadAppFromAsar]: true, - }), - ], -}; diff --git a/websrc/heal-web/package.json b/websrc/heal-web/package.json deleted file mode 100644 index 77e64fc7..00000000 --- a/websrc/heal-web/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "heal-web", - "productName": "heal-web", - "version": "1.0.0", - "description": "My Electron application description", - "main": ".webpack/main", - "scripts": { - "start": "electron-forge start", - "package": "electron-forge package", - "make": "electron-forge make", - "publish": "electron-forge publish", - "lint": "echo \"No linting configured\"" - }, - "devDependencies": { - "@electron-forge/cli": "^7.3.0", - "@electron-forge/maker-deb": "^7.3.0", - "@electron-forge/maker-rpm": "^7.3.0", - "@electron-forge/maker-squirrel": "^7.3.0", - "@electron-forge/maker-zip": "^7.3.0", - "@electron-forge/plugin-auto-unpack-natives": "^7.3.0", - "@electron-forge/plugin-fuses": "^7.2.0", - "@electron-forge/plugin-webpack": "^7.3.0", - "@electron/fuses": "^1.7.0", - "@vercel/webpack-asset-relocator-loader": "1.7.3", - "css-loader": "^6.0.0", - "node-loader": "^2.0.0", - "style-loader": "^3.0.0" - }, - "keywords": [], - "author": { - "name": "Max Qian", - "email": "astro_air@126.com" - }, - "license": "MIT", - "dependencies": { - "admin-lte": "^3.2", - "astro": "^4.4.4", - "electron-squirrel-startup": "^1.0.0" - } -} diff --git a/websrc/heal-web/src/index.css b/websrc/heal-web/src/index.css deleted file mode 100644 index 8856f90b..00000000 --- a/websrc/heal-web/src/index.css +++ /dev/null @@ -1,7 +0,0 @@ -body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, - Arial, sans-serif; - margin: auto; - max-width: 38rem; - padding: 2rem; -} diff --git a/websrc/heal-web/src/index.html b/websrc/heal-web/src/index.html deleted file mode 100644 index 219d54b9..00000000 --- a/websrc/heal-web/src/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - Hello World! - - - -

💖 Hello World!

-

Welcome to your Electron application.

- - diff --git a/websrc/heal-web/src/main.js b/websrc/heal-web/src/main.js deleted file mode 100644 index ddbffc54..00000000 --- a/websrc/heal-web/src/main.js +++ /dev/null @@ -1,49 +0,0 @@ -const { app, BrowserWindow } = require('electron'); -const path = require('path'); - -// Handle creating/removing shortcuts on Windows when installing/uninstalling. -if (require('electron-squirrel-startup')) { - app.quit(); -} - -const createWindow = () => { - // Create the browser window. - const mainWindow = new BrowserWindow({ - width: 800, - height: 600, - webPreferences: { - preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY, - }, - }); - - // and load the index.html of the app. - mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY); - - // Open the DevTools. - mainWindow.webContents.openDevTools(); -}; - -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. -app.on('ready', createWindow); - -// Quit when all windows are closed, except on macOS. There, it's common -// for applications and their menu bar to stay active until the user quits -// explicitly with Cmd + Q. -app.on('window-all-closed', () => { - if (process.platform !== 'darwin') { - app.quit(); - } -}); - -app.on('activate', () => { - // On OS X it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (BrowserWindow.getAllWindows().length === 0) { - createWindow(); - } -}); - -// In this file you can include the rest of your app's specific main process -// code. You can also put them in separate files and import them here. diff --git a/websrc/heal-web/src/preload.js b/websrc/heal-web/src/preload.js deleted file mode 100644 index 5e9d369c..00000000 --- a/websrc/heal-web/src/preload.js +++ /dev/null @@ -1,2 +0,0 @@ -// See the Electron documentation for details on how to use preload scripts: -// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts diff --git a/websrc/heal-web/src/renderer.js b/websrc/heal-web/src/renderer.js deleted file mode 100644 index 4a353474..00000000 --- a/websrc/heal-web/src/renderer.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * This file will automatically be loaded by webpack and run in the "renderer" context. - * To learn more about the differences between the "main" and the "renderer" context in - * Electron, visit: - * - * https://electronjs.org/docs/tutorial/application-architecture#main-and-renderer-processes - * - * By default, Node.js integration in this file is disabled. When enabling Node.js integration - * in a renderer process, please be aware of potential security implications. You can read - * more about security risks here: - * - * https://electronjs.org/docs/tutorial/security - * - * To enable Node.js integration in this file, open up `main.js` and enable the `nodeIntegration` - * flag: - * - * ``` - * // Create the browser window. - * mainWindow = new BrowserWindow({ - * width: 800, - * height: 600, - * webPreferences: { - * nodeIntegration: true - * } - * }); - * ``` - */ - -import './index.css'; - -console.log('👋 This message is being logged by "renderer.js", included via webpack'); diff --git a/websrc/heal-web/webpack.main.config.js b/websrc/heal-web/webpack.main.config.js deleted file mode 100644 index d23d0e36..00000000 --- a/websrc/heal-web/webpack.main.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - /** - * This is the main entry point for your application, it's the first file - * that runs in the main process. - */ - entry: './src/main.js', - // Put your normal webpack config below here - module: { - rules: require('./webpack.rules'), - }, -}; diff --git a/websrc/heal-web/webpack.renderer.config.js b/websrc/heal-web/webpack.renderer.config.js deleted file mode 100644 index e5f6a64c..00000000 --- a/websrc/heal-web/webpack.renderer.config.js +++ /dev/null @@ -1,13 +0,0 @@ -const rules = require('./webpack.rules'); - -rules.push({ - test: /\.css$/, - use: [{ loader: 'style-loader' }, { loader: 'css-loader' }], -}); - -module.exports = { - // Put your normal webpack config below here - module: { - rules, - }, -}; diff --git a/websrc/heal-web/webpack.rules.js b/websrc/heal-web/webpack.rules.js deleted file mode 100644 index 23cb22a3..00000000 --- a/websrc/heal-web/webpack.rules.js +++ /dev/null @@ -1,35 +0,0 @@ -module.exports = [ - // Add support for native node modules - { - // We're specifying native_modules in the test because the asset relocator loader generates a - // "fake" .node file which is really a cjs file. - test: /native_modules[/\\].+\.node$/, - use: 'node-loader', - }, - { - test: /[/\\]node_modules[/\\].+\.(m?js|node)$/, - parser: { amd: false }, - use: { - loader: '@vercel/webpack-asset-relocator-loader', - options: { - outputAssetBase: 'native_modules', - }, - }, - }, - // Put your webpack loader rules in this array. This is where you would put - // your ts-loader configuration for instance: - /** - * Typescript Example: - * - * { - * test: /\.tsx?$/, - * exclude: /(node_modules|.webpack)/, - * loaders: [{ - * loader: 'ts-loader', - * options: { - * transpileOnly: true - * } - * }] - * } - */ -]; diff --git a/websrc/heal-web/yarn.lock b/websrc/heal-web/yarn.lock deleted file mode 100644 index ebf81985..00000000 --- a/websrc/heal-web/yarn.lock +++ /dev/null @@ -1,8737 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@ampproject/remapping@^2.2.0": - version "2.2.1" - resolved "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" - integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== - dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" - -"@astrojs/compiler@^2.5.3": - version "2.6.0" - resolved "https://registry.npmmirror.com/@astrojs/compiler/-/compiler-2.6.0.tgz#0d27c9a60530766b247f0cd1359fbe468d147fe6" - integrity sha512-c74k8iGHL3DzkosSJ0tGcHIEBEiIfBhr7eadSaPyvWlVKaieDVzVs8OW1tnRSQyBsfMc8DZQ4RcN2KAcESD8UQ== - -"@astrojs/internal-helpers@0.2.1": - version "0.2.1" - resolved "https://registry.npmmirror.com/@astrojs/internal-helpers/-/internal-helpers-0.2.1.tgz#4e2e6aabaa9819f17119aa10f413c4d6122c94cf" - integrity sha512-06DD2ZnItMwUnH81LBLco3tWjcZ1lGU9rLCCBaeUCGYe9cI0wKyY2W3kDyoW1I6GmcWgt1fu+D1CTvz+FIKf8A== - -"@astrojs/markdown-remark@4.2.1": - version "4.2.1" - resolved "https://registry.npmmirror.com/@astrojs/markdown-remark/-/markdown-remark-4.2.1.tgz#2fba4aba6563914675cca759b635144b6c53e704" - integrity sha512-2RQBIwrq+2qPYtp99bH+eL5hfbK0BoxXla85lHsRpIX/IsGqFrPX6pXI2cbWPihBwGbKCdxS6uZNX2QerZWwpQ== - dependencies: - "@astrojs/prism" "^3.0.0" - github-slugger "^2.0.0" - import-meta-resolve "^4.0.0" - mdast-util-definitions "^6.0.0" - rehype-raw "^7.0.0" - rehype-stringify "^10.0.0" - remark-gfm "^4.0.0" - remark-parse "^11.0.0" - remark-rehype "^11.0.0" - remark-smartypants "^2.0.0" - shikiji "^0.9.18" - unified "^11.0.4" - unist-util-visit "^5.0.0" - vfile "^6.0.1" - -"@astrojs/prism@^3.0.0": - version "3.0.0" - resolved "https://registry.npmmirror.com/@astrojs/prism/-/prism-3.0.0.tgz#c9443e4cbf435acf0b5adc2c627d9789991514e7" - integrity sha512-g61lZupWq1bYbcBnYZqdjndShr/J3l/oFobBKPA3+qMat146zce3nz2kdO4giGbhYDt4gYdhmoBz0vZJ4sIurQ== - dependencies: - prismjs "^1.29.0" - -"@astrojs/telemetry@3.0.4": - version "3.0.4" - resolved "https://registry.npmmirror.com/@astrojs/telemetry/-/telemetry-3.0.4.tgz#566257082c87df84fcc136db23e071e1104b13fd" - integrity sha512-A+0c7k/Xy293xx6odsYZuXiaHO0PL+bnDoXOc47sGDF5ffIKdKQGRPFl2NMlCF4L0NqN4Ynbgnaip+pPF0s7pQ== - dependencies: - ci-info "^3.8.0" - debug "^4.3.4" - dlv "^1.1.3" - dset "^3.1.2" - is-docker "^3.0.0" - is-wsl "^3.0.0" - which-pm-runs "^1.1.0" - -"@babel/code-frame@^7.23.5": - version "7.23.5" - resolved "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" - integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== - dependencies: - "@babel/highlight" "^7.23.4" - chalk "^2.4.2" - -"@babel/compat-data@^7.23.5": - version "7.23.5" - resolved "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" - integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== - -"@babel/core@^7.23.3": - version "7.23.9" - resolved "https://registry.npmmirror.com/@babel/core/-/core-7.23.9.tgz#b028820718000f267870822fec434820e9b1e4d1" - integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.9" - "@babel/parser" "^7.23.9" - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" - convert-source-map "^2.0.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.3" - semver "^6.3.1" - -"@babel/generator@^7.23.3", "@babel/generator@^7.23.6": - version "7.23.6" - resolved "https://registry.npmmirror.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" - integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== - dependencies: - "@babel/types" "^7.23.6" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/helper-annotate-as-pure@^7.22.5": - version "7.22.5" - resolved "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" - integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-compilation-targets@^7.23.6": - version "7.23.6" - resolved "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" - integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== - dependencies: - "@babel/compat-data" "^7.23.5" - "@babel/helper-validator-option" "^7.23.5" - browserslist "^4.22.2" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-environment-visitor@^7.22.20": - version "7.22.20" - resolved "https://registry.npmmirror.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" - integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== - -"@babel/helper-function-name@^7.23.0": - version "7.23.0" - resolved "https://registry.npmmirror.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" - integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== - dependencies: - "@babel/template" "^7.22.15" - "@babel/types" "^7.23.0" - -"@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.npmmirror.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-module-imports@^7.22.15": - version "7.22.15" - resolved "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" - integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== - dependencies: - "@babel/types" "^7.22.15" - -"@babel/helper-module-transforms@^7.23.3": - version "7.23.3" - resolved "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" - integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== - dependencies: - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.20" - -"@babel/helper-plugin-utils@^7.22.5": - version "7.22.5" - resolved "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" - integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== - -"@babel/helper-simple-access@^7.22.5": - version "7.22.5" - resolved "https://registry.npmmirror.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" - integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-split-export-declaration@^7.22.6": - version "7.22.6" - resolved "https://registry.npmmirror.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" - integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-string-parser@^7.23.4": - version "7.23.4" - resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" - integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== - -"@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - -"@babel/helper-validator-option@^7.23.5": - version "7.23.5" - resolved "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" - integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== - -"@babel/helpers@^7.23.9": - version "7.23.9" - resolved "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.23.9.tgz#c3e20bbe7f7a7e10cb9b178384b4affdf5995c7d" - integrity sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ== - dependencies: - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" - -"@babel/highlight@^7.23.4": - version "7.23.4" - resolved "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" - integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== - dependencies: - "@babel/helper-validator-identifier" "^7.22.20" - chalk "^2.4.2" - js-tokens "^4.0.0" - -"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.3", "@babel/parser@^7.23.9": - version "7.23.9" - resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" - integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== - -"@babel/plugin-syntax-jsx@^7.23.3": - version "7.23.3" - resolved "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" - integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-react-jsx@^7.22.5": - version "7.23.4" - resolved "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312" - integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-module-imports" "^7.22.15" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-jsx" "^7.23.3" - "@babel/types" "^7.23.4" - -"@babel/template@^7.22.15", "@babel/template@^7.23.9": - version "7.23.9" - resolved "https://registry.npmmirror.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a" - integrity sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA== - dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/parser" "^7.23.9" - "@babel/types" "^7.23.9" - -"@babel/traverse@^7.23.3", "@babel/traverse@^7.23.9": - version "7.23.9" - resolved "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.23.9.tgz#2f9d6aead6b564669394c5ce0f9302bb65b9d950" - integrity sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg== - dependencies: - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.9" - "@babel/types" "^7.23.9" - debug "^4.3.1" - globals "^11.1.0" - -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.3", "@babel/types@^7.23.4", "@babel/types@^7.23.6", "@babel/types@^7.23.9": - version "7.23.9" - resolved "https://registry.npmmirror.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" - integrity sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q== - dependencies: - "@babel/helper-string-parser" "^7.23.4" - "@babel/helper-validator-identifier" "^7.22.20" - to-fast-properties "^2.0.0" - -"@electron-forge/cli@^7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/cli/-/cli-7.3.0.tgz#55a4a71ba46f2c777edffacc4da76f59554371e6" - integrity sha512-tIzNYTvCEjJbma7zLWpa03phLKX/pd9f+vG+0HlIpmESMFGWhyLDzunZn0YOOXPRKpCTVg5RpC+BVte1Da4VjQ== - dependencies: - "@electron-forge/core" "7.3.0" - "@electron-forge/shared-types" "7.3.0" - "@electron/get" "^3.0.0" - chalk "^4.0.0" - commander "^4.1.1" - debug "^4.3.1" - fs-extra "^10.0.0" - listr2 "^5.0.3" - semver "^7.2.1" - -"@electron-forge/core-utils@7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/core-utils/-/core-utils-7.3.0.tgz#6dd9e19e4ca53716f1d2cff475b157ee4703cd16" - integrity sha512-cKeWuC8zYcp2n9caRWvCQgwIFtDqaUlwQVeg2VBpgJTGYHNKEDQHadR2xtIXTcfNgPUbQEAXiaS2xuxuhPQLfw== - dependencies: - "@electron-forge/shared-types" "7.3.0" - "@electron/rebuild" "^3.2.10" - "@malept/cross-spawn-promise" "^2.0.0" - chalk "^4.0.0" - debug "^4.3.1" - find-up "^5.0.0" - fs-extra "^10.0.0" - log-symbols "^4.0.0" - semver "^7.2.1" - yarn-or-npm "^3.0.1" - -"@electron-forge/core@7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/core/-/core-7.3.0.tgz#7846fe5a2a177260cf90be438cdce9d2f2bc0106" - integrity sha512-Z0wvs/YutUzo5xbCBjhoWSnlO1y5DbM4LMa5Di4Dxaf8v/xi7PQ/ncjAiOJKFYI8mG23Nn8Ae13weG0tiXISbA== - dependencies: - "@electron-forge/core-utils" "7.3.0" - "@electron-forge/maker-base" "7.3.0" - "@electron-forge/plugin-base" "7.3.0" - "@electron-forge/publisher-base" "7.3.0" - "@electron-forge/shared-types" "7.3.0" - "@electron-forge/template-base" "7.3.0" - "@electron-forge/template-vite" "7.3.0" - "@electron-forge/template-vite-typescript" "7.3.0" - "@electron-forge/template-webpack" "7.3.0" - "@electron-forge/template-webpack-typescript" "7.3.0" - "@electron-forge/tracer" "7.3.0" - "@electron/get" "^3.0.0" - "@electron/packager" "^18.1.2" - "@electron/rebuild" "^3.2.10" - "@malept/cross-spawn-promise" "^2.0.0" - chalk "^4.0.0" - debug "^4.3.1" - fast-glob "^3.2.7" - filenamify "^4.1.0" - find-up "^5.0.0" - fs-extra "^10.0.0" - got "^11.8.5" - interpret "^3.1.1" - listr2 "^5.0.3" - lodash "^4.17.20" - log-symbols "^4.0.0" - node-fetch "^2.6.7" - progress "^2.0.3" - rechoir "^0.8.0" - resolve-package "^1.0.1" - semver "^7.2.1" - source-map-support "^0.5.13" - sudo-prompt "^9.1.1" - username "^5.1.0" - yarn-or-npm "^3.0.1" - -"@electron-forge/maker-base@7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/maker-base/-/maker-base-7.3.0.tgz#d56d13592fd0aa7b593da0629b5051ca9a121617" - integrity sha512-1o0YT1QBCf9oAfQNJmWQehn+DQp8mqaUbwaivNtIgTKRlzAVfD7UoAS7izuUqEW6M6NOvFXfCQjp7IgGckVVBg== - dependencies: - "@electron-forge/shared-types" "7.3.0" - fs-extra "^10.0.0" - which "^2.0.2" - -"@electron-forge/maker-deb@^7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/maker-deb/-/maker-deb-7.3.0.tgz#150913e13c0de11d11d3f10311503e70e89a0409" - integrity sha512-rlTYjF18p1rVVzInr9kJPSwELvu2ycLp7qGi/Nrj91N2cS92D3z8l6UkQE6wvhsBMhhL0sOX+NyDhvzKoRsWNQ== - dependencies: - "@electron-forge/maker-base" "7.3.0" - "@electron-forge/shared-types" "7.3.0" - optionalDependencies: - electron-installer-debian "^3.2.0" - -"@electron-forge/maker-rpm@^7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/maker-rpm/-/maker-rpm-7.3.0.tgz#0fa63234fa0e3d4bde4bb31dc76df25efc2f755a" - integrity sha512-EzfkRcnWeLYHUvGmtP2KcGU8I93izAaGfYze1xQqG6BQ0FXjEm7xcESy2gZThX/2vEtQUdjCdIbfVf4yveZKFQ== - dependencies: - "@electron-forge/maker-base" "7.3.0" - "@electron-forge/shared-types" "7.3.0" - optionalDependencies: - electron-installer-redhat "^3.2.0" - -"@electron-forge/maker-squirrel@^7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/maker-squirrel/-/maker-squirrel-7.3.0.tgz#d0cb4911851f37a66416506ee72c0fa3060efc9a" - integrity sha512-JXKKwztnIDiMjzwUwROphZyIAtUivw7YOsWhskuxj/KhxtHpksNboBbwhvbvX8stfzVl2M95IgqATyPJXClQ9w== - dependencies: - "@electron-forge/maker-base" "7.3.0" - "@electron-forge/shared-types" "7.3.0" - fs-extra "^10.0.0" - optionalDependencies: - electron-winstaller "^5.0.0" - -"@electron-forge/maker-zip@^7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/maker-zip/-/maker-zip-7.3.0.tgz#064ab2857fdbb183c3226bb0f69b77abdab1e172" - integrity sha512-VYYLScDpibVIiMRK7JWeCS9G8VYvPXa1X6p6fNYQoFOWommG9zC7OOnFfNnLBrH1+0ginZRJeLD1zo+cM12JuQ== - dependencies: - "@electron-forge/maker-base" "7.3.0" - "@electron-forge/shared-types" "7.3.0" - cross-zip "^4.0.0" - fs-extra "^10.0.0" - got "^11.8.5" - -"@electron-forge/plugin-auto-unpack-natives@^7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/plugin-auto-unpack-natives/-/plugin-auto-unpack-natives-7.3.0.tgz#277772dc8275143e93cb5955decef1dc2354366c" - integrity sha512-cWcor6pEplaAo2OSr+khth4282xoLUtESdSLtGtRYE1Q8sNM5BbPKcwyr3tP8FFgWnj2TAYbpubwScLyb5BXZA== - dependencies: - "@electron-forge/plugin-base" "7.3.0" - "@electron-forge/shared-types" "7.3.0" - -"@electron-forge/plugin-base@7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/plugin-base/-/plugin-base-7.3.0.tgz#df8c7c92e7280a973d111d0a1e39676706913415" - integrity sha512-cS0dqi9yTMgKzy1RhJ21YheRjWSbUh0bOKuByYAWevdqMZfqO2KyhXIsmH5QizL+bub8uWOUsknXVrOp73NLfw== - dependencies: - "@electron-forge/shared-types" "7.3.0" - -"@electron-forge/plugin-fuses@^7.2.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/plugin-fuses/-/plugin-fuses-7.3.0.tgz#d67f389d03dde722026d87e6e9c903c7c82cacf0" - integrity sha512-+p23S+NQwia+uX1D6Hx01R2b0Ck2bNVDgS1JL8lyU8cz3OXAA3J52Ck94wMslwmj2TfdV0ErsMxI/QOQlPffiw== - dependencies: - "@electron-forge/plugin-base" "7.3.0" - "@electron-forge/shared-types" "7.3.0" - -"@electron-forge/plugin-webpack@^7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/plugin-webpack/-/plugin-webpack-7.3.0.tgz#be7580d73e3c151a24ca4aa0aacff34e488b1c55" - integrity sha512-yCAJBl4INfTINd1tlLZ7tsHKWAo2VXndomixOdwvBGPpfq8I3+lVsC6H7zu7qQJtQFfkbGbJovq4xxIRLcBakQ== - dependencies: - "@electron-forge/core-utils" "7.3.0" - "@electron-forge/plugin-base" "7.3.0" - "@electron-forge/shared-types" "7.3.0" - "@electron-forge/web-multi-logger" "7.3.0" - chalk "^4.0.0" - debug "^4.3.1" - fast-glob "^3.2.7" - fs-extra "^10.0.0" - html-webpack-plugin "^5.5.3" - webpack "^5.69.1" - webpack-dev-server "^4.0.0" - webpack-merge "^5.7.3" - -"@electron-forge/publisher-base@7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/publisher-base/-/publisher-base-7.3.0.tgz#4da19c3f3f448ad90f09c88d8347a2cb7d6398b0" - integrity sha512-iO8QuM0zTLcEA0/7adEUOzMrhu/h6Qk9UiWNUllBctZXZ+FO0CbAY7eGzOgjOKqH5akbEKHwCSRnjrFt91QpQg== - dependencies: - "@electron-forge/shared-types" "7.3.0" - -"@electron-forge/shared-types@7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/shared-types/-/shared-types-7.3.0.tgz#95e0d7699dd876287e9442f45b99a4aa4029c2a5" - integrity sha512-+YGOTGUGVrcaRm9zO3xsWSj2GS9iVY6E1WTG0vD2OxZtdEGcdy3hZryV72f5gH+qeZZvujYSR2s7VvZjhFEHaQ== - dependencies: - "@electron-forge/tracer" "7.3.0" - "@electron/packager" "^18.1.2" - "@electron/rebuild" "^3.2.10" - listr2 "^5.0.3" - -"@electron-forge/template-base@7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/template-base/-/template-base-7.3.0.tgz#7a26a9b4de21116e7a91d08e04860834b2e940fa" - integrity sha512-Lf0fupMzuO9XuBOaWoQ5QljjQ89B7TYU40+eEUvxnIMNAfnU3n+cfhC4xGLldmh+LYRuotB1jJitX79BwRqzIA== - dependencies: - "@electron-forge/shared-types" "7.3.0" - "@malept/cross-spawn-promise" "^2.0.0" - debug "^4.3.1" - fs-extra "^10.0.0" - username "^5.1.0" - -"@electron-forge/template-vite-typescript@7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/template-vite-typescript/-/template-vite-typescript-7.3.0.tgz#e4a00f3211201425ade95fa3adf4e7bb24d6757f" - integrity sha512-4gVlJihXLM+r6GBOCeO6mSv5vZImew9Vp/xFfxMrf3nDThMCnA6HYLIGA361ZTbn4z3ARquXPo6Vsm7/s4ykbw== - dependencies: - "@electron-forge/shared-types" "7.3.0" - "@electron-forge/template-base" "7.3.0" - fs-extra "^10.0.0" - -"@electron-forge/template-vite@7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/template-vite/-/template-vite-7.3.0.tgz#2dcc69584eb0eabf88a2e4946427c972384154f2" - integrity sha512-4vdOLmd0/rThA9lO/Tf16oCoDBPWGLRZZF+XM+ECPDfaL0CbFNoEa/NLrr6T/2D6IlV5+GnmVjz29LlVOFUB7w== - dependencies: - "@electron-forge/shared-types" "7.3.0" - "@electron-forge/template-base" "7.3.0" - fs-extra "^10.0.0" - -"@electron-forge/template-webpack-typescript@7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/template-webpack-typescript/-/template-webpack-typescript-7.3.0.tgz#1e2974f0ffd7b7b3a9b47a8b36f4326d2be4562d" - integrity sha512-eiBhsY/LUaV1vIy/PZqnmkxWyjEyN/PsXyq79lr1nuOKrqkVgZUe/IdvtNxr8wvPoKSScORNLHjiD/C2Jp74HA== - dependencies: - "@electron-forge/shared-types" "7.3.0" - "@electron-forge/template-base" "7.3.0" - fs-extra "^10.0.0" - -"@electron-forge/template-webpack@7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/template-webpack/-/template-webpack-7.3.0.tgz#fbc476efcf1025b9899083a38cf7b5b2279e7141" - integrity sha512-5Cv0g+AHdEI2R4hPI38PzWTnqUwqpM36jpQgkXV1RnL3V9FvNuza/w9RLMj5bhGzB0j5M4bVbcnglMX0pDvVBQ== - dependencies: - "@electron-forge/shared-types" "7.3.0" - "@electron-forge/template-base" "7.3.0" - fs-extra "^10.0.0" - -"@electron-forge/tracer@7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/tracer/-/tracer-7.3.0.tgz#f0c27b5288dce514a7765157dcf339b9d6e18f49" - integrity sha512-FS7ABTm52BMP2BlR/pDmUIKtH9NI1i+BBJuKke58KguToBRuvAX1cLt0Hhkq4HlqYR9fNjRoCo1vrK4OBb0Bew== - dependencies: - chrome-trace-event "^1.0.3" - -"@electron-forge/web-multi-logger@7.3.0": - version "7.3.0" - resolved "https://registry.npmmirror.com/@electron-forge/web-multi-logger/-/web-multi-logger-7.3.0.tgz#48bacfcbc09db59844acdf97fc63ebf332105a15" - integrity sha512-NekmSeNCEQ8uvshCJw3PlsjY27LSJM+u4bfv+Wabt7NXpbR9I+kCtLPwm6GTp/LfE7OwU7iD+QbEVaynUTnUOQ== - dependencies: - express "^4.17.1" - express-ws "^5.0.2" - xterm "^4.9.0" - xterm-addon-fit "^0.5.0" - xterm-addon-search "^0.8.0" - -"@electron/asar@^3.2.1", "@electron/asar@^3.2.7": - version "3.2.8" - resolved "https://registry.npmmirror.com/@electron/asar/-/asar-3.2.8.tgz#2ea722f3452583dbd4ffdcc4b4f5dc903f1d8178" - integrity sha512-cmskk5M06ewHMZAplSiF4AlME3IrnnZhKnWbtwKVLRkdJkKyUVjMLhDIiPIx/+6zQWVlKX/LtmK9xDme7540Sg== - dependencies: - commander "^5.0.0" - glob "^7.1.6" - minimatch "^3.0.4" - -"@electron/fuses@^1.7.0": - version "1.7.0" - resolved "https://registry.npmmirror.com/@electron/fuses/-/fuses-1.7.0.tgz#0800d5404fffe5683705297990fea089d49811a2" - integrity sha512-mfhLoZGQdqrSU/SeOFBs6r+D7g1tYiVs2C/hh7t3NFQ0chcXGoWrrad17rCQL1ImNJuCXs4cu23YBj5CAnj5SA== - dependencies: - chalk "^4.1.1" - fs-extra "^9.0.1" - minimist "^1.2.5" - -"@electron/get@^3.0.0": - version "3.0.0" - resolved "https://registry.npmmirror.com/@electron/get/-/get-3.0.0.tgz#2b0c794b98902d0bc5218546872c1379bef68aa2" - integrity sha512-hLv4BYFiyrNRI+U0Mm2X7RxCCdJLkDUn8GCEp9QJzbLpZRko+UaLlCjOMkj6TEtirNLPyBA7y1SeGfnpOB21aQ== - dependencies: - debug "^4.1.1" - env-paths "^2.2.0" - fs-extra "^8.1.0" - got "^11.8.5" - progress "^2.0.3" - semver "^6.2.0" - sumchecker "^3.0.1" - optionalDependencies: - global-agent "^3.0.0" - -"@electron/notarize@^2.1.0": - version "2.3.0" - resolved "https://registry.npmmirror.com/@electron/notarize/-/notarize-2.3.0.tgz#9659cf6c92563dd69411afce229f52f9f7196227" - integrity sha512-EiTBU0BwE7HZZjAG1fFWQaiQpCuPrVGn7jPss1kUjD6eTTdXXd29RiZqEqkgN7xqt/Pgn4g3I7Saqovanrfj3w== - dependencies: - debug "^4.1.1" - fs-extra "^9.0.1" - promise-retry "^2.0.1" - -"@electron/osx-sign@^1.0.5": - version "1.0.5" - resolved "https://registry.npmmirror.com/@electron/osx-sign/-/osx-sign-1.0.5.tgz#0af7149f2fce44d1a8215660fd25a9fb610454d8" - integrity sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww== - dependencies: - compare-version "^0.1.2" - debug "^4.3.4" - fs-extra "^10.0.0" - isbinaryfile "^4.0.8" - minimist "^1.2.6" - plist "^3.0.5" - -"@electron/packager@^18.1.2": - version "18.1.3" - resolved "https://registry.npmmirror.com/@electron/packager/-/packager-18.1.3.tgz#53eba507e5dccaa4cbfae56bf6e2f58a7bc257a5" - integrity sha512-21T5MxUf7DwV07IIes3jO/571mXCjOGVPdmYJFPCVDTimFiHQSW0Oy+OIGQaKBiNIXfnP29KylsCQbmds6O6Iw== - dependencies: - "@electron/asar" "^3.2.1" - "@electron/get" "^3.0.0" - "@electron/notarize" "^2.1.0" - "@electron/osx-sign" "^1.0.5" - "@electron/universal" "^2.0.1" - "@electron/windows-sign" "^1.0.0" - cross-spawn-windows-exe "^1.2.0" - debug "^4.0.1" - extract-zip "^2.0.0" - filenamify "^4.1.0" - fs-extra "^11.1.0" - galactus "^1.0.0" - get-package-info "^1.0.0" - junk "^3.1.0" - parse-author "^2.0.0" - plist "^3.0.0" - rcedit "^4.0.0" - resolve "^1.1.6" - semver "^7.1.3" - yargs-parser "^21.1.1" - -"@electron/rebuild@^3.2.10": - version "3.6.0" - resolved "https://registry.npmmirror.com/@electron/rebuild/-/rebuild-3.6.0.tgz#60211375a5f8541a71eb07dd2f97354ad0b2b96f" - integrity sha512-zF4x3QupRU3uNGaP5X1wjpmcjfw1H87kyqZ00Tc3HvriV+4gmOGuvQjGNkrJuXdsApssdNyVwLsy+TaeTGGcVw== - dependencies: - "@malept/cross-spawn-promise" "^2.0.0" - chalk "^4.0.0" - debug "^4.1.1" - detect-libc "^2.0.1" - fs-extra "^10.0.0" - got "^11.7.0" - node-abi "^3.45.0" - node-api-version "^0.2.0" - node-gyp "^9.0.0" - ora "^5.1.0" - read-binary-file-arch "^1.0.6" - semver "^7.3.5" - tar "^6.0.5" - yargs "^17.0.1" - -"@electron/universal@^2.0.1": - version "2.0.1" - resolved "https://registry.npmmirror.com/@electron/universal/-/universal-2.0.1.tgz#7b070ab355e02957388f3dbd68e2c3cd08c448ae" - integrity sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA== - dependencies: - "@electron/asar" "^3.2.7" - "@malept/cross-spawn-promise" "^2.0.0" - debug "^4.3.1" - dir-compare "^4.2.0" - fs-extra "^11.1.1" - minimatch "^9.0.3" - plist "^3.1.0" - -"@electron/windows-sign@^1.0.0": - version "1.1.1" - resolved "https://registry.npmmirror.com/@electron/windows-sign/-/windows-sign-1.1.1.tgz#4ac6a2f782e70251cc1bd7349485a764ee07320f" - integrity sha512-g8/atfOCKuuGedjVE6Xu/rlBtJvfDrmBH9UokBrjrvBVWdVz3SGV7DTjPTLvl7F+XUlmqj4genub62r3jKHIHw== - dependencies: - cross-dirname "^0.1.0" - debug "^4.3.4" - fs-extra "^11.1.1" - minimist "^1.2.8" - postject "^1.0.0-alpha.6" - -"@esbuild/aix-ppc64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz#d1bc06aedb6936b3b6d313bf809a5a40387d2b7f" - integrity sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA== - -"@esbuild/android-arm64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz#7ad65a36cfdb7e0d429c353e00f680d737c2aed4" - integrity sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA== - -"@esbuild/android-arm@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.19.12.tgz#b0c26536f37776162ca8bde25e42040c203f2824" - integrity sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w== - -"@esbuild/android-x64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.19.12.tgz#cb13e2211282012194d89bf3bfe7721273473b3d" - integrity sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew== - -"@esbuild/darwin-arm64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz#cbee41e988020d4b516e9d9e44dd29200996275e" - integrity sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g== - -"@esbuild/darwin-x64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz#e37d9633246d52aecf491ee916ece709f9d5f4cd" - integrity sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A== - -"@esbuild/freebsd-arm64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz#1ee4d8b682ed363b08af74d1ea2b2b4dbba76487" - integrity sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA== - -"@esbuild/freebsd-x64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz#37a693553d42ff77cd7126764b535fb6cc28a11c" - integrity sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg== - -"@esbuild/linux-arm64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz#be9b145985ec6c57470e0e051d887b09dddb2d4b" - integrity sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA== - -"@esbuild/linux-arm@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz#207ecd982a8db95f7b5279207d0ff2331acf5eef" - integrity sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w== - -"@esbuild/linux-ia32@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz#d0d86b5ca1562523dc284a6723293a52d5860601" - integrity sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA== - -"@esbuild/linux-loong64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz#9a37f87fec4b8408e682b528391fa22afd952299" - integrity sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA== - -"@esbuild/linux-mips64el@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz#4ddebd4e6eeba20b509d8e74c8e30d8ace0b89ec" - integrity sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w== - -"@esbuild/linux-ppc64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz#adb67dadb73656849f63cd522f5ecb351dd8dee8" - integrity sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg== - -"@esbuild/linux-riscv64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz#11bc0698bf0a2abf8727f1c7ace2112612c15adf" - integrity sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg== - -"@esbuild/linux-s390x@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz#e86fb8ffba7c5c92ba91fc3b27ed5a70196c3cc8" - integrity sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg== - -"@esbuild/linux-x64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz#5f37cfdc705aea687dfe5dfbec086a05acfe9c78" - integrity sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg== - -"@esbuild/netbsd-x64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz#29da566a75324e0d0dd7e47519ba2f7ef168657b" - integrity sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA== - -"@esbuild/openbsd-x64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz#306c0acbdb5a99c95be98bdd1d47c916e7dc3ff0" - integrity sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw== - -"@esbuild/sunos-x64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz#0933eaab9af8b9b2c930236f62aae3fc593faf30" - integrity sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA== - -"@esbuild/win32-arm64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz#773bdbaa1971b36db2f6560088639ccd1e6773ae" - integrity sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A== - -"@esbuild/win32-ia32@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz#000516cad06354cc84a73f0943a4aa690ef6fd67" - integrity sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ== - -"@esbuild/win32-x64@0.19.12": - version "0.19.12" - resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz#c57c8afbb4054a3ab8317591a0b7320360b444ae" - integrity sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA== - -"@foliojs-fork/fontkit@^1.9.1": - version "1.9.1" - resolved "https://registry.npmmirror.com/@foliojs-fork/fontkit/-/fontkit-1.9.1.tgz#8124649168eb5273f580f66697a139fb5041296b" - integrity sha512-U589voc2/ROnvx1CyH9aNzOQWJp127JGU1QAylXGQ7LoEAF6hMmahZLQ4eqAcgHUw+uyW4PjtCItq9qudPkK3A== - dependencies: - "@foliojs-fork/restructure" "^2.0.2" - brfs "^2.0.0" - brotli "^1.2.0" - browserify-optional "^1.0.1" - clone "^1.0.4" - deep-equal "^1.0.0" - dfa "^1.2.0" - tiny-inflate "^1.0.2" - unicode-properties "^1.2.2" - unicode-trie "^2.0.0" - -"@foliojs-fork/linebreak@^1.1.1": - version "1.1.1" - resolved "https://registry.npmmirror.com/@foliojs-fork/linebreak/-/linebreak-1.1.1.tgz#93ecd695b7d2bb0334b9481058c3e610e019a4eb" - integrity sha512-pgY/+53GqGQI+mvDiyprvPWgkTlVBS8cxqee03ejm6gKAQNsR1tCYCIvN9FHy7otZajzMqCgPOgC4cHdt4JPig== - dependencies: - base64-js "1.3.1" - brfs "^2.0.2" - unicode-trie "^2.0.0" - -"@foliojs-fork/pdfkit@^0.14.0": - version "0.14.0" - resolved "https://registry.npmmirror.com/@foliojs-fork/pdfkit/-/pdfkit-0.14.0.tgz#ed1868050edd2904284655f8dcddd56b49576c98" - integrity sha512-nMOiQAv6id89MT3tVTCgc7HxD5ZMANwio2o5yvs5sexQkC0KI3BLaLakpsrHmFfeGFAhqPmZATZGbJGXTUebpg== - dependencies: - "@foliojs-fork/fontkit" "^1.9.1" - "@foliojs-fork/linebreak" "^1.1.1" - crypto-js "^4.2.0" - png-js "^1.0.0" - -"@foliojs-fork/restructure@^2.0.2": - version "2.0.2" - resolved "https://registry.npmmirror.com/@foliojs-fork/restructure/-/restructure-2.0.2.tgz#73759aba2aff1da87b7c4554e6839c70d43c92b4" - integrity sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA== - -"@fortawesome/fontawesome-free@^5.15.4": - version "5.15.4" - resolved "https://registry.npmmirror.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.4.tgz#ecda5712b61ac852c760d8b3c79c96adca5554e5" - integrity sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg== - -"@gar/promisify@^1.1.3": - version "1.1.3" - resolved "https://registry.npmmirror.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" - integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== - -"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.4" - resolved "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz#9b18145d26cf33d08576cf4c7665b28554480ed7" - integrity sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/resolve-uri@^3.1.0": - version "3.1.2" - resolved "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - -"@jridgewell/source-map@^0.3.3": - version "0.3.5" - resolved "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" - integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== - dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": - version "1.4.15" - resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== - -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.23" - resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz#afc96847f3f07841477f303eed687707a5aacd80" - integrity sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@leichtgewicht/ip-codec@^2.0.1": - version "2.0.4" - resolved "https://registry.npmmirror.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" - integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== - -"@lgaitan/pace-progress@^1.0.7": - version "1.0.7" - resolved "https://registry.npmmirror.com/@lgaitan/pace-progress/-/pace-progress-1.0.7.tgz#c96fbbd9fd4cf528feed34ea0c8f9d8b3e98f0dd" - integrity sha512-GMoTcF6WBpno7a7Iyx7M79os26d5bCDbh7YTZmXZM8YuLR3DDtwo0/CBYddStGD6QIBTieFDz4IAQiO0dAdRGw== - -"@malept/cross-spawn-promise@^1.0.0", "@malept/cross-spawn-promise@^1.1.0": - version "1.1.1" - resolved "https://registry.npmmirror.com/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz#504af200af6b98e198bce768bc1730c6936ae01d" - integrity sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ== - dependencies: - cross-spawn "^7.0.1" - -"@malept/cross-spawn-promise@^2.0.0": - version "2.0.0" - resolved "https://registry.npmmirror.com/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz#d0772de1aa680a0bfb9ba2f32b4c828c7857cb9d" - integrity sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg== - dependencies: - cross-spawn "^7.0.1" - -"@medv/finder@^3.1.0": - version "3.1.0" - resolved "https://registry.npmmirror.com/@medv/finder/-/finder-3.1.0.tgz#e157c68f166ade9f113a1314603365bf81dd8b8c" - integrity sha512-ojkXjR3K0Zz3jnCR80tqPL+0yvbZk/lEodb6RIVjLz7W8RVA2wrw8ym/CzCpXO9SYVUIKHFUpc7jvf8UKfIM3w== - -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.3": - version "1.2.8" - resolved "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - -"@npmcli/fs@^2.1.0": - version "2.1.2" - resolved "https://registry.npmmirror.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" - integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== - dependencies: - "@gar/promisify" "^1.1.3" - semver "^7.3.5" - -"@npmcli/move-file@^2.0.0": - version "2.0.1" - resolved "https://registry.npmmirror.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" - integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== - dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" - -"@rollup/rollup-android-arm-eabi@4.12.0": - version "4.12.0" - resolved "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz#38c3abd1955a3c21d492af6b1a1dca4bb1d894d6" - integrity sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w== - -"@rollup/rollup-android-arm64@4.12.0": - version "4.12.0" - resolved "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz#3822e929f415627609e53b11cec9a4be806de0e2" - integrity sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ== - -"@rollup/rollup-darwin-arm64@4.12.0": - version "4.12.0" - resolved "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz#6c082de71f481f57df6cfa3701ab2a7afde96f69" - integrity sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ== - -"@rollup/rollup-darwin-x64@4.12.0": - version "4.12.0" - resolved "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz#c34ca0d31f3c46a22c9afa0e944403eea0edcfd8" - integrity sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg== - -"@rollup/rollup-linux-arm-gnueabihf@4.12.0": - version "4.12.0" - resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz#48e899c1e438629c072889b824a98787a7c2362d" - integrity sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA== - -"@rollup/rollup-linux-arm64-gnu@4.12.0": - version "4.12.0" - resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz#788c2698a119dc229062d40da6ada8a090a73a68" - integrity sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA== - -"@rollup/rollup-linux-arm64-musl@4.12.0": - version "4.12.0" - resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz#3882a4e3a564af9e55804beeb67076857b035ab7" - integrity sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ== - -"@rollup/rollup-linux-riscv64-gnu@4.12.0": - version "4.12.0" - resolved "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz#0c6ad792e1195c12bfae634425a3d2aa0fe93ab7" - integrity sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw== - -"@rollup/rollup-linux-x64-gnu@4.12.0": - version "4.12.0" - resolved "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz#9d62485ea0f18d8674033b57aa14fb758f6ec6e3" - integrity sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA== - -"@rollup/rollup-linux-x64-musl@4.12.0": - version "4.12.0" - resolved "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz#50e8167e28b33c977c1f813def2b2074d1435e05" - integrity sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw== - -"@rollup/rollup-win32-arm64-msvc@4.12.0": - version "4.12.0" - resolved "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz#68d233272a2004429124494121a42c4aebdc5b8e" - integrity sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw== - -"@rollup/rollup-win32-ia32-msvc@4.12.0": - version "4.12.0" - resolved "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz#366ca62221d1689e3b55a03f4ae12ae9ba595d40" - integrity sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA== - -"@rollup/rollup-win32-x64-msvc@4.12.0": - version "4.12.0" - resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz#9ffdf9ed133a7464f4ae187eb9e1294413fab235" - integrity sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg== - -"@sindresorhus/is@^4.0.0": - version "4.6.0" - resolved "https://registry.npmmirror.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" - integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== - -"@sweetalert2/theme-bootstrap-4@^5.0.8": - version "5.0.16" - resolved "https://registry.npmmirror.com/@sweetalert2/theme-bootstrap-4/-/theme-bootstrap-4-5.0.16.tgz#530431834990fdb0ebcf50a732d014255d169a32" - integrity sha512-KHFJZBTrZqZGdx9TBL1t8wmkGPzq593QKX8FWr67z6foLiVjjcPK9fvdkrUrGcOJ4sjpwrk1QdELIrsdGOyp8A== - -"@szmarczak/http-timer@^4.0.5": - version "4.0.6" - resolved "https://registry.npmmirror.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" - integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== - dependencies: - defer-to-connect "^2.0.0" - -"@tootallnate/once@2": - version "2.0.0" - resolved "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" - integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== - -"@ttskch/select2-bootstrap4-theme@^1.5.2": - version "1.5.2" - resolved "https://registry.npmmirror.com/@ttskch/select2-bootstrap4-theme/-/select2-bootstrap4-theme-1.5.2.tgz#3b4519b349f3e7831c28752a1e9617312a192656" - integrity sha512-gAj8qNy/VYwQDBkACm0USM66kxFai8flX83ayRXPNhzZckEgSqIBB9sM74SCM3ssgeX+ZVy4BifTnLis+KpIyg== - -"@types/babel__core@^7.20.4": - version "7.20.5" - resolved "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" - integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== - dependencies: - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.6.8" - resolved "https://registry.npmmirror.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab" - integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.4.4" - resolved "https://registry.npmmirror.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f" - integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*": - version "7.20.5" - resolved "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.20.5.tgz#7b7502be0aa80cc4ef22978846b983edaafcd4dd" - integrity sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ== - dependencies: - "@babel/types" "^7.20.7" - -"@types/body-parser@*": - version "1.19.5" - resolved "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" - integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== - dependencies: - "@types/connect" "*" - "@types/node" "*" - -"@types/bonjour@^3.5.9": - version "3.5.13" - resolved "https://registry.npmmirror.com/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" - integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== - dependencies: - "@types/node" "*" - -"@types/cacheable-request@^6.0.1": - version "6.0.3" - resolved "https://registry.npmmirror.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" - integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== - dependencies: - "@types/http-cache-semantics" "*" - "@types/keyv" "^3.1.4" - "@types/node" "*" - "@types/responselike" "^1.0.0" - -"@types/connect-history-api-fallback@^1.3.5": - version "1.5.4" - resolved "https://registry.npmmirror.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" - integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== - dependencies: - "@types/express-serve-static-core" "*" - "@types/node" "*" - -"@types/connect@*": - version "3.4.38" - resolved "https://registry.npmmirror.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" - integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== - dependencies: - "@types/node" "*" - -"@types/debug@^4.0.0": - version "4.1.12" - resolved "https://registry.npmmirror.com/@types/debug/-/debug-4.1.12.tgz#a155f21690871953410df4b6b6f53187f0500917" - integrity sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ== - dependencies: - "@types/ms" "*" - -"@types/eslint-scope@^3.7.3": - version "3.7.7" - resolved "https://registry.npmmirror.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" - integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" - -"@types/eslint@*": - version "8.56.3" - resolved "https://registry.npmmirror.com/@types/eslint/-/eslint-8.56.3.tgz#d1f6b2303ac5ed53cb2cf59e0ab680cde1698f5f" - integrity sha512-PvSf1wfv2wJpVIFUMSb+i4PvqNYkB9Rkp9ZDO3oaWzq4SKhsQk4mrMBr3ZH06I0hKrVGLBacmgl8JM4WVjb9dg== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/estree@*", "@types/estree@1.0.5", "@types/estree@^1.0.0", "@types/estree@^1.0.5": - version "1.0.5" - resolved "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" - integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== - -"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": - version "4.17.43" - resolved "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz#10d8444be560cb789c4735aea5eac6e5af45df54" - integrity sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - "@types/send" "*" - -"@types/express@*", "@types/express@^4.17.13": - version "4.17.21" - resolved "https://registry.npmmirror.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" - integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.33" - "@types/qs" "*" - "@types/serve-static" "*" - -"@types/fs-extra@^9.0.1": - version "9.0.13" - resolved "https://registry.npmmirror.com/@types/fs-extra/-/fs-extra-9.0.13.tgz#7594fbae04fe7f1918ce8b3d213f74ff44ac1f45" - integrity sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA== - dependencies: - "@types/node" "*" - -"@types/glob@^7.1.1": - version "7.2.0" - resolved "https://registry.npmmirror.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" - integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== - dependencies: - "@types/minimatch" "*" - "@types/node" "*" - -"@types/hast@^3.0.0": - version "3.0.4" - resolved "https://registry.npmmirror.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" - integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== - dependencies: - "@types/unist" "*" - -"@types/html-minifier-terser@^6.0.0": - version "6.1.0" - resolved "https://registry.npmmirror.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" - integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== - -"@types/http-cache-semantics@*": - version "4.0.4" - resolved "https://registry.npmmirror.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" - integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== - -"@types/http-errors@*": - version "2.0.4" - resolved "https://registry.npmmirror.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" - integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== - -"@types/http-proxy@^1.17.8": - version "1.17.14" - resolved "https://registry.npmmirror.com/@types/http-proxy/-/http-proxy-1.17.14.tgz#57f8ccaa1c1c3780644f8a94f9c6b5000b5e2eec" - integrity sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w== - dependencies: - "@types/node" "*" - -"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": - version "7.0.15" - resolved "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" - integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== - -"@types/keyv@^3.1.4": - version "3.1.4" - resolved "https://registry.npmmirror.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" - integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== - dependencies: - "@types/node" "*" - -"@types/mdast@^4.0.0": - version "4.0.3" - resolved "https://registry.npmmirror.com/@types/mdast/-/mdast-4.0.3.tgz#1e011ff013566e919a4232d1701ad30d70cab333" - integrity sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg== - dependencies: - "@types/unist" "*" - -"@types/mime@*": - version "3.0.4" - resolved "https://registry.npmmirror.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45" - integrity sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw== - -"@types/mime@^1": - version "1.3.5" - resolved "https://registry.npmmirror.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" - integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== - -"@types/minimatch@*": - version "5.1.2" - resolved "https://registry.npmmirror.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" - integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== - -"@types/ms@*": - version "0.7.34" - resolved "https://registry.npmmirror.com/@types/ms/-/ms-0.7.34.tgz#10964ba0dee6ac4cd462e2795b6bebd407303433" - integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g== - -"@types/nlcst@^1.0.0": - version "1.0.4" - resolved "https://registry.npmmirror.com/@types/nlcst/-/nlcst-1.0.4.tgz#3b8a9c279a2367602512588a0ba6a0e93634ee3e" - integrity sha512-ABoYdNQ/kBSsLvZAekMhIPMQ3YUZvavStpKYs7BjLLuKVmIMA0LUgZ7b54zzuWJRbHF80v1cNf4r90Vd6eMQDg== - dependencies: - "@types/unist" "^2" - -"@types/node-forge@^1.3.0": - version "1.3.11" - resolved "https://registry.npmmirror.com/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" - integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== - dependencies: - "@types/node" "*" - -"@types/node@*": - version "20.11.20" - resolved "https://registry.npmmirror.com/@types/node/-/node-20.11.20.tgz#f0a2aee575215149a62784210ad88b3a34843659" - integrity sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg== - dependencies: - undici-types "~5.26.4" - -"@types/qs@*": - version "6.9.11" - resolved "https://registry.npmmirror.com/@types/qs/-/qs-6.9.11.tgz#208d8a30bc507bd82e03ada29e4732ea46a6bbda" - integrity sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ== - -"@types/range-parser@*": - version "1.2.7" - resolved "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" - integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== - -"@types/responselike@^1.0.0": - version "1.0.3" - resolved "https://registry.npmmirror.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" - integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== - dependencies: - "@types/node" "*" - -"@types/retry@0.12.0": - version "0.12.0" - resolved "https://registry.npmmirror.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" - integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== - -"@types/send@*": - version "0.17.4" - resolved "https://registry.npmmirror.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" - integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== - dependencies: - "@types/mime" "^1" - "@types/node" "*" - -"@types/serve-index@^1.9.1": - version "1.9.4" - resolved "https://registry.npmmirror.com/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" - integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== - dependencies: - "@types/express" "*" - -"@types/serve-static@*", "@types/serve-static@^1.13.10": - version "1.15.5" - resolved "https://registry.npmmirror.com/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033" - integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ== - dependencies: - "@types/http-errors" "*" - "@types/mime" "*" - "@types/node" "*" - -"@types/sockjs@^0.3.33": - version "0.3.36" - resolved "https://registry.npmmirror.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" - integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== - dependencies: - "@types/node" "*" - -"@types/unist@*", "@types/unist@^3.0.0": - version "3.0.2" - resolved "https://registry.npmmirror.com/@types/unist/-/unist-3.0.2.tgz#6dd61e43ef60b34086287f83683a5c1b2dc53d20" - integrity sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ== - -"@types/unist@^2", "@types/unist@^2.0.0": - version "2.0.10" - resolved "https://registry.npmmirror.com/@types/unist/-/unist-2.0.10.tgz#04ffa7f406ab628f7f7e97ca23e290cd8ab15efc" - integrity sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA== - -"@types/ws@^8.5.5": - version "8.5.10" - resolved "https://registry.npmmirror.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" - integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== - dependencies: - "@types/node" "*" - -"@types/yauzl@^2.9.1": - version "2.10.3" - resolved "https://registry.npmmirror.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999" - integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== - dependencies: - "@types/node" "*" - -"@ungap/structured-clone@^1.0.0": - version "1.2.0" - resolved "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" - integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== - -"@vercel/webpack-asset-relocator-loader@1.7.3": - version "1.7.3" - resolved "https://registry.npmmirror.com/@vercel/webpack-asset-relocator-loader/-/webpack-asset-relocator-loader-1.7.3.tgz#e65ca1fd9feb045039788f9b4710e5acc84b01b0" - integrity sha512-vizrI18v8Lcb1PmNNUBz7yxPxxXoOeuaVEjTG9MjvDrphjiSxFZrRJ5tIghk+qdLFRCXI5HBCshgobftbmrC5g== - dependencies: - resolve "^1.10.0" - -"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" - integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== - dependencies: - "@webassemblyjs/helper-numbers" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - -"@webassemblyjs/floating-point-hex-parser@1.11.6": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" - integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== - -"@webassemblyjs/helper-api-error@1.11.6": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" - integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== - -"@webassemblyjs/helper-buffer@1.11.6": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" - integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== - -"@webassemblyjs/helper-numbers@1.11.6": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" - integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== - dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.6" - "@webassemblyjs/helper-api-error" "1.11.6" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/helper-wasm-bytecode@1.11.6": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" - integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== - -"@webassemblyjs/helper-wasm-section@1.11.6": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" - integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - -"@webassemblyjs/ieee754@1.11.6": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" - integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.11.6": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" - integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.11.6": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" - integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== - -"@webassemblyjs/wasm-edit@^1.11.5": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" - integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/helper-wasm-section" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-opt" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" - "@webassemblyjs/wast-printer" "1.11.6" - -"@webassemblyjs/wasm-gen@1.11.6": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" - integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/ieee754" "1.11.6" - "@webassemblyjs/leb128" "1.11.6" - "@webassemblyjs/utf8" "1.11.6" - -"@webassemblyjs/wasm-opt@1.11.6": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" - integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-buffer" "1.11.6" - "@webassemblyjs/wasm-gen" "1.11.6" - "@webassemblyjs/wasm-parser" "1.11.6" - -"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" - integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@webassemblyjs/helper-api-error" "1.11.6" - "@webassemblyjs/helper-wasm-bytecode" "1.11.6" - "@webassemblyjs/ieee754" "1.11.6" - "@webassemblyjs/leb128" "1.11.6" - "@webassemblyjs/utf8" "1.11.6" - -"@webassemblyjs/wast-printer@1.11.6": - version "1.11.6" - resolved "https://registry.npmmirror.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" - integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== - dependencies: - "@webassemblyjs/ast" "1.11.6" - "@xtuc/long" "4.2.2" - -"@xmldom/xmldom@^0.8.8": - version "0.8.10" - resolved "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99" - integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.npmmirror.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - -abbrev@^1.0.0: - version "1.1.1" - resolved "https://registry.npmmirror.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -acorn-import-assertions@^1.9.0: - version "1.9.0" - resolved "https://registry.npmmirror.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" - integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== - -acorn-node@^1.3.0: - version "1.8.2" - resolved "https://registry.npmmirror.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" - integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== - dependencies: - acorn "^7.0.0" - acorn-walk "^7.0.0" - xtend "^4.0.2" - -acorn-walk@^7.0.0: - version "7.2.0" - resolved "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - -acorn@^7.0.0: - version "7.4.1" - resolved "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.11.2, acorn@^8.7.1, acorn@^8.8.2: - version "8.11.3" - resolved "https://registry.npmmirror.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" - integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== - -admin-lte@^3.2: - version "3.2.0" - resolved "https://registry.npmmirror.com/admin-lte/-/admin-lte-3.2.0.tgz#9be383a6418c2323828466ab95af0eb083e4a597" - integrity sha512-IDUfoU8jo9DVlB59lDEASAJXTesAEXDZ68DOHI3qg93r5ehVTkMl2x0ixgIyff8NiHeNYXpcOZh3tr6oGbvu9g== - dependencies: - "@fortawesome/fontawesome-free" "^5.15.4" - "@lgaitan/pace-progress" "^1.0.7" - "@sweetalert2/theme-bootstrap-4" "^5.0.8" - "@ttskch/select2-bootstrap4-theme" "^1.5.2" - bootstrap "^4.6.1" - bootstrap-colorpicker "^3.4.0" - bootstrap-slider "^11.0.2" - bootstrap-switch "3.3.4" - bootstrap4-duallistbox "^4.0.2" - bs-custom-file-input "^1.3.4" - bs-stepper "^1.7.0" - chart.js "^2.9.4" - codemirror "^5.65.1" - datatables.net "^1.11.4" - datatables.net-autofill-bs4 "^2.3.9" - datatables.net-bs4 "^1.11.4" - datatables.net-buttons-bs4 "^2.2.2" - datatables.net-colreorder-bs4 "^1.5.5" - datatables.net-fixedcolumns-bs4 "^4.0.1" - datatables.net-fixedheader-bs4 "^3.2.1" - datatables.net-keytable-bs4 "^2.6.4" - datatables.net-responsive-bs4 "^2.2.9" - datatables.net-rowgroup-bs4 "^1.1.4" - datatables.net-rowreorder-bs4 "^1.2.8" - datatables.net-scroller-bs4 "^2.0.5" - datatables.net-searchbuilder-bs4 "^1.3.1" - datatables.net-searchpanes-bs4 "^1.4.0" - datatables.net-select-bs4 "^1.3.4" - daterangepicker "^3.1.0" - dropzone "^5.9.3" - ekko-lightbox "^5.3.0" - fastclick "^1.0.6" - filterizr "^2.2.4" - flag-icon-css "^4.1.7" - flot "^4.2.2" - fs-extra "^10.0.0" - fullcalendar "^5.10.1" - icheck-bootstrap "^3.0.1" - inputmask "^5.0.7" - ion-rangeslider "^2.3.1" - jquery "^3.6.0" - jquery-knob-chif "^1.2.13" - jquery-mapael "^2.2.0" - jquery-mousewheel "^3.1.13" - jquery-ui-dist "^1.13.0" - jquery-validation "^1.19.3" - jqvmap-novulnerability "^1.5.1" - jsgrid "^1.5.3" - jszip "^3.7.1" - moment "^2.29.1" - overlayscrollbars "^1.13.1" - pdfmake "^0.2.4" - popper.js "^1.16.1" - raphael "^2.3.0" - select2 "^4.0.13" - sparklines "^1.3.0" - summernote "^0.8.20" - sweetalert2 "^11.4.0" - tempusdominus-bootstrap-4 "^5.39.0" - toastr "^2.1.4" - uplot "^1.6.18" - -agent-base@6, agent-base@^6.0.2: - version "6.0.2" - resolved "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - -agentkeepalive@^4.2.1: - version "4.5.0" - resolved "https://registry.npmmirror.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" - integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== - dependencies: - humanize-ms "^1.2.1" - -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.npmmirror.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - -ajv-formats@^2.1.1: - version "2.1.1" - resolved "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" - integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== - dependencies: - ajv "^8.0.0" - -ajv-keywords@^3.5.2: - version "3.5.2" - resolved "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== - -ajv-keywords@^5.1.0: - version "5.1.0" - resolved "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" - integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== - dependencies: - fast-deep-equal "^3.1.3" - -ajv@^6.12.5: - version "6.12.6" - resolved "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - 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" - -ajv@^8.0.0, ajv@^8.9.0: - version "8.12.0" - resolved "https://registry.npmmirror.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.npmmirror.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - integrity sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg== - -ansi-align@^3.0.1: - version "3.0.1" - resolved "https://registry.npmmirror.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" - integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== - dependencies: - string-width "^4.1.0" - -ansi-escapes@^4.3.0: - version "4.3.2" - resolved "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-html-community@^0.0.8: - version "0.0.8" - resolved "https://registry.npmmirror.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" - integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-regex@^6.0.1: - version "6.0.1" - resolved "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" - integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - -anymatch@~3.1.2: - version "3.1.3" - resolved "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -"aproba@^1.0.3 || ^2.0.0": - version "2.0.0" - resolved "https://registry.npmmirror.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" - integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== - -are-we-there-yet@^3.0.0: - version "3.0.1" - resolved "https://registry.npmmirror.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" - integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -aria-query@^5.3.0: - version "5.3.0" - resolved "https://registry.npmmirror.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" - integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== - dependencies: - dequal "^2.0.3" - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - -array-from@^2.1.1: - version "2.1.1" - resolved "https://registry.npmmirror.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" - integrity sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg== - -array-iterate@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/array-iterate/-/array-iterate-2.0.1.tgz#6efd43f8295b3fee06251d3d62ead4bd9805dd24" - integrity sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg== - -asar@^3.0.0: - version "3.2.0" - resolved "https://registry.npmmirror.com/asar/-/asar-3.2.0.tgz#e6edb5edd6f627ebef04db62f771c61bea9c1221" - integrity sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg== - dependencies: - chromium-pickle-js "^0.2.0" - commander "^5.0.0" - glob "^7.1.6" - minimatch "^3.0.4" - optionalDependencies: - "@types/glob" "^7.1.1" - -ast-transform@0.0.0: - version "0.0.0" - resolved "https://registry.npmmirror.com/ast-transform/-/ast-transform-0.0.0.tgz#74944058887d8283e189d954600947bc98fe0062" - integrity sha512-e/JfLiSoakfmL4wmTGPjv0HpTICVmxwXgYOB8x+mzozHL8v+dSfCbrJ8J8hJ0YBP0XcYu1aLZ6b/3TnxNK3P2A== - dependencies: - escodegen "~1.2.0" - esprima "~1.0.4" - through "~2.3.4" - -ast-types@^0.7.0: - version "0.7.8" - resolved "https://registry.npmmirror.com/ast-types/-/ast-types-0.7.8.tgz#902d2e0d60d071bdcd46dc115e1809ed11c138a9" - integrity sha512-RIOpVnVlltB6PcBJ5BMLx+H+6JJ/zjDGU0t7f0L6c2M1dqcK92VQopLBlPQ9R80AVXelfqYgjcPLtHtDbNFg0Q== - -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - -astro@^4.4.4: - version "4.4.4" - resolved "https://registry.npmmirror.com/astro/-/astro-4.4.4.tgz#5087cca5779d888e5c5b15690fd4738bc9c7bbdc" - integrity sha512-EZrDTN888w4sFKqavGsHu8jSaymyxNwnoqIq5NKlMG9WNU/Xn4Yn41pUdBuAOrgNzRp1NyXXhhV6GV1pN71V2Q== - dependencies: - "@astrojs/compiler" "^2.5.3" - "@astrojs/internal-helpers" "0.2.1" - "@astrojs/markdown-remark" "4.2.1" - "@astrojs/telemetry" "3.0.4" - "@babel/core" "^7.23.3" - "@babel/generator" "^7.23.3" - "@babel/parser" "^7.23.3" - "@babel/plugin-transform-react-jsx" "^7.22.5" - "@babel/traverse" "^7.23.3" - "@babel/types" "^7.23.3" - "@medv/finder" "^3.1.0" - "@types/babel__core" "^7.20.4" - acorn "^8.11.2" - aria-query "^5.3.0" - axobject-query "^4.0.0" - boxen "^7.1.1" - chokidar "^3.5.3" - ci-info "^4.0.0" - clsx "^2.0.0" - common-ancestor-path "^1.0.1" - cookie "^0.6.0" - cssesc "^3.0.0" - debug "^4.3.4" - deterministic-object-hash "^2.0.1" - devalue "^4.3.2" - diff "^5.1.0" - dlv "^1.1.3" - dset "^3.1.3" - es-module-lexer "^1.4.1" - esbuild "^0.19.6" - estree-walker "^3.0.3" - execa "^8.0.1" - fast-glob "^3.3.2" - flattie "^1.1.0" - github-slugger "^2.0.0" - gray-matter "^4.0.3" - html-escaper "^3.0.3" - http-cache-semantics "^4.1.1" - js-yaml "^4.1.0" - kleur "^4.1.4" - magic-string "^0.30.3" - mdast-util-to-hast "13.0.2" - mime "^3.0.0" - ora "^7.0.1" - p-limit "^5.0.0" - p-queue "^8.0.1" - path-to-regexp "^6.2.1" - preferred-pm "^3.1.2" - prompts "^2.4.2" - rehype "^13.0.1" - resolve "^1.22.4" - semver "^7.5.4" - shikiji "^0.9.19" - shikiji-core "^0.9.19" - string-width "^7.0.0" - strip-ansi "^7.1.0" - tsconfck "^3.0.0" - unist-util-visit "^5.0.0" - vfile "^6.0.1" - vite "^5.1.2" - vitefu "^0.2.5" - which-pm "^2.1.1" - yargs-parser "^21.1.1" - zod "^3.22.4" - optionalDependencies: - sharp "^0.32.6" - -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - -author-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/author-regex/-/author-regex-1.0.0.tgz#d08885be6b9bbf9439fe087c76287245f0a81450" - integrity sha512-KbWgR8wOYRAPekEmMXrYYdc7BRyhn2Ftk7KWfMUnQ43hFdojWEFRxhhRUm3/OFEdPa1r0KAvTTg9YQK57xTe0g== - -axobject-query@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/axobject-query/-/axobject-query-4.0.0.tgz#04a4c90dce33cc5d606c76d6216e3b250ff70dab" - integrity sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw== - dependencies: - dequal "^2.0.3" - -b4a@^1.6.4: - version "1.6.6" - resolved "https://registry.npmmirror.com/b4a/-/b4a-1.6.6.tgz#a4cc349a3851987c3c4ac2d7785c18744f6da9ba" - integrity sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg== - -bail@^2.0.0: - version "2.0.2" - resolved "https://registry.npmmirror.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" - integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -bare-events@^2.0.0, bare-events@^2.2.0: - version "2.2.0" - resolved "https://registry.npmmirror.com/bare-events/-/bare-events-2.2.0.tgz#a7a7263c107daf8b85adf0b64f908503454ab26e" - integrity sha512-Yyyqff4PIFfSuthCZqLlPISTWHmnQxoPuAvkmgzsJEmG3CesdIv6Xweayl0JkCZJSB2yYIdJyEz97tpxNhgjbg== - -bare-fs@^2.1.1: - version "2.2.0" - resolved "https://registry.npmmirror.com/bare-fs/-/bare-fs-2.2.0.tgz#e23ab841be0e08b29ad2933281ce4ad5ddd60384" - integrity sha512-+VhW202E9eTVGkX7p+TNXtZC4RTzj9JfJW7PtfIbZ7mIQ/QT9uOafQTx7lx2n9ERmWsXvLHF4hStAFn4gl2mQw== - dependencies: - bare-events "^2.0.0" - bare-os "^2.0.0" - bare-path "^2.0.0" - streamx "^2.13.0" - -bare-os@^2.0.0, bare-os@^2.1.0: - version "2.2.0" - resolved "https://registry.npmmirror.com/bare-os/-/bare-os-2.2.0.tgz#24364692984d0bd507621754781b31d7872736b2" - integrity sha512-hD0rOPfYWOMpVirTACt4/nK8mC55La12K5fY1ij8HAdfQakD62M+H4o4tpfKzVGLgRDTuk3vjA4GqGXXCeFbag== - -bare-path@^2.0.0, bare-path@^2.1.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/bare-path/-/bare-path-2.1.0.tgz#830f17fd39842813ca77d211ebbabe238a88cb4c" - integrity sha512-DIIg7ts8bdRKwJRJrUMy/PICEaQZaPGZ26lsSx9MJSwIhSrcdHn7/C8W+XmnG/rKi6BaRcz+JO00CjZteybDtw== - dependencies: - bare-os "^2.1.0" - -base-64@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/base-64/-/base-64-1.0.0.tgz#09d0f2084e32a3fd08c2475b973788eee6ae8f4a" - integrity sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg== - -base64-js@1.3.1: - version "1.3.1" - resolved "https://registry.npmmirror.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" - integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== - -base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1: - version "1.5.1" - resolved "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -batch@0.6.1: - version "0.6.1" - resolved "https://registry.npmmirror.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== - -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -bl@^4.0.3, bl@^4.1.0: - version "4.1.0" - resolved "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" - integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - -bl@^5.0.0: - version "5.1.0" - resolved "https://registry.npmmirror.com/bl/-/bl-5.1.0.tgz#183715f678c7188ecef9fe475d90209400624273" - integrity sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ== - dependencies: - buffer "^6.0.3" - inherits "^2.0.4" - readable-stream "^3.4.0" - -bluebird@^3.1.1: - version "3.7.2" - resolved "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -body-parser@1.20.1: - version "1.20.1" - resolved "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" - integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== - dependencies: - bytes "3.1.2" - content-type "~1.0.4" - 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.11.0" - raw-body "2.5.1" - type-is "~1.6.18" - unpipe "1.0.0" - -bonjour-service@^1.0.11: - version "1.2.1" - resolved "https://registry.npmmirror.com/bonjour-service/-/bonjour-service-1.2.1.tgz#eb41b3085183df3321da1264719fbada12478d02" - integrity sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw== - dependencies: - fast-deep-equal "^3.1.3" - multicast-dns "^7.2.5" - -boolbase@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== - -boolean@^3.0.1: - version "3.2.0" - resolved "https://registry.npmmirror.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" - integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== - -bootstrap-colorpicker@^3.4.0: - version "3.4.0" - resolved "https://registry.npmmirror.com/bootstrap-colorpicker/-/bootstrap-colorpicker-3.4.0.tgz#3d1873071542755a9b31cf5b314f771e2fcc7727" - integrity sha512-7vA0hvLrat3ptobEKlT9+6amzBUJcDAoh6hJRQY/AD+5dVZYXXf1ivRfrTwmuwiVLJo9rZwM8YB4lYzp6agzqg== - dependencies: - bootstrap ">=4.0" - jquery ">=2.2" - popper.js ">=1.10" - -bootstrap-slider@^11.0.2: - version "11.0.2" - resolved "https://registry.npmmirror.com/bootstrap-slider/-/bootstrap-slider-11.0.2.tgz#4b167c165f322339cc66f0c7166b5a39e289e251" - integrity sha512-CdwS+Z6X79OkLes9RfDgPB9UIY/+81wTkm6ktdSB6hdyiRbjJLFQIjZdnEr55tDyXZfgC7U6yeSXkNN9ZdGqjA== - -bootstrap-switch@3.3.4: - version "3.3.4" - resolved "https://registry.npmmirror.com/bootstrap-switch/-/bootstrap-switch-3.3.4.tgz#70e0aeb2a877c0dc766991de108e2170fc29a2ff" - integrity sha512-7YQo+Ir6gCUqC36FFp1Zvec5dRF/+obq+1drMtdIMi9Xc84Kx63Evi0kdcp4HfiGsZpiz6IskyYDNlSKcxsL7w== - -bootstrap4-duallistbox@^4.0.2: - version "4.0.2" - resolved "https://registry.npmmirror.com/bootstrap4-duallistbox/-/bootstrap4-duallistbox-4.0.2.tgz#c6942e34a39d0d05e436d51ebaf31c9349f119d3" - integrity sha512-vQdANVE2NN0HMaZO9qWJy0C7u04uTpAmtUGO3KLq3xAZKCboJweQ437hDTszI6pbYV2olJCGZMbdhvIkBNGeGQ== - -bootstrap@>=4.0: - version "5.3.3" - resolved "https://registry.npmmirror.com/bootstrap/-/bootstrap-5.3.3.tgz#de35e1a765c897ac940021900fcbb831602bac38" - integrity sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg== - -bootstrap@^4.6.1: - version "4.6.2" - resolved "https://registry.npmmirror.com/bootstrap/-/bootstrap-4.6.2.tgz#8e0cd61611728a5bf65a3a2b8d6ff6c77d5d7479" - integrity sha512-51Bbp/Uxr9aTuy6ca/8FbFloBUJZLHwnhTcnjIeRn2suQWsWzcuJhGjKDB5eppVte/8oCdOL3VuwxvZDUggwGQ== - -boxen@^7.1.1: - version "7.1.1" - resolved "https://registry.npmmirror.com/boxen/-/boxen-7.1.1.tgz#f9ba525413c2fec9cdb88987d835c4f7cad9c8f4" - integrity sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog== - dependencies: - ansi-align "^3.0.1" - camelcase "^7.0.1" - chalk "^5.2.0" - cli-boxes "^3.0.0" - string-width "^5.1.2" - type-fest "^2.13.0" - widest-line "^4.0.1" - wrap-ansi "^8.1.0" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - -braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -brfs@^2.0.0, brfs@^2.0.2: - version "2.0.2" - resolved "https://registry.npmmirror.com/brfs/-/brfs-2.0.2.tgz#44237878fa82aa479ce4f5fe2c1796ec69f07845" - integrity sha512-IrFjVtwu4eTJZyu8w/V2gxU7iLTtcHih67sgEdzrhjLBMHp2uYefUBfdM4k2UvcuWMgV7PQDZHSLeNWnLFKWVQ== - dependencies: - quote-stream "^1.0.1" - resolve "^1.1.5" - static-module "^3.0.2" - through2 "^2.0.0" - -brotli@^1.2.0: - version "1.3.3" - resolved "https://registry.npmmirror.com/brotli/-/brotli-1.3.3.tgz#7365d8cc00f12cf765d2b2c898716bcf4b604d48" - integrity sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg== - dependencies: - base64-js "^1.1.2" - -browser-resolve@^1.8.1: - version "1.11.3" - resolved "https://registry.npmmirror.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" - integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== - dependencies: - resolve "1.1.7" - -browserify-optional@^1.0.1: - version "1.0.1" - resolved "https://registry.npmmirror.com/browserify-optional/-/browserify-optional-1.0.1.tgz#1e13722cfde0d85f121676c2a72ced533a018869" - integrity sha512-VrhjbZ+Ba5mDiSYEuPelekQMfTbhcA2DhLk2VQWqdcCROWeFqlTcXZ7yfRkXCIl8E+g4gINJYJiRB7WEtfomAQ== - dependencies: - ast-transform "0.0.0" - ast-types "^0.7.0" - browser-resolve "^1.8.1" - -browserslist@^4.21.10, browserslist@^4.22.2: - version "4.23.0" - resolved "https://registry.npmmirror.com/browserslist/-/browserslist-4.23.0.tgz#8f3acc2bbe73af7213399430890f86c63a5674ab" - integrity sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ== - dependencies: - caniuse-lite "^1.0.30001587" - electron-to-chromium "^1.4.668" - node-releases "^2.0.14" - update-browserslist-db "^1.0.13" - -bs-custom-file-input@^1.3.4: - version "1.3.4" - resolved "https://registry.npmmirror.com/bs-custom-file-input/-/bs-custom-file-input-1.3.4.tgz#c275cb8d4f1c02ba026324292509fa9a747dbda8" - integrity sha512-NBsQzTnef3OW1MvdKBbMHAYHssCd613MSeJV7z2McXznWtVMnJCy7Ckyc+PwxV6Pk16cu6YBcYWh/ZE0XWNKCA== - -bs-stepper@^1.7.0: - version "1.7.0" - resolved "https://registry.npmmirror.com/bs-stepper/-/bs-stepper-1.7.0.tgz#bfa4cc51c4e67957caae57f5bdcba1977186bac1" - integrity sha512-+DX7UKKgw2GI6ucsSCRd19VHYrxf/8znRCLs1lQVVLxz+h7EqgIOxoHcJ0/QTaaNoR9Cwg78ydo6hXIasyd3LA== - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== - -buffer-equal@0.0.1: - version "0.0.1" - resolved "https://registry.npmmirror.com/buffer-equal/-/buffer-equal-0.0.1.tgz#91bc74b11ea405bc916bc6aa908faafa5b4aac4b" - integrity sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA== - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -buffer@^5.5.0: - version "5.7.1" - resolved "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - -buffer@^6.0.3: - version "6.0.3" - resolved "https://registry.npmmirror.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== - -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - -cacache@^16.1.0: - version "16.1.3" - resolved "https://registry.npmmirror.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" - integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== - dependencies: - "@npmcli/fs" "^2.1.0" - "@npmcli/move-file" "^2.0.0" - chownr "^2.0.0" - fs-minipass "^2.1.0" - glob "^8.0.1" - infer-owner "^1.0.4" - lru-cache "^7.7.1" - minipass "^3.1.6" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - mkdirp "^1.0.4" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^9.0.0" - tar "^6.1.11" - unique-filename "^2.0.0" - -cacheable-lookup@^5.0.3: - version "5.0.4" - resolved "https://registry.npmmirror.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" - integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== - -cacheable-request@^7.0.2: - version "7.0.4" - resolved "https://registry.npmmirror.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" - integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^4.0.0" - lowercase-keys "^2.0.0" - normalize-url "^6.0.1" - responselike "^2.0.0" - -call-bind@^1.0.2, call-bind@^1.0.6: - version "1.0.7" - resolved "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - 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" - -camel-case@^4.1.2: - version "4.1.2" - resolved "https://registry.npmmirror.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" - integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== - dependencies: - pascal-case "^3.1.2" - tslib "^2.0.3" - -camelcase@^7.0.1: - version "7.0.1" - resolved "https://registry.npmmirror.com/camelcase/-/camelcase-7.0.1.tgz#f02e50af9fd7782bc8b88a3558c32fd3a388f048" - integrity sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw== - -caniuse-lite@^1.0.30001587: - version "1.0.30001589" - resolved "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz#7ad6dba4c9bf6561aec8291976402339dc157dfb" - integrity sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg== - -ccount@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" - integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== - -chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - 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" - -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: - version "4.1.2" - resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^5.0.0, chalk@^5.2.0, chalk@^5.3.0: - version "5.3.0" - resolved "https://registry.npmmirror.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" - integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== - -character-entities-html4@^2.0.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" - integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== - -character-entities-legacy@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" - integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== - -character-entities@^2.0.0: - version "2.0.2" - resolved "https://registry.npmmirror.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" - integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== - -chart.js@^2.9.4: - version "2.9.4" - resolved "https://registry.npmmirror.com/chart.js/-/chart.js-2.9.4.tgz#0827f9563faffb2dc5c06562f8eb10337d5b9684" - integrity sha512-B07aAzxcrikjAPyV+01j7BmOpxtQETxTSlQ26BEYJ+3iUkbNKaOJ/nDbT6JjyqYxseM0ON12COHYdU2cTIjC7A== - dependencies: - chartjs-color "^2.1.0" - moment "^2.10.2" - -chartjs-color-string@^0.6.0: - version "0.6.0" - resolved "https://registry.npmmirror.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71" - integrity sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A== - dependencies: - color-name "^1.0.0" - -chartjs-color@^2.1.0: - version "2.4.1" - resolved "https://registry.npmmirror.com/chartjs-color/-/chartjs-color-2.4.1.tgz#6118bba202fe1ea79dd7f7c0f9da93467296c3b0" - integrity sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w== - dependencies: - chartjs-color-string "^0.6.0" - color-convert "^1.9.3" - -chokidar@^3.5.3: - version "3.6.0" - resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" - integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== - 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" - optionalDependencies: - fsevents "~2.3.2" - -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - -chrome-trace-event@^1.0.2, chrome-trace-event@^1.0.3: - version "1.0.3" - resolved "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== - -chromium-pickle-js@^0.2.0: - version "0.2.0" - resolved "https://registry.npmmirror.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205" - integrity sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw== - -ci-info@^3.8.0: - version "3.9.0" - resolved "https://registry.npmmirror.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" - integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== - -ci-info@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/ci-info/-/ci-info-4.0.0.tgz#65466f8b280fc019b9f50a5388115d17a63a44f2" - integrity sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg== - -clean-css@^5.2.2: - version "5.3.3" - resolved "https://registry.npmmirror.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd" - integrity sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg== - dependencies: - source-map "~0.6.0" - -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.npmmirror.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - -cli-boxes@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/cli-boxes/-/cli-boxes-3.0.0.tgz#71a10c716feeba005e4504f36329ef0b17cf3145" - integrity sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g== - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-cursor@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" - integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== - dependencies: - restore-cursor "^4.0.0" - -cli-spinners@^2.5.0, cli-spinners@^2.9.0: - version "2.9.2" - resolved "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" - integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== - -cli-truncate@^2.1.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" - integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== - dependencies: - slice-ansi "^3.0.0" - string-width "^4.2.0" - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.npmmirror.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -cliui@^8.0.1: - version "8.0.1" - resolved "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" - integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.1" - wrap-ansi "^7.0.0" - -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.npmmirror.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - -clone-response@^1.0.2: - version "1.0.3" - resolved "https://registry.npmmirror.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" - integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== - dependencies: - mimic-response "^1.0.0" - -clone@^1.0.2, clone@^1.0.4: - version "1.0.4" - resolved "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== - -clsx@^2.0.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb" - integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg== - -codemirror@^5.65.1: - version "5.65.16" - resolved "https://registry.npmmirror.com/codemirror/-/codemirror-5.65.16.tgz#efc0661be6bf4988a6a1c2fe6893294638cdb334" - integrity sha512-br21LjYmSlVL0vFCPWPfhzUCT34FM/pAdK7rRIZwa0rrtrIdotvP4Oh4GUHsu2E3IrQMCfRkL/fN3ytMNxVQvg== - -color-convert@^1.9.0, color-convert@^1.9.3: - version "1.9.3" - resolved "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - -color-name@^1.0.0, color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.9.0: - version "1.9.1" - resolved "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" - integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.npmmirror.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - -color@^4.2.3: - version "4.2.3" - resolved "https://registry.npmmirror.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" - integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== - dependencies: - color-convert "^2.0.1" - color-string "^1.9.0" - -colorette@^2.0.10, colorette@^2.0.19: - version "2.0.20" - resolved "https://registry.npmmirror.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" - integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== - -comma-separated-tokens@^2.0.0: - version "2.0.3" - resolved "https://registry.npmmirror.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" - integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg== - -commander@^2.20.0: - version "2.20.3" - resolved "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commander@^4.1.1: - version "4.1.1" - resolved "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" - integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== - -commander@^5.0.0: - version "5.1.0" - resolved "https://registry.npmmirror.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" - integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== - -commander@^8.3.0: - version "8.3.0" - resolved "https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" - integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== - -commander@^9.4.0: - version "9.5.0" - resolved "https://registry.npmmirror.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" - integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== - -common-ancestor-path@^1.0.1: - version "1.0.1" - resolved "https://registry.npmmirror.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" - integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== - -compare-version@^0.1.2: - version "0.1.2" - resolved "https://registry.npmmirror.com/compare-version/-/compare-version-0.1.2.tgz#0162ec2d9351f5ddd59a9202cba935366a725080" - integrity sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A== - -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.npmmirror.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - 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" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -concat-stream@~1.6.0: - version "1.6.2" - resolved "https://registry.npmmirror.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -connect-history-api-fallback@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" - integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== - -console-control-strings@^1.1.0: - version "1.1.0" - resolved "https://registry.npmmirror.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== - -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@~1.0.4: - version "1.0.5" - resolved "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" - integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== - -convert-source-map@^1.5.1: - version "1.9.0" - resolved "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== - -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.npmmirror.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== - -cookie@^0.6.0: - version "0.6.0" - resolved "https://registry.npmmirror.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051" - integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw== - -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -cross-dirname@^0.1.0: - version "0.1.0" - resolved "https://registry.npmmirror.com/cross-dirname/-/cross-dirname-0.1.0.tgz#b899599f30a5389f59e78c150e19f957ad16a37c" - integrity sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q== - -cross-spawn-windows-exe@^1.1.0, cross-spawn-windows-exe@^1.2.0: - version "1.2.0" - resolved "https://registry.npmmirror.com/cross-spawn-windows-exe/-/cross-spawn-windows-exe-1.2.0.tgz#46253b0f497676e766faf4a7061004618b5ac5ec" - integrity sha512-mkLtJJcYbDCxEG7Js6eUnUNndWjyUZwJ3H7bErmmtOYU/Zb99DyUkpamuIZE0b3bhmJyZ7D90uS6f+CGxRRjOw== - dependencies: - "@malept/cross-spawn-promise" "^1.1.0" - is-wsl "^2.2.0" - which "^2.0.2" - -cross-spawn@^6.0.0, cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^7.0.1, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -cross-zip@^4.0.0: - version "4.0.1" - resolved "https://registry.npmmirror.com/cross-zip/-/cross-zip-4.0.1.tgz#1bbf5d3b0e5a77b5f5ca130a6d38f770786e1270" - integrity sha512-n63i0lZ0rvQ6FXiGQ+/JFCKAUyPFhLQYJIqKaa+tSJtfKeULF/IDNDAbdnSIxgS4NTuw2b0+lj8LzfITuq+ZxQ== - -crypto-js@^4.2.0: - version "4.2.0" - resolved "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" - integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== - -css-loader@^6.0.0: - version "6.10.0" - resolved "https://registry.npmmirror.com/css-loader/-/css-loader-6.10.0.tgz#7c172b270ec7b833951b52c348861206b184a4b7" - integrity sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw== - dependencies: - icss-utils "^5.1.0" - postcss "^8.4.33" - postcss-modules-extract-imports "^3.0.0" - postcss-modules-local-by-default "^4.0.4" - postcss-modules-scope "^3.1.1" - postcss-modules-values "^4.0.0" - postcss-value-parser "^4.2.0" - semver "^7.5.4" - -css-select@^4.1.3: - version "4.3.0" - resolved "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" - 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" - -css-what@^6.0.1: - version "6.1.0" - resolved "https://registry.npmmirror.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" - integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== - -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== - -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.npmmirror.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" - -dash-ast@^2.0.1: - version "2.0.1" - resolved "https://registry.npmmirror.com/dash-ast/-/dash-ast-2.0.1.tgz#8d0fd2e601c59bf874cc22877ee7dd889f54dee8" - integrity sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ== - -datatables.net-autofill-bs4@^2.3.9: - version "2.7.0" - resolved "https://registry.npmmirror.com/datatables.net-autofill-bs4/-/datatables.net-autofill-bs4-2.7.0.tgz#fbe87395adc9681e292873a3325e3680c9b8fc16" - integrity sha512-vVMvZeioGhmvd7URJAvXnOYod/ZWKhCDsLB7Onw7SaJmeawf4CaY7lAU79ee0531Kf0FWuLOKlZcaEt8p1w4qQ== - dependencies: - datatables.net-autofill "2.7.0" - datatables.net-bs4 ">=1.11" - jquery ">=1.7" - -datatables.net-autofill@2.7.0: - version "2.7.0" - resolved "https://registry.npmmirror.com/datatables.net-autofill/-/datatables.net-autofill-2.7.0.tgz#0d496bd7e059283e11fdfb24ce27e2de673defe9" - integrity sha512-zy7Jglke0VXu7RzyGYufMLLlSJkhr+rWJubBw0cozlaXtrqR2CNjY8LBBJA19GwBeoDCNrbojNge0YN8l44b8g== - dependencies: - datatables.net ">=1.11" - jquery ">=1.7" - -datatables.net-bs4@>=1.10.25, datatables.net-bs4@>=1.11, datatables.net-bs4@>=1.11.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/datatables.net-bs4/-/datatables.net-bs4-2.0.0.tgz#5f56efbb757be56ce1e52793d8f5ac73a30c0c66" - integrity sha512-yvxPNBmruLyT39oJfoVFdIHYguqZD+YsOUzWlegu0R+C2MHkJossKVypXlkTvJTK8vf+cy1I69dZnpYNrY/leA== - dependencies: - datatables.net "2.0.0" - jquery ">=1.7" - -datatables.net-bs4@^1.11.4, datatables.net-bs4@^1.13.0: - version "1.13.10" - resolved "https://registry.npmmirror.com/datatables.net-bs4/-/datatables.net-bs4-1.13.10.tgz#fa3970d1e57daa64ce8df444517bf3c1df14d435" - integrity sha512-t6zTyuoUhOwr7ONJGYQdN57st/G3iA6Ju9A56Q2vtoJO03XBeFjpAK3sZ0R02Yz35qtzZXgBtSU8bdOiXL2xwA== - dependencies: - datatables.net "1.13.8" - jquery ">=1.7" - -datatables.net-buttons-bs4@^2.2.2: - version "2.4.3" - resolved "https://registry.npmmirror.com/datatables.net-buttons-bs4/-/datatables.net-buttons-bs4-2.4.3.tgz#d4a720b45e3f2ec3c776536a9ac057f57861438f" - integrity sha512-j/4ZFyGtRcNNknwN/+evClgmG/OZIAF+xEhfzpwFzOz7Mk/0j7GyDGO+V5tg5FTOyxyK03NSu0cT0hvPglDpmA== - dependencies: - datatables.net-bs4 "^1.13.0" - datatables.net-buttons "2.4.3" - jquery ">=1.7" - -datatables.net-buttons@2.4.3: - version "2.4.3" - resolved "https://registry.npmmirror.com/datatables.net-buttons/-/datatables.net-buttons-2.4.3.tgz#38ceb492aa7a069f6b4334ae8fbc8134bfdf7363" - integrity sha512-xoHD6I2kxnU/CEp97Ar0lSnAL1siucQ/5Q/otGWWfWE2VN0o4n5C2h2Ot/ZCS8kxbEHBGd873Bc2xPdJH87yOw== - dependencies: - datatables.net "^1.13.0" - jquery ">=1.7" - -datatables.net-colreorder-bs4@^1.5.5: - version "1.7.2" - resolved "https://registry.npmmirror.com/datatables.net-colreorder-bs4/-/datatables.net-colreorder-bs4-1.7.2.tgz#53ea3150c3a34bf67e01f159f640f8fc2c9dc201" - integrity sha512-iVlvHwD82ZCDuaDaAXsD6OukNjgWNe44+f0yu43a1bOwK1ncXYiBSohlEuIhynHnESexN2vg4saj4a0rEMNx+w== - dependencies: - datatables.net-bs4 "^1.13.0" - datatables.net-colreorder "1.7.2" - jquery ">=1.7" - -datatables.net-colreorder@1.7.2: - version "1.7.2" - resolved "https://registry.npmmirror.com/datatables.net-colreorder/-/datatables.net-colreorder-1.7.2.tgz#9806f165048daecb2fe93a482c744efc6e649c06" - integrity sha512-F8TYMFXtWLtsjciwS7hkP/Fbp3XS6WHuHLc+iMFtQqiQmbMo/59GK7YSxKuxSoqTTJU/opaPXQYjODnIuNEc/g== - dependencies: - datatables.net "^1.13.0" - jquery ">=1.7" - -datatables.net-fixedcolumns-bs4@^4.0.1: - version "4.3.1" - resolved "https://registry.npmmirror.com/datatables.net-fixedcolumns-bs4/-/datatables.net-fixedcolumns-bs4-4.3.1.tgz#00c691ff963b69a77edcb23f440a3869c51f3874" - integrity sha512-/JkG5vAil/KEB+FFvl0kcvqFMVHXZgq+6yzImlZaHE3NSApV8L5eWnyIQakShczPcxrU9iozGOxvtRRIFl0rqA== - dependencies: - datatables.net-bs4 "^1.13.0" - datatables.net-fixedcolumns "4.3.1" - jquery ">=1.7" - -datatables.net-fixedcolumns@4.3.1: - version "4.3.1" - resolved "https://registry.npmmirror.com/datatables.net-fixedcolumns/-/datatables.net-fixedcolumns-4.3.1.tgz#a6aa2831471bf9e78b21100ae862f5f52149647b" - integrity sha512-K5hEr5PIIHFMLd2sR9CBw3RRhf0nJbbsc5NHWnfjUUtnr9d808xbifuej3TpdKOtGeRJgAnRktiL9f30sM32CQ== - dependencies: - datatables.net "^1.13.0" - jquery ">=1.7" - -datatables.net-fixedheader-bs4@^3.2.1: - version "3.4.1" - resolved "https://registry.npmmirror.com/datatables.net-fixedheader-bs4/-/datatables.net-fixedheader-bs4-3.4.1.tgz#567aa2e41cd3972f8ce5eb76173ad397e74b597d" - integrity sha512-Oq6yCpswdFRn+nwrjOjD0nmpH3F0glz/ppgdT8vA6U/8qkguze4d3qZyEYd7osqrCeX9ccUZR9ptpqRYb6sVtg== - dependencies: - datatables.net-bs4 "^1.13.0" - datatables.net-fixedheader "3.4.1" - jquery ">=1.7" - -datatables.net-fixedheader@3.4.1: - version "3.4.1" - resolved "https://registry.npmmirror.com/datatables.net-fixedheader/-/datatables.net-fixedheader-3.4.1.tgz#db8b250e7819da989baa5df8c757a5a382754752" - integrity sha512-c9FJAShG5r8RJDIszWQvMFe6Ie+njfbHB9GhzOPjEF7zhbsMUQEkoYq1qW3ppOxY5psadDrT+D3f4iGM589u6w== - dependencies: - datatables.net "^1.13.0" - jquery ">=1.7" - -datatables.net-keytable-bs4@^2.6.4: - version "2.12.0" - resolved "https://registry.npmmirror.com/datatables.net-keytable-bs4/-/datatables.net-keytable-bs4-2.12.0.tgz#07f0d083d23e335c3043835623f5eeabd3e3404b" - integrity sha512-4Yn+EhUpz2ZjVmGiPUcS74hlAAtlEtXKCEoXP4tVnF/fICnvAoAr+iNC6GOUldh2LoA87/v/X5QreHA48W8z9Q== - dependencies: - datatables.net-bs4 ">=1.11.0" - datatables.net-keytable "2.12.0" - jquery ">=1.7" - -datatables.net-keytable@2.12.0: - version "2.12.0" - resolved "https://registry.npmmirror.com/datatables.net-keytable/-/datatables.net-keytable-2.12.0.tgz#9e95012b29c96a2dbd21c76c9ce45c5f685709cf" - integrity sha512-vijxMw7ZIB/fDe5FWGiDqe8CPiPXg3lvqK4lL48vQh1zoiE3+0C3za82qM9g/2zbwtIXmOLwBZc2ivrErNVPkA== - dependencies: - datatables.net ">=1.11.0" - jquery ">=1.7" - -datatables.net-responsive-bs4@^2.2.9: - version "2.5.1" - resolved "https://registry.npmmirror.com/datatables.net-responsive-bs4/-/datatables.net-responsive-bs4-2.5.1.tgz#1f5d615de94b07558bc2df0174b546fcfdc30255" - integrity sha512-cOVCG9zRioqJiqZUPXel5/vxKGt8EFhxgzVafDNy2hY3ZO+UMMuRKcs2br/QMoojbXzpKNf2rL/lM7NoXKVKZA== - dependencies: - datatables.net-bs4 "^1.13.0" - datatables.net-responsive "2.5.1" - jquery ">=1.7" - -datatables.net-responsive@2.5.1: - version "2.5.1" - resolved "https://registry.npmmirror.com/datatables.net-responsive/-/datatables.net-responsive-2.5.1.tgz#9747a3c6e6b59abd33174e1890356357151bb8c0" - integrity sha512-hyJb2faIzEWUX5Yn4HOSq/6NNB9SXDVbI4OU9ny+XU/2ghZEz4676spOgzpDHTdWvCfM+t1mbUsT70fDiTTr9w== - dependencies: - datatables.net "^1.13.0" - jquery ">=1.7" - -datatables.net-rowgroup-bs4@^1.1.4: - version "1.5.0" - resolved "https://registry.npmmirror.com/datatables.net-rowgroup-bs4/-/datatables.net-rowgroup-bs4-1.5.0.tgz#31e5ad371f187c079df8dc4275ef8b441bf4cab2" - integrity sha512-1CTXF2BsYwWYg3tjUw6cayRYsbt61DD3ms/8Evuz3/qrlKO6txdbp8OsmBNQJwT6qgEjPWx6tuGi8xdHGOey3A== - dependencies: - datatables.net-bs4 ">=1.11.0" - datatables.net-rowgroup "1.5.0" - jquery ">=1.7" - -datatables.net-rowgroup@1.5.0: - version "1.5.0" - resolved "https://registry.npmmirror.com/datatables.net-rowgroup/-/datatables.net-rowgroup-1.5.0.tgz#644b5fd7f7fc6fdb1bc4e42dfa0a9203740f31ca" - integrity sha512-V/CLJu7rMjxwfhZAv59emZOPw58cwnYmd8NXTTJSnqBDgOZsaC9mtVo0ejBpdqvNw5WmMPy3AJceH+ay6JQ3hA== - dependencies: - datatables.net ">=1.11.0" - jquery ">=1.7" - -datatables.net-rowreorder-bs4@^1.2.8: - version "1.5.0" - resolved "https://registry.npmmirror.com/datatables.net-rowreorder-bs4/-/datatables.net-rowreorder-bs4-1.5.0.tgz#5006eb235d9dfd78974d113df25a6b921328f013" - integrity sha512-4C9tzLRaisO+IOdJ1n6NRQ8ak0zC0IbX50Ed5hg+5PT7VJLI9klH+GBwE6hpF28UTWk7E6somzOwnRHLexqXdA== - dependencies: - datatables.net-bs4 ">=1.11.0" - datatables.net-rowreorder "1.5.0" - jquery ">=1.7" - -datatables.net-rowreorder@1.5.0: - version "1.5.0" - resolved "https://registry.npmmirror.com/datatables.net-rowreorder/-/datatables.net-rowreorder-1.5.0.tgz#f8b5242bd97df6b9522573378e98e8c7d64002dd" - integrity sha512-Kkq57tdJHrUCYkS8vywhL5tqKtl2q3K3p8A6wkIdwMX2b8YVjtywhQbXvvVLZnlMQsdX194FXVK1AgAwcm4hFg== - dependencies: - datatables.net ">=1.11.0" - jquery ">=1.7" - -datatables.net-scroller-bs4@^2.0.5: - version "2.4.0" - resolved "https://registry.npmmirror.com/datatables.net-scroller-bs4/-/datatables.net-scroller-bs4-2.4.0.tgz#5f97e0db6b683c5480d10a64125aa603a2af81fa" - integrity sha512-IARngb/lA/NzAozNj2lu+hNYVzuiuvTBd5Bc4ClWUoUN9L4rZ2m4KXlAOQkV6dYVAJrDdU2NH+kvQ4l6BKB/ow== - dependencies: - datatables.net-bs4 ">=1.11.0" - datatables.net-scroller "2.4.0" - jquery ">=1.7" - -datatables.net-scroller@2.4.0: - version "2.4.0" - resolved "https://registry.npmmirror.com/datatables.net-scroller/-/datatables.net-scroller-2.4.0.tgz#932b630b7ade6ec7d8d34e062bd718aae599ec79" - integrity sha512-SrIYyBD74Pjj/yajTu7i0YtGFiUMHWKlnO59kwIZoZhCyCh6d7S+e9HF/oT1tXjNvFWfvwA2wbA+PF20fDRA2g== - dependencies: - datatables.net ">=1.11.0" - jquery ">=1.7" - -datatables.net-searchbuilder-bs4@^1.3.1: - version "1.7.0" - resolved "https://registry.npmmirror.com/datatables.net-searchbuilder-bs4/-/datatables.net-searchbuilder-bs4-1.7.0.tgz#945b1f3e1ace03e3b5f0de5e31c5ceeeaeeaaa8a" - integrity sha512-uXvCl5tWikpAltKRIYtFltiG01Ned1wAVMg3i7i+ZRRhMgndOW26lUByc8yDC/W6PddLY1WX9fePGIpzbN6Cyg== - dependencies: - datatables.net-bs4 ">=1.11.0" - datatables.net-searchbuilder "1.7.0" - jquery ">=1.7" - -datatables.net-searchbuilder@1.7.0: - version "1.7.0" - resolved "https://registry.npmmirror.com/datatables.net-searchbuilder/-/datatables.net-searchbuilder-1.7.0.tgz#b57fb1abcaa807178c329b2d83813018e0a9281f" - integrity sha512-JGqxDVudgRxrHs/J+VM9O2rcwPgaUjpqBL1MHJAVJw+4vxvAY5Sbb0qP6ayo8h4yZyAE2+aPRm/smJ24N0nztw== - dependencies: - datatables.net ">=1.11.0" - jquery ">=1.7" - -datatables.net-searchpanes-bs4@^1.4.0: - version "1.4.0" - resolved "https://registry.npmmirror.com/datatables.net-searchpanes-bs4/-/datatables.net-searchpanes-bs4-1.4.0.tgz#efe09f8093fabd5ef311467eac198480873fb9bd" - integrity sha512-Floxzmw2cQkUQdI7Vv4IWtLqLmwPrmY6MPncbEWq4YvkSeaZW7OHzSmZLLUjMn2P6Huvz59WUVcwL0lSDui6GQ== - dependencies: - datatables.net-bs4 ">=1.10.25" - datatables.net-searchpanes ">=1.3.0" - jquery ">=1.7" - -datatables.net-searchpanes@>=1.3.0: - version "2.3.0" - resolved "https://registry.npmmirror.com/datatables.net-searchpanes/-/datatables.net-searchpanes-2.3.0.tgz#c973359d473e50a2c787f8f6ce4419561e223865" - integrity sha512-AAl03TQXatzQh6gqNot1BAzenMxQ0/mxX+Nzn770mdTUmhVy2VX8pa7/vZlwe0tRbTcZ9VZMBMErCb66i5X3rA== - dependencies: - datatables.net ">=1.11.0" - jquery ">=1.7" - -datatables.net-select-bs4@^1.3.4: - version "1.7.1" - resolved "https://registry.npmmirror.com/datatables.net-select-bs4/-/datatables.net-select-bs4-1.7.1.tgz#3ddd63bd69426e3fd1bc20dfd7c019305d24ccb1" - integrity sha512-iHG4C8RdJIpsGIGDCXofUflHN1fa2N0E83MZPuqQXh1ZBkeJzd700rnru2mJXbvFc+wM9vf0Xxr6C5YsYFprgA== - dependencies: - datatables.net-bs4 "^1.13.0" - datatables.net-select "1.7.1" - jquery ">=1.7" - -datatables.net-select@1.7.1: - version "1.7.1" - resolved "https://registry.npmmirror.com/datatables.net-select/-/datatables.net-select-1.7.1.tgz#1b309b1ff92c25e5ce4b9d5af19186bdf9b34597" - integrity sha512-yC+GoBDVsnbaFTYKmZ2v5Bftc66zSZCYHbPYZb/ccdvxytEEudjc9R3wn6fgkOrVx3C2X/8keQc4a7EJvdOErg== - dependencies: - datatables.net "^1.13.0" - jquery ">=1.7" - -datatables.net@1.13.8: - version "1.13.8" - resolved "https://registry.npmmirror.com/datatables.net/-/datatables.net-1.13.8.tgz#05a2fb5a036b0b65b66d1bb1eae0ba018aaea8a3" - integrity sha512-2pDamr+GUwPTby2OgriVB9dR9ftFKD2AQyiuCXzZIiG4d9KkKFQ7gqPfNmG7uj9Tc5kDf+rGj86do4LAb/V71g== - dependencies: - jquery ">=1.7" - -datatables.net@2.0.0, datatables.net@>=1.11, datatables.net@>=1.11.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/datatables.net/-/datatables.net-2.0.0.tgz#232c45855d78b0019ccea854eaf044e1927118d3" - integrity sha512-HKndZD/qHNZyAhAyC1Bs8S+H652HedZK76xYAjzXR3xDohvxomJ8iWevsoRV4MvHSPgx8GQl3PAdBNpUvn0xJg== - dependencies: - jquery ">=1.7" - -datatables.net@^1.11.4, datatables.net@^1.13.0: - version "1.13.10" - resolved "https://registry.npmmirror.com/datatables.net/-/datatables.net-1.13.10.tgz#4527e2b5428394732331c8b7db8fa20a56c0cc7b" - integrity sha512-lVjpXGX9VzOpiPYnJ/KxOsYMAVa3oc3UniYGGo6pLBVCyajzMo80yisgXRz8J392wkZOc3bpp6VPs2pDbpKlyw== - dependencies: - jquery ">=1.7" - -daterangepicker@^3.1.0: - version "3.1.0" - resolved "https://registry.npmmirror.com/daterangepicker/-/daterangepicker-3.1.0.tgz#718d606614331df3e774c9aba82ccd8838d45da1" - integrity sha512-DxWXvvPq4srWLCqFugqSV+6CBt/CvQ0dnpXhQ3gl0autcIDAruG1PuGG3gC7yPRNytAD1oU1AcUOzaYhOawhTw== - dependencies: - jquery ">=1.10" - moment "^2.9.0" - -debug@2.6.9, debug@^2.2.0: - version "2.6.9" - resolved "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -decode-named-character-reference@^1.0.0: - version "1.0.2" - resolved "https://registry.npmmirror.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" - integrity sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg== - dependencies: - character-entities "^2.0.0" - -decompress-response@^6.0.0: - version "6.0.0" - resolved "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" - integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== - dependencies: - mimic-response "^3.1.0" - -deep-equal@^1.0.0: - version "1.1.2" - resolved "https://registry.npmmirror.com/deep-equal/-/deep-equal-1.1.2.tgz#78a561b7830eef3134c7f6f3a3d6af272a678761" - integrity sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg== - dependencies: - is-arguments "^1.1.1" - is-date-object "^1.0.5" - is-regex "^1.1.4" - object-is "^1.1.5" - object-keys "^1.1.1" - regexp.prototype.flags "^1.5.1" - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -deep-is@~0.1.3: - version "0.1.4" - resolved "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -default-gateway@^6.0.3: - version "6.0.3" - resolved "https://registry.npmmirror.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" - integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== - dependencies: - execa "^5.0.0" - -defaults@^1.0.3: - version "1.0.4" - resolved "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" - integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== - dependencies: - clone "^1.0.2" - -defer-to-connect@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" - integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== - -define-data-property@^1.0.1, define-data-property@^1.1.2, define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - 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" - -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== - -define-properties@^1.1.3, define-properties@^1.2.1: - version "1.2.1" - resolved "https://registry.npmmirror.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" - integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== - dependencies: - define-data-property "^1.0.1" - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== - -depd@2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.npmmirror.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - -dequal@^2.0.0, dequal@^2.0.3: - version "2.0.3" - resolved "https://registry.npmmirror.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" - integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== - -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -detect-libc@^2.0.0, detect-libc@^2.0.1, detect-libc@^2.0.2: - version "2.0.2" - resolved "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" - integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== - -detect-node@^2.0.4: - version "2.1.0" - resolved "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" - integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== - -deterministic-object-hash@^2.0.1: - version "2.0.2" - resolved "https://registry.npmmirror.com/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz#b251ddc801443905f0e9fef08816a46bc9fe3807" - integrity sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ== - dependencies: - base-64 "^1.0.0" - -devalue@^4.3.2: - version "4.3.2" - resolved "https://registry.npmmirror.com/devalue/-/devalue-4.3.2.tgz#cc44e4cf3872ac5a78229fbce3b77e57032727b5" - integrity sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg== - -devlop@^1.0.0, devlop@^1.1.0: - version "1.1.0" - resolved "https://registry.npmmirror.com/devlop/-/devlop-1.1.0.tgz#4db7c2ca4dc6e0e834c30be70c94bbc976dc7018" - integrity sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA== - dependencies: - dequal "^2.0.0" - -dfa@^1.2.0: - version "1.2.0" - resolved "https://registry.npmmirror.com/dfa/-/dfa-1.2.0.tgz#96ac3204e2d29c49ea5b57af8d92c2ae12790657" - integrity sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q== - -diff@^5.1.0: - version "5.2.0" - resolved "https://registry.npmmirror.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" - integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== - -dir-compare@^4.2.0: - version "4.2.0" - resolved "https://registry.npmmirror.com/dir-compare/-/dir-compare-4.2.0.tgz#d1d4999c14fbf55281071fdae4293b3b9ce86f19" - integrity sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ== - dependencies: - minimatch "^3.0.5" - p-limit "^3.1.0 " - -dlv@^1.1.3: - version "1.1.3" - resolved "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" - integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== - -dns-packet@^5.2.2: - version "5.6.1" - resolved "https://registry.npmmirror.com/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" - integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== - dependencies: - "@leichtgewicht/ip-codec" "^2.0.1" - -dom-converter@^0.2.0: - version "0.2.0" - resolved "https://registry.npmmirror.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" - integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== - dependencies: - utila "~0.4" - -dom-serializer@^1.0.1: - version "1.4.1" - resolved "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" - integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.0" - entities "^2.0.0" - -domelementtype@^2.0.1, domelementtype@^2.2.0: - version "2.3.0" - resolved "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" - integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== - -domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: - version "4.3.1" - resolved "https://registry.npmmirror.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" - integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== - dependencies: - domelementtype "^2.2.0" - -domutils@^2.5.2, domutils@^2.8.0: - version "2.8.0" - resolved "https://registry.npmmirror.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" - integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== - dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" - -dot-case@^3.0.4: - version "3.0.4" - resolved "https://registry.npmmirror.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" - integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== - dependencies: - no-case "^3.0.4" - tslib "^2.0.3" - -dropzone@^5.9.3: - version "5.9.3" - resolved "https://registry.npmmirror.com/dropzone/-/dropzone-5.9.3.tgz#b3070ae090fa48cbc04c17535635537ca72d70d6" - integrity sha512-Azk8kD/2/nJIuVPK+zQ9sjKMRIpRvNyqn9XwbBHNq+iNuSccbJS6hwm1Woy0pMST0erSo0u4j+KJaodndDk4vA== - -dset@^3.1.2, dset@^3.1.3: - version "3.1.3" - resolved "https://registry.npmmirror.com/dset/-/dset-3.1.3.tgz#c194147f159841148e8e34ca41f638556d9542d2" - integrity sha512-20TuZZHCEZ2O71q9/+8BwKwZ0QtD9D8ObhrihJPr+vLLYlSuAU3/zL4cSlgbfeoGHTjCSJBa7NGcrF9/Bx/WJQ== - -duplexer2@~0.1.4: - version "0.1.4" - resolved "https://registry.npmmirror.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" - integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA== - dependencies: - readable-stream "^2.0.2" - -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== - -ekko-lightbox@^5.3.0: - version "5.3.0" - resolved "https://registry.npmmirror.com/ekko-lightbox/-/ekko-lightbox-5.3.0.tgz#fbfcd9df93a8d1cdbf8770adc8c05aaac4d24f56" - integrity sha512-mbacwySuVD3Ad6F2hTkjSTvJt59bcVv2l/TmBerp4xZnLak8tPtA4AScUn4DL42c1ksTiAO6sGhJZ52P/1Qgew== - -electron-installer-common@^0.10.2: - version "0.10.3" - resolved "https://registry.npmmirror.com/electron-installer-common/-/electron-installer-common-0.10.3.tgz#40f9db644ca60eb28673d545b67ee0113aef4444" - integrity sha512-mYbP+6i+nHMIm0WZHXgGdmmXMe+KXncl6jZYQNcCF9C1WsNA9C5SZ2VP4TLQMSIoFO+X4ugkMEA5uld1bmyEvA== - dependencies: - "@malept/cross-spawn-promise" "^1.0.0" - asar "^3.0.0" - debug "^4.1.1" - fs-extra "^9.0.0" - glob "^7.1.4" - lodash "^4.17.15" - parse-author "^2.0.0" - semver "^7.1.1" - tmp-promise "^3.0.2" - optionalDependencies: - "@types/fs-extra" "^9.0.1" - -electron-installer-debian@^3.2.0: - version "3.2.0" - resolved "https://registry.npmmirror.com/electron-installer-debian/-/electron-installer-debian-3.2.0.tgz#2a9c8220f50a57807de8f93619a0d61ec41271e0" - integrity sha512-58ZrlJ1HQY80VucsEIG9tQ//HrTlG6sfofA3nRGr6TmkX661uJyu4cMPPh6kXW+aHdq/7+q25KyQhDrXvRL7jw== - dependencies: - "@malept/cross-spawn-promise" "^1.0.0" - debug "^4.1.1" - electron-installer-common "^0.10.2" - fs-extra "^9.0.0" - get-folder-size "^2.0.1" - lodash "^4.17.4" - word-wrap "^1.2.3" - yargs "^16.0.2" - -electron-installer-redhat@^3.2.0: - version "3.4.0" - resolved "https://registry.npmmirror.com/electron-installer-redhat/-/electron-installer-redhat-3.4.0.tgz#4a7f8d67b48b7d5b23bd1eb074f4b684ae43b192" - integrity sha512-gEISr3U32Sgtj+fjxUAlSDo3wyGGq6OBx7rF5UdpIgbnpUvMN4W5uYb0ThpnAZ42VEJh/3aODQXHbFS4f5J3Iw== - dependencies: - "@malept/cross-spawn-promise" "^1.0.0" - debug "^4.1.1" - electron-installer-common "^0.10.2" - fs-extra "^9.0.0" - lodash "^4.17.15" - word-wrap "^1.2.3" - yargs "^16.0.2" - -electron-squirrel-startup@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/electron-squirrel-startup/-/electron-squirrel-startup-1.0.0.tgz#19b4e55933fa0ef8f556784b9c660f772546a0b8" - integrity sha512-Oce8mvgGdFmwr+DsAcXBmFK8jFfN6yaFAP9IvyhTfupM3nFkBku/7VS/mdtJteWumImkC6P+BKGsxScoDDkv9Q== - dependencies: - debug "^2.2.0" - -electron-to-chromium@^1.4.668: - version "1.4.681" - resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.681.tgz#5f23fad8aa7e1f64cbb7dd9d15c7e39a1cd7e6e3" - integrity sha512-1PpuqJUFWoXZ1E54m8bsLPVYwIVCRzvaL+n5cjigGga4z854abDnFRc+cTa2th4S79kyGqya/1xoR7h+Y5G5lg== - -electron-winstaller@^5.0.0: - version "5.2.2" - resolved "https://registry.npmmirror.com/electron-winstaller/-/electron-winstaller-5.2.2.tgz#529a3528a0dcb36268c0c5048d0f05ed0cedfb4f" - integrity sha512-nw+SbS0DA6SF8bEkcL4SqtPOoLKc5JkEXAz7kzdzz22rY12PZRtTn9zpztbwy+xrLqSBBFR1u0bdvNLpvlmrkw== - dependencies: - "@electron/asar" "^3.2.1" - debug "^4.1.1" - fs-extra "^7.0.1" - lodash.template "^4.2.2" - temp "^0.9.0" - -emoji-regex@^10.2.1, emoji-regex@^10.3.0: - version "10.3.0" - resolved "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" - integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.npmmirror.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -encoding@^0.1.13: - version "0.1.13" - resolved "https://registry.npmmirror.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" - integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== - dependencies: - iconv-lite "^0.6.2" - -end-of-stream@^1.1.0, end-of-stream@^1.4.1: - version "1.4.4" - resolved "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -enhanced-resolve@^5.15.0: - version "5.15.0" - resolved "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" - integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - -entities@^2.0.0: - version "2.2.0" - resolved "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== - -entities@^4.4.0: - version "4.5.0" - resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" - integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== - -env-paths@^2.2.0: - version "2.2.1" - resolved "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" - integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== - -err-code@^2.0.2: - version "2.0.3" - resolved "https://registry.npmmirror.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" - integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== - -error-ex@^1.2.0: - version "1.3.2" - resolved "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-module-lexer@^1.2.1, es-module-lexer@^1.4.1: - version "1.4.1" - resolved "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" - integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== - -es5-ext@^0.10.35, es5-ext@^0.10.50, es5-ext@^0.10.62, es5-ext@~0.10.14: - version "0.10.63" - resolved "https://registry.npmmirror.com/es5-ext/-/es5-ext-0.10.63.tgz#9c222a63b6a332ac80b1e373b426af723b895bd6" - integrity sha512-hUCZd2Byj/mNKjfP9jXrdVZ62B8KuA/VoK7X8nUh5qT+AxDmcbvZz041oDVZdbIN1qW6XY9VDNwzkvKnZvK2TQ== - dependencies: - es6-iterator "^2.0.3" - es6-symbol "^3.1.3" - esniff "^2.0.1" - next-tick "^1.1.0" - -es6-error@^4.1.1: - version "4.1.1" - resolved "https://registry.npmmirror.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" - integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== - -es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3: - version "2.0.3" - resolved "https://registry.npmmirror.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-map@^0.1.5: - version "0.1.5" - resolved "https://registry.npmmirror.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" - integrity sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A== - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-set "~0.1.5" - es6-symbol "~3.1.1" - event-emitter "~0.3.5" - -es6-set@^0.1.5, es6-set@~0.1.5: - version "0.1.6" - resolved "https://registry.npmmirror.com/es6-set/-/es6-set-0.1.6.tgz#5669e3b2aa01d61a50ba79964f733673574983b8" - integrity sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw== - dependencies: - d "^1.0.1" - es5-ext "^0.10.62" - es6-iterator "~2.0.3" - es6-symbol "^3.1.3" - event-emitter "^0.3.5" - type "^2.7.2" - -es6-symbol@^3.1.1, es6-symbol@^3.1.3, es6-symbol@~3.1.1: - version "3.1.3" - resolved "https://registry.npmmirror.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== - dependencies: - d "^1.0.1" - ext "^1.1.2" - -esbuild@^0.19.3, esbuild@^0.19.6: - version "0.19.12" - resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.19.12.tgz#dc82ee5dc79e82f5a5c3b4323a2a641827db3e04" - integrity sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg== - optionalDependencies: - "@esbuild/aix-ppc64" "0.19.12" - "@esbuild/android-arm" "0.19.12" - "@esbuild/android-arm64" "0.19.12" - "@esbuild/android-x64" "0.19.12" - "@esbuild/darwin-arm64" "0.19.12" - "@esbuild/darwin-x64" "0.19.12" - "@esbuild/freebsd-arm64" "0.19.12" - "@esbuild/freebsd-x64" "0.19.12" - "@esbuild/linux-arm" "0.19.12" - "@esbuild/linux-arm64" "0.19.12" - "@esbuild/linux-ia32" "0.19.12" - "@esbuild/linux-loong64" "0.19.12" - "@esbuild/linux-mips64el" "0.19.12" - "@esbuild/linux-ppc64" "0.19.12" - "@esbuild/linux-riscv64" "0.19.12" - "@esbuild/linux-s390x" "0.19.12" - "@esbuild/linux-x64" "0.19.12" - "@esbuild/netbsd-x64" "0.19.12" - "@esbuild/openbsd-x64" "0.19.12" - "@esbuild/sunos-x64" "0.19.12" - "@esbuild/win32-arm64" "0.19.12" - "@esbuild/win32-ia32" "0.19.12" - "@esbuild/win32-x64" "0.19.12" - -escalade@^3.1.1: - version "3.1.2" - resolved "https://registry.npmmirror.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" - integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -escape-string-regexp@^5.0.0: - version "5.0.0" - resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" - integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== - -escodegen@^1.11.1: - version "1.14.3" - resolved "https://registry.npmmirror.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" - integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== - dependencies: - esprima "^4.0.1" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -escodegen@^2.1.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" - integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionalDependencies: - source-map "~0.6.1" - -escodegen@~1.2.0: - version "1.2.0" - resolved "https://registry.npmmirror.com/escodegen/-/escodegen-1.2.0.tgz#09de7967791cc958b7f89a2ddb6d23451af327e1" - integrity sha512-yLy3Cc+zAC0WSmoT2fig3J87TpQ8UaZGx8ahCAs9FL8qNbyV7CVyPKS74DG4bsHiL5ew9sxdYx131OkBQMFnvA== - dependencies: - esprima "~1.0.4" - estraverse "~1.5.0" - esutils "~1.0.0" - optionalDependencies: - source-map "~0.1.30" - -eslint-scope@5.1.1: - version "5.1.1" - resolved "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -esniff@^2.0.1: - version "2.0.1" - resolved "https://registry.npmmirror.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" - integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== - dependencies: - d "^1.0.1" - es5-ext "^0.10.62" - event-emitter "^0.3.5" - type "^2.7.2" - -esprima@^4.0.0, esprima@^4.0.1: - version "4.0.1" - resolved "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esprima@~1.0.4: - version "1.0.4" - resolved "https://registry.npmmirror.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" - integrity sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA== - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1, estraverse@^4.2.0: - version "4.3.0" - resolved "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -estraverse@~1.5.0: - version "1.5.1" - resolved "https://registry.npmmirror.com/estraverse/-/estraverse-1.5.1.tgz#867a3e8e58a9f84618afb6c2ddbcd916b7cbaf71" - integrity sha512-FpCjJDfmo3vsc/1zKSeqR5k42tcIhxFIlvq+h9j0fO2q/h2uLKyweq7rYJ+0CoVvrGQOxIS5wyBrW/+vF58BUQ== - -estree-is-function@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/estree-is-function/-/estree-is-function-1.0.0.tgz#c0adc29806d7f18a74db7df0f3b2666702e37ad2" - integrity sha512-nSCWn1jkSq2QAtkaVLJZY2ezwcFO161HVc174zL1KPW3RJ+O6C3eJb8Nx7OXzvhoEv+nLgSR1g71oWUHUDTrJA== - -estree-walker@^3.0.3: - version "3.0.3" - resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" - integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== - dependencies: - "@types/estree" "^1.0.0" - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -esutils@~1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/esutils/-/esutils-1.0.0.tgz#8151d358e20c8acc7fb745e7472c0025fe496570" - integrity sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.npmmirror.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== - -ev-emitter@^1.0.0: - version "1.1.1" - resolved "https://registry.npmmirror.com/ev-emitter/-/ev-emitter-1.1.1.tgz#8f18b0ce5c76a5d18017f71c0a795c65b9138f2a" - integrity sha512-ipiDYhdQSCZ4hSbX4rMW+XzNKMD1prg/sTvoVmSLkuQ1MVlwjJQQA+sW8tMYR3BLUr9KjodFV4pvzunvRhd33Q== - -eve-raphael@0.5.0: - version "0.5.0" - resolved "https://registry.npmmirror.com/eve-raphael/-/eve-raphael-0.5.0.tgz#17c754b792beef3fa6684d79cf5a47c63c4cda30" - integrity sha512-jrxnPsCGqng1UZuEp9DecX/AuSyAszATSjf4oEcRxvfxa1Oux4KkIPKBAAWWnpdwfARtr+Q0o9aPYWjsROD7ug== - -event-emitter@^0.3.5, event-emitter@~0.3.5: - version "0.3.5" - resolved "https://registry.npmmirror.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== - dependencies: - d "1" - es5-ext "~0.10.14" - -eventemitter3@^4.0.0: - version "4.0.7" - resolved "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - -eventemitter3@^5.0.1: - version "5.0.1" - resolved "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" - integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== - -events@^3.2.0: - version "3.3.0" - resolved "https://registry.npmmirror.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - 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" - -execa@^8.0.1: - version "8.0.1" - resolved "https://registry.npmmirror.com/execa/-/execa-8.0.1.tgz#51f6a5943b580f963c3ca9c6321796db8cc39b8c" - integrity sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^8.0.1" - human-signals "^5.0.0" - is-stream "^3.0.0" - merge-stream "^2.0.0" - npm-run-path "^5.1.0" - onetime "^6.0.0" - signal-exit "^4.1.0" - strip-final-newline "^3.0.0" - -expand-template@^2.0.3: - version "2.0.3" - resolved "https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" - integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== - -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.npmmirror.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - integrity sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw== - dependencies: - homedir-polyfill "^1.0.1" - -exponential-backoff@^3.1.1: - version "3.1.1" - resolved "https://registry.npmmirror.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" - integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== - -express-ws@^5.0.2: - version "5.0.2" - resolved "https://registry.npmmirror.com/express-ws/-/express-ws-5.0.2.tgz#5b02d41b937d05199c6c266d7cc931c823bda8eb" - integrity sha512-0uvmuk61O9HXgLhGl3QhNSEtRsQevtmbL94/eILaliEADZBHZOQUAiHFrGPrgsjikohyrmSG5g+sCfASTt0lkQ== - dependencies: - ws "^7.4.6" - -express@^4.17.1, express@^4.17.3: - version "4.18.2" - resolved "https://registry.npmmirror.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" - integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.1" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.5.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.2.0" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.11.0" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -ext@^1.1.2: - version "1.7.0" - resolved "https://registry.npmmirror.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" - integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== - dependencies: - type "^2.7.2" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== - dependencies: - is-extendable "^0.1.0" - -extend@^3.0.0: - version "3.0.2" - resolved "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extract-zip@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" - integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== - dependencies: - debug "^4.1.1" - get-stream "^5.1.0" - yauzl "^2.10.0" - optionalDependencies: - "@types/yauzl" "^2.9.1" - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-fifo@^1.1.0, fast-fifo@^1.2.0: - version "1.3.2" - resolved "https://registry.npmmirror.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" - integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== - -fast-glob@^3.2.7, fast-glob@^3.3.2: - version "3.3.2" - resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" - integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== - 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" - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - -fast-memoize@^2.5.1: - version "2.5.2" - resolved "https://registry.npmmirror.com/fast-memoize/-/fast-memoize-2.5.2.tgz#79e3bb6a4ec867ea40ba0e7146816f6cdce9b57e" - integrity sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw== - -fastclick@^1.0.6: - version "1.0.6" - resolved "https://registry.npmmirror.com/fastclick/-/fastclick-1.0.6.tgz#161625b27b1a5806405936bda9a2c1926d06be6a" - integrity sha512-cXyDBT4g0uWl/Xe75QspBDAgAWQ0lkPi/zgp6YFEUHj6WV6VIZl7R6TiDZhdOVU3W4ehp/8tG61Jev1jit+ztQ== - -fastq@^1.6.0: - version "1.17.1" - resolved "https://registry.npmmirror.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" - integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== - dependencies: - reusify "^1.0.4" - -faye-websocket@^0.11.3: - version "0.11.4" - resolved "https://registry.npmmirror.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" - integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== - dependencies: - websocket-driver ">=0.5.1" - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== - dependencies: - pend "~1.2.0" - -filename-reserved-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz#abf73dfab735d045440abfea2d91f389ebbfa229" - integrity sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ== - -filenamify@^4.1.0: - version "4.3.0" - resolved "https://registry.npmmirror.com/filenamify/-/filenamify-4.3.0.tgz#62391cb58f02b09971c9d4f9d63b3cf9aba03106" - integrity sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg== - dependencies: - filename-reserved-regex "^2.0.0" - strip-outer "^1.0.1" - trim-repeated "^1.0.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -filterizr@^2.2.4: - version "2.2.4" - resolved "https://registry.npmmirror.com/filterizr/-/filterizr-2.2.4.tgz#02fd75cffd46ef7acc85f55e132064650ae90a14" - integrity sha512-hqyEdg7RrvJMVFOeF0yysS75HP6jLu0wBSUtSPAc3BysAtHpwcXaPnR1kYp2uZtd3YXyhH6JRfF9+H4SRvrqXg== - dependencies: - fast-memoize "^2.5.1" - imagesloaded "^4.1.4" - -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" - -find-up@^2.0.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== - dependencies: - locate-path "^2.0.0" - -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -find-yarn-workspace-root2@1.2.16: - version "1.2.16" - resolved "https://registry.npmmirror.com/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz#60287009dd2f324f59646bdb4b7610a6b301c2a9" - integrity sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA== - dependencies: - micromatch "^4.0.2" - pkg-dir "^4.2.0" - -flag-icon-css@^4.1.7: - version "4.1.7" - resolved "https://registry.npmmirror.com/flag-icon-css/-/flag-icon-css-4.1.7.tgz#5471197f9ab965a3603b3e0face31dd513fec289" - integrity sha512-AFjSU+fv98XbU0vnTQ32vcLj89UEr1MhwDFcooQv14qWJCjg9fGZzfh9BVyDhAhIOZW/pGmJmq38RqpgPaeybQ== - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.npmmirror.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -flattie@^1.1.0: - version "1.1.0" - resolved "https://registry.npmmirror.com/flattie/-/flattie-1.1.0.tgz#1459504209f2001c478751b4e2fb69d6b1ee3241" - integrity sha512-xU99gDEnciIwJdGcBmNHnzTJ/w5AT+VFJOu6sTB6WM8diOYNA3Sa+K1DiEBQ7XH4QikQq3iFW1U+jRVcotQnBw== - -flora-colossus@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/flora-colossus/-/flora-colossus-2.0.0.tgz#af1e85db0a8256ef05f3fb531c1235236c97220a" - integrity sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA== - dependencies: - debug "^4.3.4" - fs-extra "^10.1.0" - -flot@^4.2.2: - version "4.2.6" - resolved "https://registry.npmmirror.com/flot/-/flot-4.2.6.tgz#f2ea48270099567a6ccf8b5ab1ef18a5b65f356d" - integrity sha512-Iz4HCet1ZBQnjGwECz4jseD1zMnh7m2XJIMI6A62l8h2SeceLYOEmYGNQto1XhkSM9fUmqzyKhWwJ+RolWLydg== - -follow-redirects@^1.0.0: - version "1.15.5" - resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" - integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== - -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.npmmirror.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== - -fs-constants@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" - integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== - -fs-extra@^10.0.0, fs-extra@^10.1.0: - version "10.1.0" - resolved "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-extra@^11.1.0, fs-extra@^11.1.1: - version "11.2.0" - resolved "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" - integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-extra@^7.0.1: - version "7.0.1" - resolved "https://registry.npmmirror.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" - integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@^9.0.0, fs-extra@^9.0.1: - version "9.1.0" - resolved "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - 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" - -fs-minipass@^2.0.0, fs-minipass@^2.1.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - -fs-monkey@^1.0.4: - version "1.0.5" - resolved "https://registry.npmmirror.com/fs-monkey/-/fs-monkey-1.0.5.tgz#fe450175f0db0d7ea758102e1d84096acb925788" - integrity sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@~2.3.2, fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -fullcalendar@^5.10.1: - version "5.11.5" - resolved "https://registry.npmmirror.com/fullcalendar/-/fullcalendar-5.11.5.tgz#db57191c4741ceb9a217e47a4cdba8c9ad81b738" - integrity sha512-eaVD6zOvuFXVpoMKlg2FQAj8e+PcpitBMwlzwRJJr1zOi/dXMYAksx2hLzwtsr93FVUNSSo16xwMTTZz6+prKQ== - -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -functions-have-names@^1.2.3: - version "1.2.3" - resolved "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -galactus@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/galactus/-/galactus-1.0.0.tgz#c2615182afa0c6d0859b92e56ae36d052827db7e" - integrity sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ== - dependencies: - debug "^4.3.4" - flora-colossus "^2.0.0" - fs-extra "^10.1.0" - -gar@^1.0.4: - version "1.0.4" - resolved "https://registry.npmmirror.com/gar/-/gar-1.0.4.tgz#f777bc7db425c0572fdeb52676172ca1ae9888b8" - integrity sha512-w4n9cPWyP7aHxKxYHFQMegj7WIAsL/YX/C4Bs5Rr8s1H9M1rNtRWRsw+ovYMkXDQ5S4ZbYHsHAPmevPjPgw44w== - -gauge@^4.0.3: - version "4.0.4" - resolved "https://registry.npmmirror.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" - integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.3" - console-control-strings "^1.1.0" - has-unicode "^2.0.1" - signal-exit "^3.0.7" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.5" - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-assigned-identifiers@^1.1.0: - version "1.2.0" - resolved "https://registry.npmmirror.com/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz#6dbf411de648cbaf8d9169ebb0d2d576191e2ff1" - integrity sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-east-asian-width@^1.0.0: - version "1.2.0" - resolved "https://registry.npmmirror.com/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz#5e6ebd9baee6fb8b7b6bd505221065f0cd91f64e" - integrity sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA== - -get-folder-size@^2.0.1: - version "2.0.1" - resolved "https://registry.npmmirror.com/get-folder-size/-/get-folder-size-2.0.1.tgz#3fe0524dd3bad05257ef1311331417bcd020a497" - integrity sha512-+CEb+GDCM7tkOS2wdMKTn9vU7DgnKUTuDlehkNJKNSovdCOVxs14OfKCk4cvSaR3za4gj+OBdl9opPN9xrJ0zA== - dependencies: - gar "^1.0.4" - tiny-each-async "2.0.3" - -get-installed-path@^2.0.3: - version "2.1.1" - resolved "https://registry.npmmirror.com/get-installed-path/-/get-installed-path-2.1.1.tgz#a1f33dc6b8af542c9331084e8edbe37fe2634152" - integrity sha512-Qkn9eq6tW5/q9BDVdMpB8tOHljX9OSP0jRC5TRNVA4qRc839t4g8KQaR8t0Uv0EFVL0MlyG7m/ofjEgAROtYsA== - dependencies: - global-modules "1.0.0" - -get-intrinsic@^1.1.3, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - 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" - -get-package-info@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/get-package-info/-/get-package-info-1.0.0.tgz#6432796563e28113cd9474dbbd00052985a4999c" - integrity sha512-SCbprXGAPdIhKAXiG+Mk6yeoFH61JlYunqdFQFHDtLjJlDjFf6x07dsS8acO+xWt52jpdVo49AlVDnUVK1sDNw== - dependencies: - bluebird "^3.1.1" - debug "^2.2.0" - lodash.get "^4.0.0" - read-pkg-up "^2.0.0" - -get-stream@^4.0.0: - version "4.1.0" - resolved "https://registry.npmmirror.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - -get-stream@^8.0.1: - version "8.0.1" - resolved "https://registry.npmmirror.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" - integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== - -github-from-package@0.0.0: - version "0.0.0" - resolved "https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" - integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== - -github-slugger@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/github-slugger/-/github-slugger-2.0.0.tgz#52cf2f9279a21eb6c59dd385b410f0c0adda8f1a" - integrity sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw== - -glob-parent@^5.1.2, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-to-regexp@^0.4.1: - version "0.4.1" - resolved "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" - integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== - -glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.2.3" - resolved "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - 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" - -glob@^8.0.1: - version "8.1.0" - resolved "https://registry.npmmirror.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - -global-agent@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6" - integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q== - dependencies: - boolean "^3.0.1" - es6-error "^4.1.1" - matcher "^3.0.0" - roarr "^2.15.3" - semver "^7.3.2" - serialize-error "^7.0.1" - -global-modules@1.0.0, global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.npmmirror.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - integrity sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg== - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globalthis@^1.0.1: - version "1.0.3" - resolved "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" - integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== - dependencies: - define-properties "^1.1.3" - -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" - -got@^11.7.0, got@^11.8.5: - version "11.8.6" - resolved "https://registry.npmmirror.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" - integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== - dependencies: - "@sindresorhus/is" "^4.0.0" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.2" - decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.2" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" - -graceful-fs@^4.1.2, graceful-fs@^4.1.5, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: - version "4.2.11" - resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -gray-matter@^4.0.3: - version "4.0.3" - resolved "https://registry.npmmirror.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" - integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== - dependencies: - js-yaml "^3.13.1" - kind-of "^6.0.2" - section-matter "^1.0.0" - strip-bom-string "^1.0.0" - -handle-thing@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" - integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1, has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.0.1: - version "1.0.3" - resolved "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== - -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.2" - resolved "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - -has-unicode@^2.0.1: - version "2.0.1" - resolved "https://registry.npmmirror.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== - -has@^1.0.1: - version "1.0.4" - resolved "https://registry.npmmirror.com/has/-/has-1.0.4.tgz#2eb2860e000011dae4f1406a86fe80e530fb2ec6" - integrity sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ== - -hasown@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.1.tgz#26f48f039de2c0f8d3356c223fb8d50253519faa" - integrity sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA== - dependencies: - function-bind "^1.1.2" - -hast-util-from-html@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/hast-util-from-html/-/hast-util-from-html-2.0.1.tgz#9cd38ee81bf40b2607368b92a04b0905fa987488" - integrity sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g== - dependencies: - "@types/hast" "^3.0.0" - devlop "^1.1.0" - hast-util-from-parse5 "^8.0.0" - parse5 "^7.0.0" - vfile "^6.0.0" - vfile-message "^4.0.0" - -hast-util-from-parse5@^8.0.0: - version "8.0.1" - resolved "https://registry.npmmirror.com/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz#654a5676a41211e14ee80d1b1758c399a0327651" - integrity sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ== - dependencies: - "@types/hast" "^3.0.0" - "@types/unist" "^3.0.0" - devlop "^1.0.0" - hastscript "^8.0.0" - property-information "^6.0.0" - vfile "^6.0.0" - vfile-location "^5.0.0" - web-namespaces "^2.0.0" - -hast-util-parse-selector@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz#352879fa86e25616036037dd8931fb5f34cb4a27" - integrity sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A== - dependencies: - "@types/hast" "^3.0.0" - -hast-util-raw@^9.0.0: - version "9.0.2" - resolved "https://registry.npmmirror.com/hast-util-raw/-/hast-util-raw-9.0.2.tgz#39b4a4886bd9f0a5dd42e86d02c966c2c152884c" - integrity sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA== - dependencies: - "@types/hast" "^3.0.0" - "@types/unist" "^3.0.0" - "@ungap/structured-clone" "^1.0.0" - hast-util-from-parse5 "^8.0.0" - hast-util-to-parse5 "^8.0.0" - html-void-elements "^3.0.0" - mdast-util-to-hast "^13.0.0" - parse5 "^7.0.0" - unist-util-position "^5.0.0" - unist-util-visit "^5.0.0" - vfile "^6.0.0" - web-namespaces "^2.0.0" - zwitch "^2.0.0" - -hast-util-to-html@^9.0.0: - version "9.0.0" - resolved "https://registry.npmmirror.com/hast-util-to-html/-/hast-util-to-html-9.0.0.tgz#51c0ae2a3550b9aa988c094c4fc4e327af0dddd1" - integrity sha512-IVGhNgg7vANuUA2XKrT6sOIIPgaYZnmLx3l/CCOAK0PtgfoHrZwX7jCSYyFxHTrGmC6S9q8aQQekjp4JPZF+cw== - dependencies: - "@types/hast" "^3.0.0" - "@types/unist" "^3.0.0" - ccount "^2.0.0" - comma-separated-tokens "^2.0.0" - hast-util-raw "^9.0.0" - hast-util-whitespace "^3.0.0" - html-void-elements "^3.0.0" - mdast-util-to-hast "^13.0.0" - property-information "^6.0.0" - space-separated-tokens "^2.0.0" - stringify-entities "^4.0.0" - zwitch "^2.0.4" - -hast-util-to-parse5@^8.0.0: - version "8.0.0" - resolved "https://registry.npmmirror.com/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz#477cd42d278d4f036bc2ea58586130f6f39ee6ed" - integrity sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw== - dependencies: - "@types/hast" "^3.0.0" - comma-separated-tokens "^2.0.0" - devlop "^1.0.0" - property-information "^6.0.0" - space-separated-tokens "^2.0.0" - web-namespaces "^2.0.0" - zwitch "^2.0.0" - -hast-util-whitespace@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz#7778ed9d3c92dd9e8c5c8f648a49c21fc51cb621" - integrity sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw== - dependencies: - "@types/hast" "^3.0.0" - -hastscript@^8.0.0: - version "8.0.0" - resolved "https://registry.npmmirror.com/hastscript/-/hastscript-8.0.0.tgz#4ef795ec8dee867101b9f23cc830d4baf4fd781a" - integrity sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw== - dependencies: - "@types/hast" "^3.0.0" - comma-separated-tokens "^2.0.0" - hast-util-parse-selector "^4.0.0" - property-information "^6.0.0" - space-separated-tokens "^2.0.0" - -he@^1.2.0: - version "1.2.0" - resolved "https://registry.npmmirror.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.npmmirror.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.npmmirror.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== - dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" - -html-entities@^2.3.2: - version "2.4.0" - resolved "https://registry.npmmirror.com/html-entities/-/html-entities-2.4.0.tgz#edd0cee70402584c8c76cc2c0556db09d1f45061" - integrity sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ== - -html-escaper@^3.0.3: - version "3.0.3" - resolved "https://registry.npmmirror.com/html-escaper/-/html-escaper-3.0.3.tgz#4d336674652beb1dcbc29ef6b6ba7f6be6fdfed6" - integrity sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ== - -html-minifier-terser@^6.0.2: - version "6.1.0" - resolved "https://registry.npmmirror.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" - 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" - -html-void-elements@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-3.0.0.tgz#fc9dbd84af9e747249034d4d62602def6517f1d7" - integrity sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg== - -html-webpack-plugin@^5.5.3: - version "5.6.0" - resolved "https://registry.npmmirror.com/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz#50a8fa6709245608cb00e811eacecb8e0d7b7ea0" - integrity sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw== - 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" - -htmlparser2@^6.1.0: - version "6.1.0" - resolved "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" - integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - domutils "^2.5.2" - entities "^2.0.0" - -http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: - version "4.1.1" - resolved "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" - integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== - -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.npmmirror.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== - -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - 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" - -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.npmmirror.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-parser-js@>=0.5.1: - version "0.5.8" - resolved "https://registry.npmmirror.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" - integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== - -http-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" - integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== - dependencies: - "@tootallnate/once" "2" - agent-base "6" - debug "4" - -http-proxy-middleware@^2.0.3: - version "2.0.6" - resolved "https://registry.npmmirror.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" - integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== - 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" - -http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.npmmirror.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - -http2-wrapper@^1.0.0-beta.5.2: - version "1.0.3" - resolved "https://registry.npmmirror.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" - integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== - dependencies: - quick-lru "^5.1.1" - resolve-alpn "^1.0.0" - -https-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - -human-signals@^5.0.0: - version "5.0.0" - resolved "https://registry.npmmirror.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" - integrity sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ== - -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.npmmirror.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" - integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== - dependencies: - ms "^2.0.0" - -icheck-bootstrap@^3.0.1: - version "3.0.1" - resolved "https://registry.npmmirror.com/icheck-bootstrap/-/icheck-bootstrap-3.0.1.tgz#60c9c9a71524e1d9dd5bd05167a62fef05cc3a1b" - integrity sha512-Rj3SybdcMcayhsP4IJ+hmCNgCKclaFcs/5zwCuLXH1WMo468NegjhZVxbSNKhEjJjnwc4gKETogUmPYSQ9lEZQ== - -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@^0.6.2, iconv-lite@^0.6.3: - version "0.6.3" - resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - -icss-utils@^5.0.0, icss-utils@^5.1.0: - version "5.1.0" - resolved "https://registry.npmmirror.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" - integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== - -ieee754@^1.1.13, ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -imagesloaded@^4.1.4: - version "4.1.4" - resolved "https://registry.npmmirror.com/imagesloaded/-/imagesloaded-4.1.4.tgz#1376efcd162bb768c34c3727ac89cc04051f3cc7" - integrity sha512-ltiBVcYpc/TYTF5nolkMNsnREHW+ICvfQ3Yla2Sgr71YFwQ86bDwV9hgpFhFtrGPuwEx5+LqOHIrdXBdoWwwsA== - dependencies: - ev-emitter "^1.0.0" - -immediate@~3.0.5: - version "3.0.6" - resolved "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" - integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== - -import-meta-resolve@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz#0b1195915689f60ab00f830af0f15cc841e8919e" - integrity sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA== - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -infer-owner@^1.0.4: - version "1.0.4" - resolved "https://registry.npmmirror.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== - -ini@^1.3.4, ini@~1.3.0: - version "1.3.8" - resolved "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -inputmask@^5.0.7: - version "5.0.8" - resolved "https://registry.npmmirror.com/inputmask/-/inputmask-5.0.8.tgz#cd0f70b058c3291a0d4f27de25dbfc179c998bb4" - integrity sha512-1WcbyudPTXP1B28ozWWyFa6QRIUG4KiLoyR6LFHlpT4OfTzRqFfWgHFadNvRuMN1S9XNVz9CdNvCGjJi+uAMqQ== - -interpret@^3.1.1: - version "3.1.1" - resolved "https://registry.npmmirror.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" - integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== - -ion-rangeslider@^2.3.1: - version "2.3.1" - resolved "https://registry.npmmirror.com/ion-rangeslider/-/ion-rangeslider-2.3.1.tgz#92ade52cb56fc30b9162d0483ff02b6f9ed237c2" - integrity sha512-6V+24FD13/feliI485gnRHZYD9Ev64M5NAFTxnVib516ATHa9PlXQrC+nOiPngouRYTCLPJyokAJEi3e1Umi5g== - -ip-address@^9.0.5: - version "9.0.5" - resolved "https://registry.npmmirror.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" - integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== - dependencies: - jsbn "1.1.0" - sprintf-js "^1.1.3" - -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -ipaddr.js@^2.0.1: - version "2.1.0" - resolved "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f" - integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== - -is-arguments@^1.1.1: - version "1.1.1" - resolved "https://registry.npmmirror.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== - -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-buffer@^2.0.0: - version "2.0.5" - resolved "https://registry.npmmirror.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" - integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== - -is-core-module@^2.13.0: - version "2.13.1" - resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" - integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== - dependencies: - hasown "^2.0.0" - -is-date-object@^1.0.5: - version "1.0.5" - resolved "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== - dependencies: - has-tostringtag "^1.0.0" - -is-docker@^2.0.0, is-docker@^2.1.1: - version "2.2.1" - resolved "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - -is-docker@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/is-docker/-/is-docker-3.0.0.tgz#90093aa3106277d8a77a5910dbae71747e15a200" - integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== - -is-extendable@^0.1.0: - version "0.1.1" - resolved "https://registry.npmmirror.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-inside-container@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" - integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== - dependencies: - is-docker "^3.0.0" - -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== - -is-interactive@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" - integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== - -is-lambda@^1.0.1: - version "1.0.1" - resolved "https://registry.npmmirror.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" - integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-plain-obj@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" - integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== - -is-plain-obj@^4.0.0: - version "4.1.0" - resolved "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" - integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== - -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.npmmirror.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.npmmirror.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== - -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" - integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -is-unicode-supported@^1.1.0, is-unicode-supported@^1.3.0: - version "1.3.0" - resolved "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" - integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== - -is-windows@^1.0.1: - version "1.0.2" - resolved "https://registry.npmmirror.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -is-wsl@^3.0.0: - version "3.1.0" - resolved "https://registry.npmmirror.com/is-wsl/-/is-wsl-3.1.0.tgz#e1c657e39c10090afcbedec61720f6b924c3cbd2" - integrity sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw== - dependencies: - is-inside-container "^1.0.0" - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== - -isbinaryfile@^4.0.8: - version "4.0.10" - resolved "https://registry.npmmirror.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" - integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.npmmirror.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - -jest-worker@^27.4.5: - version "27.5.1" - resolved "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jquery-knob-chif@^1.2.13: - version "1.2.13" - resolved "https://registry.npmmirror.com/jquery-knob-chif/-/jquery-knob-chif-1.2.13.tgz#5f1e462ef3745d27a9fd66ce1141fe82b44a5762" - integrity sha512-dveq9MZCr68bRrsziuRusKS+/ciT1yOOHdENOSTcXVRM9MsEyCK/DjqR9nc7V3on41269PFdDE2Fuib8Cw4jAA== - -jquery-mapael@^2.2.0: - version "2.2.0" - resolved "https://registry.npmmirror.com/jquery-mapael/-/jquery-mapael-2.2.0.tgz#a68850c680ef0ce3f8b865e8a48b2a018250ca73" - integrity sha512-B5cVcCkfs7Ezia1Zs8bEfVacYD/GvaASyqQeidApR/NJ1C4igcExk9VULVsgLcTPkxohcZrrz5uCaPXvuKeZWw== - dependencies: - jquery "^3.0 || ^2.0 || ^1.0" - raphael "^2.2.0 || ^2.1.1" - optionalDependencies: - jquery-mousewheel "^3.1" - -jquery-mousewheel@^3.1, jquery-mousewheel@^3.1.13: - version "3.1.13" - resolved "https://registry.npmmirror.com/jquery-mousewheel/-/jquery-mousewheel-3.1.13.tgz#06f0335f16e353a695e7206bf50503cb523a6ee5" - integrity sha512-GXhSjfOPyDemM005YCEHvzrEALhKDIswtxSHSR2e4K/suHVJKJxxRCGz3skPjNxjJjQa9AVSGGlYjv1M3VLIPg== - -jquery-ui-dist@^1.13.0: - version "1.13.2" - resolved "https://registry.npmmirror.com/jquery-ui-dist/-/jquery-ui-dist-1.13.2.tgz#899fbb3c6210de19ace10657cf844a3b97f3be70" - integrity sha512-oVDRd1NLtTbBwpRKAYdIRgpWVDzeBhfy7Gu0RmY6JEaZtmBq6kDn1pm5SgDiAotrnDS+RoTRXO6xvcNTxA9tOA== - dependencies: - jquery ">=1.8.0 <4.0.0" - -jquery-validation@^1.19.3: - version "1.20.0" - resolved "https://registry.npmmirror.com/jquery-validation/-/jquery-validation-1.20.0.tgz#dbff6d8fe61b07d4b6f844bf2f5405146556b991" - integrity sha512-c8tg4ltIIP6L7l0bZ79sRzOJYquyjS48kQZ6iv8MJ2r0OYztxtkWYKTReZyU2/zVFYiINB29i0Z/IRNNuJQN1g== - -jquery@>=1.10, jquery@>=1.12.0, jquery@>=1.7, "jquery@>=1.8.0 <4.0.0", jquery@>=2.2, "jquery@^3.0 || ^2.0 || ^1.0", jquery@^3.4.0, jquery@^3.6.0: - version "3.7.1" - resolved "https://registry.npmmirror.com/jquery/-/jquery-3.7.1.tgz#083ef98927c9a6a74d05a6af02806566d16274de" - integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== - -jqvmap-novulnerability@^1.5.1: - version "1.5.1" - resolved "https://registry.npmmirror.com/jqvmap-novulnerability/-/jqvmap-novulnerability-1.5.1.tgz#140c42623ebbe9b9076ea2dd3b8d155fe9f38ae7" - integrity sha512-O6Jr7AGiut9iNJMelPdy8pH83tNXadOqmhJm5FZy9gtaZ5uuhZK3VNu+YLFuTpXeZI8YXUvlFUYbJJi5XHA+tw== - dependencies: - jquery "^3.4.0" - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.0, js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -jsbn@1.1.0: - version "1.1.0" - resolved "https://registry.npmmirror.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" - integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsgrid@^1.5.3: - version "1.5.3" - resolved "https://registry.npmmirror.com/jsgrid/-/jsgrid-1.5.3.tgz#b15fc426483153bee2b6b567312f675d92834a0d" - integrity sha512-/BJgQp7gZe8o/VgNelwXc21jHc9HN+l7WPOkBhC9b9jPXFtOrU9ftNLPVBmKYCNlIulAbGTW8SDJI0mpw7uWxQ== - -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - -json-parse-even-better-errors@^2.3.1: - version "2.3.1" - resolved "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - -json-stringify-safe@^5.0.1: - version "5.0.1" - resolved "https://registry.npmmirror.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== - -json5@^2.1.2, json5@^2.2.3: - version "2.2.3" - resolved "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== - optionalDependencies: - graceful-fs "^4.1.6" - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -jszip@^3.7.1: - version "3.10.1" - resolved "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" - integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== - dependencies: - lie "~3.3.0" - pako "~1.0.2" - readable-stream "~2.3.6" - setimmediate "^1.0.5" - -junk@^3.1.0: - version "3.1.0" - resolved "https://registry.npmmirror.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" - integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== - -keyv@^4.0.0: - version "4.5.4" - resolved "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" - integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== - dependencies: - json-buffer "3.0.1" - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -kleur@^3.0.3: - version "3.0.3" - resolved "https://registry.npmmirror.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - -kleur@^4.1.4: - version "4.1.5" - resolved "https://registry.npmmirror.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" - integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== - -launch-editor@^2.6.0: - version "2.6.1" - resolved "https://registry.npmmirror.com/launch-editor/-/launch-editor-2.6.1.tgz#f259c9ef95cbc9425620bbbd14b468fcdb4ffe3c" - integrity sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw== - dependencies: - picocolors "^1.0.0" - shell-quote "^1.8.1" - -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.npmmirror.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -lie@~3.3.0: - version "3.3.0" - resolved "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" - integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== - dependencies: - immediate "~3.0.5" - -listr2@^5.0.3: - version "5.0.8" - resolved "https://registry.npmmirror.com/listr2/-/listr2-5.0.8.tgz#a9379ffeb4bd83a68931a65fb223a11510d6ba23" - integrity sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA== - dependencies: - cli-truncate "^2.1.0" - colorette "^2.0.19" - log-update "^4.0.0" - p-map "^4.0.0" - rfdc "^1.3.0" - rxjs "^7.8.0" - through "^2.3.8" - wrap-ansi "^7.0.0" - -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" - integrity sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ== - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" - -load-yaml-file@^0.2.0: - version "0.2.0" - resolved "https://registry.npmmirror.com/load-yaml-file/-/load-yaml-file-0.2.0.tgz#af854edaf2bea89346c07549122753c07372f64d" - integrity sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw== - dependencies: - graceful-fs "^4.1.5" - js-yaml "^3.13.0" - pify "^4.0.1" - strip-bom "^3.0.0" - -loader-runner@^4.2.0: - version "4.3.0" - resolved "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" - integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== - -loader-utils@^2.0.0: - version "2.0.4" - resolved "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" - integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^2.1.2" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA== - -lodash.get@^4.0.0: - version "4.4.2" - resolved "https://registry.npmmirror.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" - integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== - -lodash.template@^4.2.2: - version "4.5.0" - resolved "https://registry.npmmirror.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.npmmirror.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - -lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4: - version "4.17.21" - resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@^4.0.0, log-symbols@^4.1.0: - version "4.1.0" - resolved "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -log-symbols@^5.1.0: - version "5.1.0" - resolved "https://registry.npmmirror.com/log-symbols/-/log-symbols-5.1.0.tgz#a20e3b9a5f53fac6aeb8e2bb22c07cf2c8f16d93" - integrity sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA== - dependencies: - chalk "^5.0.0" - is-unicode-supported "^1.1.0" - -log-update@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" - integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== - dependencies: - ansi-escapes "^4.3.0" - cli-cursor "^3.1.0" - slice-ansi "^4.0.0" - wrap-ansi "^6.2.0" - -longest-streak@^3.0.0: - version "3.1.0" - resolved "https://registry.npmmirror.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4" - integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g== - -lower-case@^2.0.2: - version "2.0.2" - resolved "https://registry.npmmirror.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" - integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== - dependencies: - tslib "^2.0.3" - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -lru-cache@^7.7.1: - version "7.18.3" - resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" - integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== - -magic-string@0.25.1: - version "0.25.1" - resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.1.tgz#b1c248b399cd7485da0fe7385c2fc7011843266e" - integrity sha512-sCuTz6pYom8Rlt4ISPFn6wuFodbKMIHUMv4Qko9P17dpxb7s52KJTmRuZZqHdGmLCK9AOcDare039nRIcfdkEg== - dependencies: - sourcemap-codec "^1.4.1" - -magic-string@^0.30.3: - version "0.30.7" - resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.7.tgz#0cecd0527d473298679da95a2d7aeb8c64048505" - integrity sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA== - dependencies: - "@jridgewell/sourcemap-codec" "^1.4.15" - -make-fetch-happen@^10.0.3: - version "10.2.1" - resolved "https://registry.npmmirror.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" - integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== - dependencies: - agentkeepalive "^4.2.1" - cacache "^16.1.0" - http-cache-semantics "^4.1.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" - is-lambda "^1.0.1" - lru-cache "^7.7.1" - minipass "^3.1.6" - minipass-collect "^1.0.2" - minipass-fetch "^2.0.3" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - negotiator "^0.6.3" - promise-retry "^2.0.1" - socks-proxy-agent "^7.0.0" - ssri "^9.0.0" - -map-age-cleaner@^0.1.1: - version "0.1.3" - resolved "https://registry.npmmirror.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" - integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== - dependencies: - p-defer "^1.0.0" - -markdown-table@^3.0.0: - version "3.0.3" - resolved "https://registry.npmmirror.com/markdown-table/-/markdown-table-3.0.3.tgz#e6331d30e493127e031dd385488b5bd326e4a6bd" - integrity sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw== - -matcher@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" - integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== - dependencies: - escape-string-regexp "^4.0.0" - -mdast-util-definitions@^6.0.0: - version "6.0.0" - resolved "https://registry.npmmirror.com/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz#c1bb706e5e76bb93f9a09dd7af174002ae69ac24" - integrity sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ== - dependencies: - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - unist-util-visit "^5.0.0" - -mdast-util-find-and-replace@^3.0.0: - version "3.0.1" - resolved "https://registry.npmmirror.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz#a6fc7b62f0994e973490e45262e4bc07607b04e0" - integrity sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA== - dependencies: - "@types/mdast" "^4.0.0" - escape-string-regexp "^5.0.0" - unist-util-is "^6.0.0" - unist-util-visit-parents "^6.0.0" - -mdast-util-from-markdown@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz#52f14815ec291ed061f2922fd14d6689c810cb88" - integrity sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA== - dependencies: - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - mdast-util-to-string "^4.0.0" - micromark "^4.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-decode-string "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - unist-util-stringify-position "^4.0.0" - -mdast-util-gfm-autolink-literal@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz#5baf35407421310a08e68c15e5d8821e8898ba2a" - integrity sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg== - dependencies: - "@types/mdast" "^4.0.0" - ccount "^2.0.0" - devlop "^1.0.0" - mdast-util-find-and-replace "^3.0.0" - micromark-util-character "^2.0.0" - -mdast-util-gfm-footnote@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz#25a1753c7d16db8bfd53cd84fe50562bd1e6d6a9" - integrity sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ== - dependencies: - "@types/mdast" "^4.0.0" - devlop "^1.1.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - -mdast-util-gfm-strikethrough@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz#d44ef9e8ed283ac8c1165ab0d0dfd058c2764c16" - integrity sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg== - dependencies: - "@types/mdast" "^4.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - -mdast-util-gfm-table@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz#7a435fb6223a72b0862b33afbd712b6dae878d38" - integrity sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg== - dependencies: - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - markdown-table "^3.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - -mdast-util-gfm-task-list-item@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz#e68095d2f8a4303ef24094ab642e1047b991a936" - integrity sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ== - dependencies: - "@types/mdast" "^4.0.0" - devlop "^1.0.0" - mdast-util-from-markdown "^2.0.0" - mdast-util-to-markdown "^2.0.0" - -mdast-util-gfm@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz#3f2aecc879785c3cb6a81ff3a243dc11eca61095" - integrity sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw== - dependencies: - mdast-util-from-markdown "^2.0.0" - mdast-util-gfm-autolink-literal "^2.0.0" - mdast-util-gfm-footnote "^2.0.0" - mdast-util-gfm-strikethrough "^2.0.0" - mdast-util-gfm-table "^2.0.0" - mdast-util-gfm-task-list-item "^2.0.0" - mdast-util-to-markdown "^2.0.0" - -mdast-util-phrasing@^4.0.0: - version "4.1.0" - resolved "https://registry.npmmirror.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3" - integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w== - dependencies: - "@types/mdast" "^4.0.0" - unist-util-is "^6.0.0" - -mdast-util-to-hast@13.0.2: - version "13.0.2" - resolved "https://registry.npmmirror.com/mdast-util-to-hast/-/mdast-util-to-hast-13.0.2.tgz#74c0a9f014bb2340cae6118f6fccd75467792be7" - integrity sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - "@ungap/structured-clone" "^1.0.0" - devlop "^1.0.0" - micromark-util-sanitize-uri "^2.0.0" - trim-lines "^3.0.0" - unist-util-position "^5.0.0" - unist-util-visit "^5.0.0" - -mdast-util-to-hast@^13.0.0: - version "13.1.0" - resolved "https://registry.npmmirror.com/mdast-util-to-hast/-/mdast-util-to-hast-13.1.0.tgz#1ae54d903150a10fe04d59f03b2b95fd210b2124" - integrity sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - "@ungap/structured-clone" "^1.0.0" - devlop "^1.0.0" - micromark-util-sanitize-uri "^2.0.0" - trim-lines "^3.0.0" - unist-util-position "^5.0.0" - unist-util-visit "^5.0.0" - vfile "^6.0.0" - -mdast-util-to-markdown@^2.0.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz#9813f1d6e0cdaac7c244ec8c6dabfdb2102ea2b4" - integrity sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ== - dependencies: - "@types/mdast" "^4.0.0" - "@types/unist" "^3.0.0" - longest-streak "^3.0.0" - mdast-util-phrasing "^4.0.0" - mdast-util-to-string "^4.0.0" - micromark-util-decode-string "^2.0.0" - unist-util-visit "^5.0.0" - zwitch "^2.0.0" - -mdast-util-to-string@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz#7a5121475556a04e7eddeb67b264aae79d312814" - integrity sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg== - dependencies: - "@types/mdast" "^4.0.0" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -mem@^4.3.0: - version "4.3.0" - resolved "https://registry.npmmirror.com/mem/-/mem-4.3.0.tgz#461af497bc4ae09608cdb2e60eefb69bff744178" - integrity sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w== - dependencies: - map-age-cleaner "^0.1.1" - mimic-fn "^2.0.0" - p-is-promise "^2.0.0" - -memfs@^3.4.3: - version "3.6.0" - resolved "https://registry.npmmirror.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" - integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== - dependencies: - fs-monkey "^1.0.4" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== - -merge-source-map@1.0.4: - version "1.0.4" - resolved "https://registry.npmmirror.com/merge-source-map/-/merge-source-map-1.0.4.tgz#a5de46538dae84d4114cc5ea02b4772a6346701f" - integrity sha512-PGSmS0kfnTnMJCzJ16BLLCEe6oeYCamKFFdQKshi4BmM6FUwipjVOcBFGxqtQtirtAG4iZvHlqST9CpZKqlRjA== - dependencies: - source-map "^0.5.6" - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -merge2@^1.3.0: - version "1.4.1" - resolved "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.npmmirror.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - -micromark-core-commonmark@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz#50740201f0ee78c12a675bf3e68ffebc0bf931a3" - integrity sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA== - dependencies: - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - micromark-factory-destination "^2.0.0" - micromark-factory-label "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-factory-title "^2.0.0" - micromark-factory-whitespace "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-classify-character "^2.0.0" - micromark-util-html-tag-name "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-subtokenize "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-extension-gfm-autolink-literal@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.0.0.tgz#f1e50b42e67d441528f39a67133eddde2bbabfd9" - integrity sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-sanitize-uri "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-extension-gfm-footnote@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.0.0.tgz#91afad310065a94b636ab1e9dab2c60d1aab953c" - integrity sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg== - dependencies: - devlop "^1.0.0" - micromark-core-commonmark "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-sanitize-uri "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-extension-gfm-strikethrough@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.0.0.tgz#6917db8e320da70e39ffbf97abdbff83e6783e61" - integrity sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw== - dependencies: - devlop "^1.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-classify-character "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-extension-gfm-table@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz#2cf3fe352d9e089b7ef5fff003bdfe0da29649b7" - integrity sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw== - dependencies: - devlop "^1.0.0" - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-extension-gfm-tagfilter@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz#f26d8a7807b5985fba13cf61465b58ca5ff7dc57" - integrity sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg== - dependencies: - micromark-util-types "^2.0.0" - -micromark-extension-gfm-task-list-item@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.0.1.tgz#ee8b208f1ced1eb9fb11c19a23666e59d86d4838" - integrity sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw== - dependencies: - devlop "^1.0.0" - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-extension-gfm@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz#3e13376ab95dd7a5cfd0e29560dfe999657b3c5b" - integrity sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w== - dependencies: - micromark-extension-gfm-autolink-literal "^2.0.0" - micromark-extension-gfm-footnote "^2.0.0" - micromark-extension-gfm-strikethrough "^2.0.0" - micromark-extension-gfm-table "^2.0.0" - micromark-extension-gfm-tagfilter "^2.0.0" - micromark-extension-gfm-task-list-item "^2.0.0" - micromark-util-combine-extensions "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-destination@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz#857c94debd2c873cba34e0445ab26b74f6a6ec07" - integrity sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-label@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz#17c5c2e66ce39ad6f4fc4cbf40d972f9096f726a" - integrity sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw== - dependencies: - devlop "^1.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-space@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz#5e7afd5929c23b96566d0e1ae018ae4fcf81d030" - integrity sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-title@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz#726140fc77892af524705d689e1cf06c8a83ea95" - integrity sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A== - dependencies: - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-factory-whitespace@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz#9e92eb0f5468083381f923d9653632b3cfb5f763" - integrity sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA== - dependencies: - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-character@^2.0.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/micromark-util-character/-/micromark-util-character-2.1.0.tgz#31320ace16b4644316f6bf057531689c71e2aee1" - integrity sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ== - dependencies: - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-chunked@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz#e51f4db85fb203a79dbfef23fd41b2f03dc2ef89" - integrity sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-classify-character@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz#8c7537c20d0750b12df31f86e976d1d951165f34" - integrity sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-combine-extensions@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz#75d6ab65c58b7403616db8d6b31315013bfb7ee5" - integrity sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ== - dependencies: - micromark-util-chunked "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-decode-numeric-character-reference@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz#2698bbb38f2a9ba6310e359f99fcb2b35a0d2bd5" - integrity sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-decode-string@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz#7dfa3a63c45aecaa17824e656bcdb01f9737154a" - integrity sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA== - dependencies: - decode-named-character-reference "^1.0.0" - micromark-util-character "^2.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-symbol "^2.0.0" - -micromark-util-encode@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz#0921ac7953dc3f1fd281e3d1932decfdb9382ab1" - integrity sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA== - -micromark-util-html-tag-name@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz#ae34b01cbe063363847670284c6255bb12138ec4" - integrity sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw== - -micromark-util-normalize-identifier@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz#91f9a4e65fe66cc80c53b35b0254ad67aa431d8b" - integrity sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w== - dependencies: - micromark-util-symbol "^2.0.0" - -micromark-util-resolve-all@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz#189656e7e1a53d0c86a38a652b284a252389f364" - integrity sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA== - dependencies: - micromark-util-types "^2.0.0" - -micromark-util-sanitize-uri@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz#ec8fbf0258e9e6d8f13d9e4770f9be64342673de" - integrity sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw== - dependencies: - micromark-util-character "^2.0.0" - micromark-util-encode "^2.0.0" - micromark-util-symbol "^2.0.0" - -micromark-util-subtokenize@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz#9f412442d77e0c5789ffdf42377fa8a2bcbdf581" - integrity sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg== - dependencies: - devlop "^1.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromark-util-symbol@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz#12225c8f95edf8b17254e47080ce0862d5db8044" - integrity sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw== - -micromark-util-types@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/micromark-util-types/-/micromark-util-types-2.0.0.tgz#63b4b7ffeb35d3ecf50d1ca20e68fc7caa36d95e" - integrity sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w== - -micromark@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/micromark/-/micromark-4.0.0.tgz#84746a249ebd904d9658cfabc1e8e5f32cbc6249" - integrity sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ== - dependencies: - "@types/debug" "^4.0.0" - debug "^4.0.0" - decode-named-character-reference "^1.0.0" - devlop "^1.0.0" - micromark-core-commonmark "^2.0.0" - micromark-factory-space "^2.0.0" - micromark-util-character "^2.0.0" - micromark-util-chunked "^2.0.0" - micromark-util-combine-extensions "^2.0.0" - micromark-util-decode-numeric-character-reference "^2.0.0" - micromark-util-encode "^2.0.0" - micromark-util-normalize-identifier "^2.0.0" - micromark-util-resolve-all "^2.0.0" - micromark-util-sanitize-uri "^2.0.0" - micromark-util-subtokenize "^2.0.0" - micromark-util-symbol "^2.0.0" - micromark-util-types "^2.0.0" - -micromatch@^4.0.2, micromatch@^4.0.4: - version "4.0.5" - resolved "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== - dependencies: - braces "^3.0.2" - picomatch "^2.3.1" - -mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": - version "1.52.0" - resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mime@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" - integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== - -mimic-fn@^2.0.0, mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -mimic-fn@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" - integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== - -mimic-response@^1.0.0: - version "1.0.1" - resolved "https://registry.npmmirror.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -mimic-response@^3.1.0: - version "3.1.0" - resolved "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" - integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== - -minimalistic-assert@^1.0.0: - version "1.0.1" - resolved "https://registry.npmmirror.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^5.0.1: - version "5.1.6" - resolved "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^9.0.3: - version "9.0.3" - resolved "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - -minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8: - version "1.2.8" - resolved "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -minipass-collect@^1.0.2: - version "1.0.2" - resolved "https://registry.npmmirror.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" - integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== - dependencies: - minipass "^3.0.0" - -minipass-fetch@^2.0.3: - version "2.1.2" - resolved "https://registry.npmmirror.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" - integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== - dependencies: - minipass "^3.1.6" - minipass-sized "^1.0.3" - minizlib "^2.1.2" - optionalDependencies: - encoding "^0.1.13" - -minipass-flush@^1.0.5: - version "1.0.5" - resolved "https://registry.npmmirror.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" - integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== - dependencies: - minipass "^3.0.0" - -minipass-pipeline@^1.2.4: - version "1.2.4" - resolved "https://registry.npmmirror.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" - integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== - dependencies: - minipass "^3.0.0" - -minipass-sized@^1.0.3: - version "1.0.3" - resolved "https://registry.npmmirror.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" - integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== - dependencies: - minipass "^3.0.0" - -minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: - version "3.3.6" - resolved "https://registry.npmmirror.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" - integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== - dependencies: - yallist "^4.0.0" - -minipass@^5.0.0: - version "5.0.0" - resolved "https://registry.npmmirror.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" - integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== - -minizlib@^2.1.1, minizlib@^2.1.2: - version "2.1.2" - resolved "https://registry.npmmirror.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - -mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: - version "0.5.3" - resolved "https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - -mkdirp@^0.5.1: - version "0.5.6" - resolved "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - -mkdirp@^1.0.3, mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.npmmirror.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -moment-timezone@^0.5.34: - version "0.5.45" - resolved "https://registry.npmmirror.com/moment-timezone/-/moment-timezone-0.5.45.tgz#cb685acd56bac10e69d93c536366eb65aa6bcf5c" - integrity sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ== - dependencies: - moment "^2.29.4" - -moment@^2.10.2, moment@^2.29.1, moment@^2.29.2, moment@^2.29.4, moment@^2.9.0: - version "2.30.1" - resolved "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" - integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3, ms@^2.0.0: - version "2.1.3" - resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -multicast-dns@^7.2.5: - version "7.2.5" - resolved "https://registry.npmmirror.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" - integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== - dependencies: - dns-packet "^5.2.2" - thunky "^1.0.2" - -nanoid@^3.3.7: - version "3.3.7" - resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" - integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== - -napi-build-utils@^1.0.1: - version "1.0.2" - resolved "https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" - integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== - -negotiator@0.6.3, negotiator@^0.6.3: - version "0.6.3" - resolved "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - -next-tick@^1.1.0: - version "1.1.0" - resolved "https://registry.npmmirror.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" - integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.npmmirror.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -nlcst-to-string@^3.0.0: - version "3.1.1" - resolved "https://registry.npmmirror.com/nlcst-to-string/-/nlcst-to-string-3.1.1.tgz#83b90f2e1ee2081e14701317efc26d3bbadc806e" - integrity sha512-63mVyqaqt0cmn2VcI2aH6kxe1rLAmSROqHMA0i4qqg1tidkfExgpb0FGMikMCn86mw5dFtBtEANfmSSK7TjNHw== - dependencies: - "@types/nlcst" "^1.0.0" - -no-case@^3.0.4: - version "3.0.4" - resolved "https://registry.npmmirror.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" - integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== - dependencies: - lower-case "^2.0.2" - tslib "^2.0.3" - -node-abi@^3.3.0, node-abi@^3.45.0: - version "3.56.0" - resolved "https://registry.npmmirror.com/node-abi/-/node-abi-3.56.0.tgz#ca807d5ff735ac6bbbd684ae3ff2debc1c2a40a7" - integrity sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q== - dependencies: - semver "^7.3.5" - -node-addon-api@^6.1.0: - version "6.1.0" - resolved "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" - integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== - -node-api-version@^0.2.0: - version "0.2.0" - resolved "https://registry.npmmirror.com/node-api-version/-/node-api-version-0.2.0.tgz#5177441da2b1046a4d4547ab9e0972eed7b1ac1d" - integrity sha512-fthTTsi8CxaBXMaBAD7ST2uylwvsnYxh2PfaScwpMhos6KlSFajXQPcM4ogNE1q2s3Lbz9GCGqeIHC+C6OZnKg== - dependencies: - semver "^7.3.5" - -node-fetch@^2.6.7: - version "2.7.0" - resolved "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" - integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== - dependencies: - whatwg-url "^5.0.0" - -node-forge@^1: - version "1.3.1" - resolved "https://registry.npmmirror.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== - -node-gyp@^9.0.0: - version "9.4.1" - resolved "https://registry.npmmirror.com/node-gyp/-/node-gyp-9.4.1.tgz#8a1023e0d6766ecb52764cc3a734b36ff275e185" - integrity sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ== - dependencies: - env-paths "^2.2.0" - exponential-backoff "^3.1.1" - glob "^7.1.4" - graceful-fs "^4.2.6" - make-fetch-happen "^10.0.3" - nopt "^6.0.0" - npmlog "^6.0.0" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.2" - which "^2.0.2" - -node-loader@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/node-loader/-/node-loader-2.0.0.tgz#9109a6d828703fd3e0aa03c1baec12a798071562" - integrity sha512-I5VN34NO4/5UYJaUBtkrODPWxbobrE4hgDqPrjB25yPkonFhCmZ146vTH+Zg417E9Iwoh1l/MbRs1apc5J295Q== - dependencies: - loader-utils "^2.0.0" - -node-releases@^2.0.14: - version "2.0.14" - resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" - integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== - -nopt@^6.0.0: - version "6.0.0" - resolved "https://registry.npmmirror.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" - integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== - dependencies: - abbrev "^1.0.0" - -normalize-package-data@^2.3.2: - version "2.5.0" - resolved "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-url@^6.0.1: - version "6.1.0" - resolved "https://registry.npmmirror.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw== - dependencies: - path-key "^2.0.0" - -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -npm-run-path@^5.1.0: - version "5.3.0" - resolved "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" - integrity sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ== - dependencies: - path-key "^4.0.0" - -npmlog@^6.0.0: - version "6.0.2" - resolved "https://registry.npmmirror.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" - integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== - dependencies: - are-we-there-yet "^3.0.0" - console-control-strings "^1.1.0" - gauge "^4.0.3" - set-blocking "^2.0.0" - -nth-check@^2.0.1: - version "2.1.1" - resolved "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" - integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== - dependencies: - boolbase "^1.0.0" - -object-inspect@^1.13.1, object-inspect@^1.6.0: - version "1.13.1" - resolved "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" - integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== - -object-is@^1.1.5: - version "1.1.5" - resolved "https://registry.npmmirror.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -obuf@^1.0.0, obuf@^1.1.2: - version "1.1.2" - resolved "https://registry.npmmirror.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" - integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== - -on-finished@2.4.1: - version "2.4.1" - resolved "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.npmmirror.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.npmmirror.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -onetime@^5.1.0, onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -onetime@^6.0.0: - version "6.0.0" - resolved "https://registry.npmmirror.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4" - integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== - dependencies: - mimic-fn "^4.0.0" - -open@^8.0.9: - version "8.4.2" - resolved "https://registry.npmmirror.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" - integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== - dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" - is-wsl "^2.2.0" - -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.npmmirror.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - 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" - -ora@^5.1.0: - version "5.4.1" - resolved "https://registry.npmmirror.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" - integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== - dependencies: - bl "^4.1.0" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.5.0" - is-interactive "^1.0.0" - is-unicode-supported "^0.1.0" - log-symbols "^4.1.0" - strip-ansi "^6.0.0" - wcwidth "^1.0.1" - -ora@^7.0.1: - version "7.0.1" - resolved "https://registry.npmmirror.com/ora/-/ora-7.0.1.tgz#cdd530ecd865fe39e451a0e7697865669cb11930" - integrity sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw== - dependencies: - chalk "^5.3.0" - cli-cursor "^4.0.0" - cli-spinners "^2.9.0" - is-interactive "^2.0.0" - is-unicode-supported "^1.3.0" - log-symbols "^5.1.0" - stdin-discarder "^0.1.0" - string-width "^6.1.0" - strip-ansi "^7.1.0" - -overlayscrollbars@^1.13.1: - version "1.13.3" - resolved "https://registry.npmmirror.com/overlayscrollbars/-/overlayscrollbars-1.13.3.tgz#992c96fd2c8535802d1e56c40bc12ec03076c626" - integrity sha512-1nB/B5kaakJuHXaLXLRK0bUIilWhUGT6q5g+l2s5vqYdLle/sd0kscBHkQC1kuuDg9p9WR4MTdySDOPbeL/86g== - -p-cancelable@^2.0.0: - version "2.1.1" - resolved "https://registry.npmmirror.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" - integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== - -p-defer@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" - integrity sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw== - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== - -p-is-promise@^2.0.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" - integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== - -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.npmmirror.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2, "p-limit@^3.1.0 ": - version "3.1.0" - resolved "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-limit@^5.0.0: - version "5.0.0" - resolved "https://registry.npmmirror.com/p-limit/-/p-limit-5.0.0.tgz#6946d5b7140b649b7a33a027d89b4c625b3a5985" - integrity sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ== - dependencies: - yocto-queue "^1.0.0" - -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== - dependencies: - p-limit "^1.1.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - -p-queue@^8.0.1: - version "8.0.1" - resolved "https://registry.npmmirror.com/p-queue/-/p-queue-8.0.1.tgz#718b7f83836922ef213ddec263ff4223ce70bef8" - integrity sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA== - dependencies: - eventemitter3 "^5.0.1" - p-timeout "^6.1.2" - -p-retry@^4.5.0: - version "4.6.2" - resolved "https://registry.npmmirror.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" - integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== - dependencies: - "@types/retry" "0.12.0" - retry "^0.13.1" - -p-timeout@^6.1.2: - version "6.1.2" - resolved "https://registry.npmmirror.com/p-timeout/-/p-timeout-6.1.2.tgz#22b8d8a78abf5e103030211c5fc6dee1166a6aa5" - integrity sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ== - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -pako@^0.2.5: - version "0.2.9" - resolved "https://registry.npmmirror.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" - integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== - -pako@~1.0.2: - version "1.0.11" - resolved "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" - integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== - -param-case@^3.0.4: - version "3.0.4" - resolved "https://registry.npmmirror.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" - integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== - dependencies: - dot-case "^3.0.4" - tslib "^2.0.3" - -parse-author@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/parse-author/-/parse-author-2.0.0.tgz#d3460bf1ddd0dfaeed42da754242e65fb684a81f" - integrity sha512-yx5DfvkN8JsHL2xk2Os9oTia467qnvRgey4ahSm2X8epehBLx/gWLcy5KI+Y36ful5DzGbCS6RazqZGgy1gHNw== - dependencies: - author-regex "^1.0.0" - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.npmmirror.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - integrity sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ== - dependencies: - error-ex "^1.2.0" - -parse-latin@^5.0.0: - version "5.0.1" - resolved "https://registry.npmmirror.com/parse-latin/-/parse-latin-5.0.1.tgz#f3b4fac54d06f6a0501cf8b8ecfafa4cbb4f2f47" - integrity sha512-b/K8ExXaWC9t34kKeDV8kGXBkXZ1HCSAZRYE7HR14eA1GlXX5L8iWhs8USJNhQU9q5ci413jCKF0gOyovvyRBg== - dependencies: - nlcst-to-string "^3.0.0" - unist-util-modify-children "^3.0.0" - unist-util-visit-children "^2.0.0" - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== - -parse5@^7.0.0: - version "7.1.2" - resolved "https://registry.npmmirror.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" - integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== - dependencies: - entities "^4.4.0" - -parseurl@~1.3.2, parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -pascal-case@^3.1.2: - version "3.1.2" - resolved "https://registry.npmmirror.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" - integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== - dependencies: - no-case "^3.0.4" - tslib "^2.0.3" - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.npmmirror.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-key@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18" - integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== - -path-to-regexp@^6.2.1: - version "6.2.1" - resolved "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5" - integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== - -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" - integrity sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ== - dependencies: - pify "^2.0.0" - -pdfmake@^0.2.4: - version "0.2.9" - resolved "https://registry.npmmirror.com/pdfmake/-/pdfmake-0.2.9.tgz#1768cf85b2b137afb47fb4bc2dd8c2cd3304ebbe" - integrity sha512-LAtYwlR8cCQqbxESK2d50DYaVAzAC9Id9NjilRte6Tb9pyHUB+Z50nhD0imuBL0eDyXQKvEYSNjo3P5AOc2ZCg== - dependencies: - "@foliojs-fork/linebreak" "^1.1.1" - "@foliojs-fork/pdfkit" "^0.14.0" - iconv-lite "^0.6.3" - xmldoc "^1.1.2" - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== - -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== - -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -plist@^3.0.0, plist@^3.0.5, plist@^3.1.0: - version "3.1.0" - resolved "https://registry.npmmirror.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9" - integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== - dependencies: - "@xmldom/xmldom" "^0.8.8" - base64-js "^1.5.1" - xmlbuilder "^15.1.1" - -png-js@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/png-js/-/png-js-1.0.0.tgz#e5484f1e8156996e383aceebb3789fd75df1874d" - integrity sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g== - -popper.js@>=1.10, popper.js@^1.16.1: - version "1.16.1" - resolved "https://registry.npmmirror.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" - integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== - -postcss-modules-extract-imports@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" - integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== - -postcss-modules-local-by-default@^4.0.4: - version "4.0.4" - resolved "https://registry.npmmirror.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz#7cbed92abd312b94aaea85b68226d3dec39a14e6" - integrity sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q== - dependencies: - icss-utils "^5.0.0" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.1.0" - -postcss-modules-scope@^3.1.1: - version "3.1.1" - resolved "https://registry.npmmirror.com/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz#32cfab55e84887c079a19bbb215e721d683ef134" - integrity sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA== - dependencies: - postcss-selector-parser "^6.0.4" - -postcss-modules-values@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" - integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== - dependencies: - icss-utils "^5.0.0" - -postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: - version "6.0.15" - resolved "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535" - integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== - dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" - -postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: - version "4.2.0" - resolved "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" - integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== - -postcss@^8.4.33, postcss@^8.4.35: - version "8.4.35" - resolved "https://registry.npmmirror.com/postcss/-/postcss-8.4.35.tgz#60997775689ce09011edf083a549cea44aabe2f7" - integrity sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA== - dependencies: - nanoid "^3.3.7" - picocolors "^1.0.0" - source-map-js "^1.0.2" - -postject@^1.0.0-alpha.6: - version "1.0.0-alpha.6" - resolved "https://registry.npmmirror.com/postject/-/postject-1.0.0-alpha.6.tgz#9d022332272e2cfce8dea4cfce1ee6dd1b2ee135" - integrity sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A== - dependencies: - commander "^9.4.0" - -prebuild-install@^7.1.1: - version "7.1.1" - resolved "https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" - integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== - dependencies: - detect-libc "^2.0.0" - expand-template "^2.0.3" - github-from-package "0.0.0" - minimist "^1.2.3" - mkdirp-classic "^0.5.3" - napi-build-utils "^1.0.1" - node-abi "^3.3.0" - pump "^3.0.0" - rc "^1.2.7" - simple-get "^4.0.0" - tar-fs "^2.0.0" - tunnel-agent "^0.6.0" - -preferred-pm@^3.1.2: - version "3.1.3" - resolved "https://registry.npmmirror.com/preferred-pm/-/preferred-pm-3.1.3.tgz#4125ea5154603136c3b6444e5f5c94ecf90e4916" - integrity sha512-MkXsENfftWSRpzCzImcp4FRsCc3y1opwB73CfCNWyzMqArju2CrlMHlqB7VexKiPEOjGMbttv1r9fSCn5S610w== - dependencies: - find-up "^5.0.0" - find-yarn-workspace-root2 "1.2.16" - path-exists "^4.0.0" - which-pm "2.0.0" - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== - -pretty-error@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" - integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== - dependencies: - lodash "^4.17.20" - renderkid "^3.0.0" - -prismjs@^1.29.0: - version "1.29.0" - resolved "https://registry.npmmirror.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" - integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -progress@^2.0.3: - version "2.0.3" - resolved "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.npmmirror.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== - -promise-retry@^2.0.1: - version "2.0.1" - resolved "https://registry.npmmirror.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" - integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== - dependencies: - err-code "^2.0.2" - retry "^0.12.0" - -prompts@^2.4.2: - version "2.4.2" - resolved "https://registry.npmmirror.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" - integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" - -property-information@^6.0.0: - version "6.4.1" - resolved "https://registry.npmmirror.com/property-information/-/property-information-6.4.1.tgz#de8b79a7415fd2107dfbe65758bb2cc9dfcf60ac" - integrity sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w== - -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@^2.1.0: - version "2.3.1" - resolved "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" - integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== - -qs@6.11.0: - version "6.11.0" - resolved "https://registry.npmmirror.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== - dependencies: - side-channel "^1.0.4" - -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -queue-tick@^1.0.1: - version "1.0.1" - resolved "https://registry.npmmirror.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" - integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== - -quick-lru@^5.1.1: - version "5.1.1" - resolved "https://registry.npmmirror.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" - integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== - -quote-stream@^1.0.1: - version "1.0.2" - resolved "https://registry.npmmirror.com/quote-stream/-/quote-stream-1.0.2.tgz#84963f8c9c26b942e153feeb53aae74652b7e0b2" - integrity sha512-kKr2uQ2AokadPjvTyKJQad9xELbZwYzWlNfI3Uz2j/ib5u6H9lDP7fUUR//rMycd0gv4Z5P1qXMfXR8YpIxrjQ== - dependencies: - buffer-equal "0.0.1" - minimist "^1.1.3" - through2 "^2.0.0" - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -range-parser@^1.2.1, range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -"raphael@^2.2.0 || ^2.1.1", raphael@^2.3.0: - version "2.3.0" - resolved "https://registry.npmmirror.com/raphael/-/raphael-2.3.0.tgz#eabeb09dba861a1d4cee077eaafb8c53f3131f89" - integrity sha512-w2yIenZAQnp257XUWGni4bLMVxpUpcIl7qgxEgDIXtmSypYtlNxfXWpOBxs7LBTps5sDwhRnrToJrMUrivqNTQ== - dependencies: - eve-raphael "0.5.0" - -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -rcedit@^4.0.0: - version "4.0.1" - resolved "https://registry.npmmirror.com/rcedit/-/rcedit-4.0.1.tgz#892ac47a19204a380f49e00ea38ce070443343c2" - integrity sha512-bZdaQi34krFWhrDn+O53ccBDw0MkAT2Vhu75SqhtvhQu4OPyFM4RoVheyYiVQYdjhUi6EJMVWQ0tR6bCIYVkUg== - dependencies: - cross-spawn-windows-exe "^1.1.0" - -read-binary-file-arch@^1.0.6: - version "1.0.6" - resolved "https://registry.npmmirror.com/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz#959c4637daa932280a9b911b1a6766a7e44288fc" - integrity sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg== - dependencies: - debug "^4.3.4" - -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" - integrity sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w== - dependencies: - find-up "^2.0.0" - read-pkg "^2.0.0" - -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" - integrity sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA== - dependencies: - load-json-file "^2.0.0" - normalize-package-data "^2.3.2" - path-type "^2.0.0" - -readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@~2.3.3, readable-stream@~2.3.6: - version "2.3.8" - resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - 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" - -readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: - version "3.6.2" - resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -rechoir@^0.8.0: - version "0.8.0" - resolved "https://registry.npmmirror.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" - integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== - dependencies: - resolve "^1.20.0" - -regexp.prototype.flags@^1.5.1: - version "1.5.2" - resolved "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" - integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== - dependencies: - call-bind "^1.0.6" - define-properties "^1.2.1" - es-errors "^1.3.0" - set-function-name "^2.0.1" - -rehype-parse@^9.0.0: - version "9.0.0" - resolved "https://registry.npmmirror.com/rehype-parse/-/rehype-parse-9.0.0.tgz#3949faeec6f466ec57774215661e0d75469195d9" - integrity sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw== - dependencies: - "@types/hast" "^3.0.0" - hast-util-from-html "^2.0.0" - unified "^11.0.0" - -rehype-raw@^7.0.0: - version "7.0.0" - resolved "https://registry.npmmirror.com/rehype-raw/-/rehype-raw-7.0.0.tgz#59d7348fd5dbef3807bbaa1d443efd2dd85ecee4" - integrity sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww== - dependencies: - "@types/hast" "^3.0.0" - hast-util-raw "^9.0.0" - vfile "^6.0.0" - -rehype-stringify@^10.0.0: - version "10.0.0" - resolved "https://registry.npmmirror.com/rehype-stringify/-/rehype-stringify-10.0.0.tgz#2031cf6fdd0355393706f0474ec794c75e5492f2" - integrity sha512-1TX1i048LooI9QoecrXy7nGFFbFSufxVRAfc6Y9YMRAi56l+oB0zP51mLSV312uRuvVLPV1opSlJmslozR1XHQ== - dependencies: - "@types/hast" "^3.0.0" - hast-util-to-html "^9.0.0" - unified "^11.0.0" - -rehype@^13.0.1: - version "13.0.1" - resolved "https://registry.npmmirror.com/rehype/-/rehype-13.0.1.tgz#56384ba83955e2f3aa7eca1975b406c67d9dbd5e" - integrity sha512-AcSLS2mItY+0fYu9xKxOu1LhUZeBZZBx8//5HKzF+0XP+eP8+6a5MXn2+DW2kfXR6Dtp1FEXMVrjyKAcvcU8vg== - dependencies: - "@types/hast" "^3.0.0" - rehype-parse "^9.0.0" - rehype-stringify "^10.0.0" - unified "^11.0.0" - -relateurl@^0.2.7: - version "0.2.7" - resolved "https://registry.npmmirror.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" - integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== - -remark-gfm@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/remark-gfm/-/remark-gfm-4.0.0.tgz#aea777f0744701aa288b67d28c43565c7e8c35de" - integrity sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA== - dependencies: - "@types/mdast" "^4.0.0" - mdast-util-gfm "^3.0.0" - micromark-extension-gfm "^3.0.0" - remark-parse "^11.0.0" - remark-stringify "^11.0.0" - unified "^11.0.0" - -remark-parse@^11.0.0: - version "11.0.0" - resolved "https://registry.npmmirror.com/remark-parse/-/remark-parse-11.0.0.tgz#aa60743fcb37ebf6b069204eb4da304e40db45a1" - integrity sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA== - dependencies: - "@types/mdast" "^4.0.0" - mdast-util-from-markdown "^2.0.0" - micromark-util-types "^2.0.0" - unified "^11.0.0" - -remark-rehype@^11.0.0: - version "11.1.0" - resolved "https://registry.npmmirror.com/remark-rehype/-/remark-rehype-11.1.0.tgz#d5f264f42bcbd4d300f030975609d01a1697ccdc" - integrity sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g== - dependencies: - "@types/hast" "^3.0.0" - "@types/mdast" "^4.0.0" - mdast-util-to-hast "^13.0.0" - unified "^11.0.0" - vfile "^6.0.0" - -remark-smartypants@^2.0.0: - version "2.1.0" - resolved "https://registry.npmmirror.com/remark-smartypants/-/remark-smartypants-2.1.0.tgz#afd26d8ff40def346c6516e38b46994449fb2efe" - integrity sha512-qoF6Vz3BjU2tP6OfZqHOvCU0ACmu/6jhGaINSQRI9mM7wCxNQTKB3JUAN4SVoN2ybElEDTxBIABRep7e569iJw== - dependencies: - retext "^8.1.0" - retext-smartypants "^5.2.0" - unist-util-visit "^5.0.0" - -remark-stringify@^11.0.0: - version "11.0.0" - resolved "https://registry.npmmirror.com/remark-stringify/-/remark-stringify-11.0.0.tgz#4c5b01dd711c269df1aaae11743eb7e2e7636fd3" - integrity sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw== - dependencies: - "@types/mdast" "^4.0.0" - mdast-util-to-markdown "^2.0.0" - unified "^11.0.0" - -renderkid@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" - 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" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - -resolve-alpn@^1.0.0: - version "1.2.1" - resolved "https://registry.npmmirror.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" - integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== - -resolve-dir@^1.0.0: - version "1.0.1" - resolved "https://registry.npmmirror.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - integrity sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg== - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - -resolve-package@^1.0.1: - version "1.0.1" - resolved "https://registry.npmmirror.com/resolve-package/-/resolve-package-1.0.1.tgz#686f70b188bd7d675f5bbc4282ccda060abb9d27" - integrity sha512-rzB7NnQpOkPHBWFPP3prUMqOP6yg3HkRGgcvR+lDyvyHoY3fZLFLYDkPXh78SPVBAE6VTCk/V+j8we4djg6o4g== - dependencies: - get-installed-path "^2.0.3" - -resolve@1.1.7: - version "1.1.7" - resolved "https://registry.npmmirror.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" - integrity sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg== - -resolve@^1.1.5, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.20.0, resolve@^1.22.4: - version "1.22.8" - resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" - integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== - dependencies: - is-core-module "^2.13.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -responselike@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" - integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== - dependencies: - lowercase-keys "^2.0.0" - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -restore-cursor@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" - integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -retext-latin@^3.0.0: - version "3.1.0" - resolved "https://registry.npmmirror.com/retext-latin/-/retext-latin-3.1.0.tgz#72b0176af2c69a373fd0d37eadd3924418bb3a89" - integrity sha512-5MrD1tuebzO8ppsja5eEu+ZbBeUNCjoEarn70tkXOS7Bdsdf6tNahsv2bY0Z8VooFF6cw7/6S+d3yI/TMlMVVQ== - dependencies: - "@types/nlcst" "^1.0.0" - parse-latin "^5.0.0" - unherit "^3.0.0" - unified "^10.0.0" - -retext-smartypants@^5.2.0: - version "5.2.0" - resolved "https://registry.npmmirror.com/retext-smartypants/-/retext-smartypants-5.2.0.tgz#da9cb79cc60f36aa33a20a462dfc663bec0068b4" - integrity sha512-Do8oM+SsjrbzT2UNIKgheP0hgUQTDDQYyZaIY3kfq0pdFzoPk+ZClYJ+OERNXveog4xf1pZL4PfRxNoVL7a/jw== - dependencies: - "@types/nlcst" "^1.0.0" - nlcst-to-string "^3.0.0" - unified "^10.0.0" - unist-util-visit "^4.0.0" - -retext-stringify@^3.0.0: - version "3.1.0" - resolved "https://registry.npmmirror.com/retext-stringify/-/retext-stringify-3.1.0.tgz#46ed45e077bfc4a8334977f6c2d6611e1d36263a" - integrity sha512-767TLOaoXFXyOnjx/EggXlb37ZD2u4P1n0GJqVdpipqACsQP+20W+BNpMYrlJkq7hxffnFk+jc6mAK9qrbuB8w== - dependencies: - "@types/nlcst" "^1.0.0" - nlcst-to-string "^3.0.0" - unified "^10.0.0" - -retext@^8.1.0: - version "8.1.0" - resolved "https://registry.npmmirror.com/retext/-/retext-8.1.0.tgz#c43437fb84cd46285ad240a9279142e239bada8d" - integrity sha512-N9/Kq7YTn6ZpzfiGW45WfEGJqFf1IM1q8OsRa1CGzIebCJBNCANDRmOrholiDRGKo/We7ofKR4SEvcGAWEMD3Q== - dependencies: - "@types/nlcst" "^1.0.0" - retext-latin "^3.0.0" - retext-stringify "^3.0.0" - unified "^10.0.0" - -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.npmmirror.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== - -retry@^0.13.1: - version "0.13.1" - resolved "https://registry.npmmirror.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" - integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== - -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rfdc@^1.3.0: - version "1.3.1" - resolved "https://registry.npmmirror.com/rfdc/-/rfdc-1.3.1.tgz#2b6d4df52dffe8bb346992a10ea9451f24373a8f" - integrity sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg== - -rimraf@^3.0.0, rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -rimraf@~2.6.2: - version "2.6.3" - resolved "https://registry.npmmirror.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" - -roarr@^2.15.3: - version "2.15.4" - resolved "https://registry.npmmirror.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" - integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== - dependencies: - boolean "^3.0.1" - detect-node "^2.0.4" - globalthis "^1.0.1" - json-stringify-safe "^5.0.1" - semver-compare "^1.0.0" - sprintf-js "^1.1.2" - -rollup@^4.2.0: - version "4.12.0" - resolved "https://registry.npmmirror.com/rollup/-/rollup-4.12.0.tgz#0b6d1e5f3d46bbcf244deec41a7421dc54cc45b5" - integrity sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q== - dependencies: - "@types/estree" "1.0.5" - optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.12.0" - "@rollup/rollup-android-arm64" "4.12.0" - "@rollup/rollup-darwin-arm64" "4.12.0" - "@rollup/rollup-darwin-x64" "4.12.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.12.0" - "@rollup/rollup-linux-arm64-gnu" "4.12.0" - "@rollup/rollup-linux-arm64-musl" "4.12.0" - "@rollup/rollup-linux-riscv64-gnu" "4.12.0" - "@rollup/rollup-linux-x64-gnu" "4.12.0" - "@rollup/rollup-linux-x64-musl" "4.12.0" - "@rollup/rollup-win32-arm64-msvc" "4.12.0" - "@rollup/rollup-win32-ia32-msvc" "4.12.0" - "@rollup/rollup-win32-x64-msvc" "4.12.0" - fsevents "~2.3.2" - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -rxjs@^7.8.0: - version "7.8.1" - resolved "https://registry.npmmirror.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" - integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== - dependencies: - tslib "^2.1.0" - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": - version "2.1.2" - resolved "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sax@^1.2.4: - version "1.3.0" - resolved "https://registry.npmmirror.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" - integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== - -schema-utils@^3.1.1, schema-utils@^3.2.0: - version "3.3.0" - resolved "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" - integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== - dependencies: - "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - -schema-utils@^4.0.0: - version "4.2.0" - resolved "https://registry.npmmirror.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" - integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== - dependencies: - "@types/json-schema" "^7.0.9" - ajv "^8.9.0" - ajv-formats "^2.1.1" - ajv-keywords "^5.1.0" - -scope-analyzer@^2.0.1: - version "2.1.2" - resolved "https://registry.npmmirror.com/scope-analyzer/-/scope-analyzer-2.1.2.tgz#b958162feb59823c2835c7b0229187a97c77e9cd" - integrity sha512-5cfCmsTYV/wPaRIItNxatw02ua/MThdIUNnUOCYp+3LSEJvnG804ANw2VLaavNILIfWXF1D1G2KNANkBBvInwQ== - dependencies: - array-from "^2.1.1" - dash-ast "^2.0.1" - es6-map "^0.1.5" - es6-set "^0.1.5" - es6-symbol "^3.1.1" - estree-is-function "^1.0.0" - get-assigned-identifiers "^1.1.0" - -section-matter@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" - integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== - dependencies: - extend-shallow "^2.0.1" - kind-of "^6.0.0" - -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== - -select2@^4.0.13: - version "4.0.13" - resolved "https://registry.npmmirror.com/select2/-/select2-4.0.13.tgz#0dbe377df3f96167c4c1626033e924372d8ef44d" - integrity sha512-1JeB87s6oN/TDxQQYCvS5EFoQyvV6eYMZZ0AeA4tdFDYWN3BAGZ8npr17UBFddU0lgAt3H0yjX3X6/ekOj1yjw== - -selfsigned@^2.1.1: - version "2.4.1" - resolved "https://registry.npmmirror.com/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" - integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== - dependencies: - "@types/node-forge" "^1.3.0" - node-forge "^1" - -semver-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" - integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== - -"semver@2 || 3 || 4 || 5", semver@^5.5.0: - version "5.7.2" - resolved "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - -semver@^6.2.0, semver@^6.3.1: - version "6.3.1" - resolved "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semver@^7.5.4: - version "7.6.0" - resolved "https://registry.npmmirror.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" - integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== - dependencies: - lru-cache "^6.0.0" - -send@0.18.0: - version "0.18.0" - resolved "https://registry.npmmirror.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== - 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" - -serialize-error@^7.0.1: - version "7.0.1" - resolved "https://registry.npmmirror.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" - integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== - dependencies: - type-fest "^0.13.1" - -serialize-javascript@^6.0.1: - version "6.0.2" - resolved "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" - integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== - dependencies: - randombytes "^2.1.0" - -serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.npmmirror.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - 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" - -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.npmmirror.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.18.0" - -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== - -set-function-length@^1.2.1: - version "1.2.1" - resolved "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.1.tgz#47cc5945f2c771e2cf261c6737cf9684a2a5e425" - integrity sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g== - dependencies: - define-data-property "^1.1.2" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.3" - gopd "^1.0.1" - has-property-descriptors "^1.0.1" - -set-function-name@^2.0.1: - version "2.0.2" - resolved "https://registry.npmmirror.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" - integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - functions-have-names "^1.2.3" - has-property-descriptors "^1.0.2" - -setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.npmmirror.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - -shallow-copy@~0.0.1: - version "0.0.1" - resolved "https://registry.npmmirror.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" - integrity sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw== - -sharp@^0.32.6: - version "0.32.6" - resolved "https://registry.npmmirror.com/sharp/-/sharp-0.32.6.tgz#6ad30c0b7cd910df65d5f355f774aa4fce45732a" - integrity sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w== - dependencies: - color "^4.2.3" - detect-libc "^2.0.2" - node-addon-api "^6.1.0" - prebuild-install "^7.1.1" - semver "^7.5.4" - simple-get "^4.0.1" - tar-fs "^3.0.4" - tunnel-agent "^0.6.0" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.npmmirror.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== - dependencies: - shebang-regex "^1.0.0" - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -shell-quote@^1.8.1: - version "1.8.1" - resolved "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" - integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== - -shikiji-core@0.9.19, shikiji-core@^0.9.19: - version "0.9.19" - resolved "https://registry.npmmirror.com/shikiji-core/-/shikiji-core-0.9.19.tgz#227975e998eb2a579cf83de30977762be3802507" - integrity sha512-AFJu/vcNT21t0e6YrfadZ+9q86gvPum6iywRyt1OtIPjPFe25RQnYJyxHQPMLKCCWA992TPxmEmbNcOZCAJclw== - -shikiji@^0.9.18, shikiji@^0.9.19: - version "0.9.19" - resolved "https://registry.npmmirror.com/shikiji/-/shikiji-0.9.19.tgz#351a32b291a04cf9a6b69933f8044fe135b70f6f" - integrity sha512-Kw2NHWktdcdypCj1GkKpXH4o6Vxz8B8TykPlPuLHOGSV8VkhoCLcFOH4k19K4LXAQYRQmxg+0X/eM+m2sLhAkg== - dependencies: - shikiji-core "0.9.19" - -side-channel@^1.0.4: - version "1.0.5" - resolved "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.5.tgz#9a84546599b48909fb6af1211708d23b1946221b" - integrity sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ== - dependencies: - call-bind "^1.0.6" - es-errors "^1.3.0" - get-intrinsic "^1.2.4" - object-inspect "^1.13.1" - -signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: - version "3.0.7" - resolved "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -signal-exit@^4.1.0: - version "4.1.0" - resolved "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - -simple-concat@^1.0.0: - version "1.0.1" - resolved "https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" - integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== - -simple-get@^4.0.0, simple-get@^4.0.1: - version "4.0.1" - resolved "https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" - integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== - dependencies: - decompress-response "^6.0.0" - once "^1.3.1" - simple-concat "^1.0.0" - -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== - dependencies: - is-arrayish "^0.3.1" - -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://registry.npmmirror.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== - -slice-ansi@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" - integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -smart-buffer@^4.2.0: - version "4.2.0" - resolved "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" - integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== - -sockjs@^0.3.24: - version "0.3.24" - resolved "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" - integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== - dependencies: - faye-websocket "^0.11.3" - uuid "^8.3.2" - websocket-driver "^0.7.4" - -socks-proxy-agent@^7.0.0: - version "7.0.0" - resolved "https://registry.npmmirror.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" - integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== - dependencies: - agent-base "^6.0.2" - debug "^4.3.3" - socks "^2.6.2" - -socks@^2.6.2: - version "2.8.1" - resolved "https://registry.npmmirror.com/socks/-/socks-2.8.1.tgz#22c7d9dd7882649043cba0eafb49ae144e3457af" - integrity sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ== - dependencies: - ip-address "^9.0.5" - smart-buffer "^4.2.0" - -source-map-js@^1.0.2: - version "1.0.2" - resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== - -source-map-support@^0.5.13, source-map-support@~0.5.20: - version "0.5.21" - resolved "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.npmmirror.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== - -source-map@^0.6.0, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@~0.1.30: - version "0.1.43" - resolved "https://registry.npmmirror.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" - integrity sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ== - dependencies: - amdefine ">=0.0.4" - -sourcemap-codec@^1.4.1: - version "1.4.8" - resolved "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== - -space-separated-tokens@^2.0.0: - version "2.0.2" - resolved "https://registry.npmmirror.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz#1ecd9d2350a3844572c3f4a312bceb018348859f" - integrity sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q== - -sparklines@^1.3.0: - version "1.3.0" - resolved "https://registry.npmmirror.com/sparklines/-/sparklines-1.3.0.tgz#08015a34c295b62196b91e98df53bfbcb1fde175" - integrity sha512-CkFtpDE3hmOeu1IJyIQIOH0AQtHnPj1c61ALxJZQ9cPEFKDgWC1fcNAHuwPi1i1klTDYvlKKseoYHSwe7JmdLA== - -spdx-correct@^3.0.0: - version "3.2.0" - resolved "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" - integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.5.0" - resolved "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" - integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.17" - resolved "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz#887da8aa73218e51a1d917502d79863161a93f9c" - integrity sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg== - -spdy-transport@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" - 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" - -spdy@^4.0.2: - version "4.0.2" - resolved "https://registry.npmmirror.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" - 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" - -sprintf-js@^1.1.2, sprintf-js@^1.1.3: - version "1.1.3" - resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" - integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== - -ssri@^9.0.0: - version "9.0.1" - resolved "https://registry.npmmirror.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" - integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== - dependencies: - minipass "^3.1.1" - -static-eval@^2.0.5: - version "2.1.1" - resolved "https://registry.npmmirror.com/static-eval/-/static-eval-2.1.1.tgz#71ac6a13aa32b9e14c5b5f063c362176b0d584ba" - integrity sha512-MgWpQ/ZjGieSVB3eOJVs4OA2LT/q1vx98KPCTTQPzq/aLr0YUXTsgryTXr4SLfR0ZfUUCiedM9n/ABeDIyy4mA== - dependencies: - escodegen "^2.1.0" - -static-module@^3.0.2: - version "3.0.4" - resolved "https://registry.npmmirror.com/static-module/-/static-module-3.0.4.tgz#bfbd1d1c38dd1fbbf0bb4af0c1b3ae18a93a2b68" - integrity sha512-gb0v0rrgpBkifXCa3yZXxqVmXDVE+ETXj6YlC/jt5VzOnGXR2C15+++eXuMDUYsePnbhf+lwW0pE1UXyOLtGCw== - dependencies: - acorn-node "^1.3.0" - concat-stream "~1.6.0" - convert-source-map "^1.5.1" - duplexer2 "~0.1.4" - escodegen "^1.11.1" - has "^1.0.1" - magic-string "0.25.1" - merge-source-map "1.0.4" - object-inspect "^1.6.0" - readable-stream "~2.3.3" - scope-analyzer "^2.0.1" - shallow-copy "~0.0.1" - static-eval "^2.0.5" - through2 "~2.0.3" - -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -"statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.npmmirror.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - -stdin-discarder@^0.1.0: - version "0.1.0" - resolved "https://registry.npmmirror.com/stdin-discarder/-/stdin-discarder-0.1.0.tgz#22b3e400393a8e28ebf53f9958f3880622efde21" - integrity sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ== - dependencies: - bl "^5.0.0" - -streamx@^2.13.0, streamx@^2.15.0: - version "2.16.1" - resolved "https://registry.npmmirror.com/streamx/-/streamx-2.16.1.tgz#2b311bd34832f08aa6bb4d6a80297c9caef89614" - integrity sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ== - dependencies: - fast-fifo "^1.1.0" - queue-tick "^1.0.1" - optionalDependencies: - bare-events "^2.2.0" - -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - 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" - -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - -string-width@^6.1.0: - version "6.1.0" - resolved "https://registry.npmmirror.com/string-width/-/string-width-6.1.0.tgz#96488d6ed23f9ad5d82d13522af9e4c4c3fd7518" - integrity sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^10.2.1" - strip-ansi "^7.0.1" - -string-width@^7.0.0: - version "7.1.0" - resolved "https://registry.npmmirror.com/string-width/-/string-width-7.1.0.tgz#d994252935224729ea3719c49f7206dc9c46550a" - integrity sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw== - dependencies: - emoji-regex "^10.3.0" - get-east-asian-width "^1.0.0" - strip-ansi "^7.1.0" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -stringify-entities@^4.0.0: - version "4.0.3" - resolved "https://registry.npmmirror.com/stringify-entities/-/stringify-entities-4.0.3.tgz#cfabd7039d22ad30f3cc435b0ca2c1574fc88ef8" - integrity sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g== - dependencies: - character-entities-html4 "^2.0.0" - character-entities-legacy "^3.0.0" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^7.0.1, strip-ansi@^7.1.0: - version "7.1.0" - resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - -strip-bom-string@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" - integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-final-newline@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" - integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== - -strip-outer@^1.0.1: - version "1.0.1" - resolved "https://registry.npmmirror.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" - integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== - dependencies: - escape-string-regexp "^1.0.2" - -style-loader@^3.0.0: - version "3.3.4" - resolved "https://registry.npmmirror.com/style-loader/-/style-loader-3.3.4.tgz#f30f786c36db03a45cbd55b6a70d930c479090e7" - integrity sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w== - -sudo-prompt@^9.1.1: - version "9.2.1" - resolved "https://registry.npmmirror.com/sudo-prompt/-/sudo-prompt-9.2.1.tgz#77efb84309c9ca489527a4e749f287e6bdd52afd" - integrity sha512-Mu7R0g4ig9TUuGSxJavny5Rv0egCEtpZRNMrZaYS1vxkiIxGiGUwoezU3LazIQ+KE04hTrTfNPgxU5gzi7F5Pw== - -sumchecker@^3.0.1: - version "3.0.1" - resolved "https://registry.npmmirror.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" - integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== - dependencies: - debug "^4.1.0" - -summernote@^0.8.20: - version "0.8.20" - resolved "https://registry.npmmirror.com/summernote/-/summernote-0.8.20.tgz#395905f2cec0aceebc712edc019d91b8ef88f7cf" - integrity sha512-W9RhjQjsn+b1s9xiJQgJbCiYGJaDAc9CdEqXo+D13WuStG8lCdtKaO5AiNiSSMJsQJN2EfGSwbBQt+SFE2B8Kw== - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^8.0.0: - version "8.1.1" - resolved "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -sweetalert2@^11.4.0: - version "11.10.5" - resolved "https://registry.npmmirror.com/sweetalert2/-/sweetalert2-11.10.5.tgz#ff01f45b40ad678ca1f0c2e6c98800d79a74c49f" - integrity sha512-q9eE3EKhMcpIDU/Xcz7z5lk8axCGkgxwK47gXGrrfncnBJWxHPPHnBVAjfsVXcTt8Yi8U6HNEcBRSu+qGeyFdA== - -tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: - version "2.2.1" - resolved "https://registry.npmmirror.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" - integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== - -tar-fs@^2.0.0: - version "2.1.1" - resolved "https://registry.npmmirror.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" - integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== - dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.1.4" - -tar-fs@^3.0.4: - version "3.0.5" - resolved "https://registry.npmmirror.com/tar-fs/-/tar-fs-3.0.5.tgz#f954d77767e4e6edf973384e1eb95f8f81d64ed9" - integrity sha512-JOgGAmZyMgbqpLwct7ZV8VzkEB6pxXFBVErLtb+XCOqzc6w1xiWKI9GVd6bwk68EX7eJ4DWmfXVmq8K2ziZTGg== - dependencies: - pump "^3.0.0" - tar-stream "^3.1.5" - optionalDependencies: - bare-fs "^2.1.1" - bare-path "^2.1.0" - -tar-stream@^2.1.4: - version "2.2.0" - resolved "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" - integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - -tar-stream@^3.1.5: - version "3.1.7" - resolved "https://registry.npmmirror.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" - integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== - dependencies: - b4a "^1.6.4" - fast-fifo "^1.2.0" - streamx "^2.15.0" - -tar@^6.0.5, tar@^6.1.11, tar@^6.1.2: - version "6.2.0" - resolved "https://registry.npmmirror.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73" - integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^5.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - -temp@^0.9.0: - version "0.9.4" - resolved "https://registry.npmmirror.com/temp/-/temp-0.9.4.tgz#cd20a8580cb63635d0e4e9d4bd989d44286e7620" - integrity sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA== - dependencies: - mkdirp "^0.5.1" - rimraf "~2.6.2" - -tempusdominus-bootstrap-4@^5.39.0: - version "5.39.2" - resolved "https://registry.npmmirror.com/tempusdominus-bootstrap-4/-/tempusdominus-bootstrap-4-5.39.2.tgz#f8f12194287eb6f159e9b9d94a441abb54daf94d" - integrity sha512-8Au4miSAsMGdsElPg87EUmsN7aGJFaRA5Y8Ale7dDTfhhnQL1Za53LclIJkq+t/7NO5+Ufr1jY7tmEPvWGHaVg== - dependencies: - bootstrap "^4.6.1" - jquery "^3.6.0" - moment "^2.29.2" - moment-timezone "^0.5.34" - popper.js "^1.16.1" - -terser-webpack-plugin@^5.3.10: - version "5.3.10" - resolved "https://registry.npmmirror.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" - integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== - dependencies: - "@jridgewell/trace-mapping" "^0.3.20" - jest-worker "^27.4.5" - schema-utils "^3.1.1" - serialize-javascript "^6.0.1" - terser "^5.26.0" - -terser@^5.10.0, terser@^5.26.0: - version "5.28.1" - resolved "https://registry.npmmirror.com/terser/-/terser-5.28.1.tgz#bf00f7537fd3a798c352c2d67d67d65c915d1b28" - integrity sha512-wM+bZp54v/E9eRRGXb5ZFDvinrJIOaTapx3WUokyVGZu5ucVCK55zEgGd5Dl2fSr3jUo5sDiERErUWLY6QPFyA== - dependencies: - "@jridgewell/source-map" "^0.3.3" - acorn "^8.8.2" - commander "^2.20.0" - source-map-support "~0.5.20" - -through2@^2.0.0, through2@~2.0.3: - version "2.0.5" - resolved "https://registry.npmmirror.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through@^2.3.8, through@~2.3.4: - version "2.3.8" - resolved "https://registry.npmmirror.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== - -thunky@^1.0.2: - version "1.1.0" - resolved "https://registry.npmmirror.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" - integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== - -tiny-each-async@2.0.3: - version "2.0.3" - resolved "https://registry.npmmirror.com/tiny-each-async/-/tiny-each-async-2.0.3.tgz#8ebbbfd6d6295f1370003fbb37162afe5a0a51d1" - integrity sha512-5ROII7nElnAirvFn8g7H7MtpfV1daMcyfTGQwsn/x2VtyV+VPiO5CjReCJtWLvoKTDEDmZocf3cNPraiMnBXLA== - -tiny-inflate@^1.0.0, tiny-inflate@^1.0.2: - version "1.0.3" - resolved "https://registry.npmmirror.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" - integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== - -tmp-promise@^3.0.2: - version "3.0.3" - resolved "https://registry.npmmirror.com/tmp-promise/-/tmp-promise-3.0.3.tgz#60a1a1cc98c988674fcbfd23b6e3367bdeac4ce7" - integrity sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ== - dependencies: - tmp "^0.2.0" - -tmp@^0.2.0: - version "0.2.1" - resolved "https://registry.npmmirror.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" - integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== - dependencies: - rimraf "^3.0.0" - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -toastr@^2.1.4: - version "2.1.4" - resolved "https://registry.npmmirror.com/toastr/-/toastr-2.1.4.tgz#8b43be64fb9d0c414871446f2db8e8ca4e95f181" - integrity sha512-LIy77F5n+sz4tefMmFOntcJ6HL0Fv3k1TDnNmFZ0bU/GcvIIfy6eG2v7zQmMiYgaalAiUv75ttFrPn5s0gyqlA== - dependencies: - jquery ">=1.12.0" - -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - -trim-lines@^3.0.0: - version "3.0.1" - resolved "https://registry.npmmirror.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" - integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== - -trim-repeated@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/trim-repeated/-/trim-repeated-1.0.0.tgz#e3646a2ea4e891312bf7eace6cfb05380bc01c21" - integrity sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg== - dependencies: - escape-string-regexp "^1.0.2" - -trough@^2.0.0: - version "2.2.0" - resolved "https://registry.npmmirror.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f" - integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw== - -tsconfck@^3.0.0: - version "3.0.2" - resolved "https://registry.npmmirror.com/tsconfck/-/tsconfck-3.0.2.tgz#d8e279f7a049d55f207f528d13fa493e1d8e7ceb" - integrity sha512-6lWtFjwuhS3XI4HsX4Zg0izOI3FU/AI9EGVlPEUMDIhvLPMD4wkiof0WCoDgW7qY+Dy198g4d9miAqUHWHFH6Q== - -tslib@^2.0.3, tslib@^2.1.0: - version "2.6.2" - resolved "https://registry.npmmirror.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== - dependencies: - safe-buffer "^5.0.1" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.npmmirror.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== - dependencies: - prelude-ls "~1.1.2" - -type-fest@^0.13.1: - version "0.13.1" - resolved "https://registry.npmmirror.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" - integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type-fest@^2.13.0: - version "2.19.0" - resolved "https://registry.npmmirror.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" - integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== - -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -type@^1.0.1: - version "1.2.0" - resolved "https://registry.npmmirror.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -type@^2.7.2: - version "2.7.2" - resolved "https://registry.npmmirror.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" - integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.npmmirror.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== - -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.npmmirror.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - -unherit@^3.0.0: - version "3.0.1" - resolved "https://registry.npmmirror.com/unherit/-/unherit-3.0.1.tgz#65b98bb7cb58cee755d7ec699a49e9e8ff172e23" - integrity sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg== - -unicode-properties@^1.2.2: - version "1.4.1" - resolved "https://registry.npmmirror.com/unicode-properties/-/unicode-properties-1.4.1.tgz#96a9cffb7e619a0dc7368c28da27e05fc8f9be5f" - integrity sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg== - dependencies: - base64-js "^1.3.0" - unicode-trie "^2.0.0" - -unicode-trie@^2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/unicode-trie/-/unicode-trie-2.0.0.tgz#8fd8845696e2e14a8b67d78fa9e0dd2cad62fec8" - integrity sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ== - dependencies: - pako "^0.2.5" - tiny-inflate "^1.0.0" - -unified@^10.0.0: - version "10.1.2" - resolved "https://registry.npmmirror.com/unified/-/unified-10.1.2.tgz#b1d64e55dafe1f0b98bb6c719881103ecf6c86df" - integrity sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q== - dependencies: - "@types/unist" "^2.0.0" - bail "^2.0.0" - extend "^3.0.0" - is-buffer "^2.0.0" - is-plain-obj "^4.0.0" - trough "^2.0.0" - vfile "^5.0.0" - -unified@^11.0.0, unified@^11.0.4: - version "11.0.4" - resolved "https://registry.npmmirror.com/unified/-/unified-11.0.4.tgz#f4be0ac0fe4c88cb873687c07c64c49ed5969015" - integrity sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ== - dependencies: - "@types/unist" "^3.0.0" - bail "^2.0.0" - devlop "^1.0.0" - extend "^3.0.0" - is-plain-obj "^4.0.0" - trough "^2.0.0" - vfile "^6.0.0" - -unique-filename@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" - integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== - dependencies: - unique-slug "^3.0.0" - -unique-slug@^3.0.0: - version "3.0.0" - resolved "https://registry.npmmirror.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" - integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== - dependencies: - imurmurhash "^0.1.4" - -unist-util-is@^5.0.0: - version "5.2.1" - resolved "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-5.2.1.tgz#b74960e145c18dcb6226bc57933597f5486deae9" - integrity sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw== - dependencies: - "@types/unist" "^2.0.0" - -unist-util-is@^6.0.0: - version "6.0.0" - resolved "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-6.0.0.tgz#b775956486aff107a9ded971d996c173374be424" - integrity sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw== - dependencies: - "@types/unist" "^3.0.0" - -unist-util-modify-children@^3.0.0: - version "3.1.1" - resolved "https://registry.npmmirror.com/unist-util-modify-children/-/unist-util-modify-children-3.1.1.tgz#c4018b86441aa3b54b3edff1151d0aa062384c82" - integrity sha512-yXi4Lm+TG5VG+qvokP6tpnk+r1EPwyYL04JWDxLvgvPV40jANh7nm3udk65OOWquvbMDe+PL9+LmkxDpTv/7BA== - dependencies: - "@types/unist" "^2.0.0" - array-iterate "^2.0.0" - -unist-util-position@^5.0.0: - version "5.0.0" - resolved "https://registry.npmmirror.com/unist-util-position/-/unist-util-position-5.0.0.tgz#678f20ab5ca1207a97d7ea8a388373c9cf896be4" - integrity sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA== - dependencies: - "@types/unist" "^3.0.0" - -unist-util-stringify-position@^3.0.0: - version "3.0.3" - resolved "https://registry.npmmirror.com/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz#03ad3348210c2d930772d64b489580c13a7db39d" - integrity sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg== - dependencies: - "@types/unist" "^2.0.0" - -unist-util-stringify-position@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz#449c6e21a880e0855bf5aabadeb3a740314abac2" - integrity sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ== - dependencies: - "@types/unist" "^3.0.0" - -unist-util-visit-children@^2.0.0: - version "2.0.2" - resolved "https://registry.npmmirror.com/unist-util-visit-children/-/unist-util-visit-children-2.0.2.tgz#0f00a5caff567074568da2d89c54b5ee4a8c5440" - integrity sha512-+LWpMFqyUwLGpsQxpumsQ9o9DG2VGLFrpz+rpVXYIEdPy57GSy5HioC0g3bg/8WP9oCLlapQtklOzQ8uLS496Q== - dependencies: - "@types/unist" "^2.0.0" - -unist-util-visit-parents@^5.1.1: - version "5.1.3" - resolved "https://registry.npmmirror.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz#b4520811b0ca34285633785045df7a8d6776cfeb" - integrity sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^5.0.0" - -unist-util-visit-parents@^6.0.0: - version "6.0.1" - resolved "https://registry.npmmirror.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz#4d5f85755c3b8f0dc69e21eca5d6d82d22162815" - integrity sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-is "^6.0.0" - -unist-util-visit@^4.0.0: - version "4.1.2" - resolved "https://registry.npmmirror.com/unist-util-visit/-/unist-util-visit-4.1.2.tgz#125a42d1eb876283715a3cb5cceaa531828c72e2" - integrity sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^5.0.0" - unist-util-visit-parents "^5.1.1" - -unist-util-visit@^5.0.0: - version "5.0.0" - resolved "https://registry.npmmirror.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz#a7de1f31f72ffd3519ea71814cccf5fd6a9217d6" - integrity sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg== - dependencies: - "@types/unist" "^3.0.0" - unist-util-is "^6.0.0" - unist-util-visit-parents "^6.0.0" - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -universalify@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" - integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== - -update-browserslist-db@^1.0.13: - version "1.0.13" - resolved "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" - integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -uplot@^1.6.18: - version "1.6.30" - resolved "https://registry.npmmirror.com/uplot/-/uplot-1.6.30.tgz#1622a96b7cb2e50622c74330823c321847cbc147" - integrity sha512-48oVVRALM/128ttW19F2a2xobc2WfGdJ0VJFX00099CfqbCTuML7L2OrTKxNzeFP34eo1+yJbqFSoFAp2u28/Q== - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -username@^5.1.0: - version "5.1.0" - resolved "https://registry.npmmirror.com/username/-/username-5.1.0.tgz#a7f9325adce2d0166448cdd55d4985b1360f2508" - integrity sha512-PCKbdWw85JsYMvmCv5GH3kXmM66rCd9m1hBEDutPNv94b/pqCMT4NtcKyeWYvLFiE8b+ha1Jdl8XAaUdPn5QTg== - dependencies: - execa "^1.0.0" - mem "^4.3.0" - -util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -utila@~0.4: - version "0.4.0" - resolved "https://registry.npmmirror.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" - integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.npmmirror.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - -vfile-location@^5.0.0: - version "5.0.2" - resolved "https://registry.npmmirror.com/vfile-location/-/vfile-location-5.0.2.tgz#220d9ca1ab6f8b2504a4db398f7ebc149f9cb464" - integrity sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg== - dependencies: - "@types/unist" "^3.0.0" - vfile "^6.0.0" - -vfile-message@^3.0.0: - version "3.1.4" - resolved "https://registry.npmmirror.com/vfile-message/-/vfile-message-3.1.4.tgz#15a50816ae7d7c2d1fa87090a7f9f96612b59dea" - integrity sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw== - dependencies: - "@types/unist" "^2.0.0" - unist-util-stringify-position "^3.0.0" - -vfile-message@^4.0.0: - version "4.0.2" - resolved "https://registry.npmmirror.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181" - integrity sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-stringify-position "^4.0.0" - -vfile@^5.0.0: - version "5.3.7" - resolved "https://registry.npmmirror.com/vfile/-/vfile-5.3.7.tgz#de0677e6683e3380fafc46544cfe603118826ab7" - integrity sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g== - dependencies: - "@types/unist" "^2.0.0" - is-buffer "^2.0.0" - unist-util-stringify-position "^3.0.0" - vfile-message "^3.0.0" - -vfile@^6.0.0, vfile@^6.0.1: - version "6.0.1" - resolved "https://registry.npmmirror.com/vfile/-/vfile-6.0.1.tgz#1e8327f41eac91947d4fe9d237a2dd9209762536" - integrity sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw== - dependencies: - "@types/unist" "^3.0.0" - unist-util-stringify-position "^4.0.0" - vfile-message "^4.0.0" - -vite@^5.1.2: - version "5.1.4" - resolved "https://registry.npmmirror.com/vite/-/vite-5.1.4.tgz#14e9d3e7a6e488f36284ef13cebe149f060bcfb6" - integrity sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg== - dependencies: - esbuild "^0.19.3" - postcss "^8.4.35" - rollup "^4.2.0" - optionalDependencies: - fsevents "~2.3.3" - -vitefu@^0.2.5: - version "0.2.5" - resolved "https://registry.npmmirror.com/vitefu/-/vitefu-0.2.5.tgz#c1b93c377fbdd3e5ddd69840ea3aa70b40d90969" - integrity sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q== - -watchpack@^2.4.0: - version "2.4.0" - resolved "https://registry.npmmirror.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" - integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== - dependencies: - glob-to-regexp "^0.4.1" - graceful-fs "^4.1.2" - -wbuf@^1.1.0, wbuf@^1.7.3: - version "1.7.3" - resolved "https://registry.npmmirror.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" - integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== - dependencies: - minimalistic-assert "^1.0.0" - -wcwidth@^1.0.1: - version "1.0.1" - resolved "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== - dependencies: - defaults "^1.0.3" - -web-namespaces@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692" - integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - -webpack-dev-middleware@^5.3.1: - version "5.3.3" - resolved "https://registry.npmmirror.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" - integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== - dependencies: - colorette "^2.0.10" - memfs "^3.4.3" - mime-types "^2.1.31" - range-parser "^1.2.1" - schema-utils "^4.0.0" - -webpack-dev-server@^4.0.0: - version "4.15.1" - resolved "https://registry.npmmirror.com/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz#8944b29c12760b3a45bdaa70799b17cb91b03df7" - integrity sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA== - 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.5" - 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" - launch-editor "^2.6.0" - 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.13.0" - -webpack-merge@^5.7.3: - version "5.10.0" - resolved "https://registry.npmmirror.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" - integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== - dependencies: - clone-deep "^4.0.1" - flat "^5.0.2" - wildcard "^2.0.0" - -webpack-sources@^3.2.3: - version "3.2.3" - resolved "https://registry.npmmirror.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" - integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== - -webpack@^5.69.1: - version "5.90.3" - resolved "https://registry.npmmirror.com/webpack/-/webpack-5.90.3.tgz#37b8f74d3ded061ba789bb22b31e82eed75bd9ac" - integrity sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA== - dependencies: - "@types/eslint-scope" "^3.7.3" - "@types/estree" "^1.0.5" - "@webassemblyjs/ast" "^1.11.5" - "@webassemblyjs/wasm-edit" "^1.11.5" - "@webassemblyjs/wasm-parser" "^1.11.5" - acorn "^8.7.1" - acorn-import-assertions "^1.9.0" - browserslist "^4.21.10" - chrome-trace-event "^1.0.2" - enhanced-resolve "^5.15.0" - es-module-lexer "^1.2.1" - eslint-scope "5.1.1" - events "^3.2.0" - glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" - json-parse-even-better-errors "^2.3.1" - loader-runner "^4.2.0" - mime-types "^2.1.27" - neo-async "^2.6.2" - schema-utils "^3.2.0" - tapable "^2.1.1" - terser-webpack-plugin "^5.3.10" - watchpack "^2.4.0" - webpack-sources "^3.2.3" - -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: - version "0.7.4" - resolved "https://registry.npmmirror.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== - dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.npmmirror.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -which-pm-runs@^1.1.0: - version "1.1.0" - resolved "https://registry.npmmirror.com/which-pm-runs/-/which-pm-runs-1.1.0.tgz#35ccf7b1a0fce87bd8b92a478c9d045785d3bf35" - integrity sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA== - -which-pm@2.0.0: - version "2.0.0" - resolved "https://registry.npmmirror.com/which-pm/-/which-pm-2.0.0.tgz#8245609ecfe64bf751d0eef2f376d83bf1ddb7ae" - integrity sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w== - dependencies: - load-yaml-file "^0.2.0" - path-exists "^4.0.0" - -which-pm@^2.1.1: - version "2.1.1" - resolved "https://registry.npmmirror.com/which-pm/-/which-pm-2.1.1.tgz#0be2b70c67e94a32e87b9768a94a7f0954f2dcfa" - integrity sha512-xzzxNw2wMaoCWXiGE8IJ9wuPMU+EYhFksjHxrRT8kMT5SnocBPRg69YAMtyV4D12fP582RA+k3P8H9J5EMdIxQ== - dependencies: - load-yaml-file "^0.2.0" - path-exists "^4.0.0" - -which@^1.2.14, which@^1.2.9: - version "1.3.1" - resolved "https://registry.npmmirror.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -which@^2.0.1, which@^2.0.2: - version "2.0.2" - resolved "https://registry.npmmirror.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -wide-align@^1.1.5: - version "1.1.5" - resolved "https://registry.npmmirror.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== - dependencies: - string-width "^1.0.2 || 2 || 3 || 4" - -widest-line@^4.0.1: - version "4.0.1" - resolved "https://registry.npmmirror.com/widest-line/-/widest-line-4.0.1.tgz#a0fc673aaba1ea6f0a0d35b3c2795c9a9cc2ebf2" - integrity sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig== - dependencies: - string-width "^5.0.1" - -wildcard@^2.0.0: - version "2.0.1" - resolved "https://registry.npmmirror.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" - integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== - -word-wrap@^1.2.3, word-wrap@~1.2.3: - version "1.2.5" - resolved "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" - integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -ws@^7.4.6: - version "7.5.9" - resolved "https://registry.npmmirror.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== - -ws@^8.13.0: - version "8.16.0" - resolved "https://registry.npmmirror.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" - integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== - -xmlbuilder@^15.1.1: - version "15.1.1" - resolved "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" - integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== - -xmldoc@^1.1.2: - version "1.3.0" - resolved "https://registry.npmmirror.com/xmldoc/-/xmldoc-1.3.0.tgz#7823225b096c74036347c9ec5924d06b6a3cebab" - integrity sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng== - dependencies: - sax "^1.2.4" - -xtend@^4.0.2, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -xterm-addon-fit@^0.5.0: - version "0.5.0" - resolved "https://registry.npmmirror.com/xterm-addon-fit/-/xterm-addon-fit-0.5.0.tgz#2d51b983b786a97dcd6cde805e700c7f913bc596" - integrity sha512-DsS9fqhXHacEmsPxBJZvfj2la30Iz9xk+UKjhQgnYNkrUIN5CYLbw7WEfz117c7+S86S/tpHPfvNxJsF5/G8wQ== - -xterm-addon-search@^0.8.0: - version "0.8.2" - resolved "https://registry.npmmirror.com/xterm-addon-search/-/xterm-addon-search-0.8.2.tgz#be7aa74d5ff12c901707c6ff674229f214318032" - integrity sha512-I1863mjn8P6uVrqm/X+btalVsqjAKLhnhpbP7SavAOpEkI1jJhbHU2UTp7NjeRtcKTks6UWk/ycgds5snDSejg== - -xterm@^4.9.0: - version "4.19.0" - resolved "https://registry.npmmirror.com/xterm/-/xterm-4.19.0.tgz#c0f9d09cd61de1d658f43ca75f992197add9ef6d" - integrity sha512-c3Cp4eOVsYY5Q839dR5IejghRPpxciGmLWWaP9g+ppfMeBChMeLa1DCA+pmX/jyDZ+zxFOmlJL/82qVdayVoGQ== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-parser@^21.1.1: - version "21.1.1" - resolved "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -yargs@^16.0.2: - version "16.2.0" - resolved "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - 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" - -yargs@^17.0.1: - version "17.7.2" - resolved "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - -yarn-or-npm@^3.0.1: - version "3.0.1" - resolved "https://registry.npmmirror.com/yarn-or-npm/-/yarn-or-npm-3.0.1.tgz#6336eea4dff7e23e226acc98c1a8ada17a1b8666" - integrity sha512-fTiQP6WbDAh5QZAVdbMQkecZoahnbOjClTQhzv74WX5h2Uaidj1isf9FDes11TKtsZ0/ZVfZsqZ+O3x6aLERHQ== - dependencies: - cross-spawn "^6.0.5" - pkg-dir "^4.2.0" - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -yocto-queue@^1.0.0: - version "1.0.0" - resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" - integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== - -zod@^3.22.4: - version "3.22.4" - resolved "https://registry.npmmirror.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff" - integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg== - -zwitch@^2.0.0, zwitch@^2.0.4: - version "2.0.4" - resolved "https://registry.npmmirror.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7" - integrity sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A== diff --git a/websrc/lithium-web/package-lock.json b/websrc/lithium-web/package-lock.json index 71641a5e..c87871b6 100644 --- a/websrc/lithium-web/package-lock.json +++ b/websrc/lithium-web/package-lock.json @@ -29,9 +29,9 @@ } }, "node_modules/@astrojs/compiler": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.7.0.tgz", - "integrity": "sha512-XpC8MAaWjD1ff6/IfkRq/5k1EFj6zhCNqXRd5J43SVJEBj/Bsmizkm8N0xOYscGcDFQkRgEw6/eKnI5x/1l6aA==" + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.7.1.tgz", + "integrity": "sha512-/POejAYuj8WEw7ZI0J8JBvevjfp9jQ9Wmu/Bg52RiNwGXkMV7JnYpsenVfHvvf1G7R5sXHGKlTcxlQWhoUTiGQ==" }, "node_modules/@astrojs/internal-helpers": { "version": "0.2.1", @@ -61,11 +61,11 @@ } }, "node_modules/@astrojs/markdown-remark": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-4.3.2.tgz", - "integrity": "sha512-4Oa4VaYiBd0MatB+rWIU/0A8pZH/sK3c2QkRYb+OO2lPl+qzevJtWaZY8hAQc4qurIOlRdn6B6ofDAGhWw+DSg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-5.1.0.tgz", + "integrity": "sha512-S6Z3K2hOB7MfjeDoHsotnP/q2UsnEDB8NlNAaCjMDsGBZfTUbWxyLW3CaphEWw08f6KLZi2ibK9yC3BaMhh2NQ==", "dependencies": { - "@astrojs/prism": "^3.0.0", + "@astrojs/prism": "^3.1.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.0", "hast-util-to-text": "^4.0.0", @@ -85,6 +85,17 @@ "vfile": "^6.0.1" } }, + "node_modules/@astrojs/markdown-remark/node_modules/@astrojs/prism": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.1.0.tgz", + "integrity": "sha512-Z9IYjuXSArkAUx3N6xj6+Bnvx8OdUSHA8YoOgyepp3+zJmtVYJIl/I18GozdJVW1p5u/CNpl3Km7/gwTJK85cw==", + "dependencies": { + "prismjs": "^1.29.0" + }, + "engines": { + "node": "^18.17.1 || ^20.3.0 || >=21.0.0" + } + }, "node_modules/@astrojs/node": { "version": "8.2.5", "resolved": "https://registry.npmjs.org/@astrojs/node/-/node-8.2.5.tgz", @@ -109,43 +120,29 @@ } }, "node_modules/@astrojs/sitemap": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.1.2.tgz", - "integrity": "sha512-FxOJldIl5ltZ5CNjocQxHkAO9orwHBjqtaU28o4smobp9vowS0nbGp+I9CrPxkzWdl1crSDm9vjL9tnvG1DSug==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.1.3.tgz", + "integrity": "sha512-9WB5c071L+FNbs/0727lcqbHvS17TxnDRdfXXiAMustBRFlqkCrziHy+MsfeghxtyXp0pFTFO3NjGRfOppbFzQ==", "dependencies": { "sitemap": "^7.1.1", "zod": "^3.22.4" } }, "node_modules/@astrojs/telemetry": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.0.4.tgz", - "integrity": "sha512-A+0c7k/Xy293xx6odsYZuXiaHO0PL+bnDoXOc47sGDF5ffIKdKQGRPFl2NMlCF4L0NqN4Ynbgnaip+pPF0s7pQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.1.0.tgz", + "integrity": "sha512-/ca/+D8MIKEC8/A9cSaPUqQNZm+Es/ZinRv0ZAzvu2ios7POQSsVD+VOj7/hypWNsNM3T7RpfgNq7H2TU1KEHA==", "dependencies": { - "ci-info": "^3.8.0", + "ci-info": "^4.0.0", "debug": "^4.3.4", "dlv": "^1.1.3", - "dset": "^3.1.2", + "dset": "^3.1.3", "is-docker": "^3.0.0", "is-wsl": "^3.0.0", "which-pm-runs": "^1.1.0" }, "engines": { - "node": ">=18.14.1" - } - }, - "node_modules/@astrojs/telemetry/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" + "node": "^18.17.1 || ^20.3.0 || >=21.0.0" } }, "node_modules/@babel/code-frame": { @@ -161,25 +158,25 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz", - "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.3.tgz", - "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", + "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.1", + "@babel/generator": "^7.24.4", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.1", - "@babel/parser": "^7.24.1", + "@babel/helpers": "^7.24.4", + "@babel/parser": "^7.24.4", "@babel/template": "^7.24.0", "@babel/traverse": "^7.24.1", "@babel/types": "^7.24.0", @@ -206,9 +203,9 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.1.tgz", - "integrity": "sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", + "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", "dependencies": { "@babel/types": "^7.24.0", "@jridgewell/gen-mapping": "^0.3.5", @@ -368,9 +365,9 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.1.tgz", - "integrity": "sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", + "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", "dependencies": { "@babel/template": "^7.24.0", "@babel/traverse": "^7.24.1", @@ -395,9 +392,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", - "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", + "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -936,9 +933,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.2.tgz", - "integrity": "sha512-3XFIDKWMFZrMnao1mJhnOT1h2g0169Os848NhhmGweEcfJ4rCi+3yMCOLG4zA61rbJdkcrM/DjVZm9Hg5p5w7g==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.3.tgz", + "integrity": "sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==", "cpu": [ "arm" ], @@ -948,9 +945,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.2.tgz", - "integrity": "sha512-GdxxXbAuM7Y/YQM9/TwwP+L0omeE/lJAR1J+olu36c3LqqZEBdsIWeQ91KBe6nxwOnb06Xh7JS2U5ooWU5/LgQ==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.3.tgz", + "integrity": "sha512-eQK5JIi+POhFpzk+LnjKIy4Ks+pwJ+NXmPxOCSvOKSNRPONzKuUvWE+P9JxGZVxrtzm6BAYMaL50FFuPe0oWMQ==", "cpu": [ "arm64" ], @@ -960,9 +957,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.2.tgz", - "integrity": "sha512-mCMlpzlBgOTdaFs83I4XRr8wNPveJiJX1RLfv4hggyIVhfB5mJfN4P8Z6yKh+oE4Luz+qq1P3kVdWrCKcMYrrA==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.3.tgz", + "integrity": "sha512-Od4vE6f6CTT53yM1jgcLqNfItTsLt5zE46fdPaEmeFHvPs5SjZYlLpHrSiHEKR1+HdRfxuzXHjDOIxQyC3ptBA==", "cpu": [ "arm64" ], @@ -972,9 +969,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.2.tgz", - "integrity": "sha512-yUoEvnH0FBef/NbB1u6d3HNGyruAKnN74LrPAfDQL3O32e3k3OSfLrPgSJmgb3PJrBZWfPyt6m4ZhAFa2nZp2A==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.3.tgz", + "integrity": "sha512-0IMAO21axJeNIrvS9lSe/PGthc8ZUS+zC53O0VhF5gMxfmcKAP4ESkKOCwEi6u2asUrt4mQv2rjY8QseIEb1aw==", "cpu": [ "x64" ], @@ -984,9 +981,21 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.2.tgz", - "integrity": "sha512-GYbLs5ErswU/Xs7aGXqzc3RrdEjKdmoCrgzhJWyFL0r5fL3qd1NPcDKDowDnmcoSiGJeU68/Vy+OMUluRxPiLQ==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.3.tgz", + "integrity": "sha512-ge2DC7tHRHa3caVEoSbPRJpq7azhG+xYsd6u2MEnJ6XzPSzQsTKyXvh6iWjXRf7Rt9ykIUWHtl0Uz3T6yXPpKw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.14.3.tgz", + "integrity": "sha512-ljcuiDI4V3ySuc7eSk4lQ9wU8J8r8KrOUvB2U+TtK0TiW6OFDmJ+DdIjjwZHIw9CNxzbmXY39wwpzYuFDwNXuw==", "cpu": [ "arm" ], @@ -996,9 +1005,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.2.tgz", - "integrity": "sha512-L1+D8/wqGnKQIlh4Zre9i4R4b4noxzH5DDciyahX4oOz62CphY7WDWqJoQ66zNR4oScLNOqQJfNSIAe/6TPUmQ==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.3.tgz", + "integrity": "sha512-Eci2us9VTHm1eSyn5/eEpaC7eP/mp5n46gTRB3Aar3BgSvDQGJZuicyq6TsH4HngNBgVqC5sDYxOzTExSU+NjA==", "cpu": [ "arm64" ], @@ -1008,9 +1017,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.2.tgz", - "integrity": "sha512-tK5eoKFkXdz6vjfkSTCupUzCo40xueTOiOO6PeEIadlNBkadH1wNOH8ILCPIl8by/Gmb5AGAeQOFeLev7iZDOA==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.3.tgz", + "integrity": "sha512-UrBoMLCq4E92/LCqlh+blpqMz5h1tJttPIniwUgOFJyjWI1qrtrDhhpHPuFxULlUmjFHfloWdixtDhSxJt5iKw==", "cpu": [ "arm64" ], @@ -1020,11 +1029,11 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.13.2.tgz", - "integrity": "sha512-zvXvAUGGEYi6tYhcDmb9wlOckVbuD+7z3mzInCSTACJ4DQrdSLPNUeDIcAQW39M3q6PDquqLWu7pnO39uSMRzQ==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.3.tgz", + "integrity": "sha512-5aRjvsS8q1nWN8AoRfrq5+9IflC3P1leMoy4r2WjXyFqf3qcqsxRCfxtZIV58tCxd+Yv7WELPcO9mY9aeQyAmw==", "cpu": [ - "ppc64le" + "ppc64" ], "optional": true, "os": [ @@ -1032,9 +1041,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.2.tgz", - "integrity": "sha512-C3GSKvMtdudHCN5HdmAMSRYR2kkhgdOfye4w0xzyii7lebVr4riCgmM6lRiSCnJn2w1Xz7ZZzHKuLrjx5620kw==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.3.tgz", + "integrity": "sha512-sk/Qh1j2/RJSX7FhEpJn8n0ndxy/uf0kI/9Zc4b1ELhqULVdTfN6HL31CDaTChiBAOgLcsJ1sgVZjWv8XNEsAQ==", "cpu": [ "riscv64" ], @@ -1044,9 +1053,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.13.2.tgz", - "integrity": "sha512-l4U0KDFwzD36j7HdfJ5/TveEQ1fUTjFFQP5qIt9gBqBgu1G8/kCaq5Ok05kd5TG9F8Lltf3MoYsUMw3rNlJ0Yg==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.3.tgz", + "integrity": "sha512-jOO/PEaDitOmY9TgkxF/TQIjXySQe5KVYB57H/8LRP/ux0ZoO8cSHCX17asMSv3ruwslXW/TLBcxyaUzGRHcqg==", "cpu": [ "s390x" ], @@ -1056,9 +1065,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.2.tgz", - "integrity": "sha512-xXMLUAMzrtsvh3cZ448vbXqlUa7ZL8z0MwHp63K2IIID2+DeP5iWIT6g1SN7hg1VxPzqx0xZdiDM9l4n9LRU1A==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.3.tgz", + "integrity": "sha512-8ybV4Xjy59xLMyWo3GCfEGqtKV5M5gCSrZlxkPGvEPCGDLNla7v48S662HSGwRd6/2cSneMQWiv+QzcttLrrOA==", "cpu": [ "x64" ], @@ -1068,9 +1077,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.2.tgz", - "integrity": "sha512-M/JYAWickafUijWPai4ehrjzVPKRCyDb1SLuO+ZyPfoXgeCEAlgPkNXewFZx0zcnoIe3ay4UjXIMdXQXOZXWqA==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.3.tgz", + "integrity": "sha512-s+xf1I46trOY10OqAtZ5Rm6lzHre/UiLA1J2uOhCFXWkbZrJRkYBPO6FhvGfHmdtQ3Bx793MNa7LvoWFAm93bg==", "cpu": [ "x64" ], @@ -1080,9 +1089,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.2.tgz", - "integrity": "sha512-2YWwoVg9KRkIKaXSh0mz3NmfurpmYoBBTAXA9qt7VXk0Xy12PoOP40EFuau+ajgALbbhi4uTj3tSG3tVseCjuA==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.3.tgz", + "integrity": "sha512-+4h2WrGOYsOumDQ5S2sYNyhVfrue+9tc9XcLWLh+Kw3UOxAvrfOrSMFon60KspcDdytkNDh7K2Vs6eMaYImAZg==", "cpu": [ "arm64" ], @@ -1092,9 +1101,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.2.tgz", - "integrity": "sha512-2FSsE9aQ6OWD20E498NYKEQLneShWes0NGMPQwxWOdws35qQXH+FplabOSP5zEe1pVjurSDOGEVCE2agFwSEsw==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.3.tgz", + "integrity": "sha512-T1l7y/bCeL/kUwh9OD4PQT4aM7Bq43vX05htPJJ46RTI4r5KNt6qJRzAfNfM+OYMNEVBWQzR2Gyk+FXLZfogGw==", "cpu": [ "ia32" ], @@ -1104,9 +1113,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.2.tgz", - "integrity": "sha512-7h7J2nokcdPePdKykd8wtc8QqqkqxIrUz7MHj6aNr8waBRU//NLDVnNjQnqQO6fqtjrtCdftpbTuOKAyrAQETQ==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz", + "integrity": "sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==", "cpu": [ "x64" ], @@ -1116,9 +1125,9 @@ ] }, "node_modules/@shikijs/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.2.1.tgz", - "integrity": "sha512-KaIS0H4EQ3KI2d++TjYqRNgwp8E3M/68e9veR4QtInzA7kKFgcjeiJqb80fuXW+blDy5fmd11PN9g9soz/3ANQ==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-1.3.0.tgz", + "integrity": "sha512-7fedsBfuILDTBmrYZNFI8B6ATTxhQAasUHllHmjvSZPnoq4bULWoTpHwmuQvZ8Aq03/tAa2IGo6RXqWtHdWaCA==" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -1157,6 +1166,11 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/cookie": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.4.tgz", + "integrity": "sha512-7z/eR6O859gyWIAjuvBWFzNURmf2oPBmJlfVWkwehU5nzIyjwBsTh7WMmEEV4JFnHuQ3ex4oyTvfKzcyJVDBNA==" + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -1227,9 +1241,9 @@ "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" }, "node_modules/@types/node": { - "version": "20.11.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", - "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "dependencies": { "undici-types": "~5.26.4" } @@ -1370,14 +1384,14 @@ } }, "node_modules/astro": { - "version": "4.5.12", - "resolved": "https://registry.npmjs.org/astro/-/astro-4.5.12.tgz", - "integrity": "sha512-xIJcFI2hbyV8+h5pWjL7SKD1jIP0K01fYVAH+gdAt0mJaXy+u8Mj+goD0cPlK6sEaykR+7zxQLYGKJ44U4qarg==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/astro/-/astro-4.6.2.tgz", + "integrity": "sha512-Kl+Wd7MJMQFnI3+V0JxF4HPbs8M67eltqQgtztReOwDLSl0VnOd39rM61W/3LEh10FZ0F13xrDgtdgfXzuLVbg==", "dependencies": { - "@astrojs/compiler": "^2.7.0", + "@astrojs/compiler": "^2.7.1", "@astrojs/internal-helpers": "0.4.0", - "@astrojs/markdown-remark": "4.3.2", - "@astrojs/telemetry": "3.0.4", + "@astrojs/markdown-remark": "5.1.0", + "@astrojs/telemetry": "3.1.0", "@babel/core": "^7.24.3", "@babel/generator": "^7.23.3", "@babel/parser": "^7.23.3", @@ -1385,6 +1399,7 @@ "@babel/traverse": "^7.23.3", "@babel/types": "^7.23.3", "@types/babel__core": "^7.20.4", + "@types/cookie": "^0.5.4", "acorn": "^8.11.2", "aria-query": "^5.3.0", "axobject-query": "^4.0.0", @@ -1441,8 +1456,9 @@ "astro": "astro.js" }, "engines": { - "node": ">=18.14.1", - "npm": ">=6.14.0" + "node": "^18.17.1 || ^20.3.0 || >=21.0.0", + "npm": ">=9.6.5", + "pnpm": ">=7.1.0" }, "optionalDependencies": { "sharp": "^0.32.6" @@ -1488,13 +1504,12 @@ "optional": true }, "node_modules/bare-fs": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.2.2.tgz", - "integrity": "sha512-X9IqgvyB0/VA5OZJyb5ZstoN62AzD7YxVGog13kkfYWYqJYcK0kcqLZ6TrmH5qr4/8//ejVcX4x/a0UvaogXmA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.2.3.tgz", + "integrity": "sha512-amG72llr9pstfXOBOHve1WjiuKKAMnebcmMbPWDZ7BCevAoJLpugjuAPRsDINEyjT0a6tbaVx3DctkXIRbLuJw==", "optional": true, "dependencies": { "bare-events": "^2.0.0", - "bare-os": "^2.0.0", "bare-path": "^2.0.0", "streamx": "^2.13.0" } @@ -1506,9 +1521,9 @@ "optional": true }, "node_modules/bare-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.0.tgz", - "integrity": "sha512-DIIg7ts8bdRKwJRJrUMy/PICEaQZaPGZ26lsSx9MJSwIhSrcdHn7/C8W+XmnG/rKi6BaRcz+JO00CjZteybDtw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.1.tgz", + "integrity": "sha512-OHM+iwRDRMDBsSW7kl3dO62JyHdBKO3B25FB9vNQBPcGHMo4+eA8Yj41Lfbk3pS/seDY+siNge0LdRTulAau/A==", "optional": true, "dependencies": { "bare-os": "^2.1.0" @@ -1689,9 +1704,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001600", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz", - "integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==", + "version": "1.0.30001611", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001611.tgz", + "integrity": "sha512-19NuN1/3PjA3QI8Eki55N8my4LzfkMCRLgCVfrl/slbSAchQfV0+GwjPrK3rq37As4UCLlM/DHajbKkAqbv92Q==", "funding": [ { "type": "opencollective", @@ -2145,9 +2160,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.721", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.721.tgz", - "integrity": "sha512-k1x2r6foI8iJOp+1qTxbbrrWMsOiHkzGBYwYigaq+apO1FSqtn44KTo3Sy69qt7CRr7149zTcsDvH7MUKsOuIQ==" + "version": "1.4.740", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.740.tgz", + "integrity": "sha512-Yvg5i+iyv7Xm18BRdVPVm8lc7kgxM3r6iwqCH2zB7QZy1kZRNmd0Zqm0zcD9XoFREE5/5rwIuIAOT+/mzGcnZg==" }, "node_modules/emoji-regex": { "version": "10.3.0", @@ -2635,9 +2650,9 @@ } }, "node_modules/hast-util-to-html": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.0.tgz", - "integrity": "sha512-IVGhNgg7vANuUA2XKrT6sOIIPgaYZnmLx3l/CCOAK0PtgfoHrZwX7jCSYyFxHTrGmC6S9q8aQQekjp4JPZF+cw==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.1.tgz", + "integrity": "sha512-hZOofyZANbyWo+9RP75xIDV/gq+OUKx+T46IlwERnKmfpwp81XBFbT9mi26ws+SJchA4RVUQwIBJpqEOBhMzEQ==", "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", @@ -2676,9 +2691,9 @@ } }, "node_modules/hast-util-to-text": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.0.tgz", - "integrity": "sha512-EWiE1FSArNBPUo1cKWtzqgnuRQwEeQbQtnFJRYV1hb1BWDgrAlBU0ExptvZMM/KSA82cDpm2sFGf3Dmc5Mza3w==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", @@ -3150,14 +3165,11 @@ } }, "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" } }, "node_modules/markdown-table": { @@ -3889,9 +3901,9 @@ } }, "node_modules/micromark-util-subtokenize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", - "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz", + "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==", "funding": [ { "type": "GitHub Sponsors", @@ -4041,9 +4053,9 @@ } }, "node_modules/node-abi": { - "version": "3.56.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.56.0.tgz", - "integrity": "sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q==", + "version": "3.59.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.59.0.tgz", + "integrity": "sha512-HyyfzvTLCE8b1SX2nWimlra8cibEsypcSu/Az4SXMhWhtuctkwAX7qsEYNjUOIoYtPV884oN3wtYTN+iZKBtvw==", "optional": true, "dependencies": { "semver": "^7.3.5" @@ -4313,9 +4325,9 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-to-regexp": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.1.tgz", - "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==" + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", + "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==" }, "node_modules/picocolors": { "version": "1.0.0", @@ -4571,9 +4583,9 @@ } }, "node_modules/property-information": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz", - "integrity": "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -5211,9 +5223,9 @@ } }, "node_modules/rollup": { - "version": "4.13.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz", - "integrity": "sha512-MIlLgsdMprDBXC+4hsPgzWUasLO9CE4zOkj/u6j+Z6j5A4zRY+CtiXAdJyPtgCsc42g658Aeh1DlrdVEJhsL2g==", + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.3.tgz", + "integrity": "sha512-ag5tTQKYsj1bhrFC9+OEWqb5O6VYgtQDO9hPDBMmIbePwhfSr+ExlcU741t8Dhw5DkPCQf6noz0jb36D6W9/hw==", "dependencies": { "@types/estree": "1.0.5" }, @@ -5225,21 +5237,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.2", - "@rollup/rollup-android-arm64": "4.13.2", - "@rollup/rollup-darwin-arm64": "4.13.2", - "@rollup/rollup-darwin-x64": "4.13.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.2", - "@rollup/rollup-linux-arm64-gnu": "4.13.2", - "@rollup/rollup-linux-arm64-musl": "4.13.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.13.2", - "@rollup/rollup-linux-riscv64-gnu": "4.13.2", - "@rollup/rollup-linux-s390x-gnu": "4.13.2", - "@rollup/rollup-linux-x64-gnu": "4.13.2", - "@rollup/rollup-linux-x64-musl": "4.13.2", - "@rollup/rollup-win32-arm64-msvc": "4.13.2", - "@rollup/rollup-win32-ia32-msvc": "4.13.2", - "@rollup/rollup-win32-x64-msvc": "4.13.2", + "@rollup/rollup-android-arm-eabi": "4.14.3", + "@rollup/rollup-android-arm64": "4.14.3", + "@rollup/rollup-darwin-arm64": "4.14.3", + "@rollup/rollup-darwin-x64": "4.14.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.3", + "@rollup/rollup-linux-arm-musleabihf": "4.14.3", + "@rollup/rollup-linux-arm64-gnu": "4.14.3", + "@rollup/rollup-linux-arm64-musl": "4.14.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.3", + "@rollup/rollup-linux-riscv64-gnu": "4.14.3", + "@rollup/rollup-linux-s390x-gnu": "4.14.3", + "@rollup/rollup-linux-x64-gnu": "4.14.3", + "@rollup/rollup-linux-x64-musl": "4.14.3", + "@rollup/rollup-win32-arm64-msvc": "4.14.3", + "@rollup/rollup-win32-ia32-msvc": "4.14.3", + "@rollup/rollup-win32-x64-msvc": "4.14.3", "fsevents": "~2.3.2" } }, @@ -5436,11 +5449,11 @@ } }, "node_modules/shiki": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.2.1.tgz", - "integrity": "sha512-u+XW6o0vCkUNlneZb914dLO+AayEIwK5tI62WeS//R5HIXBFiYaj/Hc5xcq27Yh83Grr4JbNtUBV8W6zyK4hWg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-1.3.0.tgz", + "integrity": "sha512-9aNdQy/etMXctnPzsje1h1XIGm9YfRcSksKOGqZWXA/qP9G18/8fpz5Bjpma8bOgz3tqIpjERAd6/lLjFyzoww==", "dependencies": { - "@shikijs/core": "1.2.1" + "@shikijs/core": "1.3.0" } }, "node_modules/signal-exit": { @@ -5618,9 +5631,9 @@ } }, "node_modules/stringify-entities": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", - "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" @@ -6045,9 +6058,9 @@ } }, "node_modules/vite": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.7.tgz", - "integrity": "sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==", + "version": "5.2.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.9.tgz", + "integrity": "sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==", "dependencies": { "esbuild": "^0.20.1", "postcss": "^8.4.38", diff --git a/xmake.lua b/xmake.lua index a8b3d4ed..ed33fda3 100644 --- a/xmake.lua +++ b/xmake.lua @@ -2,164 +2,50 @@ -- This project is licensed under the terms of the GPL3 license. -- -- Project Name: Lithium --- Description: Open Astrophotography Terminal +-- Description: Lithium - Open Astrophotography Terminal -- Author: Max Qian -- License: GPL3 -add_rules("mode.release", "mode.debug") +set_project("Lithium") +set_languages("c++20") --- root directory of the project -set(Lithium_PROJECT_ROOT_DIR, "$(projectdir)") -set(lithium_src_dir, "$(projectdir)/src/") - --- compiler options -if is_plat("windows") then - add_defines("WIN32") - add_cxxflags("/EHsc") -elseif is_plat("macosx") or is_plat("linux") then - add_defines("UNIX") +if is_plat("macosx") then + add_shflags("-undefined dynamic_lookup") end -add_defines("USE_FOLDERS") --- main project --- this should appear after setting the architecture -target("Lithium") +set_version("1.0.0") - set_targetdir("$(projectdir)/build") - set_objectdir("$(projectdir)/build/$(plat)") +add_includedirs("libs/", "driverlibs/", "src/", "src/atom", "libs/oatpp", "libs/oatpp-swagger", "libs/oatpp-websocket", {public = true}) - -- include directories - add_includedirs( - "$(projectdir)/libs/", - "$(lithium_src_dir)", - "$(lithium_src_dir)/modules", - "$(projectdir)/src/", - "$(projectdir)/libs/oatpp", - "$(projectdir)/libs/oatpp-swagger", - "$(projectdir)/libs/oatpp-websocket" - ) +add_defines("ENABLE_ASYNC_FLAG=1", "ENABLE_DEBUG_FLAG=1", "ENABLE_NATIVE_SERVER_FLAG=1", "ENABLE_FASHHASH_FLAG=1", "ENABLE_WEB_SERVER_FLAG=1", "ENABLE_WEB_CLIENT_FLAG=1") - -- source files - add_files( - "$(lithium_src_dir)/api/astap.cpp", - "$(lithium_src_dir)/api/astap.hpp", - "$(lithium_src_dir)/api/astrometry.cpp", - "$(lithium_src_dir)/api/astrometry.hpp", - "$(lithium_src_dir)/api/phd2client.cpp", - "$(lithium_src_dir)/api/phd2client.hpp", - "$(lithium_src_dir)/modules/config/configor.cpp", - "$(lithium_src_dir)/modules/config/configor.hpp", - "$(lithium_src_dir)/database/database.cpp", - "$(lithium_src_dir)/database/database.hpp", - "$(lithium_src_dir)/debug/terminal.cpp", - "$(lithium_src_dir)/debug/terminal.hpp", - "$(lithium_src_dir)/modules/device/device.cpp", - "$(lithium_src_dir)/modules/device/device.hpp", - "$(lithium_src_dir)/modules/device/device_manager.cpp", - "$(lithium_src_dir)/modules/device/device_manager.hpp", - "$(lithium_src_dir)/api/hydrogenclient.cpp", - "$(lithium_src_dir)/api/hydrogenclient.hpp", - "$(lithium_src_dir)/driver/indi/indi_exception.hpp", - "$(lithium_src_dir)/driver/indi/indicamera.cpp", - "$(lithium_src_dir)/driver/indi/indicamera.hpp", - "$(lithium_src_dir)/driver/indi/indifocuser.cpp", - "$(lithium_src_dir)/driver/indi/indifocuser.hpp", - "$(lithium_src_dir)/driver/indi/inditelescope.cpp", - "$(lithium_src_dir)/driver/indi/inditelescope.hpp", - "$(lithium_src_dir)/driver/indi/indifilterwheel.cpp", - "$(lithium_src_dir)/driver/indi/indifilterwheel.hpp", - "$(lithium_src_dir)/image/image.cpp", - "$(lithium_src_dir)/image/image.hpp", - "$(lithium_src_dir)/image/draw.cpp", - "$(lithium_src_dir)/modules/io/compress.cpp", - "$(lithium_src_dir)/modules/io/compress.hpp", - "$(lithium_src_dir)/modules/io/file.cpp", - "$(lithium_src_dir)/modules/io/file.hpp", - "$(lithium_src_dir)/modules/io/glob.hpp", - "$(lithium_src_dir)/modules/io/io.cpp", - "$(lithium_src_dir)/modules/io/io.hpp", - "$(lithium_src_dir)/launcher/crash.cpp", - "$(lithium_src_dir)/launcher/crash.hpp", - "$(lithium_src_dir)/logger/aptlogger.cpp", - "$(lithium_src_dir)/logger/aptlogger.hpp", - "$(lithium_src_dir)/logger/log_manager.cpp", - "$(lithium_src_dir)/logger/log_manager.hpp", - "$(lithium_src_dir)/modules/module/module_loader.cpp", - "$(lithium_src_dir)/modules/module/module_loader.hpp", - "$(lithium_src_dir)/network/downloader.cpp", - "$(lithium_src_dir)/network/downloader.hpp", - "$(lithium_src_dir)/network/httpclient.cpp", - "$(lithium_src_dir)/network/httpclient.hpp", - "$(lithium_src_dir)/network/socketclient.cpp", - "$(lithium_src_dir)/network/socketclient.hpp", - "$(lithium_src_dir)/network/time.cpp", - "$(lithium_src_dir)/network/time.hpp", - "$(lithium_src_dir)/liproperty/base64.cpp", - "$(lithium_src_dir)/liproperty/base64.hpp", - "$(lithium_src_dir)/liproperty/imessage.cpp", - "$(lithium_src_dir)/liproperty/iproperty.hpp", - "$(lithium_src_dir)/liproperty/sha256.hpp", - "$(lithium_src_dir)/liproperty/uuid.cpp", - "$(lithium_src_dir)/liproperty/uuid.hpp", - "$(lithium_src_dir)/modules/server/commander.cpp", - "$(lithium_src_dir)/modules/server/commander.hpp", - "$(lithium_src_dir)/websocket/WebSocketServer.cpp", - "$(lithium_src_dir)/modules/thread/thread.cpp", - "$(lithium_src_dir)/modules/thread/thread.hpp", - "$(lithium_src_dir)/modules/thread/threadpool.cpp", - "$(lithium_src_dir)/modules/thread/threadpool.hpp", - "$(lithium_src_dir)/modules/system/system.cpp", - "$(lithium_src_dir)/modules/system/system.hpp", - "$(lithium_src_dir)/App.cpp", - "$(lithium_src_dir)/AppComponent.hpp", - "$(lithium_src_dir)/LithiumApp.cpp", - "$(lithium_src_dir)/LithiumApp.hpp" - ) - - -- link libraries - if is_plat("windows") then - add_links("pdh", "iphlpapi") - add_cxxflags("/MD") - add_defines("_CRT_SECURE_NO_WARNINGS") - add_packages("openssl") - add_packages("cfitsio") - add_packages("zlib") - add_packages("sqlite3") - add_packages("pugixml") - add_packages("loguru") - add_packages("dlfcn-win32") - add_packages("oatpp-websocket", "oatpp-swagger", "oatpp-openssl", "oatpp") - elseif is_plat("macosx") then - add_frameworks( - "Cocoa", - "IOKit", - "Carbon", - "Security", - "Foundation" - ) - add_packages("openssl") - add_packages("cfitsio") - add_packages("zlib") - add_packages("sqlite3") - add_packages("pugixml") - add_packages("oatpp-websocket", "oatpp-swagger", "oatpp-openssl", "oatpp") - add_packages("loguru") - elseif is_plat("linux") then - add_packages("openssl") - add_packages("cfitsio") - add_packages("zlib") - add_packages("sqlite3") - add_packages("pugixml") - add_packages("oatpp-websocket", "oatpp-swagger", "oatpp-openssl", "oatpp") - add_packages("loguru") - add_packages("dl") - end +if is_plat("windows") then + set_installdir("C:/Program Files/LithiumServer") +elseif is_plat("linux") then + set_installdir("/usr/lithium") +end - -- set output directory and name for Lithium executable +target("lithium_server-library") + set_kind("static") + add_files("src/device/server/*.cpp", "src/device/manager.cpp", "src/device/utils/utils.cpp", "src/addon/*.cpp", "src/config/configor.cpp", + "src/debug/terminal.cpp", "src/script/*.cpp", "src/script/custom/*.cpp", "src/task/*.cpp", "src/LithiumApp.cpp") + add_packages("openssl", "cfitsio", "zlib", "sqlite3", "fmt") + add_deps("atomstatic", "loguru", "libzippp", "oatpp", "oatpp-websocket", "oatpp-swagger", "oatpp-openssl", "oatpp-zlib", "cpp_httplib", "backward", "tinyxml2", "pocketpy") + +target("lithium_server") + set_kind("binary") + add_files("src/app.cpp") + add_deps("lithium_server-library") + add_defines("LOGURU_DEBUG_LOGGING") + add_packages("openssl", "cfitsio", "zlib", "sqlite3", "fmt") + add_deps("atomstatic", "loguru", "libzippp", "oatpp", "oatpp-websocket", "oatpp-swagger", "oatpp-openssl", "oatpp-zlib", "cpp_httplib", "backward", "tinyxml2", "pocketpy") if is_plat("windows") then - set_targetdir("$(projectdir)/build/$(plat)/$(arch)") - set_targetname("lithium_server") + add_syslinks("pdh", "iphlpapi", "winmm", "crypt32", "wsock32", "ws2_32") + add_requires("dlfcn-win32") else - set_targetdir("$(projectdir)/build") - set_targetname("lithium_server") + add_syslinks("dl") end + set_targetdir("$(buildir)") + set_filename("lithium_server") + +includes("libs", "tools", "modules", "driver") \ No newline at end of file