diff --git a/.editorconfig b/.editorconfig index 444c4780..75c11c98 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,6 @@ root = true -[*.{h,hpp,c,cpp,rc,js,jsx}] +[*.{h,hpp,c,cpp,rc,js,jsx,ts,tsx}] end_of_line = lf insert_final_newline = true charset = utf-8 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d5b17d5a..c7ec11d0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @Zer0-bit @kstam +* @Zer0-bit @kstam @matiasjrossi diff --git a/.github/workflows/compile-sketch.yml b/.github/workflows/compile-sketch.yml index a9c636b3..5075c160 100644 --- a/.github/workflows/compile-sketch.yml +++ b/.github/workflows/compile-sketch.yml @@ -128,3 +128,22 @@ jobs: - name: Run static analysis on ${{ matrix.environment }} run: | pio check -e ${{ matrix.environment }} --fail-on-defect medium --fail-on-defect high + + web-build: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: webserver/web-interface/package-lock.json + - run: npm ci + working-directory: webserver/web-interface/ + - run: npm run build --if-present + working-directory: webserver/web-interface/ + - run: npm run lint + working-directory: webserver/web-interface/ + env: + CI: true diff --git a/lcd-hmi/nextion-lcd.HMI b/lcd-hmi/nextion-lcd.HMI deleted file mode 100755 index 544c291a..00000000 Binary files a/lcd-hmi/nextion-lcd.HMI and /dev/null differ diff --git a/lcd-hmi/scales-calibrate.HMI b/lcd-hmi/scales-calibrate.HMI deleted file mode 100644 index 10ffe2d2..00000000 Binary files a/lcd-hmi/scales-calibrate.HMI and /dev/null differ diff --git a/lib/Common/gaggia_settings.h b/lib/Common/gaggia_settings.h new file mode 100644 index 00000000..d4f3f721 --- /dev/null +++ b/lib/Common/gaggia_settings.h @@ -0,0 +1,63 @@ +#ifndef GAGGIA_SETTINGS_H +#define GAGGIA_SETTINGS_H + +#include +#include +#include + +struct BoilerSettings { + uint16_t steamSetPoint; /* Desired steam temperature */ + uint16_t offsetTemp; /* Temperature offset from what the thermocouple measures to the water temp */ + uint16_t hpwr; + uint16_t mainDivider; + uint16_t brewDivider; +}; +struct SystemSettings { + float pumpFlowAtZero; /* A PZ constant used for pump calibration */ + uint16_t lcdSleep; /* Time (in minutes) after which the screen should go to sleep */ + bool warmupState; /* Should gaggia wait to warmup */ +}; + +struct ScalesSettings { + bool forcePredictive; + bool hwScalesEnabled; + int hwScalesF1; + int hwScalesF2; + bool btScalesEnabled; + bool btScalesAutoConnect; + + friend bool operator==(const ScalesSettings& lhs, const ScalesSettings& rhs) { + return lhs.forcePredictive == rhs.forcePredictive && + lhs.hwScalesEnabled == rhs.hwScalesEnabled && + lhs.hwScalesF1 == rhs.hwScalesF1 && + lhs.hwScalesF2 == rhs.hwScalesF2 && + lhs.btScalesEnabled == rhs.btScalesEnabled && + lhs.btScalesAutoConnect == rhs.btScalesAutoConnect; + } +}; + +struct BrewSettings { + bool basketPrefill; /* Whether we should add a prefill phase at the beginning of the profile */ + bool homeOnShotFinish; /* Should we exit the graph a few seconds after the shot has finished */ + bool brewDeltaState; /* Should gaggia add more heating power during a shot to compensate for cold water entering */ +}; + +struct LedSettings { + bool state; /* Should the led be ON or OFF */ + bool disco; /* Should we activate disco mode during brew */ + struct Color { /* The led color */ + uint8_t R; + uint8_t G; + uint8_t B; + } color; +}; + +struct GaggiaSettings { + BoilerSettings boiler; + SystemSettings system; + BrewSettings brew; + LedSettings led; + ScalesSettings scales; +}; + +#endif diff --git a/lib/Common/mcu_comms.cpp b/lib/Common/mcu_comms.cpp index ea1b6f38..4bc0c70d 100644 --- a/lib/Common/mcu_comms.cpp +++ b/lib/Common/mcu_comms.cpp @@ -1,45 +1,32 @@ /* 09:32 15/03/2023 - change triggering comment */ #include "mcu_comms.h" +#include "proto/proto_serializer.h" +#include "proto/profile_converters.h" +#include "proto/message_converters.h" #include using namespace std; -size_t ProfileSerializer::neededBufferSize(Profile& profile) const { - return sizeof(profile.phaseCount()) + profile.phaseCount() * sizeof(Phase) + sizeof(profile.globalStopConditions); -} - -vector ProfileSerializer::serializeProfile(Profile& profile) const { - vector buffer; - buffer.reserve(neededBufferSize(profile)); - size_t phaseCount = profile.phaseCount(); - - memcpy(buffer.data(), &phaseCount, sizeof(phaseCount)); - memcpy(buffer.data() + sizeof(phaseCount), profile.phases.data(), phaseCount * sizeof(Phase)); - memcpy(buffer.data() + sizeof(phaseCount) + phaseCount * sizeof(Phase), &profile.globalStopConditions, sizeof(profile.globalStopConditions)); - - return buffer; -} - -void ProfileSerializer::deserializeProfile(vector& buffer, Profile& profile) const { - size_t phaseCount; - memcpy(&phaseCount, buffer.data(), sizeof(profile.phaseCount())); - profile.phases.clear(); - profile.phases.reserve(phaseCount); - memcpy(profile.phases.data(), buffer.data() + sizeof(profile.phaseCount()), phaseCount * sizeof(Phase)); - memcpy(&profile.globalStopConditions, buffer.data() + sizeof(profile.phaseCount()) + phaseCount * sizeof(Phase), sizeof(profile.globalStopConditions)); -} - //--------------------------------------------------------------------------------- //--------------------------- PRIVATE METHODS ---------------------------- //--------------------------------------------------------------------------------- -void McuComms::sendMultiPacket(vector& buffer, size_t dataSize, uint8_t packetID) { +// This is here to allow sending empty messages without allocating/deallocating memory for an empty vector +const std::vector& emptyVector() { + static const std::vector emptyVec; + return emptyVec; +} + +// Sends a message in multiple packets if necessary +void McuComms::sendMultiPacket(const vector& buffer, McuCommsMessageType messageType) { + uint8_t packetID = static_cast(messageType); + size_t dataSize = buffer.size(); log("Sending buffer[%d]: ", dataSize); logBufferHex(buffer, dataSize); auto dataPerPacket = static_cast(packetSize - 2u); // Two bytes are reserved for current index and last index auto numPackets = static_cast(dataSize / dataPerPacket); - if (dataSize % dataPerPacket > 0u) // Add an extra transmission if needed + if (numPackets == 0 || dataSize % dataPerPacket > 0u) // Add an extra transmission if needed numPackets++; for (uint8_t currentPacket = 0u; currentPacket < numPackets; currentPacket++) { @@ -111,49 +98,6 @@ vector McuComms::receiveMultiPacket() { return buffer; } - -void McuComms::shotSnapshotReceived(ShotSnapshot& snapshot) const { - if (shotSnapshotCallback) { - shotSnapshotCallback(snapshot); - } -} - -void McuComms::profileReceived(Profile& profile) const { - if (profileCallback) { - profileCallback(profile); - } -} - -void McuComms::sensorStateSnapshotReceived(SensorStateSnapshot& snapshot) const { - if (sensorStateSnapshotCallback) { - sensorStateSnapshotCallback(snapshot); - } -} - -void McuComms::remoteScalesWeightReceived(float weight) const { - if (remoteScalesWeightReceivedCallback) { - remoteScalesWeightReceivedCallback(weight); - } -} - -void McuComms::remoteScalesTareCommandReceived() const { - if (remoteScalesTareCommandCallback) { - remoteScalesTareCommandCallback(); - } -} - -void McuComms::remoteScalesDisconnected() const { - if (remoteScalesDisconnectedCallback) { - remoteScalesDisconnectedCallback(); - } -} - -void McuComms::responseReceived(McuCommsResponse& response) const { - if (responseReceivedCallback) { - responseReceivedCallback(response); - } -} - void McuComms::log(const char* format, ...) const { if (!debugPort) return; @@ -166,7 +110,7 @@ void McuComms::log(const char* format, ...) const { debugPort->print(buffer.data()); } -void McuComms::logBufferHex(vector& buffer, size_t dataSize) const { +void McuComms::logBufferHex(const vector& buffer, size_t dataSize) const { if (!debugPort) return; std::arrayhex; @@ -218,129 +162,31 @@ void McuComms::setDebugPort(Stream* dbgPort) { McuComms::debugPort = dbgPort; } -void McuComms::setShotSnapshotCallback(ShotSnapshotReceivedCallback callback) { - shotSnapshotCallback = callback; -} - -void McuComms::setProfileReceivedCallback(ProfileReceivedCallback callback) { - profileCallback = callback; +void McuComms::setMessageReceivedCallback(MessageReceivedCallback callback) { + messageReceivedCallback = callback; } -void McuComms::setSensorStateSnapshotCallback(SensorStateSnapshotReceivedCallback callback) { - sensorStateSnapshotCallback = callback; +void McuComms::sendMessage(McuCommsMessageType messageType) { + sendMessage(messageType, emptyVector()); } -void McuComms::setRemoteScalesWeightReceivedCallback(RemoteScalesWeightReceivedCallback callback) { - remoteScalesWeightReceivedCallback = callback; -} - -void McuComms::setRemoteScalesTareCommandCallback(RemoteScalesTareCommandCallback callback) { - remoteScalesTareCommandCallback = callback; -} - -void McuComms::setRemoteScalesDisconnectedCallback(RemoteScalesDisconnectedCallback callback) { - remoteScalesDisconnectedCallback = callback; -} - -void McuComms::setResponseReceivedCallback(ResponseReceivedCallback callback) { - responseReceivedCallback = callback; -} - -void McuComms::sendShotData(const ShotSnapshot& snapshot) { - if (!isConnected()) return; - uint16_t messageSize = transfer.txObj(snapshot); - transfer.sendData(messageSize, static_cast(McuCommsMessageType::MCUC_DATA_SHOT_SNAPSHOT)); -} - -void McuComms::sendProfile(Profile& profile) { - if (!isConnected()) return; - size_t dataSize = profileSerializer.neededBufferSize(profile); - vector buffer = profileSerializer.serializeProfile(profile); - sendMultiPacket(buffer, dataSize, static_cast(McuCommsMessageType::MCUC_DATA_PROFILE)); -} - -void McuComms::sendSensorStateSnapshot(const SensorStateSnapshot& snapshot) { - if (!isConnected()) return; - uint16_t messageSize = transfer.txObj(snapshot); - transfer.sendData(messageSize, static_cast(McuCommsMessageType::MCUC_DATA_SENSOR_STATE_SNAPSHOT)); -} - -void McuComms::sendResponse(McuCommsResponse response) { - if (!isConnected()) return; - uint16_t messageSize = transfer.txObj(response); - transfer.sendData(messageSize, static_cast(McuCommsMessageType::MCUC_RESPONSE)); -} - -void McuComms::sendRemoteScalesWeight(float weight) { +void McuComms::sendMessage(McuCommsMessageType messageType, const std::vector& data) { if (!isConnected()) return; - uint16_t messageSize = transfer.txObj(weight); - transfer.sendData(messageSize, static_cast(McuCommsMessageType::MCUC_DATA_REMOTE_SCALES_WEIGHT)); -} - -void McuComms::sendRemoteScalesTare() { - if (!isConnected()) return; - uint16_t messageSize = transfer.txObj(static_cast(McuCommsMessageType::MCUC_CMD_REMOTE_SCALES_TARE)); - transfer.sendData(messageSize, static_cast(McuCommsMessageType::MCUC_CMD_REMOTE_SCALES_TARE)); -} - -void McuComms::sendRemoteScalesDisconnected() { - if (!isConnected()) return; - uint16_t messageSize = transfer.txObj(static_cast(McuCommsMessageType::MCUC_DATA_REMOTE_SCALES_DISCONNECTED)); - transfer.sendData(messageSize, static_cast(McuCommsMessageType::MCUC_DATA_REMOTE_SCALES_DISCONNECTED)); + sendMultiPacket(data, messageType); } void McuComms::readDataAndTick() { uint8_t availableData = transfer.available(); if (availableData > 0) { - log("Some data is available\n"); lastByteReceived = millis(); - switch (static_cast(transfer.currentPacketID())) { - case McuCommsMessageType::MCUC_HEARTBEAT: { - break; - } case McuCommsMessageType::MCUC_RESPONSE: { - log("Received a response packet\n"); - McuCommsResponse response; - transfer.rxObj(response); - responseReceived(response); - break; - } case McuCommsMessageType::MCUC_DATA_SHOT_SNAPSHOT: { - log("Received a shot snapshot packet\n"); - ShotSnapshot snapshot; - transfer.rxObj(snapshot); - shotSnapshotReceived(snapshot); - break; - } case McuCommsMessageType::MCUC_DATA_PROFILE: { - log("Received a profile packet\n"); - vector data = receiveMultiPacket(); - Profile profile; - profileSerializer.deserializeProfile(data, profile); - profileReceived(profile); - break; - } case McuCommsMessageType::MCUC_DATA_SENSOR_STATE_SNAPSHOT: { - log("Received a sensor state snapshot packet\n"); - SensorStateSnapshot snapshot; - transfer.rxObj(snapshot); - sensorStateSnapshotReceived(snapshot); - break; - } case McuCommsMessageType::MCUC_DATA_REMOTE_SCALES_WEIGHT: { - log("Received a weight packet\n"); - float weight = 0.f; - transfer.rxObj(weight); - remoteScalesWeightReceived(weight); - break; - } case McuCommsMessageType::MCUC_CMD_REMOTE_SCALES_TARE: { - log("Received tare command"); - remoteScalesTareCommandReceived(); - break; - } case McuCommsMessageType::MCUC_DATA_REMOTE_SCALES_DISCONNECTED: { - log("Received scales disconnected message"); - remoteScalesDisconnected(); - break; - } - default: - log("WARN: Packet ID %d not handled\n", transfer.currentPacketID()); - break; + auto messageType = static_cast(transfer.currentPacketID()); + log("Received a packet [%d]\n", static_cast(messageType)); + + if (messageType == McuCommsMessageType::MCUC_HEARTBEAT) return; + auto data = receiveMultiPacket(); + if (messageReceivedCallback) { + messageReceivedCallback(messageType, data); } } diff --git a/lib/Common/mcu_comms.h b/lib/Common/mcu_comms.h index c107999b..864d69be 100644 --- a/lib/Common/mcu_comms.h +++ b/lib/Common/mcu_comms.h @@ -23,57 +23,37 @@ enum class McuCommsMessageType : uint8_t { MCUC_DATA_SENSOR_STATE_SNAPSHOT = 4, MCUC_DATA_REMOTE_SCALES_WEIGHT = 5, MCUC_DATA_REMOTE_SCALES_DISCONNECTED = 6, + MCUC_DATA_ALL_SETTINGS = 7, + MCUC_DATA_BREW_SETTINGS = 8, + MCUC_DATA_BOILER_SETTINGS = 9, + MCUC_DATA_SYSTEM_SETTINGS = 10, + MCUC_DATA_LED_SETTINGS = 11, + MCUC_DATA_NOTIFICATION = 12, + MCUC_DATA_MANUAL_BREW_PHASE = 13, + MCUC_DATA_SYSTEM_STATE = 14, + MCUC_DATA_DESCALING_PROGRESS = 18, + MCUC_DATA_SCALES_SETTINGS = 19, // Request specific data - MCUC_REQ_ACTIVE_PROFILE = 7, - MCUC_REQ_SETTINGS = 8, + MCUC_REQ_DATA = 15, // Commands - MCUC_CMD_SAVE_PROFILE = 9, - MCUC_CMD_SAVE_SETTINGS = 10, - MCUC_CMD_REMOTE_SCALES_TARE = 11, - - MCUC_RESPONSE = 12, -}; - -enum class McuCommsResponseResult : uint8_t { - MCUC_OK = 0, - MCUC_ERROR = 1, + MCUC_CMD_REMOTE_SCALES_TARE = 16, + MCUC_CMD_UPDATE_SYSTEM_STATE = 17, }; -struct McuCommsResponse { +struct McuCommsRequestData { McuCommsMessageType type; - McuCommsResponseResult result; -}; - -class ProfileSerializer { -public: - size_t neededBufferSize(Profile& profile) const; - std::vector serializeProfile(Profile& profile) const; - void deserializeProfile(std::vector& data, Profile& profile) const; }; class McuComms { private: - using ShotSnapshotReceivedCallback = std::function; - using ProfileReceivedCallback = std::function; - using SensorStateSnapshotReceivedCallback = std::function; - using ResponseReceivedCallback = std::function; - using RemoteScalesWeightReceivedCallback = std::function; - using RemoteScalesTareCommandCallback = std::function; - using RemoteScalesDisconnectedCallback = std::function; + using MessageReceivedCallback = std::function&)>; uint32_t lastByteReceived = 0; uint32_t lastHeartbeatSent = 0; - ProfileSerializer profileSerializer; SerialTransfer transfer; - ShotSnapshotReceivedCallback shotSnapshotCallback = nullptr; - ProfileReceivedCallback profileCallback = nullptr; - SensorStateSnapshotReceivedCallback sensorStateSnapshotCallback = nullptr; - ResponseReceivedCallback responseReceivedCallback = nullptr; - RemoteScalesWeightReceivedCallback remoteScalesWeightReceivedCallback = nullptr; - RemoteScalesTareCommandCallback remoteScalesTareCommandCallback = nullptr; - RemoteScalesDisconnectedCallback remoteScalesDisconnectedCallback = nullptr; + MessageReceivedCallback messageReceivedCallback = nullptr; Stream* debugPort = nullptr; size_t packetSize; @@ -87,17 +67,16 @@ class McuComms { * | | |______|____________________________________________________________ index of current packet * |______|_____________________________________________________________________ index of last packet */ - void sendMultiPacket(std::vector& buffer, size_t dataSize, uint8_t packetID); + void sendMultiPacket(const std::vector& buffer, McuCommsMessageType messageType); std::vector receiveMultiPacket(); void log(const char* format, ...) const; - void logBufferHex(std::vector& buffer, size_t dataSize) const; + void logBufferHex(const std::vector& buffer, size_t dataSize) const; void establishConnection(uint32_t timeout); void sendHeartbeat(); void shotSnapshotReceived(ShotSnapshot& snapshot) const; void profileReceived(Profile& profile) const; void sensorStateSnapshotReceived(SensorStateSnapshot& snapshot) const; - void responseReceived(McuCommsResponse& response) const; void remoteScalesWeightReceived(float weight) const; void remoteScalesTareCommandReceived() const; void remoteScalesDisconnected() const; @@ -105,25 +84,15 @@ class McuComms { public: void begin(Stream& serial, uint32_t waitConnectionMillis = 0, size_t packetSize = MAX_DATA_PER_PACKET_DEFAULT); void setDebugPort(Stream* debugPort); - void setShotSnapshotCallback(ShotSnapshotReceivedCallback callback); - void setProfileReceivedCallback(ProfileReceivedCallback callback); - void setSensorStateSnapshotCallback(SensorStateSnapshotReceivedCallback callback); - void setResponseReceivedCallback(ResponseReceivedCallback callback); - void setRemoteScalesWeightReceivedCallback(RemoteScalesWeightReceivedCallback callback); - void setRemoteScalesTareCommandCallback(RemoteScalesTareCommandCallback callback); - void setRemoteScalesDisconnectedCallback(RemoteScalesDisconnectedCallback callback); - - void sendShotData(const ShotSnapshot& snapshot); - void sendProfile(Profile& profile); - void sendSensorStateSnapshot(const SensorStateSnapshot& snapshot); - void sendResponse(McuCommsResponse response); - void sendRemoteScalesWeight(float weight); - void sendRemoteScalesTare(); - void sendRemoteScalesDisconnected(); + void setMessageReceivedCallback(MessageReceivedCallback callback); + + // Sends a message with no data. Useful for commands. This is equivalent to sending the message with an empty data vector + void sendMessage(McuCommsMessageType messageType); + // Sends a message of a given type with its corresponding data. Sender and receiver must agree on how this data is serialized/deserialized + void sendMessage(McuCommsMessageType messageType, const std::vector& data); bool isConnected(); void readDataAndTick(); }; - #endif diff --git a/lib/Common/measurements.cpp b/lib/Common/measurements.cpp index 82602c28..545822a5 100644 --- a/lib/Common/measurements.cpp +++ b/lib/Common/measurements.cpp @@ -2,7 +2,7 @@ #include "measurements.h" void Measurements::add(float measurement) { - add(Measurement{.value=measurement, .millis=(uint32_t) millis()}); + add(Measurement{.value=measurement, .millis=static_cast(millis())}); } void Measurements::add(Measurement measurement) { @@ -13,12 +13,12 @@ void Measurements::add(Measurement measurement) { } } -Measurement Measurements::previous() { +Measurement Measurements::getPrevious() { if (values.size() < 2) return Measurement{0.f, 0}; return *std::next(values.begin()); } -Measurement Measurements::latest() { +Measurement Measurements::getLatest() { if (!values.empty()) { return values.front(); } @@ -30,23 +30,26 @@ void Measurements::clear() { } /** - * See explanation in Measurements classs. This method tries to find the last 2 different - * measurements and returns the change betweeen them (delta and deltaTime) + * See explanation in Measurements class. This method tries to find the last 2 different + * measurements and returns the change between them (delta and deltaTime) */ -MeasurementChange Measurements::measurementChange() { - if (values.size() < 2) return MeasurementChange{0.f, 0}; - Measurement latest = values.front(); - Measurement closestDifferent = values.back(); - - for (auto it = values.begin(); it != values.end(); it = std::next(it)) { - if (it->value != latest.value) { - closestDifferent = *it; +MeasurementChange Measurements::getMeasurementChange() { + MeasurementChange result = MeasurementChange{ 0.f, 0 }; + + if (values.size() < 2) return result; + + for (auto latest = values.begin(), prev = std::next(values.begin()); + prev != values.end(); + latest = std::next(latest), prev = std::next(prev) + ) { + if (latest->value != prev->value) { + result = MeasurementChange{ + .deltaValue = latest->value - prev->value, + .deltaMillis = latest->millis - prev->millis, + }; break; } } - return MeasurementChange{ - .deltaValue = latest.value - closestDifferent.value, - .deltaMillis=latest.millis - closestDifferent.millis - }; + return result; } diff --git a/lib/Common/measurements.h b/lib/Common/measurements.h index 9beceed9..983d5dc7 100644 --- a/lib/Common/measurements.h +++ b/lib/Common/measurements.h @@ -2,12 +2,17 @@ #define MEASUREMENTS_UTIL_H #include "Arduino.h" +#include "utils.h" #include /** Holds a measurement value and the time it was taken */ struct Measurement { - float value; - uint32_t millis; + float value = 0.f; + uint32_t millis = 0u; + + void setPrecision(uint8_t precision) { + value = truncate(value, precision); + } }; /** Holds a measurement delta and can calculate its speed of change */ @@ -15,7 +20,7 @@ struct MeasurementChange { float deltaValue; uint32_t deltaMillis; - float changeSpeed() { + float speed() { if (deltaMillis == 0) { return 0.f; } @@ -40,9 +45,9 @@ class Measurements { public: Measurements(size_t size) : size(size) {} - Measurement latest(); - Measurement previous(); - MeasurementChange measurementChange(); + Measurement getLatest(); + Measurement getPrevious(); + MeasurementChange getMeasurementChange(); void clear(); void add(float value); void add(Measurement measurement); diff --git a/lib/Common/notification_message.h b/lib/Common/notification_message.h new file mode 100644 index 00000000..a5add733 --- /dev/null +++ b/lib/Common/notification_message.h @@ -0,0 +1,23 @@ +#ifndef NOTIFICATION_MESSAGE_H +#define NOTIFICATION_MESSAGE_H + +#include "string" + +enum class NotificationType { + INFO = 0, + SUCCESS = 1, + WARN = 2, + ERROR = 3, +}; + +struct Notification { + NotificationType type; + std::string message; + + static Notification info(std::string message) { return Notification{ NotificationType::INFO, message }; }; + static Notification success(std::string message) { return Notification{ NotificationType::SUCCESS, message }; }; + static Notification error(std::string message) { return Notification{ NotificationType::ERROR, message }; }; + static Notification warn(std::string message) { return Notification{ NotificationType::WARN, message }; }; +}; + +#endif diff --git a/lib/Common/profiling_phases.cpp b/lib/Common/profiling_phases.cpp index fe4f4108..7cf81762 100644 --- a/lib/Common/profiling_phases.cpp +++ b/lib/Common/profiling_phases.cpp @@ -1,36 +1,14 @@ /* 09:32 15/03/2023 - change triggering comment */ #include "profiling_phases.h" -//----------------------------------------------------------------------// -//------------------------- ShotSnapshot -------------------------------// -//----------------------------------------------------------------------// - -ShotSnapshot buildShotSnapshot(uint32_t timeInShot, const SensorState& state, CurrentPhase& phase) { - float targetFlow = (phase.getType() == PHASE_TYPE::PHASE_TYPE_FLOW) ? phase.getTarget() : phase.getRestriction(); - float targetPressure = (phase.getType() == PHASE_TYPE::PHASE_TYPE_PRESSURE) ? phase.getTarget() : phase.getRestriction(); - - return ShotSnapshot{ - .timeInShot = timeInShot, - .pressure = state.smoothedPressure, - .pumpFlow = state.smoothedPumpFlow, - .weightFlow = state.smoothedWeightFlow, - .temperature = state.waterTemperature, - .shotWeight = state.shotWeight, - .waterPumped = state.waterPumped, - .targetTemperature = -1, - .targetPumpFlow = targetFlow, - .targetPressure = targetPressure - }; -}; - //----------------------------------------------------------------------// //------------------------------ Phase ---------------------------------// //----------------------------------------------------------------------// float Phase::getTarget(uint32_t timeInPhase, const ShotSnapshot& stateAtStart) const { - long transitionTime = fmax(0L, target.time); + uint32_t transitionTime = target.time; float startValue = target.start > 0.f ? target.start - : type == PHASE_TYPE::PHASE_TYPE_FLOW ? stateAtStart.pumpFlow : stateAtStart.pressure; + : type == PhaseType::FLOW ? stateAtStart.pumpFlow : stateAtStart.pressure; return mapRange(timeInPhase, 0.f, transitionTime, startValue, target.end, 1, target.curve); } @@ -60,13 +38,13 @@ inline bool predictTargerAchieved(const float targetValue, const float currentVa return secondsRemaining < reactionTime ? true : false; } -bool PhaseStopConditions::isReached(SensorState& state, long timeInShot, ShotSnapshot stateAtPhaseStart) const { +bool PhaseStopConditions::isReached(SensorState& state, uint32_t timeInShot, ShotSnapshot stateAtPhaseStart) const { auto stopOn = this; uint32_t timeInPhase = timeInShot - stateAtPhaseStart.timeInShot; float flow = state.weight > 0.4f ? state.smoothedWeightFlow : state.smoothedPumpFlow; float currentWaterPumpedInPhase = state.waterPumped - stateAtPhaseStart.waterPumped; - return (stopOn->time >= 0L && timeInPhase >= static_cast(stopOn->time)) || + return (stopOn->time > 0L && timeInPhase >= static_cast(stopOn->time)) || (stopOn->weight > 0.f && state.shotWeight > stopOn->weight) || (stopOn->pressureAbove > 0.f && state.smoothedPressure > stopOn->pressureAbove) || (stopOn->pressureBelow > 0.f && state.smoothedPressure < stopOn->pressureBelow) || @@ -75,7 +53,7 @@ bool PhaseStopConditions::isReached(SensorState& state, long timeInShot, ShotSna (stopOn->flowBelow > 0.f && state.smoothedPumpFlow < stopOn->flowBelow); } -bool GlobalStopConditions::isReached(const SensorState& state, uint32_t timeInShot) { +bool GlobalStopConditions::isReached(const SensorState& state, uint32_t timeInShot) const { if (timeInShot < 1000) { // No shot lasts less than 1 second return false; } @@ -87,69 +65,3 @@ bool GlobalStopConditions::isReached(const SensorState& state, uint32_t timeInSh (stopOn->waterPumped > 0.f && state.waterPumped > stopOn->waterPumped) || (stopOn->time > 0L && timeInShot >= stopOn->time); } - -//----------------------------------------------------------------------// -//--------------------------- CurrentPhase -----------------------------// -//----------------------------------------------------------------------// -CurrentPhase::CurrentPhase(int index, const Phase& phase, uint32_t timeInPhase, const ShotSnapshot& shotSnapshotAtStart) : index(index), phase{ &phase }, timeInPhase(timeInPhase), shotSnapshotAtStart{ &shotSnapshotAtStart } {} -CurrentPhase::CurrentPhase(const CurrentPhase& currentPhase) : index(currentPhase.index), phase{ currentPhase.phase }, timeInPhase(currentPhase.timeInPhase), shotSnapshotAtStart{ currentPhase.shotSnapshotAtStart } {} - -Phase CurrentPhase::getPhase() { return *phase; } - -PHASE_TYPE CurrentPhase::getType() { return phase->type; } - -int CurrentPhase::getIndex() { return index; } - -long CurrentPhase::getTimeInPhase() { return timeInPhase; } - -float CurrentPhase::getTarget() { return phase->getTarget(timeInPhase, *shotSnapshotAtStart); } - -float CurrentPhase::getRestriction() { return phase->getRestriction(); } - -void CurrentPhase::update(int index, Phase& phase, uint32_t timeInPhase) { - CurrentPhase::index = index; - CurrentPhase::phase = &phase; - CurrentPhase::timeInPhase = timeInPhase; -} - -//----------------------------------------------------------------------// -//-------------------------- PhaseProfiler -----------------------------// -//----------------------------------------------------------------------// - -PhaseProfiler::PhaseProfiler(Profile& profile) : profile(profile) {} - -void PhaseProfiler::updatePhase(uint32_t timeInShot, SensorState& state) { - size_t phaseIdx = currentPhaseIdx; - uint32_t timeInPhase = timeInShot - phaseChangedSnapshot.timeInShot; - - if (phaseIdx >= profile.phaseCount() || profile.globalStopConditions.isReached(state, timeInShot)) { - currentPhaseIdx = profile.phaseCount(); - currentPhase.update(currentPhaseIdx - 1, profile.phases[phaseIdx], timeInPhase); - return; - } - - if (!profile.phases[phaseIdx].isStopConditionReached(state, timeInShot, phaseChangedSnapshot)) { - currentPhase.update(phaseIdx, profile.phases[phaseIdx], timeInPhase); - return; - } - - currentPhase.update(phaseIdx, profile.phases[phaseIdx], timeInPhase); - phaseChangedSnapshot = buildShotSnapshot(timeInShot, state, currentPhase); - currentPhaseIdx += 1; - updatePhase(timeInShot, state); -} - -// Gets the profiling phase we should be in based on the timeInShot and the Sensors state -CurrentPhase& PhaseProfiler::getCurrentPhase() { - return currentPhase; -} - -bool PhaseProfiler::isFinished() { - return currentPhaseIdx >= profile.phaseCount(); -} - -void PhaseProfiler::reset() { - currentPhaseIdx = 0; - phaseChangedSnapshot = ShotSnapshot{}; - currentPhase.update(0, profile.phases[0], 0); -} diff --git a/lib/Common/profiling_phases.h b/lib/Common/profiling_phases.h index 0d50aa3d..6c6b6f05 100644 --- a/lib/Common/profiling_phases.h +++ b/lib/Common/profiling_phases.h @@ -4,49 +4,38 @@ #include "utils.h" #include "sensors_state.h" -#include "../../src/eeprom_data/eeprom_data.h" #include +#include -enum class PHASE_TYPE { - PHASE_TYPE_FLOW, - PHASE_TYPE_PRESSURE -}; - -struct ShotSnapshot { - uint32_t timeInShot; - float pressure; - float pumpFlow; - float weightFlow; - float temperature; - float shotWeight; - float waterPumped; - - float targetTemperature; - float targetPumpFlow; - float targetPressure; +enum class PhaseType { + FLOW = 0, + PRESSURE = 1 }; struct PhaseStopConditions { - long time = -1; - float pressureAbove = -1; - float pressureBelow = -1; - float flowAbove = -1; - float flowBelow = -1; - float weight = -1; //example: when pushed weight >0 stop this phase) - float waterPumpedInPhase = -1; - - bool isReached(SensorState& state, long timeInShot, ShotSnapshot stateAtPhaseStart) const; + uint32_t time = 0; + float pressureAbove = 0; + float pressureBelow = 0; + float flowAbove = 0; + float flowBelow = 0; + float weight = 0; //example: when pushed weight >0 stop this phase) + float waterPumpedInPhase = 0; + + bool isReached(SensorState& state, uint32_t timeInShot, ShotSnapshot stateAtPhaseStart) const; }; struct Transition { float start; float end; TransitionCurve curve; - long time; + uint32_t time; - Transition(): start(-1), end(-1), curve(TransitionCurve::INSTANT), time(0) {} - Transition(float targetValue, TransitionCurve curve = TransitionCurve::INSTANT, long time = 0): start(-1), end(targetValue), curve(curve), time(time) {} - Transition(float start, float end, TransitionCurve curve = TransitionCurve::LINEAR, long time = 0): start(start), end(end), curve(curve), time(time) {} + Transition() : Transition(0) {} + Transition(float targetValue, TransitionCurve curve = TransitionCurve::INSTANT, uint32_t time = 0) : Transition(0, targetValue, curve, time) {} + Transition(float start, float end, TransitionCurve curve = TransitionCurve::LINEAR, uint32_t time = 0) : start(start), end(end), curve(curve), time(time) { + if (end <= 0) end = start; + if (end <= 0) time = 0; + } bool isInstant() { return curve == TransitionCurve::INSTANT || time == 0; @@ -54,10 +43,13 @@ struct Transition { }; struct Phase { - PHASE_TYPE type; + std::string name = ""; + PhaseType type; Transition target; - float restriction; + float restriction = 0; PhaseStopConditions stopConditions; + bool skip = false; + float waterTemperature = 0; float getTarget(uint32_t timeInPhase, const ShotSnapshot& shotSnapshotAtStart) const; float getRestriction() const; @@ -65,18 +57,29 @@ struct Phase { }; struct GlobalStopConditions { - long time = -1; - float weight = -1; - float waterPumped = -1; + uint32_t time = 0; + float weight = 0; + float waterPumped = 0; + + bool isReached(const SensorState& state, uint32_t timeInShot) const; +}; + +struct BrewRecipe { + float coffeeIn = 0; + float coffeeOut = 0; + float ratio = 0; - bool isReached(const SensorState& state, uint32_t timeInShot); + float getCoffeeOut() const { return coffeeOut != 0 ? coffeeOut : coffeeIn * ratio; }; }; struct Profile { + std::string name; std::vector phases; GlobalStopConditions globalStopConditions; + float waterTemperature = 0; + BrewRecipe recipe; - size_t phaseCount() { + size_t phaseCount() const { return phases.size(); } @@ -93,44 +96,4 @@ struct Profile { } }; -class CurrentPhase { -private: - int index; - const Phase* phase; - const ShotSnapshot* shotSnapshotAtStart; - unsigned long timeInPhase; - -public: - CurrentPhase(int index, const Phase& phase, uint32_t timeInPhase, const ShotSnapshot& shotSnapshotAtStart); - CurrentPhase(const CurrentPhase& currentPhase); - - Phase getPhase(); - PHASE_TYPE getType(); - int getIndex(); - long getTimeInPhase(); - float getTarget(); - float getRestriction(); - void update(int index, Phase& phase, uint32_t timeInPhase); -}; - -class PhaseProfiler { -private: - Profile& profile; - size_t currentPhaseIdx = 0; // The index at which the profiler currently is. - ShotSnapshot phaseChangedSnapshot = ShotSnapshot{0, 0, 0, 0, 0, 0}; // State when the profiler move to this currentPhaseIdx - CurrentPhase currentPhase = CurrentPhase(0, profile.phases[0], 0, phaseChangedSnapshot); - -public: - PhaseProfiler(Profile& profile); - // Gets the profiling phase we should be in based on the timeInShot and the Sensors state - void updatePhase(uint32_t timeInShot, SensorState& state); - CurrentPhase& getCurrentPhase(); - bool isFinished(); - void reset(); -}; - -// Helper functions - -ShotSnapshot buildShotSnapshot(uint32_t timeInShot, const SensorState& state, CurrentPhase& phase); - #endif diff --git a/lib/Common/proto/message_converters.h b/lib/Common/proto/message_converters.h new file mode 100644 index 00000000..65d4fc6d --- /dev/null +++ b/lib/Common/proto/message_converters.h @@ -0,0 +1,300 @@ +#ifndef MESSAGE_CONVERTERS_H +#define MESSAGE_CONVERTERS_H + +#include "nanopb_cpp.h" +#include "system_state.h" +#include "sensors_state.h" +#include "measurements.h" +#include "notification_message.h" +#include "messages.pb.h" +#include "mcu_comms.h" + +using namespace NanoPb::Converter; + +/** + * The below classes are converters that map our own classes to/from protobuf objects. + * The protobuf objects are autogenerated at build time based on the *.proto definition files + * (see settings.proto and profile.proto). + */ + +class OperationModeConverter : public NanoPb::Converter::EnumConverter { +public: + static ProtoType encode(const LocalType& local) { + switch (local) { + case OperationMode::BREW_AUTO: return OperationModeDto::OperationModeDto_BREW_AUTO; + case OperationMode::BREW_MANUAL: return OperationModeDto::OperationModeDto_BREW_MANUAL; + case OperationMode::FLUSH: return OperationModeDto::OperationModeDto_FLUSH; + case OperationMode::DESCALE: return OperationModeDto::OperationModeDto_DESCALE; + case OperationMode::STEAM: return OperationModeDto::OperationModeDto_STEAM; + case OperationMode::FLUSH_AUTO: return OperationModeDto::OperationModeDto_FLUSH_AUTO; + } + return OperationModeDto::OperationModeDto_BREW_AUTO; + }; + + static LocalType decode(const ProtoType& proto) { + switch (proto) { + case OperationModeDto::OperationModeDto_BREW_AUTO: return OperationMode::BREW_AUTO; + case OperationModeDto::OperationModeDto_BREW_MANUAL: return OperationMode::BREW_MANUAL; + case OperationModeDto::OperationModeDto_FLUSH: return OperationMode::FLUSH; + case OperationModeDto::OperationModeDto_DESCALE: return OperationMode::DESCALE; + case OperationModeDto::OperationModeDto_STEAM: return OperationMode::STEAM; + case OperationModeDto::OperationModeDto_FLUSH_AUTO: return OperationMode::FLUSH_AUTO; + } + return OperationMode::BREW_AUTO; + }; +}; + +class SystemStateConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return SystemStateDto{ + .startupInitFinished = local.startupInitFinished, + .tofReady = local.tofReady, + .isSteamForgottenON = local.isSteamForgottenON, + .scalesPresent = local.scalesPresent, + .operationMode = OperationModeConverter::encode(local.operationMode), + .timeAlive = local.timeAlive, + .tarePending = local.tarePending, + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return SystemStateDto{}; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.startupInitFinished = proto.startupInitFinished; + local.tofReady = proto.tofReady; + local.isSteamForgottenON = proto.isSteamForgottenON; + local.scalesPresent = proto.scalesPresent; + local.operationMode = OperationModeConverter::decode(proto.operationMode); + local.timeAlive = proto.timeAlive; + local.tarePending = proto.tarePending; + return true; + }; +}; + +class UpdateSystemStateComandConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return UpdateSystemStateComandDto{ + .operationMode = OperationModeConverter::encode(local.operationMode), + .tarePending = local.tarePending, + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return UpdateSystemStateComandDto{}; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.operationMode = OperationModeConverter::decode(proto.operationMode); + local.tarePending = proto.tarePending; + return true; + }; +}; + +class SensorStateSnapshotConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return SensorStateSnapshotDto{ + .brewActive = local.brewActive, + .steamActive = local.steamActive, + .hotWaterSwitchState = local.hotWaterSwitchState, + .temperature = local.temperature, + .waterTemperature = local.waterTemperature, + .pressure = local.pressure, + .pumpFlow = local.pumpFlow, + .weightFlow = local.weightFlow, + .weight = local.weight, + .waterLevel = local.waterLevel, + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return SensorStateSnapshotDto{}; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.brewActive = proto.brewActive; + local.steamActive = proto.steamActive; + local.hotWaterSwitchState = proto.hotWaterSwitchState; + local.temperature = proto.temperature; + local.waterTemperature = proto.waterTemperature; + local.pressure = proto.pressure; + local.pumpFlow = proto.pumpFlow; + local.weightFlow = proto.weightFlow; + local.weight = proto.weight; + local.waterLevel = proto.waterLevel; + return true; + }; +}; + + +class ShotSnapshotConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return ShotSnapshotDto{ + .timeInShot = local.timeInShot, + .pressure = local.pressure, + .pumpFlow = local.pumpFlow, + .weightFlow = local.weightFlow, + .temperature = local.temperature, + .shotWeight = local.shotWeight, + .waterPumped = local.waterPumped, + .targetTemperature = local.targetTemperature, + .targetPumpFlow = local.targetPumpFlow, + .targetPressure = local.targetPressure , + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return ShotSnapshotDto{}; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.timeInShot = proto.timeInShot; + local.pressure = proto.pressure; + local.pumpFlow = proto.pumpFlow; + local.weightFlow = proto.weightFlow; + local.temperature = proto.temperature; + local.shotWeight = proto.shotWeight; + local.waterPumped = proto.waterPumped; + local.targetTemperature = proto.targetTemperature; + local.targetPumpFlow = proto.targetPumpFlow; + local.targetPressure = proto.targetPressure; + return true; + }; +}; + +class MeasurementConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return MeasurementDto{ + .value = local.value, + .time = local.millis, + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return MeasurementDto{}; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.value = proto.value; + local.millis = proto.time; + return true; + }; +}; + +class NotificationTypeConverter : public NanoPb::Converter::EnumConverter { +public: + static ProtoType encode(const LocalType& local) { + switch (local) { + case NotificationType::INFO: return NotificationDto_NotificationTypeDto::NotificationDto_NotificationTypeDto_INFO; + case NotificationType::SUCCESS: return NotificationDto_NotificationTypeDto::NotificationDto_NotificationTypeDto_SUCCESS; + case NotificationType::WARN: return NotificationDto_NotificationTypeDto::NotificationDto_NotificationTypeDto_WARN; + case NotificationType::ERROR: return NotificationDto_NotificationTypeDto::NotificationDto_NotificationTypeDto_ERROR; + } + return NotificationDto_NotificationTypeDto::NotificationDto_NotificationTypeDto_INFO; + }; + + static LocalType decode(const ProtoType& proto) { + switch (proto) { + case NotificationDto_NotificationTypeDto::NotificationDto_NotificationTypeDto_INFO: return NotificationType::INFO; + case NotificationDto_NotificationTypeDto::NotificationDto_NotificationTypeDto_SUCCESS: return NotificationType::SUCCESS; + case NotificationDto_NotificationTypeDto::NotificationDto_NotificationTypeDto_WARN: return NotificationType::WARN; + case NotificationDto_NotificationTypeDto::NotificationDto_NotificationTypeDto_ERROR: return NotificationType::ERROR; + } + return NotificationType::INFO; + }; +}; + +class NotificationConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return NotificationDto{ + .type = NotificationTypeConverter::encode(local.type), + .message = NanoPb::Converter::StringConverter::encoderInit(local.message), + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return NotificationDto{ + .message = NanoPb::Converter::StringConverter::decoderInit(local.message), + }; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.type = NotificationTypeConverter::decode(proto.type); + return true; + }; +}; + +class McuCommsRequestDataConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return RequestDataDto{ + .type = static_cast(local.type), + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return RequestDataDto{}; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.type = static_cast(proto.type); + return true; + }; +}; + + +class DescalingStateConverter : public NanoPb::Converter::EnumConverter { +public: + static ProtoType encode(const LocalType& local) { + switch (local) { + case DescalingState::IDLE: return DescalingStateDto::DescalingStateDto_IDLE; + case DescalingState::PHASE1: return DescalingStateDto::DescalingStateDto_PHASE1; + case DescalingState::PHASE2: return DescalingStateDto::DescalingStateDto_PHASE2; + case DescalingState::PHASE3: return DescalingStateDto::DescalingStateDto_PHASE3; + case DescalingState::FINISHED: return DescalingStateDto::DescalingStateDto_FINISHED; + } + return DescalingStateDto::DescalingStateDto_IDLE; + }; + + static LocalType decode(const ProtoType& proto) { + switch (proto) { + case DescalingStateDto::DescalingStateDto_IDLE: return DescalingState::IDLE; + case DescalingStateDto::DescalingStateDto_PHASE1: return DescalingState::PHASE1; + case DescalingStateDto::DescalingStateDto_PHASE2: return DescalingState::PHASE2; + case DescalingStateDto::DescalingStateDto_PHASE3: return DescalingState::PHASE3; + case DescalingStateDto::DescalingStateDto_FINISHED: return DescalingState::FINISHED; + } + return DescalingState::IDLE; + }; +}; + + +class DescalingProgressConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return DescalingProgressDto{ + .state = DescalingStateConverter::encode(local.state), + .time = local.time, + .progess = local.progess, + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return DescalingProgressDto{}; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.state = DescalingStateConverter::decode(proto.state); + local.time = proto.time; + local.progess = proto.progess; + return true; + }; +}; +#endif diff --git a/lib/Common/proto/messages.proto b/lib/Common/proto/messages.proto new file mode 100644 index 00000000..838dea99 --- /dev/null +++ b/lib/Common/proto/messages.proto @@ -0,0 +1,85 @@ +syntax = "proto3"; + +enum OperationModeDto { + BREW_AUTO = 0; + BREW_MANUAL = 1; + FLUSH = 2; + DESCALE = 3; + STEAM = 4; + FLUSH_AUTO = 5; +}; + +message UpdateSystemStateComandDto { + OperationModeDto operationMode = 1; + bool tarePending = 2; +} + +message RequestDataDto { + uint32 type = 1; +} + +message SystemStateDto { + bool startupInitFinished = 1; + bool tofReady = 2; + bool isSteamForgottenON = 3; + bool scalesPresent = 4; + OperationModeDto operationMode = 5; + uint32 timeAlive = 6; + bool tarePending = 8; +} + +message SensorStateSnapshotDto { + bool brewActive = 1; + bool steamActive = 2; + bool hotWaterSwitchState = 3; + float temperature = 4; + float waterTemperature = 5; + float pressure = 6; + float pumpFlow = 7; + float weightFlow = 8; + float weight = 9; + uint32 waterLevel = 10; +} + +message ShotSnapshotDto { + uint32 timeInShot = 1; + float pressure = 2; + float pumpFlow = 3; + float weightFlow = 4; + float temperature = 5; + float shotWeight = 6; + float waterPumped = 7; + float targetTemperature = 8; + float targetPumpFlow = 9; + float targetPressure = 10; +} + +message MeasurementDto { + float value = 1; + uint32 time = 2; +} + +message NotificationDto { + enum NotificationTypeDto { + INFO = 0; + SUCCESS = 1; + WARN = 2; + ERROR = 3; + } + NotificationTypeDto type = 1; + string message = 2; +} + +enum DescalingStateDto { + IDLE = 0; + PHASE1 = 1; + PHASE2 = 2; + PHASE3 = 3; + FINISHED = 4; +} + +message DescalingProgressDto { + DescalingStateDto state = 1; + uint32 time = 2; + uint32 progess = 3; +} diff --git a/lib/Common/proto/profile.proto b/lib/Common/proto/profile.proto new file mode 100644 index 00000000..6c286583 --- /dev/null +++ b/lib/Common/proto/profile.proto @@ -0,0 +1,61 @@ +syntax = "proto3"; + +enum PhaseTypeDto { + FLOW = 0; + PRESSURE = 1; +} + +enum TransitionCurveDto { + EASE_IN_OUT = 0; + EASE_IN = 1; + EASE_OUT = 2; + LINEAR = 3; + INSTANT = 4; +} + +message PhaseStopConditionsDto { + uint32 time = 1; + float pressureAbove = 2; + float pressureBelow = 3; + float flowAbove = 4; + float flowBelow = 5; + float weight = 6; + float waterPumpedInPhase = 7; +} + +message TransitionDto { + float start = 1; + float end = 2; + TransitionCurveDto curve = 3; + uint32 time = 4; +} + +message PhaseDto { + PhaseTypeDto type = 1; + TransitionDto target = 2; + float restriction = 3; + PhaseStopConditionsDto stopConditions = 4; + float waterTemperature = 5; + string name = 6; + bool skip = 7; +} + +message GlobalStopConditionsDto { + uint32 time = 1; + float weight = 2; + float waterPumped = 3; +} + +message BrewRecipeDto { + float coffeeIn = 1; + float coffeeOut = 2; + float ratio = 3; +} + +message ProfileDto { + string name = 1; + repeated PhaseDto phases = 2; + GlobalStopConditionsDto globalStopConditions = 3; + float waterTemperature = 4; + BrewRecipeDto recipe = 5; +} diff --git a/lib/Common/proto/profile_converters.h b/lib/Common/proto/profile_converters.h new file mode 100644 index 00000000..220920b5 --- /dev/null +++ b/lib/Common/proto/profile_converters.h @@ -0,0 +1,154 @@ +#ifndef PROFILE_CONVERTERS_H +#define PROFILE_CONVERTERS_H + +#include "profiling_phases.h" +#include "nanopb_cpp.h" +#include "profile.pb.h" + +/** + * The below classes are converters that map our own classes to/from protobuf objects. + * The protobuf objects are autogenerated at build time based on the *.proto definition files + * (see settings.proto and profile.proto). + */ +class PhaseTypeConverter : public NanoPb::Converter::EnumConverter { +public: + static ProtoType encode(const LocalType& local) { + switch (local) { + case PhaseType::FLOW: return PhaseTypeDto::PhaseTypeDto_FLOW; + case PhaseType::PRESSURE: return PhaseTypeDto::PhaseTypeDto_PRESSURE; + } + return PhaseTypeDto::PhaseTypeDto_PRESSURE; + }; + + static LocalType decode(const ProtoType& proto) { + switch (proto) { + case PhaseTypeDto::PhaseTypeDto_FLOW: return PhaseType::FLOW; + case PhaseTypeDto::PhaseTypeDto_PRESSURE: return PhaseType::PRESSURE; + } + return PhaseType::PRESSURE; + }; +}; + +class TransitionCurveConverter : public NanoPb::Converter::EnumConverter { +public: + static ProtoType encode(const LocalType& local) { + switch (local) { + case TransitionCurve::LINEAR: return TransitionCurveDto::TransitionCurveDto_LINEAR; + case TransitionCurve::EASE_IN: return TransitionCurveDto::TransitionCurveDto_EASE_IN; + case TransitionCurve::EASE_OUT: return TransitionCurveDto::TransitionCurveDto_EASE_OUT; + case TransitionCurve::EASE_IN_OUT: return TransitionCurveDto::TransitionCurveDto_EASE_IN_OUT; + case TransitionCurve::INSTANT: return TransitionCurveDto::TransitionCurveDto_INSTANT; + } + return TransitionCurveDto::TransitionCurveDto_LINEAR; + }; + + static LocalType decode(const ProtoType& proto) { + switch (proto) { + case TransitionCurveDto::TransitionCurveDto_LINEAR: return TransitionCurve::LINEAR; + case TransitionCurveDto::TransitionCurveDto_EASE_IN: return TransitionCurve::EASE_IN; + case TransitionCurveDto::TransitionCurveDto_EASE_OUT: return TransitionCurve::EASE_OUT; + case TransitionCurveDto::TransitionCurveDto_EASE_IN_OUT: return TransitionCurve::EASE_IN_OUT; + case TransitionCurveDto::TransitionCurveDto_INSTANT: return TransitionCurve::INSTANT; + } + return TransitionCurve::LINEAR; + }; +}; + +class PhaseConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return PhaseDto{ + .type = PhaseTypeConverter::encode(local.type), + .has_target = true, + .target = TransitionDto { + .start = local.target.start, + .end = local.target.end, + .curve = TransitionCurveConverter::encode(local.target.curve), + .time = local.target.time, + }, + .restriction = local.restriction, + .has_stopConditions = true, + .stopConditions = PhaseStopConditionsDto { + .time = local.stopConditions.time, + .pressureAbove = local.stopConditions.pressureAbove, + .pressureBelow = local.stopConditions.pressureBelow, + .flowAbove = local.stopConditions.flowAbove, + .flowBelow = local.stopConditions.flowBelow, + .weight = local.stopConditions.weight, + .waterPumpedInPhase = local.stopConditions.waterPumpedInPhase, + }, + .waterTemperature = local.waterTemperature, + .name = NanoPb::Converter::StringConverter::encoderInit(local.name), + .skip = local.skip, + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return ProtoType{ + .name = NanoPb::Converter::StringConverter::decoderInit(local.name), + }; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.type = PhaseTypeConverter::decode(proto.type); + local.restriction = proto.restriction; + local.target.start = proto.target.start; + local.target.end = proto.target.end; + local.target.curve = TransitionCurveConverter::decode(proto.target.curve); + local.target.time = proto.target.time; + local.stopConditions.time = proto.stopConditions.time; + local.stopConditions.pressureAbove = proto.stopConditions.pressureAbove; + local.stopConditions.pressureBelow = proto.stopConditions.pressureBelow; + local.stopConditions.flowAbove = proto.stopConditions.flowAbove; + local.stopConditions.flowBelow = proto.stopConditions.flowBelow; + local.stopConditions.weight = proto.stopConditions.weight; + local.stopConditions.waterPumpedInPhase = proto.stopConditions.waterPumpedInPhase; + local.waterTemperature = proto.waterTemperature; + local.skip = proto.skip; + return true; + }; +}; + +class ProfileConverter : public NanoPb::Converter::MessageConverter { +public: + using PhaseArrayConverter = NanoPb::Converter::ArrayConverter>; + + static ProtoType encoderInit(const LocalType& local) { + return ProfileDto{ + .name = NanoPb::Converter::StringConverter::encoderInit(local.name), + .phases = PhaseArrayConverter::encoderCallbackInit(local.phases), + .has_globalStopConditions = true, + .globalStopConditions = GlobalStopConditionsDto { + .time = local.globalStopConditions.time, + .weight = local.globalStopConditions.weight, + .waterPumped = local.globalStopConditions.waterPumped, + }, + .waterTemperature = local.waterTemperature, + .has_recipe = true, + .recipe = BrewRecipeDto { + .coffeeIn = local.recipe.coffeeIn, + .coffeeOut = local.recipe.coffeeOut, + .ratio = local.recipe.ratio, + }, + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return ProfileDto{ + .name = NanoPb::Converter::StringConverter::decoderInit(local.name), + .phases = PhaseArrayConverter::decoderCallbackInit(local.phases), + }; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.globalStopConditions.time = proto.globalStopConditions.time; + local.globalStopConditions.weight = proto.globalStopConditions.weight; + local.globalStopConditions.waterPumped = proto.globalStopConditions.waterPumped; + local.waterTemperature = proto.waterTemperature; + local.recipe.coffeeIn = proto.recipe.coffeeIn; + local.recipe.coffeeOut = proto.recipe.coffeeOut; + local.recipe.ratio = proto.recipe.ratio; + return true; + }; +}; +#endif diff --git a/lib/Common/proto/proto_serializer.h b/lib/Common/proto/proto_serializer.h new file mode 100644 index 00000000..75a93835 --- /dev/null +++ b/lib/Common/proto/proto_serializer.h @@ -0,0 +1,37 @@ +#ifndef PROTO_SERIALIZER_H +#define PROTO_SERIALIZER_H + +#include +#include "profiling_phases.h" +#include "nanopb_cpp.h" + +/** + * These are 2 utility function to make it easier to use NanoPB in our project where we use + * Vectors to transmit or persist the serialized data (whereas NanoPB was designed to work with streams) + * + * The below two methods (serialize and deserialize) are just syntact sugar that serialize to/from a vector + * instead of forcing us to use NanoPb::StringInputStream / NanoPb::StringOutputStream + */ +namespace ProtoSerializer { + template + std::vector serialize(const typename MESSAGE_CONVERTER::LocalType& source) { + NanoPb::StringOutputStream outputStream; + bool check = NanoPb::encode(outputStream, source); + if (!check) { + return std::vector(); + } + NanoPb::BufferPtr str = outputStream.release(); + + std::vector result(str->begin(), str->end()); + return result; + } + + template + bool deserialize(std::vector& data, typename MESSAGE_CONVERTER::LocalType& target) { + // Invoke the decoding + bool result = NanoPb::decode(data.data(), data.size(), target); + return result; + } +} + +#endif diff --git a/lib/Common/proto/settings.proto b/lib/Common/proto/settings.proto new file mode 100644 index 00000000..9d519e85 --- /dev/null +++ b/lib/Common/proto/settings.proto @@ -0,0 +1,52 @@ +syntax = "proto3"; + +message BoilerSettingsDto { + uint32 steamSetPoint = 1; + uint32 offsetTemp = 2; + uint32 hpwr = 3; + uint32 mainDivider = 4; + uint32 brewDivider = 5; +} + +message SystemSettingsDto { + reserved 1; + float pumpFlowAtZero = 2; + int32 scalesF1 = 3 [deprecated = true]; + int32 scalesF2 = 4 [deprecated = true]; + uint32 lcdSleep = 5; + bool warmupState = 6; +} + +message BrewSettingsDto { + bool homeOnShotFinish = 1; + bool brewDeltaState = 2; + bool basketPrefill = 3; +} + +message LedSettingsDto { + message Color { + uint32 R = 1; + uint32 G = 2; + uint32 B = 3; + } + bool state = 1; + Color color = 2; + bool disco = 3; +} + +message ScalesSettingsDto { + bool forcePredictive = 1; + bool hwScalesEnabled = 2; + int32 hwScalesF1 = 3; + int32 hwScalesF2 = 4; + bool btScalesEnabled = 5; + bool btScalesAutoConnect = 6; +} + +message GaggiaSettingsDto { + BoilerSettingsDto boiler = 1; + SystemSettingsDto system = 2; + BrewSettingsDto brew = 3; + LedSettingsDto led = 4; + ScalesSettingsDto scales = 5; +} diff --git a/lib/Common/proto/settings_converters.h b/lib/Common/proto/settings_converters.h new file mode 100644 index 00000000..8cba6958 --- /dev/null +++ b/lib/Common/proto/settings_converters.h @@ -0,0 +1,186 @@ +#ifndef SETTINGS_CONVERTERS_H +#define SETTINGS_CONVERTERS_H + +#include "gaggia_settings.h" +#include "nanopb_cpp.h" +#include "settings.pb.h" + +using namespace NanoPb::Converter; + +/** + * The below classes are converters that map our own classes to/from protobuf objects. + * The protobuf objects are autogenerated at build time based on the *.proto definition files + * (see settings.proto and profile.proto). + */ + +class BoilerSettingsConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return BoilerSettingsDto{ + .steamSetPoint = local.steamSetPoint, + .offsetTemp = local.offsetTemp, + .hpwr = local.hpwr, + .mainDivider = local.mainDivider, + .brewDivider = local.brewDivider, + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return BoilerSettingsDto{}; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.steamSetPoint = proto.steamSetPoint; + local.offsetTemp = proto.offsetTemp; + local.hpwr = proto.hpwr; + local.mainDivider = proto.mainDivider; + local.brewDivider = proto.brewDivider; + return true; + }; +}; + +class LedSettingsConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return LedSettingsDto{ + .state = local.state, + .has_color = true, + .color = LedSettingsDto_Color { + .R = local.color.R, + .G = local.color.G, + .B = local.color.B, + }, + .disco = local.disco, + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return LedSettingsDto{}; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.state = proto.state; + local.color.R = proto.color.R; + local.color.G = proto.color.G; + local.color.B = proto.color.B; + local.disco = proto.disco; + return true; + }; +}; + +class SystemSettingsConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return SystemSettingsDto{ + .pumpFlowAtZero = local.pumpFlowAtZero, + .lcdSleep = local.lcdSleep, + .warmupState = local.warmupState, + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return SystemSettingsDto{}; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.pumpFlowAtZero = proto.pumpFlowAtZero; + local.warmupState = proto.warmupState; + local.lcdSleep = proto.lcdSleep; + return true; + }; +}; + +class BrewSettingsConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return BrewSettingsDto{ + .homeOnShotFinish = local.homeOnShotFinish, + .brewDeltaState = local.brewDeltaState, + .basketPrefill = local.basketPrefill, + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return BrewSettingsDto{}; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.homeOnShotFinish = proto.homeOnShotFinish; + local.brewDeltaState = proto.brewDeltaState; + local.basketPrefill = proto.basketPrefill; + + return true; + }; +}; + +class ScalesSettingsConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return ScalesSettingsDto{ + .forcePredictive = local.forcePredictive, + .hwScalesEnabled = local.hwScalesEnabled, + .hwScalesF1 = local.hwScalesF1, + .hwScalesF2 = local.hwScalesF2, + .btScalesEnabled = local.btScalesEnabled, + .btScalesAutoConnect = local.btScalesAutoConnect, + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return ScalesSettingsDto{}; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.forcePredictive = proto.forcePredictive; + local.hwScalesEnabled = proto.hwScalesEnabled; + local.hwScalesF1 = proto.hwScalesF1; + local.hwScalesF2 = proto.hwScalesF2; + local.btScalesEnabled = proto.btScalesEnabled; + local.btScalesAutoConnect = proto.btScalesAutoConnect; + + return true; + }; +}; + +class GaggiaSettingsConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return GaggiaSettingsDto{ + .has_boiler = true, + .boiler = BoilerSettingsConverter::encoderInit(local.boiler), + .has_system = true, + .system = SystemSettingsConverter::encoderInit(local.system), + .has_brew = true, + .brew = BrewSettingsConverter::encoderInit(local.brew), + .has_led = true, + .led = LedSettingsConverter::encoderInit(local.led), + .has_scales = true, + .scales = ScalesSettingsConverter::encoderInit(local.scales), + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return GaggiaSettingsDto{ + .boiler = BoilerSettingsConverter::decoderInit(local.boiler), + .system = SystemSettingsConverter::decoderInit(local.system), + .brew = BrewSettingsConverter::decoderInit(local.brew), + .led = LedSettingsConverter::decoderInit(local.led), + .scales = ScalesSettingsConverter::decoderInit(local.scales), + }; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + BoilerSettingsConverter::decoderApply(proto.boiler, local.boiler); + SystemSettingsConverter::decoderApply(proto.system, local.system); + BrewSettingsConverter::decoderApply(proto.brew, local.brew); + LedSettingsConverter::decoderApply(proto.led, local.led); + ScalesSettingsConverter::decoderApply(proto.scales, local.scales); + + if (proto.system.scalesF1 != 0 && local.scales.hwScalesF1 == 0) local.scales.hwScalesF1 = proto.system.scalesF1; + if (proto.system.scalesF2 != 0 && local.scales.hwScalesF2 == 0) local.scales.hwScalesF2 = proto.system.scalesF2; + + return true; + }; +}; + +#endif diff --git a/lib/Common/sensors_state.h b/lib/Common/sensors_state.h index ea44e029..d78d5f18 100644 --- a/lib/Common/sensors_state.h +++ b/lib/Common/sensors_state.h @@ -6,9 +6,6 @@ struct SensorState { bool brewSwitchState; bool steamSwitchState; bool hotWaterSwitchState; - bool isSteamForgottenON; - bool scalesPresent; - bool tarePending; float temperature; // °C /* calculated water temperature as wanted but not guaranteed due to boiler having a hard limit of 4ml/s heat capacity */ @@ -25,21 +22,36 @@ struct SensorState { float smoothedPumpFlow; float smoothedWeightFlow; float consideredFlow; - long pumpClicks; + float pumpClicks; uint16_t waterLvl; - bool tofReady; + float powerLineFrequency; }; struct SensorStateSnapshot { bool brewActive; bool steamActive; - bool scalesPresent; + bool hotWaterSwitchState; float temperature; + float waterTemperature; float pressure; float pumpFlow; float weightFlow; float weight; - uint16_t waterLvl; + uint16_t waterLevel; +}; + +struct ShotSnapshot { + uint32_t timeInShot; + float pressure; + float pumpFlow; + float weightFlow; + float temperature; + float shotWeight; + float waterPumped; + + float targetTemperature; + float targetPumpFlow; + float targetPressure; }; #endif diff --git a/lib/Common/system_state.h b/lib/Common/system_state.h index 0d133112..c06081ea 100644 --- a/lib/Common/system_state.h +++ b/lib/Common/system_state.h @@ -2,8 +2,43 @@ #ifndef SYSTEM_STATE_H #define SYSTEM_STATE_H +enum class OperationMode { + BREW_AUTO = 0, + BREW_MANUAL = 1, + FLUSH = 2, + DESCALE = 3, + STEAM = 4, + FLUSH_AUTO = 5, +}; + struct SystemState { - bool startupInitFinished; + bool startupInitFinished = false; + OperationMode operationMode = OperationMode::BREW_AUTO; + bool tofReady = false; + bool isSteamForgottenON = false; + bool scalesPresent = false; + uint32_t timeAlive = 0; // sec + bool tarePending = false; +}; + +struct UpdateSystemStateComand { + OperationMode operationMode; + bool tarePending; +}; + + +enum class DescalingState { + IDLE, + PHASE1, + PHASE2, + PHASE3, + FINISHED +}; + +struct DescalingProgress { + DescalingState state; + uint32_t time; + uint8_t progess; }; #endif diff --git a/lib/Common/utils.cpp b/lib/Common/utils.cpp index 12420fbd..a2fc15f3 100644 --- a/lib/Common/utils.cpp +++ b/lib/Common/utils.cpp @@ -6,6 +6,10 @@ #define M_PI 3.14159265359 #endif +const uint8_t BUFFER_SIZE = 5; +float avgBuffer[BUFFER_SIZE] = {0.f}; +int currentAvgIndex = 0; + float percentageWithTransition(float pct, TransitionCurve transition); float mapRange(float refNumber, float refStart, float refEnd, float targetStart, float targetEnd, int decimalPrecision, TransitionCurve transition) { @@ -53,3 +57,20 @@ float percentageWithTransition(float pct, TransitionCurve transition) { return easeInOut(pct); } } + +float getAverage(float value) { + avgBuffer[currentAvgIndex] = value; + currentAvgIndex = (currentAvgIndex + 1) % BUFFER_SIZE; + + float sum = 0; + for (int i = 0; i < BUFFER_SIZE; ++i) { + sum += avgBuffer[i]; + } + + return sum / BUFFER_SIZE; +} + +float truncate(float num, uint8_t precision) { + float factor = std::pow(10.0f, precision); + return roundf(num * factor) / factor; +} diff --git a/lib/Common/utils.h b/lib/Common/utils.h index f44c3fc1..7519d4e8 100644 --- a/lib/Common/utils.h +++ b/lib/Common/utils.h @@ -5,13 +5,15 @@ #include "Arduino.h" enum class TransitionCurve { - EASE_IN_OUT, - EASE_IN, - EASE_OUT, - LINEAR, - INSTANT, + EASE_IN_OUT = 0, + EASE_IN = 1, + EASE_OUT = 2, + LINEAR = 3, + INSTANT = 4, }; float mapRange(float sourceNumber, float fromA, float fromB, float toA, float toB, int decimalPrecision, TransitionCurve transition = TransitionCurve::LINEAR); +float getAverage(float value); +float truncate(float num, uint8_t precision); #endif diff --git a/nextion-basic-lcd.tft b/nextion-basic-lcd.tft deleted file mode 100644 index 59ebcf4c..00000000 Binary files a/nextion-basic-lcd.tft and /dev/null differ diff --git a/nextion-discovery-lcd.tft b/nextion-discovery-lcd.tft deleted file mode 100644 index cd111b72..00000000 Binary files a/nextion-discovery-lcd.tft and /dev/null differ diff --git a/platformio.ini b/platformio.ini index c1e1757d..c10e417a 100644 --- a/platformio.ini +++ b/platformio.ini @@ -56,17 +56,6 @@ build_flags = ${blackpill-core.build_flags} -DSINGLE_BOARD -[env:all-pcb-forced-predictive-stlink] -extends = blackpill-core -lib_deps = - ${blackpill-core.lib_deps} - adafruit/Adafruit MAX31855 library@1.3.0 -build_type = debug -build_flags = - ${blackpill-core.build_flags} - -DSINGLE_BOARD - -DFORCE_PREDICTIVE_SCALES=1 - [env:scales-calibration-stlink] extends = blackpill-core custom_src_dir = scales-calibration @@ -77,7 +66,7 @@ build_flags = [blackpill-core] framework = arduino -platform = ststm32@15.6.0 +platform = ststm32@16.0.0 board = blackpill_f411ce extra_scripts = pre:scripts/auto-version.py @@ -99,15 +88,19 @@ lib_deps = https://github.com/Zer0-bit/Adafruit_VL53L0X.git https://github.com/JChristensen/movingAvg.git https://github.com/SomeLucky1/PCA9632.git + nanopb/Nanopb@^0.4.7 + nanopb/Nanopb_Cpp@^0.1.10 build_flags = -DPIO_FRAMEWORK_ARDUINO_ENABLE_CDC -DUSBCON -DLOG_LEVEL=3 ; -DDEBUG_ENABLED ;if using a bigger boilr machine like Silvia set -DDREAM_STEAM_DISABLED in the extra_defines.ini + ; Define the tof placeholder and init with 0 ${extra.build_flags} -DPIO_FRAMEWORK_ARDUINO_STANDARD_LIB -DENABLE_HWSERIAL2 + -DPB_WITHOUT_64BIT -DPIN_SERIAL2_RX=PA3 -DPIN_SERIAL2_TX=PA2 -DPIN_SERIAL1_RX=PA10 @@ -115,7 +108,8 @@ build_flags = -DSERIAL_TX_BUFFER_SIZE=256 -DSERIAL_RX_BUFFER_SIZE=256 -DBEAUTIFY_GRAPH - -O3 + -O1 + -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 build_unflags = @@ -124,14 +118,76 @@ build_src_flags = -Wdouble-promotion -Wall check_src_filters = src +custom_nanopb_protos = + + [env:test] platform = native -build_flags = -std=gnu++17 +test_framework = unity +build_flags = + -std=gnu++2a + -DPB_WITHOUT_64BIT + -DRUNNING_TESTS lib_deps = - mocks ArduinoFake@^0.4.0 powerbroker2/SerialTransfer@^3.1.3 + nanopb/Nanopb@^0.4.7 + nanopb/Nanopb_Cpp@^0.1.10 + mocks + Common lib_extra_dirs = test-lib lib_compat_mode = off +custom_nanopb_protos = + + +build_unflags = + -std=gnu++11 + -std=c++11 + +[env:webserver] +platform = https://github.com/platformio/platform-espressif32.git +board = lolin_s3 +; board = esp32dev +framework = arduino +monitor_speed = 115200 +upload_protocol = esptool +board_build.filesystem = littlefs +custom_src_dir = webserver +build_src_filter = +<*> - + +build_flags = + -std=gnu++17 + -DBOARD_HAS_PSRAM + -DPB_WITHOUT_64BIT + -DASYNCWEBSERVER_REGEX=1 + -mfix-esp32-psram-cache-issue + ; -DRX1=16 ; for ESP32DEV + ; -DTX1=17 ; for ESP32DEV + -DRX1=18 ; for lolin_s3 board + -DTX1=17 ; for lolin_s3 board + -DLOG_LEVEL=3 + ${extra.build_flags} +check_src_filters = + webserver/src + +lib_deps = + https://github.com/me-no-dev/AsyncTCP.git + https://github.com/me-no-dev/ESPAsyncWebServer.git + https://github.com/kstam/esp-arduino-ble-scales.git + nanopb/Nanopb@^0.4.7 + nanopb/Nanopb_Cpp@^0.1.10 + ArduinoJSON@^6.21.2 + powerbroker2/SerialTransfer@^3.1.3 + Wire + ; ESPAsyncWebServer + +custom_nanopb_protos = + + + + + +extra_scripts = + pre:scripts/custom-src-dir.py + webserver/replace_fs.py + +build_unflags = + -std=gnu++11 diff --git a/scales-calibration/scales-calibrate-basic.tft b/scales-calibration/scales-calibrate-basic.tft deleted file mode 100644 index c022a56a..00000000 Binary files a/scales-calibration/scales-calibrate-basic.tft and /dev/null differ diff --git a/scales-calibration/scales-calibrate-discovery.tft b/scales-calibration/scales-calibrate-discovery.tft deleted file mode 100644 index e381ba58..00000000 Binary files a/scales-calibration/scales-calibrate-discovery.tft and /dev/null differ diff --git a/scripts/make-sd.sh b/scripts/make-sd.sh deleted file mode 100644 index c5054a19..00000000 --- a/scripts/make-sd.sh +++ /dev/null @@ -1,32 +0,0 @@ -# 09:32 15/03/2023 - change triggering comment -#!/bin/bash -set -e - -if [ "$#" -ne 1 ]; then -echo Please specify one argument: "basic" or "discovery" -exit 1 -fi - -if [[ $OSTYPE == 'darwin'* ]]; then -if ! command -v brew &> /dev/null; then -echo No homebrew detected. It is required to build an image Press Enter to install it, or press Control-C to abort. -read -s -n 1 -/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -fi - -if ! command -v mformat &> /dev/null; then -brew install mtools -fi -else -if ! command -v mformat &> /dev/null; then -echo No mtools detected. Please install it iin the usual way for your operating system. -exit 1 -fi -fi - -dd if=/dev/zero of=disk.img bs=1M count=33 -mformat -F -i disk.img :: -mcopy -i disk.img nextion-$1-lcd.tft :: - -echo -echo Created disk.img. Please flash this file to your SD card with a tool such as balenaEtcher. diff --git a/src/eeprom_data/default_profiles.h b/src/eeprom_data/default_profiles.h deleted file mode 100644 index dfb4c7eb..00000000 --- a/src/eeprom_data/default_profiles.h +++ /dev/null @@ -1,78 +0,0 @@ -/* 09:32 15/03/2023 - change triggering comment */ -#ifndef DEFAULT_PROFILES_H -#define DEFAULT_PROFILES_H - -#include -#include "eeprom_data.h" - -namespace { - typedef struct { - char name[24]; - bool preinfusionState; - bool preinfusionFlowState; - uint16_t preinfusionSec; - float preinfusionBar; - float preinfusionFlowVol; - uint16_t preinfusionFlowTime; - float preinfusionFlowPressureTarget; - float preinfusionPressureFlowTarget; - float preinfusionFilled; - bool preinfusionPressureAbove; - float preinfusionWeightAbove; - bool soakState; - uint16_t soakTimePressure; - uint16_t soakTimeFlow; - float soakKeepPressure; - float soakKeepFlow; - float soakBelowPressure; - float soakAbovePressure; - float soakAboveWeight; - uint16_t preinfusionRamp; - uint16_t preinfusionRampSlope; - bool tpState; - bool tpType; - float tpProfilingStart; - float tpProfilingFinish; - uint16_t tpProfilingHold; - float tpProfilingHoldLimit; - uint16_t tpProfilingSlope; - uint16_t tpProfilingSlopeShape; - float tpProfilingFlowRestriction; - float tfProfileStart; - float tfProfileEnd; - uint16_t tfProfileHold; - float tfProfileHoldLimit; - uint16_t tfProfileSlope; - uint16_t tfProfileSlopeShape; - float tfProfilingPressureRestriction; - bool profilingState; - bool mfProfileState; - float mpProfilingStart; - float mpProfilingFinish; - uint16_t mpProfilingSlope; - uint16_t mpProfilingSlopeShape; - float mpProfilingFlowRestriction; - float mfProfileStart; - float mfProfileEnd; - uint16_t mfProfileSlope; - uint16_t mfProfileSlopeShape; - float mfProfilingPressureRestriction; - /*-----------OTHER---------------------*/ - uint16_t setpoint; - bool stopOnWeightState; - float shotDose; - float shotStopOnCustomWeight; - uint16_t shotPreset; - } profileDefaults_t; - - // DONE:: Fully customised default profiles in line wiuth the names - const profileDefaults_t defaultsProfile[MAX_PROFILES] = { - {"IUIUIU Classic",/*pi*/true, true, 10, 2.f, 3.f, 20, 4.f, 3.f, 600, true, 4.f,/*sk*/true, 7, 30, 0.f, 0.f, 2.f, 0.f, 4.f, 5, 2,/*tp*/true, false, 7.5f, 6.f, 0, 3.f, 4, 0, 3.f, 0.f, 0.f, 0, 0.f, 0, 0, 0.f,/*pf*/true, true, 0.f, 0.f, 0, 0, 0.f, 2.5f, 1.f, 15, 0, 6.f,/*other*/ 93, true, 18.f, 0.f, 2}, // profile 0 - {"Londinium",/*pi*/true, true, 0, 0.f, 9.f, 10, 4.f, 0.f, 650, true, 0.f,/*sk*/true, 15, 10, 0.f, 0.f, 0.7f, 0.f, 0.f, 1, 2,/*tp*/true, false, 9.f, 9.f, 4, 3.f, 0, 0, 3.f, 0.f, 0.f, 0, 0.f, 0, 0, 0.f,/*pf*/true, false, 9.f, 3.f, 20, 0, 3.f, 0.f, 0.f, 0, 0, 0.f,/*other*/ 92, true, 20.f, 0.f, 2}, // profile 1 - {"Adaptive",/*pi*/true, true, 0, 0.f, 7.f, 20, 3.f, 0.f, 600, true, 0.f,/*sk*/true, 0, 6, 3.f, 0.f, 0.f, 0.f, 0.f, 0, 3,/*tp*/true, false, 0.f, 9.f, 0, 0.f, 6, 0, 0.f, 0.f, 2.f, 0, 9.f, 6, 0, 9.f,/*pf*/true, true, 0.f, 0.f, 30, 0, 3.f, 0.f, 2.5f, 30, 0, 9.f,/*other*/ 95, true, 18.f, 0.f, 2}, // profile 2 - {"Filter 2.1",/*pi*/true, true, 0, 0.f, 4.f, 15, 1.f, 0.f, 600, true, 0.f,/*sk*/true, 0, 90, 0.f, 0.2f, 0.f, 0.f, 45.f, 0, 0,/*tp*/true, true, 0.f, 0.f, 0, 0.f, 0, 0, 0.f, 0.2f, 3.f, 0, 0.f, 10, 0, 9.f,/*pf*/true, true, 0.f, 0.f, 0, 0, 0.f, 3.f, 3.f, 5, 0, 9.f,/*other*/ 89, true, 18.f, 0.f, 2}, // profile 3 - {"Blooming espresso",/*pi*/true, true, 0, 0.f, 4.f, 20, 7.f, 0.f, 650, true, 0.f,/*sk*/true, 0, 30, 0.f, 0.f, 0.6f, 0.f, 5.f, 5, 2,/*tp*/true, true, 0.f, 0.f, 0, 0.f, 0, 0, 0.f, 2.f, 2.f, 1, 9.f, 0, 4, 9.f,/*pf*/true, true, 0.f, 0.f, 0, 0, 0.f, 2.f, 2.f, 15, 0, 6.f,/*other*/ 93, true, 18.f, 0.f, 2} // profile 4 - }; -} - -#endif diff --git a/src/eeprom_data/eeprom_data.cpp b/src/eeprom_data/eeprom_data.cpp deleted file mode 100644 index dd40811d..00000000 --- a/src/eeprom_data/eeprom_data.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include "eeprom_data.h" -#include -#include "eeprom_metadata.h" -#include "default_profiles.h" -#include "legacy/eeprom_data_v4.h" -#include "legacy/eeprom_data_v5.h" -#include "legacy/eeprom_data_v6.h" -#include "legacy/eeprom_data_v7.h" -#include "legacy/eeprom_data_v8.h" -#include "legacy/eeprom_data_v9.h" -#include "legacy/eeprom_data_v10.h" -#include "legacy/eeprom_data_v11.h" -#include "../log.h" - -namespace { - - struct eepromMetadata_t eepromMetadata; - - eepromValues_t getEepromDefaults(void) { - eepromValues_t defaultData; - - // Profiles - defaultData.activeProfile = 0; - for (int i=0; i TP/PF - defaultData.profiles[i].preinfusionRamp = defaultsProfile[i].preinfusionRamp; - defaultData.profiles[i].preinfusionRampSlope = defaultsProfile[i].preinfusionRampSlope; - // Transition Profile - ramp&hold || advanced profiling - defaultData.profiles[i].tpState = defaultsProfile[i].tpState; - defaultData.profiles[i].tpType = defaultsProfile[i].tpType; // transtion profile type :pressure|flow: - defaultData.profiles[i].tpProfilingStart = defaultsProfile[i].tpProfilingStart; - defaultData.profiles[i].tpProfilingFinish = defaultsProfile[i].tpProfilingFinish; - defaultData.profiles[i].tpProfilingHold = defaultsProfile[i].tpProfilingHold; - defaultData.profiles[i].tpProfilingHoldLimit = defaultsProfile[i].tpProfilingHoldLimit; - defaultData.profiles[i].tpProfilingSlope = defaultsProfile[i].tpProfilingSlope; - defaultData.profiles[i].tpProfilingSlopeShape = defaultsProfile[i].tpProfilingSlopeShape; - defaultData.profiles[i].tpProfilingFlowRestriction = defaultsProfile[i].tpProfilingFlowRestriction; - defaultData.profiles[i].tfProfileStart = defaultsProfile[i].tfProfileStart; - defaultData.profiles[i].tfProfileEnd = defaultsProfile[i].tfProfileEnd; - defaultData.profiles[i].tfProfileHold = defaultsProfile[i].tfProfileHold; - defaultData.profiles[i].tfProfileHoldLimit = defaultsProfile[i].tfProfileHoldLimit; - defaultData.profiles[i].tfProfileSlope = defaultsProfile[i].tfProfileSlope; - defaultData.profiles[i].tfProfileSlopeShape = defaultsProfile[i].tfProfileSlopeShape; - defaultData.profiles[i].tfProfilingPressureRestriction = defaultsProfile[i].tfProfilingPressureRestriction; - // Profiling - defaultData.profiles[i].profilingState = defaultsProfile[i].profilingState; - defaultData.profiles[i].mfProfileState = defaultsProfile[i].mfProfileState; - defaultData.profiles[i].mpProfilingStart = defaultsProfile[i].mpProfilingStart; - defaultData.profiles[i].mpProfilingFinish = defaultsProfile[i].mpProfilingFinish; - defaultData.profiles[i].mpProfilingSlope = defaultsProfile[i].mpProfilingSlope; - defaultData.profiles[i].mpProfilingSlopeShape = defaultsProfile[i].mpProfilingSlopeShape; - defaultData.profiles[i].mpProfilingFlowRestriction = defaultsProfile[i].mpProfilingFlowRestriction; - defaultData.profiles[i].mfProfileStart = defaultsProfile[i].mfProfileStart; - defaultData.profiles[i].mfProfileEnd = defaultsProfile[i].mfProfileEnd; - defaultData.profiles[i].mfProfileSlope = defaultsProfile[i].mfProfileSlope; - defaultData.profiles[i].mfProfileSlopeShape = defaultsProfile[i].mfProfileSlopeShape; - defaultData.profiles[i].mfProfilingPressureRestriction = defaultsProfile[i].mfProfilingPressureRestriction; - /*-----------------------OTHER-----------------*/ - defaultData.profiles[i].setpoint = defaultsProfile[i].setpoint; - // Dose settings - defaultData.profiles[i].stopOnWeightState = defaultsProfile[i].stopOnWeightState; - defaultData.profiles[i].shotDose = defaultsProfile[i].shotDose; - defaultData.profiles[i].shotStopOnCustomWeight = defaultsProfile[i].shotStopOnCustomWeight; - defaultData.profiles[i].shotPreset = defaultsProfile[i].shotPreset; - } - // General brew settings - defaultData.homeOnShotFinish = false; - defaultData.brewDeltaState = true; - defaultData.basketPrefill = false; - // System settings - defaultData.steamSetPoint = 155; - defaultData.offsetTemp = 7; - defaultData.hpwr = 550; - defaultData.mainDivider = 5; - defaultData.brewDivider = 3; - defaultData.powerLineFrequency = 50; - defaultData.lcdSleep = 16; - defaultData.warmupState = false; - defaultData.scalesF1 = 3920; - defaultData.scalesF2 = 4210; - defaultData.pumpFlowAtZero = 0.2225f; - defaultData.ledState = true; - defaultData.ledDisco = true; - defaultData.ledR = 9; - defaultData.ledG = 0; - defaultData.ledB = 9; - - return defaultData; - } - - // kind of annoying, but allows reusing macro without messing up type safety - template - bool copy_t(T& target, T& source) { - target = source; - return true; - } - - bool loadCurrentEepromData EEPROM_METADATA_LOADER(EEPROM_DATA_VERSION, eepromMetadata_t, copy_t); - -} - - -bool eepromWrite(eepromValues_t eepromValuesNew) { - const char *errMsg = "Data out of range"; - /* Check various profile array values */ - for (int i=0; i 165 - || eepromValuesNew.mainDivider < 1 - || eepromValuesNew.brewDivider < 1 - || eepromValuesNew.pumpFlowAtZero < 0.210f - || eepromValuesNew.pumpFlowAtZero > 0.310f - || eepromValuesNew.scalesF1 < -20000 - || eepromValuesNew.scalesF2 > 20000) - { - LOG_ERROR(errMsg); - return false; - } - - /* Saving the values struct + validation and versioning metadata */ - eepromMetadata.timestamp = millis(); - eepromMetadata.version = EEPROM_DATA_VERSION; - eepromMetadata.values = eepromValuesNew; - eepromMetadata.versionTimestampXOR = eepromMetadata.timestamp ^ eepromMetadata.version; - EEPROM.put(0, eepromMetadata); - - return true; -} - -void eepromInit(void) { - // initialiaze defaults on memory - eepromMetadata.values = getEepromDefaults(); - - // read version - uint16_t version; - EEPROM.get(0, version); - - // load appropriate version (including current) - bool readSuccess = false; - - if (version < EEPROM_DATA_VERSION && legacyEepromDataLoaders[version] != nullptr) { - readSuccess = (*legacyEepromDataLoaders[version])(eepromMetadata.values); - } else { - readSuccess = loadCurrentEepromData(eepromMetadata.values); - } - - if (!readSuccess) { - LOG_ERROR("SECU_CHECK FAILED! Applying defaults! eepromMetadata.version=%d", version); - eepromMetadata.values = getEepromDefaults(); - } - - if (!readSuccess || version != EEPROM_DATA_VERSION) { - eepromWrite(eepromMetadata.values); - } -} - -struct eepromValues_t eepromGetCurrentValues(void) { - return eepromMetadata.values; -} -struct eepromValues_t eepromGetDefaultValues(void) { - return getEepromDefaults(); -} diff --git a/src/eeprom_data/eeprom_data.h b/src/eeprom_data/eeprom_data.h deleted file mode 100644 index 33c89ad8..00000000 --- a/src/eeprom_data/eeprom_data.h +++ /dev/null @@ -1,135 +0,0 @@ -/* 09:32 15/03/2023 - change triggering comment */ -#ifndef EEPROM_DATA_H -#define EEPROM_DATA_H - -#include - -/** -* NOTE: changing these constants requires: -* - bumping the version number! the serialized data will have -* a different layout to accommodate more array elements -* or longer strings -* - hardcoding the current value in the archived version and -* its upgrade function, to ensure loading that version -* deserializes correctly -*/ -#define MAX_PROFILES 5 -#define PROFILE_NAME_LENGTH 25 - -/** -* current data version definition below -* -* changing the schema requires bumping version number. this forces a schema update -* on boards with a lower version after flash. to make this graceful for users: -* - archive the current version in its corresponding header in legacy/ -* - in that header, make sure to register the upgrade function for -* the loader to pick it up -* - bump up the version in eeprom_metadata.h -* - make schema changes -* -* adding fields to data below shouldn't require changes to legacy upgrade -* functions - they just won't populate the new field, and it will use -* default value. -* -* removing fields might require deleting assignments in existing legacy -* functions that reference them. this will pop up as a compile time failure -*/ - -/** -* Version 10: -* - Add multiple brew profiles -*/ -struct eepromValues_t { - uint16_t steamSetPoint; - uint16_t offsetTemp; - uint16_t hpwr; - uint16_t mainDivider; - uint16_t brewDivider; - uint8_t activeProfile; - struct profile_t { - char name[PROFILE_NAME_LENGTH]; - // Preinfusion vars section - bool preinfusionState; - bool preinfusionFlowState; - uint16_t preinfusionSec; - float preinfusionBar; - float preinfusionFlowVol; - uint16_t preinfusionFlowTime; - float preinfusionFlowPressureTarget; - float preinfusionPressureFlowTarget; - float preinfusionFilled; - bool preinfusionPressureAbove; - float preinfusionWeightAbove; - // Soak vars section - bool soakState; - uint16_t soakTimePressure; - uint16_t soakTimeFlow; - float soakKeepPressure; - float soakKeepFlow; - float soakBelowPressure; - float soakAbovePressure; - float soakAboveWeight; - // PI -> PF ramp settings - uint16_t preinfusionRamp; - uint16_t preinfusionRampSlope; - // Profiling vars section - bool tpState; - bool tpType; - float tpProfilingStart; - float tpProfilingFinish; - uint16_t tpProfilingHold; - float tpProfilingHoldLimit; - uint16_t tpProfilingSlope; - uint16_t tpProfilingSlopeShape; - float tpProfilingFlowRestriction; - float tfProfileStart; - float tfProfileEnd; - uint16_t tfProfileHold; - float tfProfileHoldLimit; - uint16_t tfProfileSlope; - uint16_t tfProfileSlopeShape; - float tfProfilingPressureRestriction; - bool profilingState; - bool mfProfileState; - float mpProfilingStart; - float mpProfilingFinish; - uint16_t mpProfilingSlope; - uint16_t mpProfilingSlopeShape; - float mpProfilingFlowRestriction; - float mfProfileStart; - float mfProfileEnd; - uint16_t mfProfileSlope; - uint16_t mfProfileSlopeShape; - float mfProfilingPressureRestriction; - /*-----------OTHER---------------------*/ - uint16_t setpoint; - bool stopOnWeightState; - float shotDose; - float shotStopOnCustomWeight; - uint16_t shotPreset; - } profiles[MAX_PROFILES]; - // Settings vars section - uint16_t powerLineFrequency; - uint16_t lcdSleep; - bool warmupState; - bool homeOnShotFinish; - bool brewDeltaState; - bool basketPrefill; - int scalesF1; - int scalesF2; - float pumpFlowAtZero; - bool ledState; - bool ledDisco; - uint8_t ledR; - uint8_t ledG; - uint8_t ledB; -}; - -void eepromInit(void); -bool eepromWrite(eepromValues_t); -struct eepromValues_t eepromGetDefaultValues(void); -struct eepromValues_t eepromGetCurrentValues(void); - -#define ACTIVE_PROFILE(eepromValues) eepromValues.profiles[eepromValues.activeProfile] - -#endif diff --git a/src/eeprom_data/eeprom_metadata.h b/src/eeprom_data/eeprom_metadata.h deleted file mode 100644 index 67e0e48b..00000000 --- a/src/eeprom_data/eeprom_metadata.h +++ /dev/null @@ -1,46 +0,0 @@ -/* 09:32 15/03/2023 - change triggering comment */ -#ifndef EEPROM_METADATA_H -#define EEPROM_METADATA_H - -#include - -#define EEPROM_DATA_VERSION 12 - -#define EEPROM_METADATA_T(__eepromValues_tName) \ - { \ - uint16_t version; \ - unsigned long timestamp; \ - struct __eepromValues_tName values; \ - uint32_t versionTimestampXOR; \ - } - -struct eepromMetadata_t EEPROM_METADATA_T(eepromValues_t); - -#define EEPROM_METADATA_LOADER(__eepromDataVersion, __eepromMetadata_tName, __upgradeSchema_fName) \ - (eepromValues_t & targetValues) \ - { \ - __eepromMetadata_tName eepromMetadata; \ - EEPROM.get(0, eepromMetadata); \ - uint32_t XOR = eepromMetadata.timestamp ^ eepromMetadata.version; \ - if (eepromMetadata.version != __eepromDataVersion || eepromMetadata.versionTimestampXOR != XOR) \ - { \ - return false; \ - } \ - return __upgradeSchema_fName(targetValues, eepromMetadata.values); \ - } - -bool (*legacyEepromDataLoaders[EEPROM_DATA_VERSION])(eepromValues_t &); - -#define REGISTER_LEGACY_EEPROM_DATA(version, eepromData_tName, upgradeSchema_fName) \ - /* declare & define metadata */ \ - struct _eepromMetadata_t_v##version EEPROM_METADATA_T(eepromData_tName); \ - /* define loader function */ \ - static bool _loadEepromMetadata_v##version \ - EEPROM_METADATA_LOADER(version, _eepromMetadata_t_v##version, upgradeSchema_fName); \ - /* register loader ptr in array */ \ - void __attribute__((constructor)) _registerEepromMetadataLoader_v##version() \ - { \ - legacyEepromDataLoaders[version] = &_loadEepromMetadata_v##version; \ - } - -#endif diff --git a/src/eeprom_data/legacy/eeprom_data_v10.h b/src/eeprom_data/legacy/eeprom_data_v10.h deleted file mode 100644 index 29037fb1..00000000 --- a/src/eeprom_data/legacy/eeprom_data_v10.h +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef EEPROM_DATA_V10_H -#define EEPROM_DATA_V10_H - -#include "../eeprom_data.h" -#include "../eeprom_metadata.h" - -/** -* Version 9: -* - Multiple changes to the brew profile settings to support more customization of phases -* - Re-organize fields -*/ - -struct eepromValues_t_v10 { - uint16_t setpoint; - uint16_t steamSetPoint; - uint16_t offsetTemp; - uint16_t hpwr; - uint16_t mainDivider; - uint16_t brewDivider; - // Preinfusion vars section - bool preinfusionState; - bool preinfusionFlowState; - uint16_t preinfusionSec; - float preinfusionBar; - float preinfusionFlowVol; - uint16_t preinfusionFlowTime; - float preinfusionFlowPressureTarget; - float preinfusionPressureFlowTarget; - float preinfusionFilled; - bool preinfusionPressureAbove; - float preinfusionWeightAbove; - // Soak vars section - bool soakState; - uint16_t soakTimePressure; - uint16_t soakTimeFlow; - float soakKeepPressure; - float soakKeepFlow; - float soakBelowPressure; - float soakAbovePressure; - float soakAboveWeight; - // PI -> PF ramp settings - uint16_t preinfusionRamp; - uint16_t preinfusionRampSlope; - // Profiling vars section - bool profilingState; - bool flowProfileState; - float pressureProfilingStart; - float pressureProfilingFinish; - uint16_t pressureProfilingHold; - float pressureProfilingHoldLimit; - uint16_t pressureProfilingSlope; - uint16_t pressureProfilingSlopeShape; - float pressureProfilingFlowRestriction; - float flowProfileStart; - float flowProfileEnd; - uint16_t flowProfileHold; - float flowProfileHoldLimit; - uint16_t flowProfileSlope; - uint16_t flowProfileSlopeShape; - float flowProfilingPressureRestriction; - // Settings vars section - uint16_t powerLineFrequency; - uint16_t lcdSleep; - bool warmupState; - bool homeOnShotFinish; - bool brewDeltaState; - bool basketPrefill; - int scalesF1; - int scalesF2; - float pumpFlowAtZero; - bool stopOnWeightState; - float shotDose; - float shotStopOnCustomWeight; - uint16_t shotPreset; -}; - -static bool upgradeSchema_v10(eepromValues_t &targetValues, eepromValues_t_v10 &loadedValues) { - ACTIVE_PROFILE(targetValues).setpoint = loadedValues.setpoint; - targetValues.steamSetPoint = loadedValues.steamSetPoint; - targetValues.offsetTemp = loadedValues.offsetTemp; - targetValues.hpwr = loadedValues.hpwr; - targetValues.mainDivider = loadedValues.mainDivider; - targetValues.brewDivider = loadedValues.brewDivider; - ACTIVE_PROFILE(targetValues).preinfusionState = loadedValues.preinfusionState; - ACTIVE_PROFILE(targetValues).preinfusionFlowState = loadedValues.preinfusionFlowState; - ACTIVE_PROFILE(targetValues).preinfusionSec = loadedValues.preinfusionSec; - ACTIVE_PROFILE(targetValues).preinfusionBar = loadedValues.preinfusionBar; - ACTIVE_PROFILE(targetValues).preinfusionFlowVol = loadedValues.preinfusionFlowVol; - ACTIVE_PROFILE(targetValues).preinfusionFlowTime = loadedValues.preinfusionFlowTime; - ACTIVE_PROFILE(targetValues).preinfusionFlowPressureTarget = loadedValues.preinfusionFlowPressureTarget; - ACTIVE_PROFILE(targetValues).preinfusionPressureFlowTarget = loadedValues.preinfusionPressureFlowTarget; - ACTIVE_PROFILE(targetValues).preinfusionFilled = loadedValues.preinfusionFilled; - ACTIVE_PROFILE(targetValues).preinfusionPressureAbove = loadedValues.preinfusionPressureAbove; - ACTIVE_PROFILE(targetValues).preinfusionWeightAbove = loadedValues.preinfusionWeightAbove; - ACTIVE_PROFILE(targetValues).soakState = loadedValues.soakState; - ACTIVE_PROFILE(targetValues).soakTimePressure = loadedValues.soakTimePressure; - ACTIVE_PROFILE(targetValues).soakTimeFlow = loadedValues.soakTimeFlow; - ACTIVE_PROFILE(targetValues).soakKeepPressure = loadedValues.soakKeepPressure; - ACTIVE_PROFILE(targetValues).soakKeepFlow = loadedValues.soakKeepFlow; - ACTIVE_PROFILE(targetValues).soakBelowPressure = loadedValues.soakBelowPressure; - ACTIVE_PROFILE(targetValues).soakAbovePressure = loadedValues.soakAbovePressure; - ACTIVE_PROFILE(targetValues).soakAboveWeight = loadedValues.soakAboveWeight; - ACTIVE_PROFILE(targetValues).preinfusionRamp = loadedValues.preinfusionRamp; - ACTIVE_PROFILE(targetValues).preinfusionRampSlope = loadedValues.preinfusionRampSlope; - ACTIVE_PROFILE(targetValues).profilingState = loadedValues.profilingState; - ACTIVE_PROFILE(targetValues).mfProfileState = loadedValues.flowProfileState; - ACTIVE_PROFILE(targetValues).mpProfilingStart = loadedValues.pressureProfilingStart; - ACTIVE_PROFILE(targetValues).mpProfilingFinish = loadedValues.pressureProfilingFinish; - ACTIVE_PROFILE(targetValues).mpProfilingSlope = loadedValues.pressureProfilingSlope; - ACTIVE_PROFILE(targetValues).mpProfilingSlopeShape = loadedValues.pressureProfilingSlopeShape; - ACTIVE_PROFILE(targetValues).mpProfilingFlowRestriction = loadedValues.pressureProfilingFlowRestriction; - ACTIVE_PROFILE(targetValues).mfProfileStart = loadedValues.flowProfileStart; - ACTIVE_PROFILE(targetValues).mfProfileEnd = loadedValues.flowProfileEnd; - ACTIVE_PROFILE(targetValues).mfProfileSlope = loadedValues.flowProfileSlope; - ACTIVE_PROFILE(targetValues).mfProfileSlopeShape = loadedValues.flowProfileSlopeShape; - ACTIVE_PROFILE(targetValues).mfProfilingPressureRestriction = loadedValues.flowProfilingPressureRestriction; - targetValues.powerLineFrequency = loadedValues.powerLineFrequency; - targetValues.lcdSleep = loadedValues.lcdSleep; - targetValues.warmupState = loadedValues.warmupState; - targetValues.homeOnShotFinish = loadedValues.homeOnShotFinish; - targetValues.brewDeltaState = loadedValues.brewDeltaState; - targetValues.basketPrefill = loadedValues.basketPrefill; - targetValues.scalesF1 = loadedValues.scalesF1; - targetValues.scalesF2 = loadedValues.scalesF2; - targetValues.pumpFlowAtZero = 0.2355f; - ACTIVE_PROFILE(targetValues).stopOnWeightState = loadedValues.stopOnWeightState; - ACTIVE_PROFILE(targetValues).shotDose = loadedValues.shotDose; - ACTIVE_PROFILE(targetValues).shotStopOnCustomWeight = loadedValues.shotStopOnCustomWeight; - ACTIVE_PROFILE(targetValues).shotPreset = loadedValues.shotPreset; - return true; -} - -REGISTER_LEGACY_EEPROM_DATA(10, eepromValues_t_v10, upgradeSchema_v10) - -#endif diff --git a/src/eeprom_data/legacy/eeprom_data_v11.h b/src/eeprom_data/legacy/eeprom_data_v11.h deleted file mode 100644 index 704a29f8..00000000 --- a/src/eeprom_data/legacy/eeprom_data_v11.h +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef EEPROM_DATA_V11_H -#define EEPROM_DATA_V11_H - -#include "../eeprom_data.h" -#include "../eeprom_metadata.h" - -/** -* Version 9: -* - Multiple changes to the brew profile settings to support more customization of phases -* - Re-organize fields -*/ - -struct eepromValues_t_v11 { - uint16_t setpoint; - uint16_t steamSetPoint; - uint16_t offsetTemp; - uint16_t hpwr; - uint16_t mainDivider; - uint16_t brewDivider; - // Preinfusion vars section - bool preinfusionState; - bool preinfusionFlowState; - uint16_t preinfusionSec; - float preinfusionBar; - float preinfusionFlowVol; - uint16_t preinfusionFlowTime; - float preinfusionFlowPressureTarget; - float preinfusionPressureFlowTarget; - float preinfusionFilled; - bool preinfusionPressureAbove; - float preinfusionWeightAbove; - // Soak vars section - bool soakState; - uint16_t soakTimePressure; - uint16_t soakTimeFlow; - float soakKeepPressure; - float soakKeepFlow; - float soakBelowPressure; - float soakAbovePressure; - float soakAboveWeight; - // PI -> PF ramp settings - uint16_t preinfusionRamp; - uint16_t preinfusionRampSlope; - // Profiling vars section - bool profilingState; - bool flowProfileState; - float pressureProfilingStart; - float pressureProfilingFinish; - uint16_t pressureProfilingHold; - float pressureProfilingHoldLimit; - uint16_t pressureProfilingSlope; - uint16_t pressureProfilingSlopeShape; - float pressureProfilingFlowRestriction; - float flowProfileStart; - float flowProfileEnd; - uint16_t flowProfileHold; - float flowProfileHoldLimit; - uint16_t flowProfileSlope; - uint16_t flowProfileSlopeShape; - float flowProfilingPressureRestriction; - // Settings vars section - uint16_t powerLineFrequency; - uint16_t lcdSleep; - bool warmupState; - bool homeOnShotFinish; - bool brewDeltaState; - bool basketPrefill; - int scalesF1; - int scalesF2; - float pumpFlowAtZero; - bool stopOnWeightState; - float shotDose; - float shotStopOnCustomWeight; - uint16_t shotPreset; -}; - -static bool upgradeSchema_v11(eepromValues_t &targetValues, eepromValues_t_v11 &loadedValues) { - ACTIVE_PROFILE(targetValues).setpoint = loadedValues.setpoint; - targetValues.steamSetPoint = loadedValues.steamSetPoint; - targetValues.offsetTemp = loadedValues.offsetTemp; - targetValues.hpwr = loadedValues.hpwr; - targetValues.mainDivider = loadedValues.mainDivider; - targetValues.brewDivider = loadedValues.brewDivider; - ACTIVE_PROFILE(targetValues).preinfusionState = loadedValues.preinfusionState; - ACTIVE_PROFILE(targetValues).preinfusionFlowState = loadedValues.preinfusionFlowState; - ACTIVE_PROFILE(targetValues).preinfusionSec = loadedValues.preinfusionSec; - ACTIVE_PROFILE(targetValues).preinfusionBar = loadedValues.preinfusionBar; - ACTIVE_PROFILE(targetValues).preinfusionFlowVol = loadedValues.preinfusionFlowVol; - ACTIVE_PROFILE(targetValues).preinfusionFlowTime = loadedValues.preinfusionFlowTime; - ACTIVE_PROFILE(targetValues).preinfusionFlowPressureTarget = loadedValues.preinfusionFlowPressureTarget; - ACTIVE_PROFILE(targetValues).preinfusionPressureFlowTarget = loadedValues.preinfusionPressureFlowTarget; - ACTIVE_PROFILE(targetValues).preinfusionFilled = loadedValues.preinfusionFilled; - ACTIVE_PROFILE(targetValues).preinfusionPressureAbove = loadedValues.preinfusionPressureAbove; - ACTIVE_PROFILE(targetValues).preinfusionWeightAbove = loadedValues.preinfusionWeightAbove; - ACTIVE_PROFILE(targetValues).soakState = loadedValues.soakState; - ACTIVE_PROFILE(targetValues).soakTimePressure = loadedValues.soakTimePressure; - ACTIVE_PROFILE(targetValues).soakTimeFlow = loadedValues.soakTimeFlow; - ACTIVE_PROFILE(targetValues).soakKeepPressure = loadedValues.soakKeepPressure; - ACTIVE_PROFILE(targetValues).soakKeepFlow = loadedValues.soakKeepFlow; - ACTIVE_PROFILE(targetValues).soakBelowPressure = loadedValues.soakBelowPressure; - ACTIVE_PROFILE(targetValues).soakAbovePressure = loadedValues.soakAbovePressure; - ACTIVE_PROFILE(targetValues).soakAboveWeight = loadedValues.soakAboveWeight; - ACTIVE_PROFILE(targetValues).preinfusionRamp = loadedValues.preinfusionRamp; - ACTIVE_PROFILE(targetValues).preinfusionRampSlope = loadedValues.preinfusionRampSlope; - ACTIVE_PROFILE(targetValues).profilingState = loadedValues.profilingState; - ACTIVE_PROFILE(targetValues).mfProfileState = loadedValues.flowProfileState; - ACTIVE_PROFILE(targetValues).mpProfilingStart = loadedValues.pressureProfilingStart; - ACTIVE_PROFILE(targetValues).mpProfilingFinish = loadedValues.pressureProfilingFinish; - ACTIVE_PROFILE(targetValues).mpProfilingSlope = loadedValues.pressureProfilingSlope; - ACTIVE_PROFILE(targetValues).mpProfilingSlopeShape = loadedValues.pressureProfilingSlopeShape; - ACTIVE_PROFILE(targetValues).mpProfilingFlowRestriction = loadedValues.pressureProfilingFlowRestriction; - ACTIVE_PROFILE(targetValues).mfProfileStart = loadedValues.flowProfileStart; - ACTIVE_PROFILE(targetValues).mfProfileEnd = loadedValues.flowProfileEnd; - ACTIVE_PROFILE(targetValues).mfProfileSlope = loadedValues.flowProfileSlope; - ACTIVE_PROFILE(targetValues).mfProfileSlopeShape = loadedValues.flowProfileSlopeShape; - ACTIVE_PROFILE(targetValues).mfProfilingPressureRestriction = loadedValues.flowProfilingPressureRestriction; - targetValues.powerLineFrequency = loadedValues.powerLineFrequency; - targetValues.lcdSleep = loadedValues.lcdSleep; - targetValues.warmupState = loadedValues.warmupState; - targetValues.homeOnShotFinish = loadedValues.homeOnShotFinish; - targetValues.brewDeltaState = loadedValues.brewDeltaState; - targetValues.basketPrefill = loadedValues.basketPrefill; - targetValues.scalesF1 = loadedValues.scalesF1; - targetValues.scalesF2 = loadedValues.scalesF2; - targetValues.pumpFlowAtZero = 0.2309f; - ACTIVE_PROFILE(targetValues).stopOnWeightState = loadedValues.stopOnWeightState; - ACTIVE_PROFILE(targetValues).shotDose = loadedValues.shotDose; - ACTIVE_PROFILE(targetValues).shotStopOnCustomWeight = loadedValues.shotStopOnCustomWeight; - ACTIVE_PROFILE(targetValues).shotPreset = loadedValues.shotPreset; - return true; -} - -REGISTER_LEGACY_EEPROM_DATA(11, eepromValues_t_v11, upgradeSchema_v11) - -#endif diff --git a/src/eeprom_data/legacy/eeprom_data_v4.h b/src/eeprom_data/legacy/eeprom_data_v4.h deleted file mode 100644 index 3ab13386..00000000 --- a/src/eeprom_data/legacy/eeprom_data_v4.h +++ /dev/null @@ -1,90 +0,0 @@ -/* 09:32 15/03/2023 - change triggering comment */ -#ifndef EEPROM_DATA_V4_H -#define EEPROM_DATA_V4_H - -#include "../eeprom_data.h" -#include "../eeprom_metadata.h" - -/** -* Version 4: -* - added switchPhaseOnThreshold -*/ -struct eepromValues_t_v4 { - uint16_t setpoint; - uint16_t offsetTemp; - uint16_t hpwr; - uint16_t mainDivider; - uint16_t brewDivider; - uint16_t pressureProfilingStart; - uint16_t pressureProfilingFinish; - uint16_t pressureProfilingHold; - uint16_t pressureProfilingLength; - bool pressureProfilingState; - bool preinfusionState; - uint16_t preinfusionSec; - uint16_t preinfusionBar; - uint16_t preinfusionSoak; - uint16_t preinfusionRamp; - bool preinfusionFlowState; - float preinfusionFlowVol; - uint16_t preinfusionFlowTime; - uint16_t preinfusionFlowSoakTime; - uint16_t preinfusionFlowPressureTarget; - bool flowProfileState; - float flowProfileStart; - float flowProfileEnd; - uint16_t flowProfilePressureTarget; - uint16_t flowProfileCurveSpeed; - uint16_t powerLineFrequency; - uint16_t lcdSleep; - bool warmupState; - bool homeOnShotFinish; - bool graphBrew; - bool brewDeltaState; - bool switchPhaseOnThreshold; - int scalesF1; - int scalesF2; - float pumpFlowAtZero; - bool stopOnWeightState; - float shotDose; - float shotStopOnCustomWeight; - uint16_t shotPreset; -}; - -static bool upgradeSchema_v4(eepromValues_t &targetValues, eepromValues_t_v4 &loadedValues) { - ACTIVE_PROFILE(targetValues).setpoint = loadedValues.setpoint; - targetValues.offsetTemp = loadedValues.offsetTemp; - targetValues.hpwr = loadedValues.hpwr; - targetValues.mainDivider = loadedValues.mainDivider; - targetValues.brewDivider = loadedValues.brewDivider; - ACTIVE_PROFILE(targetValues).mpProfilingStart = (float) loadedValues.pressureProfilingStart; - ACTIVE_PROFILE(targetValues).mpProfilingFinish = (float) loadedValues.pressureProfilingFinish; - ACTIVE_PROFILE(targetValues).preinfusionState = loadedValues.preinfusionState; - ACTIVE_PROFILE(targetValues).preinfusionSec = loadedValues.preinfusionSec; - ACTIVE_PROFILE(targetValues).preinfusionBar = (float) loadedValues.preinfusionBar; - ACTIVE_PROFILE(targetValues).preinfusionRamp = loadedValues.preinfusionRamp; - ACTIVE_PROFILE(targetValues).preinfusionFlowState = loadedValues.preinfusionFlowState; - ACTIVE_PROFILE(targetValues).preinfusionFlowVol = loadedValues.preinfusionFlowVol; - ACTIVE_PROFILE(targetValues).preinfusionFlowTime = loadedValues.preinfusionFlowTime; - ACTIVE_PROFILE(targetValues).preinfusionFlowPressureTarget = (float) loadedValues.preinfusionFlowPressureTarget; - ACTIVE_PROFILE(targetValues).mfProfileState = loadedValues.flowProfileState; - ACTIVE_PROFILE(targetValues).mfProfileStart = loadedValues.flowProfileStart; - ACTIVE_PROFILE(targetValues).mfProfileEnd = loadedValues.flowProfileEnd; - targetValues.powerLineFrequency = loadedValues.powerLineFrequency; - targetValues.lcdSleep = loadedValues.lcdSleep; - targetValues.warmupState = loadedValues.warmupState; - targetValues.homeOnShotFinish = loadedValues.homeOnShotFinish; - targetValues.brewDeltaState = loadedValues.brewDeltaState; - targetValues.scalesF1 = loadedValues.scalesF1; - targetValues.scalesF2 = loadedValues.scalesF2; - targetValues.pumpFlowAtZero = loadedValues.pumpFlowAtZero; - ACTIVE_PROFILE(targetValues).stopOnWeightState = loadedValues.stopOnWeightState; - ACTIVE_PROFILE(targetValues).shotDose = loadedValues.shotDose; - ACTIVE_PROFILE(targetValues).shotStopOnCustomWeight = loadedValues.shotStopOnCustomWeight; - ACTIVE_PROFILE(targetValues).shotPreset = loadedValues.shotPreset; - return true; -} - -REGISTER_LEGACY_EEPROM_DATA(4, eepromValues_t_v4, upgradeSchema_v4) - -#endif diff --git a/src/eeprom_data/legacy/eeprom_data_v5.h b/src/eeprom_data/legacy/eeprom_data_v5.h deleted file mode 100644 index e7695090..00000000 --- a/src/eeprom_data/legacy/eeprom_data_v5.h +++ /dev/null @@ -1,95 +0,0 @@ -/* 09:32 15/03/2023 - change triggering comment */ -#ifndef EEPROM_DATA_V5_H -#define EEPROM_DATA_V5_H - -#include "../eeprom_data.h" -#include "../eeprom_metadata.h" - -/** -* Version 5: -* - added steamSetPoint -* - added basketPrefill -*/ -struct eepromValues_t_v5 { - uint16_t setpoint; - uint16_t steamSetPoint; - uint16_t offsetTemp; - uint16_t hpwr; - uint16_t mainDivider; - uint16_t brewDivider; - uint16_t pressureProfilingStart; - uint16_t pressureProfilingFinish; - uint16_t pressureProfilingHold; - uint16_t pressureProfilingLength; - bool pressureProfilingState; - bool preinfusionState; - uint16_t preinfusionSec; - uint16_t preinfusionBar; - uint16_t preinfusionSoak; - uint16_t preinfusionRamp; - bool preinfusionFlowState; - float preinfusionFlowVol; - uint16_t preinfusionFlowTime; - uint16_t preinfusionFlowSoakTime; - uint16_t preinfusionFlowPressureTarget; - bool flowProfileState; - float flowProfileStart; - float flowProfileEnd; - uint16_t flowProfilePressureTarget; - uint16_t flowProfileCurveSpeed; - uint16_t powerLineFrequency; - uint16_t lcdSleep; - bool warmupState; - bool homeOnShotFinish; - bool graphBrew; - bool brewDeltaState; - bool switchPhaseOnThreshold; - bool basketPrefill; - int scalesF1; - int scalesF2; - float pumpFlowAtZero; - bool stopOnWeightState; - float shotDose; - float shotStopOnCustomWeight; - uint16_t shotPreset; -}; - -static bool upgradeSchema_v5(eepromValues_t &targetValues, eepromValues_t_v5 &loadedValues) { - ACTIVE_PROFILE(targetValues).setpoint = loadedValues.setpoint; - targetValues.steamSetPoint = loadedValues.steamSetPoint; - targetValues.offsetTemp = loadedValues.offsetTemp; - targetValues.hpwr = loadedValues.hpwr; - targetValues.mainDivider = loadedValues.mainDivider; - targetValues.brewDivider = loadedValues.brewDivider; - ACTIVE_PROFILE(targetValues).mpProfilingStart = (float) loadedValues.pressureProfilingStart; - ACTIVE_PROFILE(targetValues).mpProfilingFinish = (float) loadedValues.pressureProfilingFinish; - ACTIVE_PROFILE(targetValues).preinfusionState = loadedValues.preinfusionState; - ACTIVE_PROFILE(targetValues).preinfusionSec = loadedValues.preinfusionSec; - ACTIVE_PROFILE(targetValues).preinfusionBar = (float) loadedValues.preinfusionBar; - ACTIVE_PROFILE(targetValues).preinfusionRamp = loadedValues.preinfusionRamp; - ACTIVE_PROFILE(targetValues).preinfusionFlowState = loadedValues.preinfusionFlowState; - ACTIVE_PROFILE(targetValues).preinfusionFlowVol = loadedValues.preinfusionFlowVol; - ACTIVE_PROFILE(targetValues).preinfusionFlowTime = loadedValues.preinfusionFlowTime; - ACTIVE_PROFILE(targetValues).preinfusionFlowPressureTarget = (float) loadedValues.preinfusionFlowPressureTarget; - ACTIVE_PROFILE(targetValues).mfProfileState = loadedValues.flowProfileState; - ACTIVE_PROFILE(targetValues).mfProfileStart = loadedValues.flowProfileStart; - ACTIVE_PROFILE(targetValues).mfProfileEnd = loadedValues.flowProfileEnd; - targetValues.powerLineFrequency = loadedValues.powerLineFrequency; - targetValues.lcdSleep = loadedValues.lcdSleep; - targetValues.warmupState = loadedValues.warmupState; - targetValues.homeOnShotFinish = loadedValues.homeOnShotFinish; - targetValues.brewDeltaState = loadedValues.brewDeltaState; - targetValues.basketPrefill = loadedValues.basketPrefill; - targetValues.scalesF1 = loadedValues.scalesF1; - targetValues.scalesF2 = loadedValues.scalesF2; - targetValues.pumpFlowAtZero = loadedValues.pumpFlowAtZero; - ACTIVE_PROFILE(targetValues).stopOnWeightState = loadedValues.stopOnWeightState; - ACTIVE_PROFILE(targetValues).shotDose = loadedValues.shotDose; - ACTIVE_PROFILE(targetValues).shotStopOnCustomWeight = loadedValues.shotStopOnCustomWeight; - ACTIVE_PROFILE(targetValues).shotPreset = loadedValues.shotPreset; - return true; -} - -REGISTER_LEGACY_EEPROM_DATA(5, eepromValues_t_v5, upgradeSchema_v5) - -#endif diff --git a/src/eeprom_data/legacy/eeprom_data_v6.h b/src/eeprom_data/legacy/eeprom_data_v6.h deleted file mode 100644 index ae7efb9e..00000000 --- a/src/eeprom_data/legacy/eeprom_data_v6.h +++ /dev/null @@ -1,118 +0,0 @@ -/* 09:32 15/03/2023 - change triggering comment */ -#ifndef EEPROM_DATA_V6_H -#define EEPROM_DATA_V6_H - -#include "../eeprom_data.h" -#include "../eeprom_metadata.h" - -/** -* current data version definition below -* -* changing the schema requires bumping version number. this forces a schema update -* on boards with a lower version after flash. to make this graceful for users: -* - archive the current version in its corresponding header in legacy/ -* - in that header, make sure to register the upgrade function for -* the loader to pick it up -* - bump up the version in eeprom_metadata.h -* - make schema changes -* -* adding fields to data below shouldn't require changes to legacy upgrade -* functions - they just won't populate the new field, and it will use -* default value. -* -* removing fields might require deleting assignments in existing legacy -* functions that reference them. this will pop up as a compile time failure -*/ - -/** -* Version 6: -* - Pressure as float: -* - pressureProfilingStart -* - pressureProfilingFinish -* - preinfusionBar -* - preinfusionFlowPressureTarget -* - flowProfilePressureTarget -*/ -struct eepromValues_t_v6 { - uint16_t setpoint; - uint16_t steamSetPoint; - uint16_t offsetTemp; - uint16_t hpwr; - uint16_t mainDivider; - uint16_t brewDivider; - float pressureProfilingStart; - float pressureProfilingFinish; - uint16_t pressureProfilingHold; - uint16_t pressureProfilingLength; - bool pressureProfilingState; - bool preinfusionState; - uint16_t preinfusionSec; - float preinfusionBar; - uint16_t preinfusionSoak; - uint16_t preinfusionRamp; - bool preinfusionFlowState; - float preinfusionFlowVol; - uint16_t preinfusionFlowTime; - uint16_t preinfusionFlowSoakTime; - float preinfusionFlowPressureTarget; - bool flowProfileState; - float flowProfileStart; - float flowProfileEnd; - float flowProfilePressureTarget; - uint16_t flowProfileCurveSpeed; - uint16_t powerLineFrequency; - uint16_t lcdSleep; - bool warmupState; - bool homeOnShotFinish; - bool graphBrew; - bool brewDeltaState; - bool switchPhaseOnThreshold; - bool basketPrefill; - int scalesF1; - int scalesF2; - float pumpFlowAtZero; - bool stopOnWeightState; - float shotDose; - float shotStopOnCustomWeight; - uint16_t shotPreset; -}; - -static bool upgradeSchema_v6(eepromValues_t &targetValues, eepromValues_t_v6 &loadedValues) { - ACTIVE_PROFILE(targetValues).setpoint = loadedValues.setpoint; - targetValues.steamSetPoint = loadedValues.steamSetPoint; - targetValues.offsetTemp = loadedValues.offsetTemp; - targetValues.hpwr = loadedValues.hpwr; - targetValues.mainDivider = loadedValues.mainDivider; - targetValues.brewDivider = loadedValues.brewDivider; - ACTIVE_PROFILE(targetValues).mpProfilingStart = (float) loadedValues.pressureProfilingStart; - ACTIVE_PROFILE(targetValues).mpProfilingFinish = (float) loadedValues.pressureProfilingFinish; - ACTIVE_PROFILE(targetValues).preinfusionState = loadedValues.preinfusionState; - ACTIVE_PROFILE(targetValues).preinfusionSec = loadedValues.preinfusionSec; - ACTIVE_PROFILE(targetValues).preinfusionBar = (float) loadedValues.preinfusionBar; - ACTIVE_PROFILE(targetValues).preinfusionRamp = loadedValues.preinfusionRamp; - ACTIVE_PROFILE(targetValues).preinfusionFlowState = loadedValues.preinfusionFlowState; - ACTIVE_PROFILE(targetValues).preinfusionFlowVol = loadedValues.preinfusionFlowVol; - ACTIVE_PROFILE(targetValues).preinfusionFlowTime = loadedValues.preinfusionFlowTime; - ACTIVE_PROFILE(targetValues).preinfusionFlowPressureTarget = (float) loadedValues.preinfusionFlowPressureTarget; - ACTIVE_PROFILE(targetValues).mfProfileState = loadedValues.flowProfileState; - ACTIVE_PROFILE(targetValues).mfProfileStart = loadedValues.flowProfileStart; - ACTIVE_PROFILE(targetValues).mfProfileEnd = loadedValues.flowProfileEnd; - targetValues.powerLineFrequency = loadedValues.powerLineFrequency; - targetValues.lcdSleep = loadedValues.lcdSleep; - targetValues.warmupState = loadedValues.warmupState; - targetValues.homeOnShotFinish = loadedValues.homeOnShotFinish; - targetValues.brewDeltaState = loadedValues.brewDeltaState; - targetValues.basketPrefill = loadedValues.basketPrefill; - targetValues.scalesF1 = loadedValues.scalesF1; - targetValues.scalesF2 = loadedValues.scalesF2; - targetValues.pumpFlowAtZero = loadedValues.pumpFlowAtZero; - ACTIVE_PROFILE(targetValues).stopOnWeightState = loadedValues.stopOnWeightState; - ACTIVE_PROFILE(targetValues).shotDose = loadedValues.shotDose; - ACTIVE_PROFILE(targetValues).shotStopOnCustomWeight = loadedValues.shotStopOnCustomWeight; - ACTIVE_PROFILE(targetValues).shotPreset = loadedValues.shotPreset; - return true; -} - -REGISTER_LEGACY_EEPROM_DATA(6, eepromValues_t_v6, upgradeSchema_v6) - -#endif diff --git a/src/eeprom_data/legacy/eeprom_data_v7.h b/src/eeprom_data/legacy/eeprom_data_v7.h deleted file mode 100644 index 199d92e3..00000000 --- a/src/eeprom_data/legacy/eeprom_data_v7.h +++ /dev/null @@ -1,117 +0,0 @@ -/* 09:32 15/03/2023 - change triggering comment */ -#ifndef EEPROM_DATA_V7_H -#define EEPROM_DATA_V7_H - -#include "../eeprom_data.h" -#include "../eeprom_metadata.h" - -/** -* current data version definition below -* -* changing the schema requires bumping version number. this forces a schema update -* on boards with a lower version after flash. to make this graceful for users: -* - archive the current version in its corresponding header in legacy/ -* - in that header, make sure to register the upgrade function for -* the loader to pick it up -* - bump up the version in eeprom_metadata.h -* - make schema changes -* -* adding fields to data below shouldn't require changes to legacy upgrade -* functions - they just won't populate the new field, and it will use -* default value. -* -* removing fields might require deleting assignments in existing legacy -* functions that reference them. this will pop up as a compile time failure -*/ - -/** -* Version 7: -* - Add two more restrictions: -* - switchPhaseOnPressureBelow -* - switchPhaseOnFirstDrops -*/ -struct eepromValues_t_v7 { - uint16_t setpoint; - uint16_t steamSetPoint; - uint16_t offsetTemp; - uint16_t hpwr; - uint16_t mainDivider; - uint16_t brewDivider; - float pressureProfilingStart; - float pressureProfilingFinish; - uint16_t pressureProfilingHold; - uint16_t pressureProfilingLength; - bool pressureProfilingState; - bool preinfusionState; - uint16_t preinfusionSec; - float preinfusionBar; - uint16_t preinfusionSoak; - uint16_t preinfusionRamp; - bool preinfusionFlowState; - float preinfusionFlowVol; - uint16_t preinfusionFlowTime; - uint16_t preinfusionFlowSoakTime; - float preinfusionFlowPressureTarget; - bool flowProfileState; - float flowProfileStart; - float flowProfileEnd; - float flowProfilePressureTarget; - uint16_t flowProfileCurveSpeed; - uint16_t powerLineFrequency; - uint16_t lcdSleep; - bool warmupState; - bool homeOnShotFinish; - bool graphBrew; - bool brewDeltaState; - bool switchPhaseOnThreshold; - float switchPhaseOnPressureBelow; - bool switchPhaseOnFirstDrops; - bool basketPrefill; - int scalesF1; - int scalesF2; - float pumpFlowAtZero; - bool stopOnWeightState; - float shotDose; - float shotStopOnCustomWeight; - uint16_t shotPreset; -}; - -static bool upgradeSchema_v7(eepromValues_t &targetValues, eepromValues_t_v7 &loadedValues) { - ACTIVE_PROFILE(targetValues).setpoint = loadedValues.setpoint; - targetValues.steamSetPoint = loadedValues.steamSetPoint; - targetValues.offsetTemp = loadedValues.offsetTemp; - targetValues.hpwr = loadedValues.hpwr; - targetValues.mainDivider = loadedValues.mainDivider; - targetValues.brewDivider = loadedValues.brewDivider; - ACTIVE_PROFILE(targetValues).mpProfilingStart = loadedValues.pressureProfilingStart; - ACTIVE_PROFILE(targetValues).mpProfilingFinish = loadedValues.pressureProfilingFinish; - ACTIVE_PROFILE(targetValues).preinfusionState = loadedValues.preinfusionState; - ACTIVE_PROFILE(targetValues).preinfusionSec = loadedValues.preinfusionSec; - ACTIVE_PROFILE(targetValues).preinfusionBar = loadedValues.preinfusionBar; - ACTIVE_PROFILE(targetValues).preinfusionRamp = loadedValues.preinfusionRamp; - ACTIVE_PROFILE(targetValues).preinfusionFlowState = loadedValues.preinfusionFlowState; - ACTIVE_PROFILE(targetValues).preinfusionFlowVol = loadedValues.preinfusionFlowVol; - ACTIVE_PROFILE(targetValues).preinfusionFlowTime = loadedValues.preinfusionFlowTime; - ACTIVE_PROFILE(targetValues).preinfusionFlowPressureTarget = loadedValues.preinfusionFlowPressureTarget; - ACTIVE_PROFILE(targetValues).mfProfileState = loadedValues.flowProfileState; - ACTIVE_PROFILE(targetValues).mfProfileStart = loadedValues.flowProfileStart; - ACTIVE_PROFILE(targetValues).mfProfileEnd = loadedValues.flowProfileEnd; - targetValues.powerLineFrequency = loadedValues.powerLineFrequency; - targetValues.lcdSleep = loadedValues.lcdSleep; - targetValues.warmupState = loadedValues.warmupState; - targetValues.homeOnShotFinish = loadedValues.homeOnShotFinish; - targetValues.brewDeltaState = loadedValues.brewDeltaState; - targetValues.basketPrefill = loadedValues.basketPrefill; - targetValues.scalesF1 = loadedValues.scalesF1; - targetValues.scalesF2 = loadedValues.scalesF2; - targetValues.pumpFlowAtZero = loadedValues.pumpFlowAtZero; - ACTIVE_PROFILE(targetValues).stopOnWeightState = loadedValues.stopOnWeightState; - ACTIVE_PROFILE(targetValues).shotDose = loadedValues.shotDose; - ACTIVE_PROFILE(targetValues).shotStopOnCustomWeight = loadedValues.shotStopOnCustomWeight; - ACTIVE_PROFILE(targetValues).shotPreset = loadedValues.shotPreset; - return true; -} - -REGISTER_LEGACY_EEPROM_DATA(7, eepromValues_t_v7, upgradeSchema_v7) - -#endif diff --git a/src/eeprom_data/legacy/eeprom_data_v8.h b/src/eeprom_data/legacy/eeprom_data_v8.h deleted file mode 100644 index d4b190a6..00000000 --- a/src/eeprom_data/legacy/eeprom_data_v8.h +++ /dev/null @@ -1,122 +0,0 @@ -/* 09:32 15/03/2023 - change triggering comment */ -#ifndef EEPROM_DATA_V8_H -#define EEPROM_DATA_V8_H - -#include "../eeprom_data.h" -#include "../eeprom_metadata.h" -/** -* current data version definition below -* -* changing the schema requires bumping version number. this forces a schema update -* on boards with a lower version after flash. to make this graceful for users: -* - archive the current version in its corresponding header in legacy/ -* - in that header, make sure to register the upgrade function for -* the loader to pick it up -* - bump up the version in eeprom_metadata.h -* - make schema changes -* -* adding fields to data below shouldn't require changes to legacy upgrade -* functions - they just won't populate the new field, and it will use -* default value. -* -* removing fields might require deleting assignments in existing legacy -* functions that reference them. this will pop up as a compile time failure -*/ - -/** -* Version 8: -* - Replaces switchPhaseOnFirstDrops with two more restrictions: -* - switchOnWeightAbove -* - switchOnWaterPumped -*/ -struct eepromValues_t_v8 { - uint16_t setpoint; - uint16_t steamSetPoint; - uint16_t offsetTemp; - uint16_t hpwr; - uint16_t mainDivider; - uint16_t brewDivider; - float pressureProfilingStart; - float pressureProfilingFinish; - uint16_t pressureProfilingHold; - uint16_t pressureProfilingLength; - bool pressureProfilingState; - bool preinfusionState; - uint16_t preinfusionSec; - float preinfusionBar; - uint16_t preinfusionSoak; - uint16_t preinfusionRamp; - bool preinfusionFlowState; - float preinfusionFlowVol; - uint16_t preinfusionFlowTime; - uint16_t preinfusionFlowSoakTime; - float preinfusionFlowPressureTarget; - bool flowProfileState; - float flowProfileStart; - float flowProfileEnd; - float flowProfilePressureTarget; - uint16_t flowProfileCurveSpeed; - uint16_t powerLineFrequency; - uint16_t lcdSleep; - bool warmupState; - bool homeOnShotFinish; - bool graphBrew; - bool brewDeltaState; - bool switchPhaseOnThreshold; - float switchPhaseOnPressureBelow; - float switchOnWeightAbove; - float switchOnWaterPumped; - bool basketPrefill; - int scalesF1; - int scalesF2; - float pumpFlowAtZero; - bool stopOnWeightState; - float shotDose; - float shotStopOnCustomWeight; - uint16_t shotPreset; -}; - -static bool upgradeSchema_v8(eepromValues_t &targetValues, eepromValues_t_v8 &loadedValues) { - ACTIVE_PROFILE(targetValues).setpoint = loadedValues.setpoint; - targetValues.steamSetPoint = loadedValues.steamSetPoint; - targetValues.offsetTemp = loadedValues.offsetTemp; - targetValues.hpwr = loadedValues.hpwr; - targetValues.mainDivider = loadedValues.mainDivider; - targetValues.brewDivider = loadedValues.brewDivider; - ACTIVE_PROFILE(targetValues).mpProfilingStart = loadedValues.pressureProfilingStart; - ACTIVE_PROFILE(targetValues).mpProfilingFinish = loadedValues.pressureProfilingFinish; - ACTIVE_PROFILE(targetValues).mpProfilingSlope = loadedValues.pressureProfilingLength; - ACTIVE_PROFILE(targetValues).profilingState = loadedValues.pressureProfilingState; - ACTIVE_PROFILE(targetValues).preinfusionState = loadedValues.preinfusionState; - ACTIVE_PROFILE(targetValues).preinfusionSec = loadedValues.preinfusionSec; - ACTIVE_PROFILE(targetValues).preinfusionBar = loadedValues.preinfusionBar; - ACTIVE_PROFILE(targetValues).soakTimePressure = loadedValues.preinfusionSoak; - ACTIVE_PROFILE(targetValues).preinfusionRamp = loadedValues.preinfusionRamp; - ACTIVE_PROFILE(targetValues).preinfusionFlowState = loadedValues.preinfusionFlowState; - ACTIVE_PROFILE(targetValues).preinfusionFlowVol = loadedValues.preinfusionFlowVol; - ACTIVE_PROFILE(targetValues).preinfusionFlowTime = loadedValues.preinfusionFlowTime; - ACTIVE_PROFILE(targetValues).soakTimeFlow = loadedValues.preinfusionFlowSoakTime; - ACTIVE_PROFILE(targetValues).preinfusionFlowPressureTarget = loadedValues.preinfusionFlowPressureTarget; - ACTIVE_PROFILE(targetValues).mfProfileState = loadedValues.flowProfileState; - ACTIVE_PROFILE(targetValues).mfProfileStart = loadedValues.flowProfileStart; - ACTIVE_PROFILE(targetValues).mfProfileEnd = loadedValues.flowProfileEnd; - ACTIVE_PROFILE(targetValues).mfProfileSlope = loadedValues.flowProfileCurveSpeed; - targetValues.powerLineFrequency = loadedValues.powerLineFrequency; - targetValues.lcdSleep = loadedValues.lcdSleep; - targetValues.warmupState = loadedValues.warmupState; - targetValues.homeOnShotFinish = loadedValues.homeOnShotFinish; - targetValues.brewDeltaState = loadedValues.brewDeltaState; - targetValues.basketPrefill = loadedValues.basketPrefill; - targetValues.scalesF1 = loadedValues.scalesF1; - targetValues.scalesF2 = loadedValues.scalesF2; - targetValues.pumpFlowAtZero = loadedValues.pumpFlowAtZero; - ACTIVE_PROFILE(targetValues).stopOnWeightState = loadedValues.stopOnWeightState; - ACTIVE_PROFILE(targetValues).shotDose = loadedValues.shotDose; - ACTIVE_PROFILE(targetValues).shotStopOnCustomWeight = loadedValues.shotStopOnCustomWeight; - ACTIVE_PROFILE(targetValues).shotPreset = loadedValues.shotPreset; - return true; -} - -REGISTER_LEGACY_EEPROM_DATA(8, eepromValues_t_v8, upgradeSchema_v8) - -#endif diff --git a/src/eeprom_data/legacy/eeprom_data_v9.h b/src/eeprom_data/legacy/eeprom_data_v9.h deleted file mode 100644 index 75b102f2..00000000 --- a/src/eeprom_data/legacy/eeprom_data_v9.h +++ /dev/null @@ -1,135 +0,0 @@ -#ifndef EEPROM_DATA_V9_H -#define EEPROM_DATA_V9_H - -#include "../eeprom_data.h" -#include "../eeprom_metadata.h" - -/** -* Version 9: -* - Multiple changes to the brew profile settings to support more customization of phases -* - Re-organize fields -*/ - -struct eepromValues_t_v9 { - uint16_t setpoint; - uint16_t steamSetPoint; - uint16_t offsetTemp; - uint16_t hpwr; - uint16_t mainDivider; - uint16_t brewDivider; - // Preinfusion vars section - bool preinfusionState; - bool preinfusionFlowState; - uint16_t preinfusionSec; - float preinfusionBar; - float preinfusionFlowVol; - uint16_t preinfusionFlowTime; - float preinfusionFlowPressureTarget; - float preinfusionPressureFlowTarget; - float preinfusionFilled; - bool preinfusionPressureAbove; - float preinfusionWeightAbove; - // Soak vars section - bool soakState; - uint16_t soakTimePressure; - uint16_t soakTimeFlow; - float soakKeepPressure; - float soakKeepFlow; - float soakBelowPressure; - float soakAbovePressure; - float soakAboveWeight; - // PI -> PF ramp settings - uint16_t preinfusionRamp; - uint16_t preinfusionRampSlope; - // Profiling vars section - bool profilingState; - bool flowProfileState; - float pressureProfilingStart; - float pressureProfilingFinish; - uint16_t pressureProfilingHold; - float pressureProfilingHoldLimit; - uint16_t pressureProfilingSlope; - uint16_t pressureProfilingSlopeShape; - float pressureProfilingFlowRestriction; - float flowProfileStart; - float flowProfileEnd; - uint16_t flowProfileHold; - float flowProfileHoldLimit; - uint16_t flowProfileSlope; - uint16_t flowProfileSlopeShape; - float flowProfilingPressureRestriction; - // Settings vars section - uint16_t powerLineFrequency; - uint16_t lcdSleep; - bool warmupState; - bool homeOnShotFinish; - bool brewDeltaState; - bool basketPrefill; - int scalesF1; - int scalesF2; - float pumpFlowAtZero; - bool stopOnWeightState; - float shotDose; - float shotStopOnCustomWeight; - uint16_t shotPreset; -}; - -static bool upgradeSchema_v9(eepromValues_t &targetValues, eepromValues_t_v9 &loadedValues) { - ACTIVE_PROFILE(targetValues).setpoint = loadedValues.setpoint; - targetValues.steamSetPoint = loadedValues.steamSetPoint; - targetValues.offsetTemp = loadedValues.offsetTemp; - targetValues.hpwr = loadedValues.hpwr; - targetValues.mainDivider = loadedValues.mainDivider; - targetValues.brewDivider = loadedValues.brewDivider; - ACTIVE_PROFILE(targetValues).preinfusionState = loadedValues.preinfusionState; - ACTIVE_PROFILE(targetValues).preinfusionFlowState = loadedValues.preinfusionFlowState; - ACTIVE_PROFILE(targetValues).preinfusionSec = loadedValues.preinfusionSec; - ACTIVE_PROFILE(targetValues).preinfusionBar = loadedValues.preinfusionBar; - ACTIVE_PROFILE(targetValues).preinfusionFlowVol = loadedValues.preinfusionFlowVol; - ACTIVE_PROFILE(targetValues).preinfusionFlowTime = loadedValues.preinfusionFlowTime; - ACTIVE_PROFILE(targetValues).preinfusionFlowPressureTarget = loadedValues.preinfusionFlowPressureTarget; - ACTIVE_PROFILE(targetValues).preinfusionPressureFlowTarget = loadedValues.preinfusionPressureFlowTarget; - ACTIVE_PROFILE(targetValues).preinfusionFilled = loadedValues.preinfusionFilled; - ACTIVE_PROFILE(targetValues).preinfusionPressureAbove = loadedValues.preinfusionPressureAbove; - ACTIVE_PROFILE(targetValues).preinfusionWeightAbove = loadedValues.preinfusionWeightAbove; - ACTIVE_PROFILE(targetValues).soakState = loadedValues.soakState; - ACTIVE_PROFILE(targetValues).soakTimePressure = loadedValues.soakTimePressure; - ACTIVE_PROFILE(targetValues).soakTimeFlow = loadedValues.soakTimeFlow; - ACTIVE_PROFILE(targetValues).soakKeepPressure = loadedValues.soakKeepPressure; - ACTIVE_PROFILE(targetValues).soakKeepFlow = loadedValues.soakKeepFlow; - ACTIVE_PROFILE(targetValues).soakBelowPressure = loadedValues.soakBelowPressure; - ACTIVE_PROFILE(targetValues).soakAbovePressure = loadedValues.soakAbovePressure; - ACTIVE_PROFILE(targetValues).soakAboveWeight = loadedValues.soakAboveWeight; - ACTIVE_PROFILE(targetValues).preinfusionRamp = loadedValues.preinfusionRamp; - ACTIVE_PROFILE(targetValues).preinfusionRampSlope = loadedValues.preinfusionRampSlope; - ACTIVE_PROFILE(targetValues).profilingState = loadedValues.profilingState; - ACTIVE_PROFILE(targetValues).mfProfileState = loadedValues.flowProfileState; - ACTIVE_PROFILE(targetValues).mpProfilingStart = loadedValues.pressureProfilingStart; - ACTIVE_PROFILE(targetValues).mpProfilingFinish = loadedValues.pressureProfilingFinish; - ACTIVE_PROFILE(targetValues).mpProfilingSlope = loadedValues.pressureProfilingSlope; - ACTIVE_PROFILE(targetValues).mpProfilingSlopeShape = loadedValues.pressureProfilingSlopeShape; - ACTIVE_PROFILE(targetValues).mpProfilingFlowRestriction = loadedValues.pressureProfilingFlowRestriction; - ACTIVE_PROFILE(targetValues).mfProfileStart = loadedValues.flowProfileStart; - ACTIVE_PROFILE(targetValues).mfProfileEnd = loadedValues.flowProfileEnd; - ACTIVE_PROFILE(targetValues).mfProfileSlope = loadedValues.flowProfileSlope; - ACTIVE_PROFILE(targetValues).mfProfileSlopeShape = loadedValues.flowProfileSlopeShape; - ACTIVE_PROFILE(targetValues).mfProfilingPressureRestriction = loadedValues.flowProfilingPressureRestriction; - targetValues.powerLineFrequency = loadedValues.powerLineFrequency; - targetValues.lcdSleep = loadedValues.lcdSleep; - targetValues.warmupState = loadedValues.warmupState; - targetValues.homeOnShotFinish = loadedValues.homeOnShotFinish; - targetValues.brewDeltaState = loadedValues.brewDeltaState; - targetValues.basketPrefill = loadedValues.basketPrefill; - targetValues.scalesF1 = loadedValues.scalesF1; - targetValues.scalesF2 = loadedValues.scalesF2; - targetValues.pumpFlowAtZero = loadedValues.pumpFlowAtZero; - ACTIVE_PROFILE(targetValues).stopOnWeightState = loadedValues.stopOnWeightState; - ACTIVE_PROFILE(targetValues).shotDose = loadedValues.shotDose; - ACTIVE_PROFILE(targetValues).shotStopOnCustomWeight = loadedValues.shotStopOnCustomWeight; - ACTIVE_PROFILE(targetValues).shotPreset = loadedValues.shotPreset; - return true; -} - -REGISTER_LEGACY_EEPROM_DATA(9, eepromValues_t_v9, upgradeSchema_v9) - -#endif diff --git a/src/firmware/watch-upgrade.cpp b/src/firmware/watch-upgrade.cpp new file mode 100644 index 00000000..e6a9a6d6 --- /dev/null +++ b/src/firmware/watch-upgrade.cpp @@ -0,0 +1,103 @@ +#include "stm32f4xx_hal.h" +#include "watch-upgrade.h" +#include "../log.h" + +// Array that holds the start addresses of each flash sector +const std::array flashSectorStartAddresses = { + 0x08000000, + 0x08004000, + 0x08008000, + 0x0800C000, + 0x08010000, + 0x08020000, + 0x08040000, + 0x08060000 +}; + +void waitForFw() { + while (!Serial); // Make sure we got serial connectivity + + const uint32_t expectedMarker = FW_UPGRADE_MARKER; + uint32_t receivedMarker = 0x00000000; + + size_t bytesReceived = 0; + while (true) { + if (Serial.available()) { + uint8_t byteRead = Serial.read(); + + // Shift the receivedMarker and add the new byte + receivedMarker = (receivedMarker << 8) | byteRead; + bytesReceived++; + + if (bytesReceived == sizeof(receivedMarker)) { + if (receivedMarker == expectedMarker) { + // Firmware binary available -> upgrade! + updateFirmware(); + } + // Reset for the next check + receivedMarker = 0x00000000; + bytesReceived = 0; + } + } + } +} + +// Get the sector number based on the memory address +uint32_t getSectorNumber(uint32_t address) { + for (uint32_t i = 0; i < FLASH_SECTOR_COUNT - 1; ++i) { + if (address >= flashSectorStartAddresses[i] && address < flashSectorStartAddresses[i + 1]) { + return i; + } + } + return FLASH_SECTOR_COUNT - 1; +} + +void updateFirmware() { + Serial.println("Starting firmware update..."); + + // Set the starting address for firmware flashing + uint32_t address = flashSectorStartAddresses[0]; + + /* + According to the "Application Note: AN2606 - STM32™ microcontroller system memory boot mode" + Most will have to kill loads of peripherals before writing the firmware to flash + */ + + // Erase the sectors where the firmware will be written + eraseFlashSectors(address, Serial.available()); + + // Write the firmware to the flash memory + while (Serial.available()) { + char buffer[256]; + size_t bytesRead = Serial.readBytes(buffer, sizeof(buffer)); + writeFlash(address, buffer, bytesRead); + address += bytesRead; + } + + // Post firmware write-to-flash reset + NVIC_SystemReset(); +} + +// Erase the flash sectors +void eraseFlashSectors(uint32_t address, size_t size) { + uint32_t sectorNumber = getSectorNumber(address); + uint32_t lastSectorNumber = getSectorNumber(address + size - 1); + + HAL_FLASH_Unlock(); + + for (uint32_t sector = sectorNumber; sector <= lastSectorNumber; ++sector) { + FLASH_Erase_Sector(sector, VOLTAGE_RANGE_3); + } + + HAL_FLASH_Lock(); +} + +// Write data to flash +void writeFlash(uint32_t address, const char* data, size_t size) { + HAL_FLASH_Unlock(); + for (size_t i = 0; i < size; i += 4) { + uint32_t wordData = data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | (data[i + 3] << 24); + HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address + i, wordData); + } + HAL_FLASH_Lock(); +} diff --git a/src/firmware/watch-upgrade.h b/src/firmware/watch-upgrade.h new file mode 100644 index 00000000..7c6b5b41 --- /dev/null +++ b/src/firmware/watch-upgrade.h @@ -0,0 +1,17 @@ +#ifndef WATCH_UPGRADE_H +#define WATCH_UPGRADE_H + +#include + +// Replace FLASH_SECTOR_COUNT and SECTOR_SIZE with actual values for the STM32F411CEU6 mcu +#define FW_UPGRADE_MARKER 0x53544D20 // HEX for STM32 +#define FLASH_SECTOR_SIZE 0x8 +#define FLASH_SECTOR_COUNT 8 +#define SECTOR_SIZE 16384 + +void waitForFw(void); +void updateFirmware(void); +void eraseFlashSectors(uint32_t address, size_t size); +void writeFlash(uint32_t address, const char* data, size_t size); + +#endif diff --git a/src/functional/descale.cpp b/src/functional/descale.cpp index 54607ab4..9a278956 100644 --- a/src/functional/descale.cpp +++ b/src/functional/descale.cpp @@ -2,76 +2,94 @@ #include "descale.h" #include "just_do_coffee.h" #include "../peripherals/internal_watchdog.h" -#include "../lcd/lcd.h" +#include "../peripherals/esp_comms.h" + +const int DESCALE_MAX_CYCLES = 100; DescalingState descalingState = DescalingState::IDLE; short flushCounter = 0; uint8_t counter = 0; -unsigned long descalingTimer = 0; +uint32_t descalingTimer = 0; +uint32_t descalingStartTime = 0; +uint32_t descalingStopTime = 0; int descalingCycle = 0; -void deScale(eepromValues_t &runningCfg, const SensorState ¤tState) { +void sendDescalingProgressToEsp(); + +void deScale(const GaggiaSettings& gaggiaSettings, const SensorState& currentState) { + if (currentState.waterLvl <= 4u) { + setPumpOff(); + closeValve(); + setSteamValveRelayOff(); + espCommsSendNotification(Notification::info("Fill tank with fresh water to clean the system!")); + return; + } + switch (descalingState) { - case DescalingState::IDLE: // Waiting for fuckfest to begin - if (currentState.brewSwitchState) { - ACTIVE_PROFILE(runningCfg).setpoint = 9; - openValve(); - setSteamValveRelayOn(); - descalingState = DescalingState::DESCALING_PHASE1; - descalingCycle = 0; + case DescalingState::IDLE: // Waiting for fuckfest to begin + if (currentState.brewSwitchState) { + openValve(); + setSteamValveRelayOn(); + descalingState = DescalingState::PHASE1; + descalingCycle = 0; + descalingTimer = millis(); + descalingStartTime = millis(); + descalingStopTime = 0; + } + break; + case DescalingState::PHASE1: // Slowly penetrating that scale + descalingState = currentState.brewSwitchState ? descalingState : DescalingState::FINISHED; + setPumpToPercentage(0.1f); + if (millis() - descalingTimer > DESCALE_PHASE1_EVERY) { + descalingCycle += 1; + if (descalingCycle < DESCALE_MAX_CYCLES) { descalingTimer = millis(); + descalingState = DescalingState::PHASE2; } - break; - case DescalingState::DESCALING_PHASE1: // Slowly penetrating that scale - currentState.brewSwitchState ? descalingState : descalingState = DescalingState::FINISHED; - setPumpToRawValue(10); - if (millis() - descalingTimer > DESCALE_PHASE1_EVERY) { - lcdSetDescaleCycle(descalingCycle++); - if (descalingCycle < 100) { - descalingTimer = millis(); - descalingState = DescalingState::DESCALING_PHASE2; - } else { - descalingState = DescalingState::FINISHED; - } + else { + descalingState = DescalingState::FINISHED; } - break; - case DescalingState::DESCALING_PHASE2: // Softening the f outta that scale - currentState.brewSwitchState ? descalingState : descalingState = DescalingState::FINISHED; - setPumpOff(); - if (millis() - descalingTimer > DESCALE_PHASE2_EVERY) { + } + break; + case DescalingState::PHASE2: // Softening the f outta that scale + descalingState = currentState.brewSwitchState ? descalingState : DescalingState::FINISHED; + setPumpOff(); + if (millis() - descalingTimer > DESCALE_PHASE2_EVERY) { + descalingTimer = millis(); + descalingCycle += 1; + descalingState = DescalingState::PHASE3; + } + break; + case DescalingState::PHASE3: // Fucking up that scale big time + descalingState = currentState.brewSwitchState ? descalingState : DescalingState::FINISHED; + setPumpToPercentage(0.3f); + if (millis() - descalingTimer > DESCALE_PHASE3_EVERY) { + solenoidBeat(); + descalingCycle += 1; + if (descalingCycle < DESCALE_MAX_CYCLES) { descalingTimer = millis(); - lcdSetDescaleCycle(descalingCycle++); - descalingState = DescalingState::DESCALING_PHASE3; + descalingState = DescalingState::PHASE1; } - break; - case DescalingState::DESCALING_PHASE3: // Fucking up that scale big time - currentState.brewSwitchState ? descalingState : descalingState = DescalingState::FINISHED; - setPumpToRawValue(30); - if (millis() - descalingTimer > DESCALE_PHASE3_EVERY) { - solenoidBeat(); - lcdSetDescaleCycle(descalingCycle++); - if (descalingCycle < 100) { - descalingTimer = millis(); - descalingState = DescalingState::DESCALING_PHASE1; - } else { - descalingState = DescalingState::FINISHED; - } + else { + descalingState = DescalingState::FINISHED; } - break; - case DescalingState::FINISHED: // Scale successufuly fucked - setPumpOff(); - closeValve(); - setSteamValveRelayOff(); - currentState.brewSwitchState ? descalingState = DescalingState::FINISHED : descalingState = DescalingState::IDLE; - if (millis() - descalingTimer > 1000) { - lcdBrewTimerStop(); - lcdShowPopup("FINISHED"); - descalingTimer = millis(); - } - break; + } + break; + case DescalingState::FINISHED: // Scale successufuly fucked + if (descalingStopTime == 0) { + descalingStopTime = millis(); + espCommsSendNotification(Notification::info("Descale finished!")); + } + setPumpOff(); + closeValve(); + setSteamValveRelayOff(); + descalingState = currentState.brewSwitchState ? DescalingState::FINISHED : DescalingState::IDLE; + break; } - justDoCoffee(runningCfg, currentState, false); + + sendDescalingProgressToEsp(); + justDoCoffee(gaggiaSettings, currentState, 9.f, false); } void solenoidBeat() { @@ -93,7 +111,7 @@ void solenoidBeat() { setPumpOff(); } -void backFlush(const SensorState ¤tState) { +void backFlush(const SensorState& currentState) { static unsigned long backflushTimer = millis(); unsigned long elapsedTime = millis() - backflushTimer; if (currentState.brewSwitchState) { @@ -103,8 +121,10 @@ void backFlush(const SensorState ¤tState) { } else if (elapsedTime > 7000UL && currentState.smoothedPressure > 5.f) { flushPhases(); - } else flushActivated(); - } else { + } + else flushActivated(); + } + else { flushDeactivated(); flushCounter = 0; backflushTimer = millis(); @@ -113,16 +133,12 @@ void backFlush(const SensorState ¤tState) { void flushActivated(void) { - #if defined SINGLE_BOARD || defined LEGO_VALVE_RELAY - openValve(); - #endif + openValve(); setPumpFullOn(); } void flushDeactivated(void) { - #if defined SINGLE_BOARD || defined LEGO_VALVE_RELAY - closeValve(); - #endif + closeValve(); setPumpOff(); } @@ -136,7 +152,8 @@ void flushPhases(void) { } openValve(); setPumpFullOn(); - } else { + } + else { if (millis() - timer >= 5000) { flushCounter++; timer = millis(); @@ -144,8 +161,30 @@ void flushPhases(void) { closeValve(); setPumpOff(); } - } else { + } + else { flushDeactivated(); timer = millis(); } } + +void sendDescalingProgressToEsp() { + static uint32_t espUpdateTimer = 0; + uint32_t now = millis(); + + if (now - espUpdateTimer > 1000) { + espUpdateTimer = now; + uint32_t time = 0; + uint8_t progress = 0; + if (descalingState != DescalingState::IDLE) { + time = descalingStopTime == 0 ? now - descalingStartTime : descalingStopTime - descalingStartTime; + progress = (uint8_t)(100 * descalingCycle / DESCALE_MAX_CYCLES); + } + + espCommsSendDescaleProgress({ + .state = descalingState, + .time = time, + .progess = progress + }); + } +} diff --git a/src/functional/descale.h b/src/functional/descale.h index aebf216d..0b749c40 100644 --- a/src/functional/descale.h +++ b/src/functional/descale.h @@ -4,7 +4,8 @@ #include #include "sensors_state.h" -#include "../eeprom_data/eeprom_data.h" +#include "system_state.h" +#include "gaggia_settings.h" const unsigned long DESCALE_PHASE1_EVERY = 30000UL; //30000 // short pump pulses during descale const unsigned long DESCALE_PHASE2_EVERY = 60000UL; //60000 // long pause for scale softening @@ -14,15 +15,7 @@ const unsigned long DESCALE_PHASE3_EVERY = 10000UL; //10000 // short burst for d //###############################____DESCALE__CONTROL____###################################### //############################################################################################# -enum class DescalingState { - IDLE, - DESCALING_PHASE1, - DESCALING_PHASE2, - DESCALING_PHASE3, - FINISHED -}; - -void deScale(eepromValues_t &runningCfg, const SensorState ¤tState); +void deScale(const GaggiaSettings &gaggiaSettings, const SensorState ¤tState); void solenoidBeat(void); void backFlush(const SensorState ¤tState); void flushActivated(void); diff --git a/src/functional/just_do_coffee.cpp b/src/functional/just_do_coffee.cpp index 436cc912..c4d943be 100644 --- a/src/functional/just_do_coffee.cpp +++ b/src/functional/just_do_coffee.cpp @@ -1,35 +1,33 @@ /* 09:32 15/03/2023 - change triggering comment */ #include "just_do_coffee.h" -#include "../lcd/lcd.h" extern unsigned long steamTime; // inline static float TEMP_DELTA(float d) { return (d*DELTA_RANGE); } inline static float TEMP_DELTA(float d, const SensorState ¤tState) { return ( d * (currentState.pumpFlow < 1.f - ? currentState.pumpFlow / 9.f - : currentState.pumpFlow / 10.f + ? currentState.pumpFlow / 7.f + : currentState.pumpFlow / 5.f ) ); } -void justDoCoffee(const eepromValues_t &runningCfg, const SensorState ¤tState, const bool brewActive) { - lcdTargetState((int)HEATING::MODE_brew); // setting the target mode to "brew temp" - float brewTempSetPoint = ACTIVE_PROFILE(runningCfg).setpoint + runningCfg.offsetTemp; - float sensorTemperature = currentState.temperature + runningCfg.offsetTemp; +void justDoCoffee(const GaggiaSettings &settings, const SensorState ¤tState, float waterTemperature, const bool brewActive) { + float brewTempSetPoint = waterTemperature + settings.boiler.offsetTemp; + float sensorTemperature = currentState.temperature + settings.boiler.offsetTemp; if (brewActive) { //if brewState == true if(sensorTemperature <= brewTempSetPoint - 5.f) { setBoilerOn(); } else { float deltaOffset = 0.f; - if (runningCfg.brewDeltaState) { + if (settings.brew.brewDeltaState) { float tempDelta = TEMP_DELTA(brewTempSetPoint, currentState); float BREW_TEMP_DELTA = mapRange(sensorTemperature, brewTempSetPoint, brewTempSetPoint + tempDelta, tempDelta, 0, 0); deltaOffset = constrain(BREW_TEMP_DELTA, 0, tempDelta); } if (sensorTemperature <= brewTempSetPoint + deltaOffset) { - pulseHeaters(runningCfg.hpwr, runningCfg.mainDivider, runningCfg.brewDivider, brewActive); + pulseHeaters(settings.boiler.hpwr, settings.boiler.mainDivider, settings.boiler.brewDivider, brewActive); } else { setBoilerOff(); } @@ -38,15 +36,15 @@ void justDoCoffee(const eepromValues_t &runningCfg, const SensorState ¤tSt if (sensorTemperature <= ((float)brewTempSetPoint - 10.f)) { setBoilerOn(); } else { - int HPWR_LOW = runningCfg.hpwr / runningCfg.mainDivider; + int HPWR_LOW = settings.boiler.hpwr / settings.boiler.mainDivider; // Calculating the boiler heating power range based on the below input values - int HPWR_OUT = mapRange(sensorTemperature, brewTempSetPoint - 10, brewTempSetPoint, runningCfg.hpwr, HPWR_LOW, 0); - HPWR_OUT = constrain(HPWR_OUT, HPWR_LOW, runningCfg.hpwr); // limits range of sensor values to HPWR_LOW and HPWR + int HPWR_OUT = mapRange(sensorTemperature, brewTempSetPoint - 10, brewTempSetPoint, settings.boiler.hpwr, HPWR_LOW, 0); + HPWR_OUT = constrain(HPWR_OUT, HPWR_LOW, settings.boiler.hpwr); // limits range of sensor values to HPWR_LOW and HPWR if (sensorTemperature <= ((float)brewTempSetPoint - 5.f)) { - pulseHeaters(HPWR_OUT, 1, runningCfg.mainDivider, brewActive); + pulseHeaters(HPWR_OUT, 1, settings.boiler.mainDivider, brewActive); } else if (sensorTemperature < ((float)brewTempSetPoint)) { - pulseHeaters(HPWR_OUT, runningCfg.brewDivider, runningCfg.brewDivider, brewActive); + pulseHeaters(HPWR_OUT, settings.boiler.brewDivider, settings.boiler.brewDivider, brewActive); } else { setBoilerOff(); } @@ -75,11 +73,10 @@ void pulseHeaters(const uint32_t pulseLength, const int factor_1, const int fact //############################################################################################# //################################____STEAM_POWER_CONTROL____################################## //############################################################################################# -void steamCtrl(const eepromValues_t &runningCfg, SensorState ¤tState) { - currentState.steamSwitchState ? lcdTargetState((int)HEATING::MODE_steam) : lcdTargetState((int)HEATING::MODE_brew); // setting the steam/hot water target temp +void steamCtrl(const GaggiaSettings& settings, const SensorState& currentState, SystemState& systemState) { // steam temp control, needs to be aggressive to keep steam pressure acceptable - float steamTempSetPoint = runningCfg.steamSetPoint + runningCfg.offsetTemp; - float sensorTemperature = currentState.temperature + runningCfg.offsetTemp; + float steamTempSetPoint = settings.boiler.steamSetPoint + settings.boiler.offsetTemp; + float sensorTemperature = currentState.temperature + settings.boiler.offsetTemp; if (currentState.smoothedPressure > steamThreshold_ || sensorTemperature > steamTempSetPoint) { setBoilerOff(); @@ -96,7 +93,9 @@ void steamCtrl(const eepromValues_t &runningCfg, SensorState ¤tState) { setSteamBoilerRelayOn(); #ifndef DREAM_STEAM_DISABLED // disabled for bigger boilers which have no need of adding water during steaming if (currentState.smoothedPressure < activeSteamPressure_) { - setPumpToRawValue(3); + float pumpPercent = mapRange(currentState.smoothedPressure, 0.8f, 2.f, 0.08f, 0.03f, 2); + pumpPercent = constrain(pumpPercent, 0.03f, 0.08f); + setPumpToPercentage(pumpPercent); } else { setPumpOff(); } @@ -105,14 +104,14 @@ void steamCtrl(const eepromValues_t &runningCfg, SensorState ¤tState) { /*In case steam is forgotten ON for more than 15 min*/ if (currentState.smoothedPressure > passiveSteamPressure_) { - currentState.isSteamForgottenON = millis() - steamTime >= STEAM_TIMEOUT; + systemState.isSteamForgottenON = millis() - steamTime >= STEAM_TIMEOUT; } else steamTime = millis(); } /*Water mode and all that*/ void hotWaterMode(const SensorState ¤tState) { closeValve(); - setPumpToRawValue(80); + setPumpToPercentage(0.8f); setBoilerOn(); if (currentState.temperature < MAX_WATER_TEMP) setBoilerOn(); else setBoilerOff(); diff --git a/src/functional/just_do_coffee.h b/src/functional/just_do_coffee.h index 3951e006..cbc739c6 100644 --- a/src/functional/just_do_coffee.h +++ b/src/functional/just_do_coffee.h @@ -5,8 +5,9 @@ #include "utils.h" #include "../peripherals/peripherals.h" #include "../peripherals/pump.h" -#include "../eeprom_data/eeprom_data.h" +#include "gaggia_settings.h" #include "sensors_state.h" +#include "system_state.h" #include @@ -22,9 +23,9 @@ enum class HEATING { MODE_hotWater }; -void justDoCoffee(const eepromValues_t &runningCfg, const SensorState ¤tState, const bool brewActive); +void justDoCoffee(const GaggiaSettings &gaggiaSettings, const SensorState ¤tState, float waterTemperature, const bool brewActive); void pulseHeaters(const uint32_t pulseLength, const int factor_1, const int factor_2, const bool brewActive); -void steamCtrl(const eepromValues_t &runningCfg, SensorState ¤tState); +void steamCtrl(const GaggiaSettings &runningCfg, const SensorState ¤tState, SystemState& systemState); void hotWaterMode(const SensorState ¤tState); #endif diff --git a/src/functional/predictive_weight.h b/src/functional/predictive_weight.h index 9d3e6ef4..1b43d89e 100644 --- a/src/functional/predictive_weight.h +++ b/src/functional/predictive_weight.h @@ -4,13 +4,12 @@ #include "profiling_phases.h" #include "sensors_state.h" -#include "../eeprom_data/eeprom_data.h" +#include "shot_profiler.h" extern int preInfusionFinishedPhaseIdx; constexpr float crossSectionalArea = 0.0026f; // avg puck crossectional area. constexpr float dynamicViscosity = 0.0002964f; // avg water dynamic viscosity at 90-95 celsius. bool predictiveTargetReached = false; -int predictivePreinfusionFinishedCheck = 0.f; class PredictiveWeight { private: @@ -20,6 +19,7 @@ class PredictiveWeight { float truePuckResistance; float resistanceDelta; float pressureDrop; + float peakPressure; public: bool preinfusionFinished; @@ -29,8 +29,8 @@ class PredictiveWeight { puckResistance(0.f), truePuckResistance(0.f), resistanceDelta(0.f), - pressureDrop(0.f) - {} + pressureDrop(0.f) { + } bool isOutputFlow() { return outputFlowStarted; @@ -41,7 +41,7 @@ class PredictiveWeight { return resistance; } - void update(const SensorState& state, CurrentPhase& phase, const eepromValues_t& cfg) { + void update(const SensorState& state, const CurrentPhase& phase, const GaggiaSettings& cfg) { // If at least 50ml have been pumped, there has to be output (unless the water is going to the void) // No point going through all the below logic if we hardsetting the predictive scales to start counting if (isForceStarted || outputFlowStarted || state.waterPumped >= 65.f) { @@ -49,8 +49,10 @@ class PredictiveWeight { return; } float previousPuckResistance = puckResistance; + puckResistance = state.smoothedPressure * 1000.f / state.smoothedPumpFlow; // Resistance in mBar * s / g resistanceDelta = puckResistance - previousPuckResistance; + peakPressure = fmaxf(peakPressure, state.smoothedPressure); pressureDrop = state.smoothedPressure * 10.f; pressureDrop -= pressureDrop - state.pumpClicks; pressureDrop = pressureDrop > 0.f ? pressureDrop : 1.f; @@ -61,46 +63,23 @@ class PredictiveWeight { as well as there being enough pressure for water to wet the puck enough to start the output. On profiles whare pressure drop is of concern ~1 bar of drop is the point where liquid output starts. */ - bool phaseTypePressure = phase.getType() == PHASE_TYPE::PHASE_TYPE_PRESSURE; - predictivePreinfusionFinishedCheck = phase.getIndex(); - preinfusionFinished = ACTIVE_PROFILE(cfg).preinfusionState && ACTIVE_PROFILE(cfg).soakState - ? predictivePreinfusionFinishedCheck >= preInfusionFinishedPhaseIdx-1 - : predictivePreinfusionFinishedCheck >= preInfusionFinishedPhaseIdx; - - bool soakEnabled = false; - soakEnabled = ACTIVE_PROFILE(cfg).soakState - ? phaseTypePressure - ? ACTIVE_PROFILE(cfg).soakTimePressure > 0 - : ACTIVE_PROFILE(cfg).soakTimeFlow > 0 - : false; - float pressureTarget = phaseTypePressure ? ACTIVE_PROFILE(cfg).preinfusionBar : ACTIVE_PROFILE(cfg).preinfusionFlowPressureTarget; - - - // Pressure has to reach full pi target bar threshold. - if (!preinfusionFinished && soakEnabled) { - if (predictiveTargetReached) { - // pressure drop needs to be around 1.5bar since target hit for output flow to be considered started. - if (pressureTarget - state.smoothedPressure > 1.f) outputFlowStarted = true; - else return; - } - if (!predictiveTargetReached && state.smoothedPressure < pressureTarget) { - return; - } else { - predictiveTargetReached = true; - return; - } + // The following should catch soak phase dripping. We've reached a peak that is over 2bars and the pressure + // is now dropping. Has dropped at least 1 bar. + if (peakPressure > 2.f && state.smoothedPressure < peakPressure - 1.f) { + outputFlowStarted = true; } + // Pressure has to cross the 2 bar threshold. if (state.smoothedPressure < 2.1f) return; - if (phaseTypePressure) { + if (phase.getType() == PhaseType::PRESSURE) { // If the pressure or flow are raising too fast dismiss the spike from the output. if (fabsf(state.pressureChangeSpeed) > 5.f || fabsf(state.pumpFlowChangeSpeed) > 2.f) return; // If flow is too big for given pressure or the delta is changing too quickly we're not there yet if (resistanceDelta > 500.f || puckResistance < 1100.f) return; } - // If flow is too big for given pressure or the delta is changing too quickly we're not there yet + // If puck resistance is too high we're not there yet // if (puckResistance < 1100.f) return; if (truePuckResistance < -0.015f) return; @@ -113,12 +92,12 @@ class PredictiveWeight { } void reset() { + peakPressure = 0.f; puckResistance = 0.f; resistanceDelta = 0.f; isForceStarted = false; outputFlowStarted = false; predictiveTargetReached = false; - } }; diff --git a/src/functional/shot_profiler.cpp b/src/functional/shot_profiler.cpp new file mode 100644 index 00000000..3103021c --- /dev/null +++ b/src/functional/shot_profiler.cpp @@ -0,0 +1,96 @@ +#include "shot_profiler.h" + +//----------------------------------------------------------------------// +//-------------------------- PhaseProfiler -----------------------------// +//----------------------------------------------------------------------// + +PhaseProfiler::PhaseProfiler() {} +PhaseProfiler::PhaseProfiler(const Profile& profile) : profile(&profile) {} + +void PhaseProfiler::updatePhase(uint32_t timeInShot, SensorState& state) { + size_t phaseIdx = currentPhaseIdx; + uint32_t timeInPhase = timeInShot - phaseChangedSnapshot.timeInShot; + + if (phaseIdx >= profile->phaseCount() || profile->globalStopConditions.isReached(state, timeInShot)) { + currentPhaseIdx = profile->phaseCount(); + currentPhase.update(currentPhaseIdx - 1, profile->phases[phaseIdx], timeInPhase); + return; + } + + if (!profile->phases[phaseIdx].skip && !profile->phases[phaseIdx].isStopConditionReached(state, timeInShot, phaseChangedSnapshot)) { + currentPhase.update(phaseIdx, profile->phases[phaseIdx], timeInPhase); + return; + } + + currentPhase.update(phaseIdx, profile->phases[phaseIdx], timeInPhase); + phaseChangedSnapshot = buildShotSnapshot(timeInShot, state, *this); + currentPhaseIdx += 1; + updatePhase(timeInShot, state); +} + +// Gets the profiling phase we should be in based on the timeInShot and the Sensors state +const CurrentPhase& PhaseProfiler::getCurrentPhase() const { + return currentPhase; +} + +bool PhaseProfiler::isFinished() { + return currentPhaseIdx >= profile->phaseCount(); +} + +void PhaseProfiler::reset() { + currentPhaseIdx = 0; + phaseChangedSnapshot = ShotSnapshot{}; + currentPhase.update(0, profile->phases[0], 0); +} + +const Profile& PhaseProfiler::getActiveProfile() const { + return *(profile); +} + +//----------------------------------------------------------------------// +//--------------------------- CurrentPhase -----------------------------// +//----------------------------------------------------------------------// +CurrentPhase::CurrentPhase(int index, const Phase& phase, uint32_t timeInPhase, const ShotSnapshot& shotSnapshotAtStart) : index(index), phase{ &phase }, timeInPhase(timeInPhase), shotSnapshotAtStart{ &shotSnapshotAtStart } {} +CurrentPhase::CurrentPhase(const CurrentPhase& currentPhase) : index(currentPhase.index), phase{ currentPhase.phase }, timeInPhase(currentPhase.timeInPhase), shotSnapshotAtStart{ currentPhase.shotSnapshotAtStart } {} + +const Phase* CurrentPhase::getPhase() const { return phase; } + +PhaseType CurrentPhase::getType() const { return phase->type; } + +int CurrentPhase::getIndex() const { return index; } + +uint32_t CurrentPhase::getTimeInPhase() const { return timeInPhase; } + +float CurrentPhase::getTarget() const { return phase->getTarget(timeInPhase, *shotSnapshotAtStart); } + +float CurrentPhase::getRestriction() const { return phase->getRestriction(); } + +void CurrentPhase::update(int index, const Phase& phase, uint32_t timeInPhase) { + CurrentPhase::index = index; + CurrentPhase::phase = &phase; + CurrentPhase::timeInPhase = timeInPhase; +} + +//----------------------------------------------------------------------// +//------------------------- ShotSnapshot -------------------------------// +//----------------------------------------------------------------------// + +ShotSnapshot buildShotSnapshot(uint32_t timeInShot, const SensorState& state, const PhaseProfiler& profiler) { + const CurrentPhase& phase = profiler.getCurrentPhase(); + const Profile& activeProfile = profiler.getActiveProfile(); + float targetFlow = (phase.getType() == PhaseType::FLOW) ? phase.getTarget() : phase.getRestriction(); + float targetPressure = (phase.getType() == PhaseType::PRESSURE) ? phase.getTarget() : phase.getRestriction(); + + return ShotSnapshot{ + .timeInShot = timeInShot, + .pressure = state.smoothedPressure, + .pumpFlow = state.smoothedPumpFlow, + .weightFlow = state.smoothedWeightFlow, + .temperature = state.waterTemperature, + .shotWeight = state.shotWeight, + .waterPumped = state.waterPumped, + .targetTemperature = activeProfile.waterTemperature, + .targetPumpFlow = targetFlow, + .targetPressure = targetPressure + }; +}; diff --git a/src/functional/shot_profiler.h b/src/functional/shot_profiler.h new file mode 100644 index 00000000..1fe4b180 --- /dev/null +++ b/src/functional/shot_profiler.h @@ -0,0 +1,49 @@ +#ifndef SHOT_PROFILER_H +#define SHOT_PROFILER_H + +#include "profiling_phases.h" + +class CurrentPhase { +private: + int index; + const Phase* phase; + uint32_t timeInPhase; + const ShotSnapshot* shotSnapshotAtStart; + +public: + CurrentPhase(int index, const Phase& phase, uint32_t timeInPhase, const ShotSnapshot& shotSnapshotAtStart); + CurrentPhase(const CurrentPhase& currentPhase); + + const Phase* getPhase() const; + PhaseType getType() const; + int getIndex() const; + uint32_t getTimeInPhase() const; + float getTarget() const; + float getRestriction() const; + void update(int index, const Phase& phase, uint32_t timeInPhase); +}; + +class PhaseProfiler { +private: + const Profile* profile = nullptr; + size_t currentPhaseIdx = 0; // The index at which the profiler currently is. + ShotSnapshot phaseChangedSnapshot = ShotSnapshot{ 0, 0, 0, 0, 0, 0 }; // State when the profiler move to this currentPhaseIdx + CurrentPhase currentPhase = CurrentPhase(0, profile->phases[0], 0, phaseChangedSnapshot); + +public: + PhaseProfiler(); + explicit PhaseProfiler(const Profile& profile); + // Gets the profiling phase we should be in based on the timeInShot and the Sensors state + void setProfile(const Profile& profile) { this->profile = &profile; }; + void updatePhase(uint32_t timeInShot, SensorState& state); + const CurrentPhase& getCurrentPhase() const; + bool isFinished(); + void reset(); + const Profile& getActiveProfile() const; +}; + +// Helper functions + +ShotSnapshot buildShotSnapshot(uint32_t timeInShot, const SensorState& state, const PhaseProfiler& phase); + +#endif diff --git a/src/gaggiuino.h b/src/gaggiuino.h index 5d0cf4b9..61587d55 100644 --- a/src/gaggiuino.h +++ b/src/gaggiuino.h @@ -6,8 +6,6 @@ #include #include "log.h" -#include "eeprom_data/eeprom_data.h" -#include "lcd/lcd.h" #include "peripherals/internal_watchdog.h" #include "peripherals/pump.h" #include "peripherals/pressure_sensor.h" @@ -19,6 +17,7 @@ #include "functional/descale.h" #include "functional/just_do_coffee.h" #include "functional/predictive_weight.h" +#include "functional/shot_profiler.h" #include "profiling_phases.h" #include "peripherals/esp_comms.h" #include "peripherals/led.h" @@ -26,14 +25,15 @@ // Define some const values #if defined SINGLE_BOARD - #define GET_KTYPE_READ_EVERY 70 // max31855 amp module data read interval not recommended to be changed to lower than 70 (ms) +#define GET_KTYPE_READ_EVERY 70 // max31855 amp module data read interval not recommended to be changed to lower than 70 (ms) #else - #define GET_KTYPE_READ_EVERY 250 // max6675 amp module data read interval not recommended to be changed to lower than 250 (ms) +#define GET_KTYPE_READ_EVERY 250 // max6675 amp module data read interval not recommended to be changed to lower than 250 (ms) #endif #define GET_PRESSURE_READ_EVERY 10 // Pressure refresh interval (ms) -#define GET_SCALES_READ_EVERY 100 // Scales refresh interval (ms) -#define REFRESH_SCREEN_EVERY 150 // Screen refresh interval (ms) -#define REFRESH_FLOW_EVERY 50 // Flow refresh interval (ms) +#define GET_SCALES_READ_EVERY 200 // Scales refresh interval (ms) +#define GET_SCALES_ACCIDENTAL 2000u // Accidental touches or placing cup on scales post brew activation timeout +#define REFRESH_ESP_DATA_EVERY 100 // Screen refresh interval (ms) +#define REFRESH_FLOW_EVERY 200 // Flow refresh interval (ms) #define HEALTHCHECK_EVERY 30000 // System checks happen every 30sec #define BOILER_FILL_START_TIME 3000UL // Boiler fill start time - 3 sec since system init. #define BOILER_FILL_TIMEOUT 8000UL // Boiler fill timeout - 8sec since system init. @@ -41,29 +41,17 @@ #define SYS_PRESSURE_IDLE 0.7f // System pressure threshold at idle #define MIN_WATER_LVL 10u // Min allowable tank water lvl -enum class OPERATION_MODES { - OPMODE_straight9Bar, - OPMODE_justPreinfusion, - OPMODE_justPressureProfile, - OPMODE_manual, - OPMODE_preinfusionAndPressureProfile, - OPMODE_flush, - OPMODE_descale, - OPMODE_flowPreinfusionStraight9BarProfiling, - OPMODE_justFlowBasedProfiling, - OPMODE_steam, - OPMODE_FlowBasedPreinfusionPressureBasedProfiling, - OPMODE_everythingFlowProfiled, - OPMODE_pressureBasedPreinfusionAndFlowProfile -} ; - -//Some consts -#ifndef LEGO_VALVE_RELAY -const float calibrationPressure = 2.f; -#else -const float calibrationPressure = 0.65f; +// If not defined in the extra_defines.ini use default range +#if not defined(TOF_START) || not defined(TOF_END) +#define TOF_START 6u +#define TOF_END 130u #endif +const uint16_t tofStartValue = TOF_START; // Tof offset when tank is full +const uint16_t tofEndValue = TOF_END; // Tof offset when tank is nearly empty +const float weightRateThreshold = 9.f; // The rate of weigh random change(aka accidental scales touching) +const float weightIncreaseThreshold = 40.f; // Accounting for placing a cup on the scales after initiating brew + //Timers unsigned long systemHealthTimer; unsigned long pageRefreshTimer; @@ -71,6 +59,7 @@ unsigned long pressureTimer; unsigned long brewingTimer; unsigned long thermoTimer; unsigned long scalesTimer; +unsigned long scalesTimeout; unsigned long flowTimer; unsigned long steamTime; diff --git a/src/gaggiuino.ino b/src/gaggiuino.ino index cdfb2ecd..d38485ca 100644 --- a/src/gaggiuino.ino +++ b/src/gaggiuino.ino @@ -1,26 +1,24 @@ /* 09:32 15/03/2023 - change triggering comment */ #pragma GCC optimize ("Ofast") +// #define STM32F4 // This define has to be here otherwise the include of FlashStorage_STM32.h bellow fails. #if defined(DEBUG_ENABLED) #include "dbg.h" #endif #include "gaggiuino.h" -SimpleKalmanFilter smoothPressure(0.6f, 0.6f, 0.1f); -SimpleKalmanFilter smoothPumpFlow(0.1f, 0.1f, 0.01f); -SimpleKalmanFilter smoothScalesFlow(0.5f, 0.5f, 0.01f); -SimpleKalmanFilter smoothConsideredFlow(0.1f, 0.1f, 0.1f); +SimpleKalmanFilter smoothPressure(0.6f, 0.6f, 0.0589f); +SimpleKalmanFilter smoothPumpFlow(0.4f, 0.4f, 0.25f); +SimpleKalmanFilter smoothScalesFlow(0.1f, 0.1f, 0.0285f); -//default phases. Updated in updateProfilerPhases. -Profile profile; -PhaseProfiler phaseProfiler{profile}; +Profile manualProfile; +Profile activeProfile; +PhaseProfiler phaseProfiler; PredictiveWeight predictiveWeight; SensorState currentState; -OPERATION_MODES selectedOperationalMode; - -eepromValues_t runningCfg; +GaggiaSettings runningCfg; SystemState systemState; @@ -48,9 +46,6 @@ void setup(void) { closeValve(); LOG_INFO("Valve closed"); - lcdInit(); - LOG_INFO("LCD Init"); - #if defined(DEBUG_ENABLED) // Debug init if enabled dbgInit(); @@ -60,42 +55,25 @@ void setup(void) { // Initialise comms library for talking to the ESP mcu espCommsInit(); - // Initialize LED - led.begin(); - led.setColor(9u, 0u, 9u); // WHITE - // Init the tof sensor - tof.init(currentState); - - // Initialising the saved values or writing defaults if first start - eepromInit(); - runningCfg = eepromGetCurrentValues(); - LOG_INFO("EEPROM Init"); - cpsInit(runningCfg); LOG_INFO("CPS Init"); thermocoupleInit(); LOG_INFO("Thermocouple Init"); - lcdUploadCfg(runningCfg); - LOG_INFO("LCD cfg uploaded"); - adsInit(); LOG_INFO("Pressure sensor init"); // Scales handling - scalesInit(runningCfg.scalesF1, runningCfg.scalesF2); + scalesInit(runningCfg.scales); LOG_INFO("Scales init"); // Pump init - pumpInit(runningCfg.powerLineFrequency, runningCfg.pumpFlowAtZero); + pumpInit(currentState.powerLineFrequency, runningCfg.system.pumpFlowAtZero); LOG_INFO("Pump init"); - pageValuesRefresh(); - LOG_INFO("Setup sequence finished"); - - // Change LED colour on setup exit. - led.setColor(9u, 0u, 9u); // 64171 + ToFnLedInit(); + LOG_INFO("ToFnLed Init"); iwdcInit(); } @@ -108,13 +86,10 @@ void setup(void) { //Main loop where all the logic is continuously run void loop(void) { fillBoiler(); - if (lcdCurrentPageId != lcdLastCurrentPageId) pageValuesRefresh(); - lcdListen(); sensorsRead(); brewDetect(); modeSelect(); - lcdRefresh(); - espCommsSendSensorData(currentState); + espUpdateState(); sysHealthCheck(SYS_PRESSURE_IDLE); } @@ -143,41 +118,88 @@ static void sensorReadSwitches(void) { static void sensorsReadTemperature(void) { if (millis() > thermoTimer) { - currentState.temperature = thermocoupleRead() - runningCfg.offsetTemp; + currentState.temperature = thermocoupleRead() - runningCfg.boiler.offsetTemp; + + float brewTempSetPoint = activeProfile.waterTemperature + runningCfg.boiler.offsetTemp; + + currentState.waterTemperature = (currentState.temperature > activeProfile.waterTemperature && currentState.brewSwitchState) + ? currentState.temperature / (float)brewTempSetPoint + activeProfile.waterTemperature + : currentState.temperature; + thermoTimer = millis() + GET_KTYPE_READ_EVERY; } } +static Measurement handleTaringAndReadWeight() { + if (!systemState.tarePending) { // No tare needed just get weight + return scalesGetWeight(); + } + + // Tare is required. Invoke it. + scalesTare(); + weightMeasurements.clear(); + Measurement weight = scalesGetWeight(); + + if (fabsf(weight.value) < 0.5f) { // Tare was successful. return reading + systemState.tarePending = false; + return weight; + } else { // Tare was unsuccessful. return 0 weight. + return Measurement{ .value=0.f, .millis = millis()}; + } +} + static void sensorsReadWeight(void) { - uint32_t elapsedTime = millis() - scalesTimer; + uint32_t currentMillis = millis(); + uint32_t elapsedTime = currentMillis - scalesTimer; + uint32_t weightBumpTimeout = currentMillis - scalesTimeout; + static float initialWeight = 0.f; if (elapsedTime > GET_SCALES_READ_EVERY) { - currentState.scalesPresent = scalesIsPresent(); - if (currentState.scalesPresent) { - if (currentState.tarePending) { - scalesTare(); - weightMeasurements.clear(); - weightMeasurements.add(scalesGetWeight()); - currentState.tarePending = false; - } - else { - weightMeasurements.add(scalesGetWeight()); - } - currentState.weight = weightMeasurements.latest().value; - - if (brewActive) { - currentState.shotWeight = currentState.tarePending ? 0.f : currentState.weight; - currentState.weightFlow = fmax(0.f, weightMeasurements.measurementChange().changeSpeed()); + systemState.scalesPresent = scalesIsPresent(); + + if (systemState.scalesPresent) { + const auto weight = handleTaringAndReadWeight(); + weightMeasurements.add(weight); + currentState.weight = weightMeasurements.getLatest().value; + const float weightFlow = weightMeasurements.getMeasurementChange().speed(); + const bool isBrew = systemState.operationMode != OperationMode::FLUSH + || systemState.operationMode != OperationMode::FLUSH_AUTO + || systemState.operationMode != OperationMode::DESCALE + || systemState.operationMode != OperationMode::STEAM; + + if (brewActive && isBrew) { + // If there's a sudden jump in weight + bool isChangeRateHigh = weightFlow > weightRateThreshold; + bool isCupPlaced = currentState.weight - initialWeight > 0.f + && currentState.weight - initialWeight >= weightIncreaseThreshold; + if (!systemState.tarePending && (isChangeRateHigh || isCupPlaced)) { + // Ignore accidental weight bumps + if (weightBumpTimeout < GET_SCALES_ACCIDENTAL) { + scalesTimer = currentMillis; + return; + } else { // Weight increased drastically and is constant ? mark for tare. + systemState.tarePending = true; + scalesTimer = currentMillis; + scalesTimeout = currentMillis; + } + } + currentState.shotWeight = systemState.tarePending ? 0.f : currentState.weight; + initialWeight = currentState.shotWeight; + + // Only take flow measurements when tare is not pending. + currentState.weightFlow = systemState.tarePending + ? currentState.weightFlow + : fmax(0.f, weightFlow); currentState.smoothedWeightFlow = smoothScalesFlow.updateEstimate(currentState.weightFlow); } } - scalesTimer = millis(); + scalesTimer = currentMillis; + scalesTimeout = currentMillis; } } static void sensorsReadPressure(void) { uint32_t elapsedTime = millis() - pressureTimer; - if (elapsedTime > GET_PRESSURE_READ_EVERY) { float elapsedTimeSec = elapsedTime / 1000.f; currentState.pressure = getPressure(); @@ -197,16 +219,29 @@ static long sensorsReadFlow(float elapsedTimeSec) { previousSmoothedPumpFlow = currentState.smoothedPumpFlow; // Some flow smoothing currentState.smoothedPumpFlow = smoothPumpFlow.updateEstimate(currentState.pumpFlow); + float averagedSmoothedPumpFlow = getAverage(currentState.smoothedPumpFlow); + + // SMall corrections + currentState.smoothedPumpFlow = (fabs(currentState.smoothedPumpFlow - averagedSmoothedPumpFlow) <= 0.7f + && fabs(currentState.smoothedPumpFlow - averagedSmoothedPumpFlow) > 0.2f) + ? (averagedSmoothedPumpFlow + currentState.smoothedPumpFlow) / 2.f + : currentState.smoothedPumpFlow; + currentState.pumpFlowChangeSpeed = (currentState.smoothedPumpFlow - previousSmoothedPumpFlow) / elapsedTimeSec; return pumpClicks; } static void calculateWeightAndFlow(void) { + // Define variables to track peak pressure and related time + static float peakPressure = 0.0f; + static uint32_t peakTime = millis(); + static bool pressureDropping = false; + static uint32_t timeToPressureDrop = 0; uint32_t elapsedTime = millis() - flowTimer; if (brewActive) { // Marking for tare in case smth has gone wrong and it has exited tare already. - if (currentState.weight < -.3f) currentState.tarePending = true; + if (currentState.weight < -.3f) systemState.tarePending = true; if (elapsedTime > REFRESH_FLOW_EVERY) { flowTimer = millis(); @@ -214,23 +249,33 @@ static void calculateWeightAndFlow(void) { long pumpClicks = sensorsReadFlow(elapsedTimeSec); float consideredFlow = currentState.smoothedPumpFlow * elapsedTimeSec; // Update predictive class with our current phase - CurrentPhase& phase = phaseProfiler.getCurrentPhase(); + const CurrentPhase& phase = phaseProfiler.getCurrentPhase(); predictiveWeight.update(currentState, phase, runningCfg); // Start the predictive weight calculations when conditions are true - if (predictiveWeight.isOutputFlow() || currentState.weight > 0.4f) { + if (predictiveWeight.isOutputFlow()) { float flowPerClick = getPumpFlowPerClick(currentState.smoothedPressure); float actualFlow = (consideredFlow > pumpClicks * flowPerClick) ? consideredFlow : pumpClicks * flowPerClick; - /* Probabilistically the flow is lower if the shot is just started winding up and we're flow profiling, - once pressure stabilises around the setpoint the flow is either stable or puck restriction is high af. */ - if ((ACTIVE_PROFILE(runningCfg).mfProfileState || ACTIVE_PROFILE(runningCfg).tpType) && currentState.pressureChangeSpeed > 0.15f) { - if ((currentState.smoothedPressure < ACTIVE_PROFILE(runningCfg).mfProfileStart * 0.9f) - || (currentState.smoothedPressure < ACTIVE_PROFILE(runningCfg).tfProfileStart * 0.9f)) { - actualFlow *= 0.3f; - } + + // Check for peak pressure + if (currentState.smoothedPressure > peakPressure) { + peakPressure = currentState.smoothedPressure; + peakTime = millis(); + pressureDropping = false; + } + // Check for pressure drop by 1 bar from the peak pressure + if (!pressureDropping && (peakPressure - currentState.smoothedPressure) >= 1.0f) { + timeToPressureDrop = millis() - peakTime; // Save the pressure drop time + pressureDropping = true; // Set the flag to prevent further checks + } + /* TO-DO: Account for the initial moments of a shot winding up, + usually the flow starts lower and increases considerably in the next few seconds */ + if (!systemState.scalesPresent) { + currentState.shotWeight = currentState.shotWeight + actualFlow; + // Pressure dropping fast after PI, most likely low puck resistance == high cup flow + if(pressureDropping && timeToPressureDrop < 1000u && pumpClicks < 5l) + currentState.shotWeight *= 10.f; } - currentState.consideredFlow = smoothConsideredFlow.updateEstimate(actualFlow); - currentState.shotWeight = currentState.scalesPresent ? currentState.shotWeight : currentState.shotWeight + actualFlow; } currentState.waterPumped += consideredFlow; } @@ -238,38 +283,28 @@ static void calculateWeightAndFlow(void) { currentState.consideredFlow = 0.f; currentState.pumpClicks = getAndResetClickCounter(); flowTimer = millis(); + peakPressure = 0.0f; + peakTime = millis(); + pressureDropping = false; + timeToPressureDrop = 0; } } // return the reading in mm of the tank water level. static void readTankWaterLevel(void) { - if (lcdCurrentPageId == NextionPage::Home) { - // static uint32_t tof_timeout = millis(); - // if (millis() >= tof_timeout) { + if (!brewActive || systemState.operationMode == OperationMode::DESCALE) { currentState.waterLvl = tof.readLvl(); - // tof_timeout = millis() + 500; - // } } } -//############################################################################################################################## -//############################################______PAGE_CHANGE_VALUES_REFRESH_____############################################# -//############################################################################################################################## -static void pageValuesRefresh() { - // Read the page we're landing in: leaving keyboard page means a value could've changed in it - if (lcdLastCurrentPageId == NextionPage::KeyboardNumeric) lcdFetchPage(runningCfg, lcdCurrentPageId, runningCfg.activeProfile); - // Or maybe it's a page that needs constant polling - else if (lcdLastCurrentPageId == NextionPage::Led) lcdFetchPage(runningCfg, lcdCurrentPageId, runningCfg.activeProfile); - // Finally read the page we left, as it could've been changed in place (e.g. boolean toggles) - else lcdFetchPage(runningCfg, lcdLastCurrentPageId, runningCfg.activeProfile); - - homeScreenScalesEnabled = lcdGetHomeScreenScalesEnabled(); - // MODE_SELECT should always be LAST - selectedOperationalMode = (OPERATION_MODES) lcdGetSelectedOperationalMode(); - - updateProfilerPhases(); +// Function to track time since system has started +static unsigned long getTimeSinceInit(void) { + static unsigned long startTime = millis(); + return millis() - startTime; +} - lcdLastCurrentPageId = lcdCurrentPageId; +static void updateStartupTimer() { + systemState.timeAlive = getTimeSinceInit() / 1000; } //############################################################################################# @@ -278,430 +313,122 @@ static void pageValuesRefresh() { static void modeSelect(void) { if (!systemState.startupInitFinished) return; - switch (selectedOperationalMode) { - //REPLACE ALL THE BELOW WITH OPMODE_auto_profiling - case OPERATION_MODES::OPMODE_straight9Bar: - case OPERATION_MODES::OPMODE_justPreinfusion: - case OPERATION_MODES::OPMODE_justPressureProfile: - case OPERATION_MODES::OPMODE_preinfusionAndPressureProfile: - case OPERATION_MODES::OPMODE_flowPreinfusionStraight9BarProfiling: - case OPERATION_MODES::OPMODE_justFlowBasedProfiling: - case OPERATION_MODES::OPMODE_FlowBasedPreinfusionPressureBasedProfiling: - case OPERATION_MODES::OPMODE_everythingFlowProfiled: - case OPERATION_MODES::OPMODE_pressureBasedPreinfusionAndFlowProfile: - nonBrewModeActive = false; + switch (systemState.operationMode) { + case OperationMode::BREW_AUTO: if (currentState.hotWaterSwitchState) hotWaterMode(currentState); - else if (currentState.steamSwitchState) steamCtrl(runningCfg, currentState); + else if (currentState.steamSwitchState) steamCtrl(runningCfg, currentState, systemState); else { profiling(); steamTime = millis(); } break; - case OPERATION_MODES::OPMODE_manual: - nonBrewModeActive = false; + case OperationMode::BREW_MANUAL: if (!currentState.steamSwitchState) steamTime = millis(); - manualFlowControl(); + profiling(); break; - case OPERATION_MODES::OPMODE_flush: - nonBrewModeActive = true; + case OperationMode::FLUSH: if (!currentState.steamSwitchState) steamTime = millis(); backFlush(currentState); - brewActive ? setBoilerOff() : justDoCoffee(runningCfg, currentState, false); + brewActive ? setBoilerOff() : justDoCoffee(runningCfg, currentState, activeProfile.waterTemperature, false); break; - case OPERATION_MODES::OPMODE_steam: - nonBrewModeActive = true; - steamCtrl(runningCfg, currentState); + case OperationMode::FLUSH_AUTO: + if (!currentState.steamSwitchState) steamTime = millis(); + flushAuto(); + break; + case OperationMode::STEAM: + steamCtrl(runningCfg, currentState, systemState); if (!currentState.steamSwitchState) { brewActive ? flushActivated() : flushDeactivated(); - steamCtrl(runningCfg, currentState); - pageValuesRefresh(); + steamCtrl(runningCfg, currentState, systemState); } break; - case OPERATION_MODES::OPMODE_descale: - nonBrewModeActive = true; + case OperationMode::DESCALE: if (!currentState.steamSwitchState) steamTime = millis(); deScale(runningCfg, currentState); break; default: - pageValuesRefresh(); break; } } //############################################################################################# -//################################____LCD_REFRESH_CONTROL___################################### +//################################____EPS_COMMS_CONTROL___################################### //############################################################################################# -static void lcdRefresh(void) { - uint16_t tempDecimal; - +static void espUpdateState(void) { if (millis() > pageRefreshTimer) { - /*LCD pressure output, as a measure to beautify the graphs locking the live pressure read for the LCD alone*/ - #ifdef BEAUTIFY_GRAPH - lcdSetPressure(currentState.smoothedPressure * 10.f); - #else - lcdSetPressure( - currentState.pressure > 0.f - ? currentState.pressure * 10.f - : 0.f - ); - #endif - - /*LCD temp output*/ - float brewTempSetPoint = ACTIVE_PROFILE(runningCfg).setpoint + runningCfg.offsetTemp; - // float liveTempWithOffset = currentState.temperature - runningCfg.offsetTemp; - currentState.waterTemperature = (currentState.temperature > (float)ACTIVE_PROFILE(runningCfg).setpoint && currentState.brewSwitchState) - ? currentState.temperature / (float)brewTempSetPoint + (float)ACTIVE_PROFILE(runningCfg).setpoint - : currentState.temperature; - - lcdSetTemperature(std::floor((uint16_t)currentState.waterTemperature)); - - /*LCD weight & temp & water lvl output*/ - switch (lcdCurrentPageId) { - case NextionPage::Home: - // temp decimal handling - tempDecimal = (currentState.waterTemperature - (uint16_t)currentState.waterTemperature) * 10; - lcdSetTemperatureDecimal(tempDecimal); - // water lvl - lcdSetTankWaterLvl(currentState.waterLvl); - //weight - if (homeScreenScalesEnabled) lcdSetWeight(currentState.weight); - break; - case NextionPage::BrewGraph: - case NextionPage::BrewManual: - // temp decimal handling - tempDecimal = (currentState.waterTemperature - (uint16_t)currentState.waterTemperature) * 10; - lcdSetTemperatureDecimal(tempDecimal); - // If the weight output is a negative value lower than -0.8 you might want to tare again before extraction starts. - if (currentState.shotWeight) lcdSetWeight(currentState.shotWeight > -0.8f ? currentState.shotWeight : -0.9f); - /*LCD flow output*/ - lcdSetFlow( currentState.smoothedPumpFlow * 10.f); - break; - default: - break; // don't push needless data on other pages - } - - #ifdef DEBUG_ENABLED - lcdShowDebug(readTempSensor(), getAdsError()); - #endif - - /*LCD timer and warmup*/ - if (brewActive) { - lcdSetBrewTimer((millis() > brewingTimer) ? (int)((millis() - brewingTimer) / 1000) : 0); - lcdBrewTimerStart(); // nextion timer start - lcdWarmupStateStop(); // Flagging warmup notification on Nextion needs to stop (if enabled) + + if (brewActive && systemState.operationMode == OperationMode::BREW_AUTO) { + espCommsSendShotData(buildShotSnapshot(millis() - brewingTimer, currentState, phaseProfiler), 100); } else { - lcdBrewTimerStop(); // nextion timer stop + espCommsSendSystemState(systemState, 1000); + espCommsSendSensorData(currentState, 500); } - - pageRefreshTimer = millis() + REFRESH_SCREEN_EVERY; - } -} -//############################################################################################# -//###################################____SAVE_BUTTON____####################################### -//############################################################################################# -void tryEepromWrite(const eepromValues_t &eepromValues) { - bool success = eepromWrite(eepromValues); - watchdogReload(); // reload the watchdog timer on expensive operations - if (success) { - lcdShowPopup("Update successful!"); - } else { - lcdShowPopup("Data out of range!"); - } -} - -void lcdSwitchActiveToStoredProfile(const eepromValues_t & storedSettings) { - runningCfg.activeProfile = lcdGetSelectedProfile(); - ACTIVE_PROFILE(runningCfg) = storedSettings.profiles[runningCfg.activeProfile]; - updateProfilerPhases(); - lcdUploadProfile(runningCfg); -} - -// Save the desired temp values to EEPROM -void lcdSaveSettingsTrigger(void) { - LOG_VERBOSE("Saving values to EEPROM"); - - eepromValues_t eepromCurrentValues = eepromGetCurrentValues(); - lcdFetchPage(eepromCurrentValues, lcdCurrentPageId, runningCfg.activeProfile); - tryEepromWrite(eepromCurrentValues); -} - -void lcdSaveProfileTrigger(void) { - LOG_VERBOSE("Saving profile to EEPROM"); - - eepromValues_t eepromCurrentValues = eepromGetCurrentValues(); - lcdFetchCurrentProfile(eepromCurrentValues); - tryEepromWrite(eepromCurrentValues); -} - -void lcdResetSettingsTrigger(void) { - tryEepromWrite(eepromGetDefaultValues()); -} - -void lcdLoadDefaultProfileTrigger(void) { - lcdSwitchActiveToStoredProfile(eepromGetDefaultValues()); - - lcdShowPopup("Profile loaded!"); -} - -void lcdScalesTareTrigger(void) { - LOG_VERBOSE("Tare scales"); - if (currentState.scalesPresent) currentState.tarePending = true; -} - -void lcdHomeScreenScalesTrigger(void) { - LOG_VERBOSE("Scales enabled or disabled"); - homeScreenScalesEnabled = lcdGetHomeScreenScalesEnabled(); -} - -void lcdBrewGraphScalesTareTrigger(void) { - LOG_VERBOSE("Predictive scales tare action completed!"); - if (currentState.scalesPresent) { - currentState.tarePending = true; - } - else { - currentState.shotWeight = 0.f; - predictiveWeight.setIsForceStarted(true); - } -} - -void lcdRefreshElementsTrigger(void) { - - eepromValues_t eepromCurrentValues = eepromGetCurrentValues(); - - switch (lcdCurrentPageId) { - case NextionPage::BrewPreinfusion: - ACTIVE_PROFILE(eepromCurrentValues).preinfusionFlowState = lcdGetPreinfusionFlowState(); - break; - case NextionPage::BrewProfiling: - ACTIVE_PROFILE(eepromCurrentValues).mfProfileState = lcdGetProfileFlowState(); - break; - case NextionPage::BrewTransitionProfile: - ACTIVE_PROFILE(eepromCurrentValues).tpType = lcdGetTransitionFlowState(); - break; - default: - lcdShowPopup("Nope!"); - break; + pageRefreshTimer = millis() + REFRESH_ESP_DATA_EVERY; } - - // Make the necessary changes - uploadPageCfg(eepromCurrentValues, systemState); - // refresh the screen elements - pageValuesRefresh(); } -void lcdQuickProfileSwitch(void) { - lcdSwitchActiveToStoredProfile(eepromGetCurrentValues()); - lcdShowPopup("Profile switched!"); -} - -//############################################################################################# -//###############################____PROFILING_CONTROL____##################################### -//############################################################################################# -static void updateProfilerPhases(void) { - float shotTarget = -1.f; - - if (ACTIVE_PROFILE(runningCfg).stopOnWeightState) { - shotTarget = (ACTIVE_PROFILE(runningCfg).shotStopOnCustomWeight < 1.f) - ? ACTIVE_PROFILE(runningCfg).shotDose * ACTIVE_PROFILE(runningCfg).shotPreset - : ACTIVE_PROFILE(runningCfg).shotStopOnCustomWeight; - } - - //update global stop conditions (currently only stopOnWeight is configured in nextion) - profile.globalStopConditions = GlobalStopConditions{ .weight=shotTarget }; - - profile.clear(); - - //Setup release pressure + fill@7ml/sec - if (runningCfg.basketPrefill) { - addFillBasketPhase(7.f); - } - - // Setup pre-infusion if needed - if (ACTIVE_PROFILE(runningCfg).preinfusionState) { - addPreinfusionPhases(); - } - - // Setup the soak phase if neecessary - if (ACTIVE_PROFILE(runningCfg).soakState) { - addSoakPhase(); - } - preInfusionFinishedPhaseIdx = profile.phaseCount(); - - addMainExtractionPhasesAndRamp(); +void onProfileReceived(const Profile& newProfile) { + activeProfile = newProfile; } -void addPreinfusionPhases() { - if (ACTIVE_PROFILE(runningCfg).preinfusionFlowState) { // flow based PI enabled - float isPressureAbove = ACTIVE_PROFILE(runningCfg).preinfusionPressureAbove ? ACTIVE_PROFILE(runningCfg).preinfusionFlowPressureTarget : -1.f; - float isWeightAbove = ACTIVE_PROFILE(runningCfg).preinfusionWeightAbove > 0.f ? ACTIVE_PROFILE(runningCfg).preinfusionWeightAbove : -1.f; - float isWaterPumped = ACTIVE_PROFILE(runningCfg).preinfusionFilled > 0.f ? ACTIVE_PROFILE(runningCfg).preinfusionFilled : -1.f; +void onGaggiaSettingsReceived(const GaggiaSettings& newSettings) { + GaggiaSettings previous = runningCfg; + runningCfg = newSettings; + if (!systemState.startupInitFinished) return; - addFlowPhase(Transition{ ACTIVE_PROFILE(runningCfg).preinfusionFlowVol }, ACTIVE_PROFILE(runningCfg).preinfusionFlowPressureTarget, ACTIVE_PROFILE(runningCfg).preinfusionFlowTime * 1000, isPressureAbove, -1, isWeightAbove, isWaterPumped); + if (!(previous.scales == runningCfg.scales)) { + scalesInit(runningCfg.scales); } - else { // pressure based PI enabled - // For now handling phase switching on restrictions here but as this grow will have to deal with it otherwise. - float isPressureAbove = ACTIVE_PROFILE(runningCfg).preinfusionPressureAbove ? ACTIVE_PROFILE(runningCfg).preinfusionBar : -1.f; - float isWeightAbove = ACTIVE_PROFILE(runningCfg).preinfusionWeightAbove > 0.f ? ACTIVE_PROFILE(runningCfg).preinfusionWeightAbove : -1.f; - float isWaterPumped = ACTIVE_PROFILE(runningCfg).preinfusionFilled > 0.f ? ACTIVE_PROFILE(runningCfg).preinfusionFilled : -1.f; - - addPressurePhase(Transition{ ACTIVE_PROFILE(runningCfg).preinfusionBar }, ACTIVE_PROFILE(runningCfg).preinfusionPressureFlowTarget, ACTIVE_PROFILE(runningCfg).preinfusionSec * 1000, isPressureAbove, -1, isWeightAbove, isWaterPumped); + if (previous.system.pumpFlowAtZero != runningCfg.system.pumpFlowAtZero) { + pumpInit(currentState.powerLineFrequency, runningCfg.system.pumpFlowAtZero); } } -void addSoakPhase() { - uint16_t phaseSoak = ACTIVE_PROFILE(runningCfg).preinfusionFlowState ? ACTIVE_PROFILE(runningCfg).soakTimeFlow : ACTIVE_PROFILE(runningCfg).soakTimePressure; - float maintainFlow = ACTIVE_PROFILE(runningCfg).soakKeepFlow > 0.f ? ACTIVE_PROFILE(runningCfg).soakKeepFlow : -1.f; - float maintainPressure = ACTIVE_PROFILE(runningCfg).soakKeepPressure > 0.f ? ACTIVE_PROFILE(runningCfg).soakKeepPressure : -1.f; - float isPressureBelow = ACTIVE_PROFILE(runningCfg).soakBelowPressure > 0.f ? ACTIVE_PROFILE(runningCfg).soakBelowPressure : -1.f; - float isPressureAbove = ACTIVE_PROFILE(runningCfg).soakAbovePressure > 0.f ? ACTIVE_PROFILE(runningCfg).soakAbovePressure : -1.f; - float isWeightAbove = ACTIVE_PROFILE(runningCfg).soakAboveWeight > 0.f ? ACTIVE_PROFILE(runningCfg).soakAboveWeight : -1.f; - - if (maintainPressure > 0.f) - addPressurePhase(Transition{maintainPressure}, (maintainFlow > 0.f ? maintainFlow : 2.5f), phaseSoak * 1000, isPressureAbove, isPressureBelow, isWeightAbove, -1); - else if(maintainFlow > 0.f) - addFlowPhase(Transition{maintainFlow}, -1, phaseSoak * 1000, isPressureAbove, isPressureBelow, isWeightAbove, -1); - else - addPressurePhase(Transition{maintainPressure}, maintainFlow, phaseSoak * 1000, isPressureAbove, isPressureBelow, isWeightAbove, -1); -} - -void addMainExtractionPhasesAndRamp() { - int rampPhaseIndex = -1; - - if (ACTIVE_PROFILE(runningCfg).profilingState) { - if (ACTIVE_PROFILE(runningCfg).tpState) { - // ----------------- Transition Profile ----------------- // - if (ACTIVE_PROFILE(runningCfg).tpType) { // flow based profiling enabled - /* Setting the phase specific restrictions */ - /* ------------------------------------------ */ - float fpStart = ACTIVE_PROFILE(runningCfg).tfProfileStart; - float fpEnd = ACTIVE_PROFILE(runningCfg).tfProfileEnd; - uint16_t fpHold = ACTIVE_PROFILE(runningCfg).tfProfileHold * 1000; - float holdLimit = ACTIVE_PROFILE(runningCfg).tfProfileHoldLimit > 0.f ? ACTIVE_PROFILE(runningCfg).tfProfileHoldLimit : -1; - TransitionCurve curve = (TransitionCurve)ACTIVE_PROFILE(runningCfg).tfProfileSlopeShape; - uint16_t curveTime = ACTIVE_PROFILE(runningCfg).tfProfileSlope * 1000; - /* ------------------------------------------ */ - - if (fpStart > 0.f && fpHold > 0) { - addFlowPhase(Transition{ fpStart }, holdLimit, fpHold, -1, -1, -1, -1); - rampPhaseIndex = rampPhaseIndex > 0 ? rampPhaseIndex : profile.phaseCount() - 1; - } - addFlowPhase(Transition{ fpStart, fpEnd, curve, curveTime }, ACTIVE_PROFILE(runningCfg).tfProfilingPressureRestriction, curveTime, -1, -1, -1, -1); - rampPhaseIndex = rampPhaseIndex > 0 ? rampPhaseIndex : profile.phaseCount() - 1; - } - else { // pressure based profiling enabled - /* Setting the phase specific restrictions */ - /* ------------------------------------------ */ - float ppStart = ACTIVE_PROFILE(runningCfg).tpProfilingStart; - float ppEnd = ACTIVE_PROFILE(runningCfg).tpProfilingFinish; - uint16_t ppHold = ACTIVE_PROFILE(runningCfg).tpProfilingHold * 1000; - float holdLimit = ACTIVE_PROFILE(runningCfg).tpProfilingHoldLimit > 0.f ? ACTIVE_PROFILE(runningCfg).tpProfilingHoldLimit : -1; - TransitionCurve curve = (TransitionCurve)ACTIVE_PROFILE(runningCfg).tpProfilingSlopeShape; - uint16_t curveTime = ACTIVE_PROFILE(runningCfg).tpProfilingSlope * 1000; - /* ------------------------------------------ */ - - if (ppStart > 0.f && ppHold > 0) { - addPressurePhase(Transition{ ppStart }, holdLimit, ppHold, -1, -1, -1, -1); - rampPhaseIndex = rampPhaseIndex > 0 ? rampPhaseIndex : profile.phaseCount() - 1; - } - addPressurePhase(Transition{ ppStart, ppEnd, curve, curveTime }, ACTIVE_PROFILE(runningCfg).tpProfilingFlowRestriction, curveTime, -1, -1, -1, -1); - rampPhaseIndex = rampPhaseIndex > 0 ? rampPhaseIndex : profile.phaseCount() - 1; - } - } - - // ----------------- Main Profile ----------------- // - if (ACTIVE_PROFILE(runningCfg).mfProfileState) { // flow based profiling enabled - /* Setting the phase specific restrictions */ - /* ------------------------------------------ */ - float fpStart = ACTIVE_PROFILE(runningCfg).mfProfileStart; - float fpEnd = ACTIVE_PROFILE(runningCfg).mfProfileEnd; - TransitionCurve curve = (TransitionCurve)ACTIVE_PROFILE(runningCfg).mfProfileSlopeShape; - uint16_t curveTime = ACTIVE_PROFILE(runningCfg).mfProfileSlope * 1000; - - /* ------------------------------------------ */ - addFlowPhase(Transition(fpStart, fpEnd, curve, curveTime), ACTIVE_PROFILE(runningCfg).mfProfilingPressureRestriction, -1, -1, -1, -1, -1); - } - else { // pressure based profiling enabled - /* Setting the phase specific restrictions */ - /* ------------------------------------------ */ - float ppStart = ACTIVE_PROFILE(runningCfg).mpProfilingStart; - float ppEnd = ACTIVE_PROFILE(runningCfg).mpProfilingFinish; - TransitionCurve curve = (TransitionCurve)ACTIVE_PROFILE(runningCfg).mpProfilingSlopeShape; - uint16_t curveTime = ACTIVE_PROFILE(runningCfg).mpProfilingSlope * 1000; - /* ------------------------------------------ */ - addPressurePhase(Transition(ppStart, ppEnd, curve, curveTime), ACTIVE_PROFILE(runningCfg).mpProfilingFlowRestriction, -1, -1, -1, -1, -1); - } - } else { // Shot profiling disabled. Default to 9 bars - addPressurePhase(Transition(9.f), -1, -1, -1, -1, -1, -1); +void onManualBrewPhaseReceived(const Phase& phase) { + if (manualProfile.phaseCount() != 1) { + manualProfile.phases.resize(1); } - - rampPhaseIndex = rampPhaseIndex > 0 ? rampPhaseIndex : profile.phaseCount() - 1; - insertRampPhaseIfNeeded(rampPhaseIndex); + manualProfile.phases[0] = phase; } -// ------------ Insert a ramp phase in the rampPhaseIndex position ------------ // -void insertRampPhaseIfNeeded(size_t rampPhaseIndex) { - uint16_t rampTime = ACTIVE_PROFILE(runningCfg).preinfusionRamp; - TransitionCurve rampCurve = (TransitionCurve)ACTIVE_PROFILE(runningCfg).preinfusionRampSlope; - - if (rampPhaseIndex <= 0 || rampTime <= 0 || rampCurve == TransitionCurve::INSTANT) { // No ramp needed - return; - } - - // Get the phase currently in rampPhaseIndex - this is the phase we want to ramp to - Phase targetPhase = profile.phases[rampPhaseIndex]; - float targetValue = targetPhase.target.isInstant() ? targetPhase.target.end : targetPhase.target.start; - - if (targetValue <= 0) { // No ramp needed, next phase will perform a ramp. - return; - } - - profile.insertPhase(Phase { - .type = targetPhase.type, - .target = Transition(targetValue, rampCurve, rampTime * 1000), - .restriction = -1, - .stopConditions = PhaseStopConditions{ .time=rampTime * 1000 } - }, rampPhaseIndex); +void onUpdateSystemStateCommandReceived(const UpdateSystemStateComand& command) { + systemState.operationMode = command.operationMode; + systemState.tarePending = command.tarePending; } -void addFillBasketPhase(float flowRate) { - addFlowPhase(Transition(flowRate), -1, -1, 0.1f, -1, -1, -1); +void onBoilerSettingsReceived(const BoilerSettings& boilerSettings) { + runningCfg.boiler = boilerSettings; } - -void addPressurePhase(Transition pressure, float flowRestriction, int timeMs, float pressureAbove, float pressureBelow, float shotWeight, float isWaterPumped) { - addPhase(PHASE_TYPE::PHASE_TYPE_PRESSURE, pressure, flowRestriction, timeMs, pressureAbove, pressureBelow, shotWeight, isWaterPumped); +void onLedSettingsReceived(const LedSettings& ledSettings) { + runningCfg.led = ledSettings; } -void addFlowPhase(Transition flow, float pressureRestriction, int timeMs, float pressureAbove, float pressureBelow, float shotWeight, float isWaterPumped) { - addPhase(PHASE_TYPE::PHASE_TYPE_FLOW, flow, pressureRestriction, timeMs, pressureAbove, pressureBelow, shotWeight, isWaterPumped); +void onSystemSettingsReceived(const SystemSettings& systemSettings) { + runningCfg.system = systemSettings; } -void addPhase(PHASE_TYPE type, Transition target, float restriction, int timeMs, float pressureAbove, float pressureBelow, float shotWeight, float isWaterPumped) { - profile.addPhase(Phase { - .type = type, - .target = target, - .restriction = restriction, - .stopConditions = PhaseStopConditions{ .time=timeMs, .pressureAbove=pressureAbove, .pressureBelow=pressureBelow, .weight=shotWeight, .waterPumpedInPhase=isWaterPumped } - }); +void onBrewSettingsReceived(const BrewSettings& brewSettings) { + runningCfg.brew = brewSettings; } -void onProfileReceived(Profile& newProfile) { -} +//############################################################################################# +//###############################____PROFILING_CONTROL____##################################### +//############################################################################################# static void profiling(void) { if (brewActive) { //runs this only when brew button activated and pressure profile selected uint32_t timeInShot = millis() - brewingTimer; + phaseProfiler.setProfile(systemState.operationMode == OperationMode::BREW_AUTO ? activeProfile : manualProfile); phaseProfiler.updatePhase(timeInShot, currentState); - CurrentPhase& currentPhase = phaseProfiler.getCurrentPhase(); - ShotSnapshot shotSnapshot = buildShotSnapshot(timeInShot, currentState, currentPhase); - espCommsSendShotData(shotSnapshot, 100); + const CurrentPhase& currentPhase = phaseProfiler.getCurrentPhase(); if (phaseProfiler.isFinished()) { setPumpOff(); closeValve(); brewActive = false; - } else if (currentPhase.getType() == PHASE_TYPE::PHASE_TYPE_PRESSURE) { + } else if (currentPhase.getType() == PhaseType::PRESSURE) { float newBarValue = currentPhase.getTarget(); float flowRestriction = currentPhase.getRestriction(); openValve(); @@ -717,19 +444,8 @@ static void profiling(void) { closeValve(); } // Keep that water at temp - justDoCoffee(runningCfg, currentState, brewActive); -} - -static void manualFlowControl(void) { - if (brewActive) { - openValve(); - float flow_reading = lcdGetManualFlowVol() / 10.f ; - setPumpFlow(flow_reading, 0.f, currentState); - } else { - setPumpOff(); - closeValve(); - } - justDoCoffee(runningCfg, currentState, brewActive); + // TODO: If active phase overrides the water temperature, then send the active phase's temp + justDoCoffee(runningCfg, currentState, activeProfile.waterTemperature, brewActive); } //############################################################################################# @@ -745,7 +461,6 @@ static void brewDetect(void) { static bool paramsReset = true; if (currentState.brewSwitchState) { if (!paramsReset) { - lcdWakeUp(); brewParamsReset(); paramsReset = true; brewActive = true; @@ -764,7 +479,7 @@ static void brewDetect(void) { } static void brewParamsReset(void) { - currentState.tarePending = true; + systemState.tarePending = true; currentState.shotWeight = 0.f; currentState.pumpFlow = 0.f; currentState.weight = 0.f; @@ -784,10 +499,8 @@ static bool sysReadinessCheck(void) { return false; } // If there's not enough water in the tank - if ((lcdCurrentPageId != NextionPage::BrewGraph || lcdCurrentPageId != NextionPage::BrewManual) - && currentState.waterLvl < MIN_WATER_LVL) - { - lcdShowPopup("Fill the water tank!"); + if (!brewActive && currentState.waterLvl < MIN_WATER_LVL) { + espCommsSendNotification(Notification::warn("Fill the water tank!")); return false; } @@ -810,87 +523,56 @@ static inline void sysHealthCheck(float pressureThreshold) { setSteamBoilerRelayOff(); if (millis() > thermoTimer) { LOG_ERROR("Cannot read temp from thermocouple (last read: %.1lf)!", static_cast(currentState.temperature)); - currentState.steamSwitchState ? lcdShowPopup("COOLDOWN") : lcdShowPopup("TEMP READ ERROR"); // writing a LCD message - currentState.temperature = thermocoupleRead() - runningCfg.offsetTemp; // Making sure we're getting a value + currentState.steamSwitchState ? espCommsSendNotification(Notification::warn("COOLDOWN!")) : espCommsSendNotification(Notification::warn("TEMP READ ERROR")); // writing a LCD message + currentState.temperature = thermocoupleRead() - runningCfg.boiler.offsetTemp; // Making sure we're getting a value thermoTimer = millis() + GET_KTYPE_READ_EVERY; } } /*Shut down heaters if steam has been ON and unused fpr more than 10 minutes.*/ - while (currentState.isSteamForgottenON) { + while (systemState.isSteamForgottenON) { //Reloading the watchdog timer, if this function fails to run MCU is rebooted watchdogReload(); - lcdShowPopup("TURN STEAM OFF NOW!"); + espCommsSendNotification(Notification::warn("TURN STEAM OFF NOW!")); setPumpOff(); setBoilerOff(); setSteamBoilerRelayOff(); - currentState.isSteamForgottenON = currentState.steamSwitchState; + systemState.isSteamForgottenON = currentState.steamSwitchState; } - //Releasing the excess pressure after steaming or brewing if necessary - #if defined LEGO_VALVE_RELAY || defined SINGLE_BOARD - // No point going through the whole thing if this first condition isn't met. if (currentState.brewSwitchState || currentState.steamSwitchState || currentState.hotWaterSwitchState) { systemHealthTimer = millis() + HEALTHCHECK_EVERY; return; } // Should enter the block every "systemHealthTimer" seconds + // TODO Find a way to not run this when we're still showing the brewGraph if (millis() >= systemHealthTimer) { - while (currentState.smoothedPressure >= pressureThreshold && currentState.temperature < 100.f) + while (!brewActive && currentState.smoothedPressure >= pressureThreshold && currentState.temperature < 100.f) { - //Reloading the watchdog timer, if this function fails to run MCU is rebooted - watchdogReload(); - switch (lcdCurrentPageId) { - case NextionPage::BrewManual: - case NextionPage::BrewGraph: - case NextionPage::GraphPreview: - brewDetect(); - lcdRefresh(); - lcdListen(); - sensorsRead(); - justDoCoffee(runningCfg, currentState, brewActive); - break; - default: - sensorsRead(); - lcdShowPopup("Releasing pressure!"); - setPumpOff(); - setBoilerOff(); - setSteamValveRelayOff(); - setSteamBoilerRelayOff(); - openValve(); - break; - } + sensorsRead(); + espCommsSendNotification(Notification::info("Releasing pressure!")); + setPumpOff(); + setBoilerOff(); + setSteamValveRelayOff(); + setSteamBoilerRelayOff(); + openValve(); } closeValve(); systemHealthTimer = millis() + HEALTHCHECK_EVERY; } // Throwing a pressure release countodown. - if (lcdCurrentPageId == NextionPage::BrewGraph) return; - if (lcdCurrentPageId == NextionPage::BrewManual) return; + if (brewActive) return; if (currentState.smoothedPressure >= pressureThreshold && currentState.temperature < 100.f) { if (millis() >= systemHealthTimer - 3500ul && millis() <= systemHealthTimer - 500ul) { - char tmp[25]; int countdown = (int)(systemHealthTimer-millis())/1000; - unsigned int check = snprintf(tmp, sizeof(tmp), "Dropping beats in: %i", countdown); - if (check > 0 && check <= sizeof(tmp)) { - lcdShowPopup(tmp); - } + espCommsSendNotification(Notification::info("Dropping beats in: " + std::to_string(countdown))); } } - #endif -} - -// Function to track time since system has started -static unsigned long getTimeSinceInit(void) { - static unsigned long startTime = millis(); - return millis() - startTime; } static void fillBoiler(void) { - #if defined LEGO_VALVE_RELAY || defined SINGLE_BOARD - if (systemState.startupInitFinished) { return; } @@ -904,15 +586,12 @@ static void fillBoiler(void) { fillBoilerUntilThreshod(getTimeSinceInit()); } else if (isSwitchOn()) { - lcdShowPopup("Brew Switch ON!"); + espCommsSendNotification(Notification::warn("Brew Switch ON!")); } -#else - systemState.startupInitFinished = true; -#endif } static bool isBoilerFillPhase(unsigned long elapsedTime) { - return lcdCurrentPageId == NextionPage::Home && elapsedTime >= BOILER_FILL_START_TIME; + return elapsedTime >= BOILER_FILL_START_TIME; } static bool isBoilerFull(unsigned long elapsedTime) { @@ -928,7 +607,7 @@ static bool isBoilerFull(unsigned long elapsedTime) { // Checks if Brew switch is ON static bool isSwitchOn(void) { - return currentState.brewSwitchState && lcdCurrentPageId == NextionPage::Home; + return currentState.brewSwitchState; } static void fillBoilerUntilThreshod(unsigned long elapsedTime) { @@ -944,39 +623,38 @@ static void fillBoilerUntilThreshod(unsigned long elapsedTime) { return; } - lcdShowPopup("Filling boiler!"); + espCommsSendNotification(Notification::info("Filling boiler!")); openValve(); - setPumpToRawValue(35); + setPumpToPercentage(0.35f); } -static void updateStartupTimer(void) { - lcdSetUpTime(getTimeSinceInit() / 1000); -} - -static void cpsInit(eepromValues_t &eepromValues) { +static void cpsInit(GaggiaSettings &runningCfg) { int cps = getCPS(); if (cps > 110) { // double 60 Hz - eepromValues.powerLineFrequency = 60u; + currentState.powerLineFrequency = 60u; } else if (cps > 80) { // double 50 Hz - eepromValues.powerLineFrequency = 50u; + currentState.powerLineFrequency = 50u; } else if (cps > 55) { // 60 Hz - eepromValues.powerLineFrequency = 60u; + currentState.powerLineFrequency = 60u; } else if (cps > 0) { // 50 Hz - eepromValues.powerLineFrequency = 50u; + currentState.powerLineFrequency = 50u; } } static void doLed(void) { - if (runningCfg.ledDisco && brewActive) { - switch(lcdCurrentPageId) { - case NextionPage::BrewGraph: - case NextionPage::BrewManual: + if (!runningCfg.led.state) { + led.setColor(0, 0, 0); + } + else if (runningCfg.led.disco && brewActive) { + switch(systemState.operationMode) { + case OperationMode::BREW_AUTO: + case OperationMode::BREW_MANUAL: led.setDisco(led.CLASSIC); break; - case NextionPage::Flush: + case OperationMode::FLUSH: led.setDisco(led.STROBE); break; - case NextionPage::Descale: + case OperationMode::DESCALE: led.setDisco(led.DESCALE); break; default: @@ -984,15 +662,35 @@ static void doLed(void) { break; } } else { - switch(lcdCurrentPageId) { - case NextionPage::Led: - static uint32_t timer = millis(); - if (millis() > timer) { - timer = millis() + 100u; - lcdFetchLed(runningCfg); - } - default: // intentionally fall through - led.setColor(runningCfg.ledR, runningCfg.ledG, runningCfg.ledB); - } + led.setColor(runningCfg.led.color.R, runningCfg.led.color.G, runningCfg.led.color.B); + } +} + +static void flushAuto() { + #if defined(LEGO_VALVE_RELAY) || defined(SINGLE_BOARD) + static uint32_t flushAutoTimer = 0; + + + if (millis() - flushAutoTimer > 5000) { //FLUSH_AUTO just started + flushAutoTimer = millis(); + } + + if (millis() - flushAutoTimer < 4000) { + currentState.brewSwitchState = true; + backFlush(currentState); + setBoilerOff(); + } else { + flushAutoTimer = 0; + systemState.operationMode = OperationMode::BREW_AUTO; } + #endif +} + +static void ToFnLedInit() { + // Initialize LED + led.begin(); + led.setColor(9u, 0u, 9u); // WHITE + // Init the tof sensor + tof.init(systemState); + tof.setCustomRanges(tofStartValue, tofEndValue); } diff --git a/src/lcd/lcd.h b/src/lcd/lcd.h deleted file mode 100644 index 3f66e534..00000000 --- a/src/lcd/lcd.h +++ /dev/null @@ -1,81 +0,0 @@ -/* 09:32 15/03/2023 - change triggering comment */ -#ifndef LCD_H -#define LCD_H - -#include -#include "eeprom_data/eeprom_data.h" -#include "../../lib/Common/system_state.h" - - -enum class NextionPage: byte { - /* 00 */ Home, - /* 01 */ BrewPreinfusion, - /* 02 */ BrewSoak, - /* 03 */ BrewProfiling, - /* 04 */ BrewManual, - /* 05 */ Flush, - /* 06 */ Descale, - /* 07 */ SettingsBoiler, - /* 08 */ SettingsSystem, - /* 09 */ BrewGraph, - /* 0A */ BrewMore, - /* 0B */ ShotSettings, - /* 0C */ BrewTransitionProfile, - /* 0D */ GraphPreview, - /* 0E */ KeyboardNumeric, - /* 0F */ Led -}; - -extern volatile NextionPage lcdCurrentPageId; -extern volatile NextionPage lcdLastCurrentPageId; - -void lcdInit(void); -bool lcdCheckSerialInit(const char* expectedOutput, size_t expectedLen); -void lcdUploadProfile(eepromValues_t &eepromCurrentValues); -void lcdUploadCfg(eepromValues_t &eepromCurrentValues); -void uploadPageCfg(eepromValues_t &eepromCurrentValues, SystemState &sys); -void lcdListen(void); -void lcdWakeUp(void); - -void lcdFetchCurrentProfile(eepromValues_t & settings); -void lcdFetchLed(eepromValues_t & settings); -void lcdFetchPage(eepromValues_t &settings, NextionPage page, int targetProfile); -uint8_t lcdGetSelectedProfile(void); -bool lcdGetPreinfusionFlowState(void); -bool lcdGetProfileFlowState(void); -bool lcdGetTransitionFlowState(void); -int lcdGetHomeScreenScalesEnabled(void); -int lcdGetSelectedOperationalMode(void); -int lcdGetManualFlowVol(void); -int lcdGetDescaleCycle(void); - -void lcdSetDescaleCycle(int cycle); -void lcdSetPressure(float val); -void lcdSetTemperature(uint16_t val); -void lcdSetTemperatureDecimal(uint16_t val); -void lcdTargetState(int val); -void lcdSetWeight(float val); -void lcdSetFlow(int val); -void lcdSetUpTime(float val); -void lcdSetTankWaterLvl(uint16_t val); - -void lcdShowDebug(int val1, int val2); -void lcdShowPopup(const char *msg); - -void lcdBrewTimerStart(void); -void lcdBrewTimerStop(void); -void lcdSetBrewTimer(int seconds); -void lcdWarmupStateStop(void); - -/* Triggers */ -void lcdSaveSettingsTrigger(void); -void lcdScalesTareTrigger(void); -void lcdHomeScreenScalesTrigger(void); -void lcdBrewGraphScalesTareTrigger(void); -void lcdRefreshElementsTrigger(void); -void lcdQuickProfileSwitch(void); -void lcdSaveProfileTrigger(void); -void lcdResetSettingsTrigger(void); -void lcdLoadDefaultProfileTrigger(void); - -#endif diff --git a/src/lcd/nextion.cpp b/src/lcd/nextion.cpp deleted file mode 100644 index e2e5678d..00000000 --- a/src/lcd/nextion.cpp +++ /dev/null @@ -1,583 +0,0 @@ -/* 09:32 15/03/2023 - change triggering comment */ -#include "lcd.h" -#include "pindef.h" -#include "log.h" -#include - -EasyNex myNex(USART_LCD); -volatile NextionPage lcdCurrentPageId; -volatile NextionPage lcdLastCurrentPageId; - -// decode/encode bit packing. -// format is 000000sd rrrrrrrr gggggggg bbbbbbbb, where s = state, d = disco, r/g/b = colors -void lcdDecodeLedSettings(uint32_t code, bool &state, bool &disco, uint8_t &r, uint8_t &g, uint8_t &b) { - state = (code & 0x02000000); - disco = (code & 0x01000000); - r = (code & 0x00FF0000) >> 16; - g = (code & 0x0000FF00) >> 8; - b = (code & 0x000000FF); -} - -uint32_t lcdEncodeLedSettings(bool state, bool disco, uint8_t r, uint8_t g, uint8_t b) { - uint32_t code; - code = state ? 0x01 : 0x00; - code = (code << 1) | (disco ? 0x01 : 0x00); - code = (code << 8) | (r & 0xFF); - code = (code << 8) | (g & 0xFF); - code = (code << 8) | (b & 0xFF); - return code; -} - -void lcdInit(void) { - myNex.begin(115200); - while (!lcdCheckSerialInit("\x88\xFF\xFF\xFF", 4)) { - LOG_VERBOSE("Connecting to Nextion LCD..."); - delay(5); - } - myNex.writeStr("splash.build_version.txt", AUTO_VERSION); - lcdCurrentPageId = static_cast(myNex.currentPageId); - lcdLastCurrentPageId = static_cast(myNex.currentPageId); -} - -bool lcdCheckSerialInit(const char* expectedOutput, size_t expectedLen) { - size_t receivedLen = 0; - - while (receivedLen < expectedLen) { - int receivedByte = myNex.readByte(); - if (receivedByte != -1) { - if (receivedByte == expectedOutput[receivedLen]) { - receivedLen++; - } else { - // Reset receivedLen if the received byte doesn't match - receivedLen = 0; - } - } - } - - // Serial output matches expected output - return true; -} - -void lcdListen(void) { - myNex.NextionListen(); - lcdCurrentPageId = static_cast(myNex.currentPageId); -} - -void lcdWakeUp(void) { - myNex.writeNum("sleep", 0); -} - -void lcdUploadProfile(eepromValues_t &eepromCurrentValues) { - // Highlight the active profile - myNex.writeNum("pId", eepromCurrentValues.activeProfile + 1 /* 1-offset in nextion */); - String buttonElemId = String("home.qPf") + (eepromCurrentValues.activeProfile + 1) + ".txt"; - myNex.writeStr(buttonElemId, ACTIVE_PROFILE(eepromCurrentValues).name); - - // Temp - myNex.writeNum("sT.setPoint.val", ACTIVE_PROFILE(eepromCurrentValues).setpoint); - // PI - myNex.writeNum("piState", ACTIVE_PROFILE(eepromCurrentValues).preinfusionState); - myNex.writeNum("piFlowState", ACTIVE_PROFILE(eepromCurrentValues).preinfusionFlowState); - - if(ACTIVE_PROFILE(eepromCurrentValues).preinfusionFlowState == 0) { - myNex.writeNum("pi.piTime.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionSec); - myNex.writeNum("pi.piFlow.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionPressureFlowTarget * 10.f); - myNex.writeNum("pi.piBar.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionBar * 10.f); - } - else { - myNex.writeNum("pi.piTime.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionFlowTime); - myNex.writeNum("pi.piFlow.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionFlowVol * 10.f); - myNex.writeNum("pi.piBar.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionFlowPressureTarget * 10.f); - } - myNex.writeNum("pi.piPumped.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionFilled); - myNex.writeNum("piRPressure", ACTIVE_PROFILE(eepromCurrentValues).preinfusionPressureAbove); - myNex.writeNum("pi.piAbove.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionWeightAbove * 10.f); - - // SOAK - myNex.writeNum("skState", ACTIVE_PROFILE(eepromCurrentValues).soakState); - - if(ACTIVE_PROFILE(eepromCurrentValues).preinfusionFlowState == 0) - myNex.writeNum("sk.skTime.val", ACTIVE_PROFILE(eepromCurrentValues).soakTimePressure); - else - myNex.writeNum("sk.skTime.val", ACTIVE_PROFILE(eepromCurrentValues).soakTimeFlow); - - myNex.writeNum("sk.skBar.val", ACTIVE_PROFILE(eepromCurrentValues).soakKeepPressure * 10.f); - myNex.writeNum("sk.skFlow.val", ACTIVE_PROFILE(eepromCurrentValues).soakKeepFlow * 10.f); - myNex.writeNum("sk.skBelow.val", ACTIVE_PROFILE(eepromCurrentValues).soakBelowPressure * 10.f); - myNex.writeNum("sk.skAbv.val", ACTIVE_PROFILE(eepromCurrentValues).soakAbovePressure * 10.f); - myNex.writeNum("sk.skWAbv.val", ACTIVE_PROFILE(eepromCurrentValues).soakAboveWeight * 10.f); - // PI -> PF - myNex.writeNum("sk.skRamp.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionRamp); - myNex.writeNum("skCrv", ACTIVE_PROFILE(eepromCurrentValues).preinfusionRampSlope); - // PROFILING - // Adnvanced transition profile - myNex.writeNum("paState", ACTIVE_PROFILE(eepromCurrentValues).tpState); - myNex.writeNum("paType", ACTIVE_PROFILE(eepromCurrentValues).tpType); - if(ACTIVE_PROFILE(eepromCurrentValues).tpType == 0) { - myNex.writeNum("tp.tStart.val", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingStart * 10.f); - myNex.writeNum("tp.tEnd.val", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingFinish * 10.f); - myNex.writeNum("tp.tHold.val", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingHold); - myNex.writeNum("tp.hLim.val", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingHoldLimit * 10.f); - myNex.writeNum("tp.tSlope.val", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingSlope); - myNex.writeNum("paCrv", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingSlopeShape); - myNex.writeNum("tp.tLim.val", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingFlowRestriction * 10.f); - } else { - myNex.writeNum("tp.tStart.val", ACTIVE_PROFILE(eepromCurrentValues).tfProfileStart * 10.f); - myNex.writeNum("tp.tEnd.val", ACTIVE_PROFILE(eepromCurrentValues).tfProfileEnd * 10.f); - myNex.writeNum("tp.tHold.val", ACTIVE_PROFILE(eepromCurrentValues).tfProfileHold); - myNex.writeNum("tp.hLim.val", ACTIVE_PROFILE(eepromCurrentValues).tfProfileHoldLimit * 10.f); - myNex.writeNum("tp.tSlope.val", ACTIVE_PROFILE(eepromCurrentValues).tfProfileSlope); - myNex.writeNum("paCrv", ACTIVE_PROFILE(eepromCurrentValues).tfProfileSlopeShape); - myNex.writeNum("tp.tLim.val", ACTIVE_PROFILE(eepromCurrentValues).tfProfilingPressureRestriction * 10.f); - } - // Main profile - myNex.writeNum("ppState", ACTIVE_PROFILE(eepromCurrentValues).profilingState); - myNex.writeNum("ppType", ACTIVE_PROFILE(eepromCurrentValues).mfProfileState); - if(ACTIVE_PROFILE(eepromCurrentValues).mfProfileState == 0) { - myNex.writeNum("pf.pStart.val", ACTIVE_PROFILE(eepromCurrentValues).mpProfilingStart * 10.f); - myNex.writeNum("pf.pEnd.val", ACTIVE_PROFILE(eepromCurrentValues).mpProfilingFinish * 10.f); - myNex.writeNum("pf.pSlope.val", ACTIVE_PROFILE(eepromCurrentValues).mpProfilingSlope); - myNex.writeNum("pfCrv", ACTIVE_PROFILE(eepromCurrentValues).mpProfilingSlopeShape); - myNex.writeNum("pf.pLim.val", ACTIVE_PROFILE(eepromCurrentValues).mpProfilingFlowRestriction * 10.f); - } else { - myNex.writeNum("pf.pStart.val", ACTIVE_PROFILE(eepromCurrentValues).mfProfileStart * 10.f); - myNex.writeNum("pf.pEnd.val", ACTIVE_PROFILE(eepromCurrentValues).mfProfileEnd * 10.f); - myNex.writeNum("pf.pSlope.val", ACTIVE_PROFILE(eepromCurrentValues).mfProfileSlope); - myNex.writeNum("pfCrv", ACTIVE_PROFILE(eepromCurrentValues).mfProfileSlopeShape); - myNex.writeNum("pf.pLim.val", ACTIVE_PROFILE(eepromCurrentValues).mfProfilingPressureRestriction * 10.f); - } - // Dose settings - myNex.writeNum("shotState", ACTIVE_PROFILE(eepromCurrentValues).stopOnWeightState); - myNex.writeNum("dS.numDose.val", ACTIVE_PROFILE(eepromCurrentValues).shotDose * 10.f); - myNex.writeNum("shotPreset", ACTIVE_PROFILE(eepromCurrentValues).shotPreset); - myNex.writeNum("dS.numDoseForced.val", ACTIVE_PROFILE(eepromCurrentValues).shotStopOnCustomWeight * 10.f); -} - -// This is never called again after boot -void lcdUploadCfg(eepromValues_t &eepromCurrentValues) { - // Profile names for all buttons - myNex.writeStr("home.qPf1.txt", eepromCurrentValues.profiles[0].name); - myNex.writeStr("home.qPf2.txt", eepromCurrentValues.profiles[1].name); - myNex.writeStr("home.qPf3.txt", eepromCurrentValues.profiles[2].name); - myNex.writeStr("home.qPf4.txt", eepromCurrentValues.profiles[3].name); - myNex.writeStr("home.qPf5.txt", eepromCurrentValues.profiles[4].name); - - // More brew settings - myNex.writeNum("bckHome", eepromCurrentValues.homeOnShotFinish); - myNex.writeNum("basketPrefill", eepromCurrentValues.basketPrefill); - myNex.writeNum("deltaState", eepromCurrentValues.brewDeltaState); - - // System settings - myNex.writeNum("sT.steamSetPoint.val", eepromCurrentValues.steamSetPoint); - myNex.writeNum("sT.offSet.val", eepromCurrentValues.offsetTemp); - myNex.writeNum("sT.hpwr.val", eepromCurrentValues.hpwr); - myNex.writeNum("sT.mDiv.val", eepromCurrentValues.mainDivider); - myNex.writeNum("sT.bDiv.val", eepromCurrentValues.brewDivider); - - myNex.writeNum("sP.n1.val", eepromCurrentValues.lcdSleep); - myNex.writeNum("sP.lc1.val", eepromCurrentValues.scalesF1); - myNex.writeNum("sP.lc2.val", eepromCurrentValues.scalesF2); - myNex.writeNum("sP.pump_zero.val", eepromCurrentValues.pumpFlowAtZero * 10000.f); - myNex.writeNum("warmupState", eepromCurrentValues.warmupState); - - // Led - myNex.writeNum("ledNum", - lcdEncodeLedSettings( - eepromCurrentValues.ledState, - eepromCurrentValues.ledDisco, - eepromCurrentValues.ledR, - eepromCurrentValues.ledG, - eepromCurrentValues.ledB - ) - ); - - lcdUploadProfile(eepromCurrentValues); -} - -void uploadPageCfg(eepromValues_t &eepromCurrentValues, SystemState &sys) { - // Updating only page specific elements as necessary to speed up things and avoid needless writes. - switch (lcdCurrentPageId) { - case NextionPage::BrewPreinfusion: - // PI - myNex.writeNum("piState", ACTIVE_PROFILE(eepromCurrentValues).preinfusionState); - myNex.writeNum("piFlowState", ACTIVE_PROFILE(eepromCurrentValues).preinfusionFlowState); - - if(ACTIVE_PROFILE(eepromCurrentValues).preinfusionFlowState == 0) { - myNex.writeNum("pi.piTime.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionSec); - myNex.writeNum("pi.piFlow.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionPressureFlowTarget * 10.f); - myNex.writeNum("pi.piBar.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionBar * 10.f); - } - else { - myNex.writeNum("pi.piTime.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionFlowTime); - myNex.writeNum("pi.piFlow.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionFlowVol * 10.f); - myNex.writeNum("pi.piBar.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionFlowPressureTarget * 10.f); - } - myNex.writeNum("pi.piPumped.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionFilled); - myNex.writeNum("piRPressure", ACTIVE_PROFILE(eepromCurrentValues).preinfusionPressureAbove); - myNex.writeNum("pi.piAbove.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionWeightAbove * 10.f); - break; - case NextionPage::BrewSoak: - myNex.writeNum("skState", ACTIVE_PROFILE(eepromCurrentValues).soakState); - - if(ACTIVE_PROFILE(eepromCurrentValues).preinfusionFlowState == 0) - myNex.writeNum("sk.skTime.val", ACTIVE_PROFILE(eepromCurrentValues).soakTimePressure); - else - myNex.writeNum("sk.skTime.val", ACTIVE_PROFILE(eepromCurrentValues).soakTimeFlow); - - myNex.writeNum("sk.skBar.val", ACTIVE_PROFILE(eepromCurrentValues).soakKeepPressure * 10.f); - myNex.writeNum("sk.skFlow.val", ACTIVE_PROFILE(eepromCurrentValues).soakKeepFlow * 10.f); - myNex.writeNum("sk.skBelow.val", ACTIVE_PROFILE(eepromCurrentValues).soakBelowPressure * 10.f); - myNex.writeNum("sk.skAbv.val", ACTIVE_PROFILE(eepromCurrentValues).soakAbovePressure * 10.f); - myNex.writeNum("sk.skWAbv.val", ACTIVE_PROFILE(eepromCurrentValues).soakAboveWeight * 10.f); - // PI -> PF - myNex.writeNum("sk.skRamp.val", ACTIVE_PROFILE(eepromCurrentValues).preinfusionRamp); - myNex.writeNum("skCrv", ACTIVE_PROFILE(eepromCurrentValues).preinfusionRampSlope); - break; - case NextionPage::BrewProfiling: - // PROFILING - myNex.writeNum("ppState", ACTIVE_PROFILE(eepromCurrentValues).profilingState); - myNex.writeNum("ppType", ACTIVE_PROFILE(eepromCurrentValues).mfProfileState); - - if(ACTIVE_PROFILE(eepromCurrentValues).mfProfileState == 0) { - myNex.writeNum("pf.pStart.val", ACTIVE_PROFILE(eepromCurrentValues).mpProfilingStart * 10.f); - myNex.writeNum("pf.pEnd.val", ACTIVE_PROFILE(eepromCurrentValues).mpProfilingFinish * 10.f); - myNex.writeNum("pf.pSlope.val", ACTIVE_PROFILE(eepromCurrentValues).mpProfilingSlope); - myNex.writeNum("pfCrv", ACTIVE_PROFILE(eepromCurrentValues).mpProfilingSlopeShape); - myNex.writeNum("pf.pLim.val", ACTIVE_PROFILE(eepromCurrentValues).mpProfilingFlowRestriction * 10.f); - } else { - myNex.writeNum("pf.pStart.val", ACTIVE_PROFILE(eepromCurrentValues).mfProfileStart * 10.f); - myNex.writeNum("pf.pEnd.val", ACTIVE_PROFILE(eepromCurrentValues).mfProfileEnd * 10.f); - myNex.writeNum("pf.pSlope.val", ACTIVE_PROFILE(eepromCurrentValues).mfProfileSlope); - myNex.writeNum("pfCrv", ACTIVE_PROFILE(eepromCurrentValues).mfProfileSlopeShape); - myNex.writeNum("pf.pLim.val", ACTIVE_PROFILE(eepromCurrentValues).mfProfilingPressureRestriction * 10.f); - } - break; - case NextionPage::BrewTransitionProfile: - myNex.writeNum("paState", ACTIVE_PROFILE(eepromCurrentValues).tpState); - myNex.writeNum("paType", ACTIVE_PROFILE(eepromCurrentValues).tpType); - // Adnvanced transition profile - if(ACTIVE_PROFILE(eepromCurrentValues).tpType == 0) { - myNex.writeNum("tp.tStart.val", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingStart * 10.f); - myNex.writeNum("tp.tEnd.val", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingFinish * 10.f); - myNex.writeNum("tp.tHold.val", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingHold); - myNex.writeNum("tp.hLim.val", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingHoldLimit * 10.f); - myNex.writeNum("tp.tSlope.val", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingSlope); - myNex.writeNum("paCrv", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingSlopeShape); - myNex.writeNum("tp.tLim.val", ACTIVE_PROFILE(eepromCurrentValues).tpProfilingFlowRestriction * 10.f); - } else { - myNex.writeNum("tp.tStart.val", ACTIVE_PROFILE(eepromCurrentValues).tfProfileStart * 10.f); - myNex.writeNum("tp.tEnd.val", ACTIVE_PROFILE(eepromCurrentValues).tfProfileEnd * 10.f); - myNex.writeNum("tp.tHold.val", ACTIVE_PROFILE(eepromCurrentValues).tfProfileHold); - myNex.writeNum("tp.hLim.val", ACTIVE_PROFILE(eepromCurrentValues).tfProfileHoldLimit * 10.f); - myNex.writeNum("tp.tSlope.val", ACTIVE_PROFILE(eepromCurrentValues).tfProfileSlope); - myNex.writeNum("paCrv", ACTIVE_PROFILE(eepromCurrentValues).tfProfileSlopeShape); - myNex.writeNum("tp.tLim.val", ACTIVE_PROFILE(eepromCurrentValues).tfProfilingPressureRestriction * 10.f); - } - break; - default: - lcdUploadCfg(eepromCurrentValues); - break; - } -} - -void lcdFetchProfileName(eepromValues_t::profile_t &profile, uint8_t index /* 0-offset */) { - String buttonElemId = String("home.qPf") + (index + 1) + ".txt"; - snprintf(profile.name, sizeof(profile.name), "%s", myNex.readStr(buttonElemId).c_str()); -} - -void lcdFetchPreinfusion(eepromValues_t::profile_t &profile) { - profile.preinfusionState = myNex.readNumber("piState"); - profile.preinfusionFlowState = lcdGetPreinfusionFlowState(); - - if(profile.preinfusionFlowState == 0) { - profile.preinfusionSec = myNex.readNumber("pi.piTime.val"); - profile.preinfusionPressureFlowTarget = myNex.readNumber("pi.piFlow.val") / 10.f; - profile.preinfusionBar = myNex.readNumber("pi.piBar.val") / 10.f; - } - else { - profile.preinfusionFlowTime = myNex.readNumber("pi.piTime.val"); - profile.preinfusionFlowVol = myNex.readNumber("pi.piFlow.val") / 10.f; - profile.preinfusionFlowPressureTarget = myNex.readNumber("pi.piBar.val") / 10.f; - } - profile.preinfusionFilled = myNex.readNumber("pi.piPumped.val"); - profile.preinfusionPressureAbove = myNex.readNumber("piRPressure"); - profile.preinfusionWeightAbove = myNex.readNumber("pi.piAbove.val") / 10.f; -} - -void lcdFetchSoak(eepromValues_t::profile_t &profile) { - // SOAK - profile.soakState = myNex.readNumber("skState"); - - if(profile.preinfusionFlowState == 0) - profile.soakTimePressure = myNex.readNumber("sk.skTime.val"); - else - profile.soakTimeFlow = myNex.readNumber("sk.skTime.val"); - - profile.soakKeepPressure = myNex.readNumber("sk.skBar.val") / 10.f; - profile.soakKeepFlow = myNex.readNumber("sk.skFlow.val") / 10.f; - profile.soakBelowPressure = myNex.readNumber("sk.skBelow.val") / 10.f; - profile.soakAbovePressure = myNex.readNumber("sk.skAbv.val") / 10.f; - profile.soakAboveWeight = myNex.readNumber("sk.skWAbv.val") / 10.f; - // PI -> PF - profile.preinfusionRamp = myNex.readNumber("sk.skRamp.val"); - profile.preinfusionRampSlope = myNex.readNumber("skCrv"); -} - -void lcdFetchBrewProfile(eepromValues_t::profile_t &profile) { - // PROFILING - profile.profilingState = myNex.readNumber("ppState"); - profile.mfProfileState = lcdGetProfileFlowState(); - - if(profile.mfProfileState == 0) { - profile.mpProfilingStart = myNex.readNumber("pf.pStart.val") / 10.f; - profile.mpProfilingFinish = myNex.readNumber("pf.pEnd.val") / 10.f; - profile.mpProfilingSlope = myNex.readNumber("pf.pSlope.val"); - profile.mpProfilingSlopeShape = myNex.readNumber("pfCrv"); - profile.mpProfilingFlowRestriction = myNex.readNumber("pf.pLim.val") / 10.f; - } else { - profile.mfProfileStart = myNex.readNumber("pf.pStart.val") / 10.f; - profile.mfProfileEnd = myNex.readNumber("pf.pEnd.val") / 10.f; - profile.mfProfileSlope = myNex.readNumber("pf.pSlope.val"); - profile.mfProfileSlopeShape = myNex.readNumber("pfCrv"); - profile.mfProfilingPressureRestriction = myNex.readNumber("pf.pLim.val") / 10.f; - } -} - -void lcdFetchTransitionProfile(eepromValues_t::profile_t &profile) { - profile.tpState = myNex.readNumber("paState"); - profile.tpType = lcdGetTransitionFlowState(); - - if(profile.tpType == 0) { - profile.tpProfilingStart = myNex.readNumber("tp.tStart.val") / 10.f; - profile.tpProfilingFinish = myNex.readNumber("tp.tEnd.val") / 10.f; - profile.tpProfilingHold = myNex.readNumber("tp.tHold.val"); - profile.tpProfilingHoldLimit = myNex.readNumber("tp.hLim.val") / 10.f; - profile.tpProfilingSlope = myNex.readNumber("tp.tSlope.val"); - profile.tpProfilingSlopeShape = myNex.readNumber("paCrv"); - profile.tpProfilingFlowRestriction = myNex.readNumber("tp.tLim.val") / 10.f; - } else { - profile.tfProfileStart = myNex.readNumber("tp.tStart.val") / 10.f; - profile.tfProfileEnd = myNex.readNumber("tp.tEnd.val") / 10.f; - profile.tfProfileHold = myNex.readNumber("tp.tHold.val"); - profile.tfProfileHoldLimit = myNex.readNumber("tp.tLim.val") / 10.f; - profile.tfProfileSlope = myNex.readNumber("tp.tSlope.val"); - profile.tfProfileSlopeShape = myNex.readNumber("paCrv"); - profile.tfProfilingPressureRestriction = myNex.readNumber("tp.tLim.val") / 10.f; - } -} - -void lcdFetchDoseSettings(eepromValues_t::profile_t &profile) { - // DOse settings - profile.stopOnWeightState = myNex.readNumber("shotState"); - profile.shotDose = myNex.readNumber("dS.numDose.val") / 10.f; - profile.shotStopOnCustomWeight = myNex.readNumber("dS.numDoseForced.val") / 10.f; - profile.shotPreset = myNex.readNumber("shotPreset"); -} - -void lcdFetchTemp(eepromValues_t::profile_t &profile) { - profile.setpoint = myNex.readNumber("sT.setPoint.val"); -} - -/** -* Overwrites the entire profile in the index corresponding to -* the currently selected profile on the screen. -*/ -void lcdFetchCurrentProfile(eepromValues_t & settings) { - // Target save to the currently selected profile on screen (can be different from runningCfg on long press) - settings.activeProfile = lcdGetSelectedProfile(); - eepromValues_t::profile_t *profile = &settings.profiles[settings.activeProfile]; - - lcdFetchProfileName(*profile, settings.activeProfile); - lcdFetchPreinfusion(*profile); - lcdFetchSoak(*profile); - lcdFetchBrewProfile(*profile); - lcdFetchTransitionProfile(*profile); - lcdFetchDoseSettings(*profile); - lcdFetchTemp(*profile); -} - -void lcdFetchBrewSettings(eepromValues_t &settings) { - // More brew settings - settings.homeOnShotFinish = myNex.readNumber("bckHome"); - settings.basketPrefill = myNex.readNumber("basketPrefill"); - settings.brewDeltaState = myNex.readNumber("deltaState"); -} - -void lcdFetchBoiler(eepromValues_t &settings) { - settings.steamSetPoint = myNex.readNumber("sT.steamSetPoint.val"); - settings.offsetTemp = myNex.readNumber("sT.offSet.val"); - settings.hpwr = myNex.readNumber("sT.hpwr.val"); - settings.mainDivider = myNex.readNumber("sT.mDiv.val"); - settings.brewDivider = myNex.readNumber("sT.bDiv.val"); -} - -void lcdFetchSystem(eepromValues_t &settings) { - // System settings - settings.lcdSleep = myNex.readNumber("sP.n1.val"); // nextion sleep var - settings.warmupState = myNex.readNumber("warmupState"); - settings.scalesF1 = myNex.readNumber("sP.lc1.val"); - settings.scalesF2 = myNex.readNumber("sP.lc2.val"); - settings.pumpFlowAtZero = myNex.readNumber("sP.pump_zero.val") / 10000.f; -} - -void lcdFetchLed(eepromValues_t &settings) { - // Led Settings - uint32_t ledNum = myNex.readNumber("ledNum"); - lcdDecodeLedSettings(ledNum, settings.ledState, settings.ledDisco, settings.ledR, settings.ledG, settings.ledB); -} - -void lcdFetchPage(eepromValues_t &settings, NextionPage page, int targetProfile) { - switch (page) { - case NextionPage::BrewMore: - lcdFetchBrewSettings(settings); - break; - case NextionPage::BrewPreinfusion: - lcdFetchPreinfusion(settings.profiles[targetProfile]); - break; - case NextionPage::BrewSoak: - lcdFetchSoak(settings.profiles[targetProfile]); - break; - case NextionPage::BrewProfiling: - lcdFetchBrewProfile(settings.profiles[targetProfile]); - break; - case NextionPage::BrewTransitionProfile: - lcdFetchTransitionProfile(settings.profiles[targetProfile]); - break; - case NextionPage::SettingsBoiler: - lcdFetchTemp(settings.profiles[targetProfile]); - lcdFetchBoiler(settings); - break; - case NextionPage::SettingsSystem: - lcdFetchSystem(settings); - break; - case NextionPage::ShotSettings: - lcdFetchDoseSettings(settings.profiles[targetProfile]); - break; - case NextionPage::Led: - lcdFetchLed(settings); - break; - default: - break; - } -} - -uint8_t lcdGetSelectedProfile(void) { - uint8_t pId; - int attempts = 2; - do { - if (attempts-- <= 0) { - lcdShowPopup((String("getProfile rekt: ") + pId).c_str()); - return 0; - } - pId = myNex.readNumber("pId"); - } while (pId < 1 || pId > 5); - return pId - 1; /* 1-offset in nextion */ -} - -bool lcdGetPreinfusionFlowState(void) { - return myNex.readNumber("piFlowState"); -} - -bool lcdGetProfileFlowState(void) { - return myNex.readNumber("ppType"); -} - -bool lcdGetTransitionFlowState(void) { - return myNex.readNumber("paType"); -} - -int lcdGetManualFlowVol(void) { - return myNex.readNumber("h0.val"); -} - -int lcdGetHomeScreenScalesEnabled(void) { - return myNex.readNumber("scEn.val"); -} - -int lcdGetSelectedOperationalMode(void) { - return myNex.readNumber("modeSelect"); -} - -int lcdGetDescaleCycle(void) { - return myNex.readNumber("j0.val"); -} - -void lcdSetDescaleCycle(int cycle) { - myNex.writeNum("j0.val", cycle); -} - -void lcdSetPressure(float val) { - myNex.writeNum("pressure.val", val); -} - -void lcdSetUpTime(float val) { - myNex.writeNum("systemUpTime", val); -} - -void lcdSetTemperature(uint16_t val) { - myNex.writeNum("currentTemp", val); -} - -void lcdSetTemperatureDecimal(uint16_t val) { - myNex.writeNum("dE.val", val); -} - -void lcdSetWeight(float val) { - char tmp[6]; - int check = snprintf(tmp, sizeof(tmp), "%.1f", static_cast(val)); - if (check > 0 && static_cast(check) <= sizeof(tmp)) { - strcat(tmp, "g"); - myNex.writeStr("weight.txt", tmp); - } -} - -void lcdSetFlow(int val) { - myNex.writeNum("flow.val", val); -} - -void lcdShowDebug(int val1, int val2) { - myNex.writeNum("debug1",val1); - myNex.writeNum("debug2",val2); -} - -void lcdShowPopup(const char *msg) { - static unsigned int timer; - if(millis() > timer + 1150) { - myNex.writeStr("popupMSG.t0.txt", msg); - myNex.writeStr("page popupMSG"); - timer = millis(); - } -} - -void lcdSetTankWaterLvl(uint16_t val) { - myNex.writeNum("j0.val", val); -} -void lcdTargetState(int val) { - myNex.writeNum("targetState", val); -} - -void lcdBrewTimerStart(void) { - myNex.writeNum("timerState", 1); -} - -void lcdBrewTimerStop(void) { - myNex.writeNum("timerState", 0); -} - -void lcdSetBrewTimer(int seconds) { - myNex.writeNum("activeBrewTime", seconds); -} - -void lcdWarmupStateStop(void) { - myNex.writeNum("warmupState", 0); -} - -void trigger1(void) { lcdSaveSettingsTrigger(); } -void trigger2(void) { lcdScalesTareTrigger(); } -void trigger3(void) { lcdHomeScreenScalesTrigger(); } -void trigger4(void) { lcdBrewGraphScalesTareTrigger(); } -void trigger6(void) { lcdRefreshElementsTrigger(); } -void trigger7(void) { lcdQuickProfileSwitch(); } -void trigger8(void) { lcdSaveProfileTrigger(); } -void trigger9(void) { lcdResetSettingsTrigger(); } -void trigger10(void) { lcdLoadDefaultProfileTrigger(); } diff --git a/src/peripherals/esp_comms.cpp b/src/peripherals/esp_comms.cpp index 2240df5a..c8b11fe3 100644 --- a/src/peripherals/esp_comms.cpp +++ b/src/peripherals/esp_comms.cpp @@ -1,65 +1,214 @@ /* 09:32 15/03/2023 - change triggering comment */ #include "esp_comms.h" #include "pindef.h" +#include "proto/message_converters.h" +#include "proto/profile_converters.h" +#include "proto/settings_converters.h" +#include "proto/proto_serializer.h" +#include "../log.h" -namespace { - class McuCommsSingleton { - public: - static McuComms& getInstance() { - static McuComms instance; - return instance; +namespace esp { + McuComms mcuComms; + bool receivedSettingsAtLeastOnce = false; + bool receivedProfileAtLeastOnce = false; + uint32_t requestDataTimer = 0; + + void initialiseState() { + // Load settings from ESP + requestDataTimer = 0; + while (!receivedSettingsAtLeastOnce) { + if (millis() - requestDataTimer > 1000) { + espCommsRequestData(McuCommsMessageType::MCUC_DATA_ALL_SETTINGS); + requestDataTimer = millis(); + } + espCommsReadData(); + delay(2); } - private: - McuCommsSingleton() = default; - ~McuCommsSingleton() = default; - }; + LOG_INFO("Settings Init"); + + // Load profile from ESP + requestDataTimer = 0; + while (!receivedProfileAtLeastOnce) { + if (millis() - requestDataTimer > 1000) { + espCommsRequestData(McuCommsMessageType::MCUC_DATA_PROFILE); + requestDataTimer = millis(); + } + espCommsReadData(); + delay(2); + } + LOG_INFO("Profile Init"); + } } +void handleMessageReceived(McuCommsMessageType messageType, std::vector& data); void espCommsInit() { - USART_ESP.begin(460800); + USART_LCD.begin(921600); // mcuComms.setDebugPort(&USART_ESP); - McuCommsSingleton::getInstance().begin(USART_ESP); + esp::mcuComms.begin(USART_LCD, 1000); // Set callbacks - McuCommsSingleton::getInstance().setProfileReceivedCallback(onProfileReceived); - McuCommsSingleton::getInstance().setRemoteScalesWeightReceivedCallback(onRemoteScalesWeightReceived); - McuCommsSingleton::getInstance().setRemoteScalesDisconnectedCallback(onRemoteScalesDisconnected); -} + esp::mcuComms.setMessageReceivedCallback(handleMessageReceived); -void espCommsReadData() { - McuCommsSingleton::getInstance().readDataAndTick(); + esp::initialiseState(); } +//--------------------------------------------------------------------------- +//--------------------------- SENDING TO ESP -------------------------------- +//--------------------------------------------------------------------------- + + volatile uint32_t sensorDataTimer = 0; void espCommsSendSensorData(const SensorState& state, uint32_t frequency) { uint32_t now = millis(); - if (now - sensorDataTimer > frequency) { - SensorStateSnapshot sensorSnapshot = SensorStateSnapshot{ - .brewActive = state.brewSwitchState, - .steamActive = state.steamSwitchState, - .scalesPresent = state.scalesPresent, - .temperature = state.waterTemperature, - .pressure = state.smoothedPressure, - .pumpFlow = state.smoothedPumpFlow, - .weightFlow = state.smoothedWeightFlow, - .weight = state.weight, - .waterLvl = state.waterLvl - }; - McuCommsSingleton::getInstance().sendSensorStateSnapshot(sensorSnapshot); - sensorDataTimer = now; - } + if (now - sensorDataTimer < frequency) return; + + SensorStateSnapshot sensorSnapshot = SensorStateSnapshot { + .brewActive = state.brewSwitchState, + .steamActive = state.steamSwitchState, + .hotWaterSwitchState = state.hotWaterSwitchState, + .temperature = state.temperature, + .waterTemperature = state.waterTemperature, + .pressure = state.smoothedPressure, + .pumpFlow = state.smoothedPumpFlow, + .weightFlow = state.smoothedWeightFlow, + .weight = state.weight, + .waterLevel = state.waterLvl + }; + + esp::mcuComms.sendMessage( + McuCommsMessageType::MCUC_DATA_SENSOR_STATE_SNAPSHOT, + ProtoSerializer::serialize(sensorSnapshot) + ); + sensorDataTimer = now; } volatile uint32_t shotDataTimer; -void espCommsSendShotData(ShotSnapshot& shotData, uint32_t frequency) { +void espCommsSendShotData(const ShotSnapshot& shotData, uint32_t frequency) { uint32_t now = millis(); - if (now - shotDataTimer > frequency) { - McuCommsSingleton::getInstance().sendShotData(shotData); - shotDataTimer = now; - } + if (now - shotDataTimer < frequency) return; + + esp::mcuComms.sendMessage( + McuCommsMessageType::MCUC_DATA_SHOT_SNAPSHOT, + ProtoSerializer::serialize(shotData) + ); + shotDataTimer = now; } void espCommsSendTareScalesCommand() { - McuCommsSingleton::getInstance().sendRemoteScalesTare(); + esp::mcuComms.sendMessage(McuCommsMessageType::MCUC_CMD_REMOTE_SCALES_TARE); +} + +volatile uint32_t notificationTimer; +void espCommsSendNotification(const Notification& notification, uint32_t frequency) { + uint32_t now = millis(); + if (now - notificationTimer < frequency) return; + notificationTimer = now; + + esp::mcuComms.sendMessage( + McuCommsMessageType::MCUC_DATA_NOTIFICATION, + ProtoSerializer::serialize(notification) + ); +} + +volatile uint32_t systemStateTimer; +void espCommsSendSystemState(const SystemState& systemState, uint32_t frequency) { + uint32_t now = millis(); + if (now - systemStateTimer < frequency) return; + systemStateTimer = now; + + esp::mcuComms.sendMessage( + McuCommsMessageType::MCUC_DATA_SYSTEM_STATE, + ProtoSerializer::serialize(systemState) + ); +} + +void espCommsSendDescaleProgress(const DescalingProgress& descalingProgress) { + esp::mcuComms.sendMessage( + McuCommsMessageType::MCUC_DATA_DESCALING_PROGRESS, + ProtoSerializer::serialize(descalingProgress) + ); +} + + +void espCommsRequestData(McuCommsMessageType dataType) { + esp::mcuComms.sendMessage( + McuCommsMessageType::MCUC_REQ_DATA, + ProtoSerializer::serialize(McuCommsRequestData{ dataType }) + ); +} + +//--------------------------------------------------------------------------- +//------------------------- RECEIVING FROM ESP ------------------------------ +//--------------------------------------------------------------------------- + +void espCommsReadData() { + esp::mcuComms.readDataAndTick(); +} + +void handleMessageReceived(McuCommsMessageType messageType, std::vector& data) { + switch (messageType) { + case McuCommsMessageType::MCUC_DATA_PROFILE: { + Profile profile; + ProtoSerializer::deserialize(data, profile); + esp::receivedProfileAtLeastOnce = true; + onProfileReceived(profile); + break; + } + case McuCommsMessageType::MCUC_DATA_REMOTE_SCALES_WEIGHT: { + Measurement weight = {}; + ProtoSerializer::deserialize(data, weight); + onRemoteScalesWeightReceived(weight.value); + break; + } + case McuCommsMessageType::MCUC_DATA_REMOTE_SCALES_DISCONNECTED: { + onRemoteScalesDisconnected(); + break; + } + case McuCommsMessageType::MCUC_DATA_ALL_SETTINGS: { + GaggiaSettings settings; + ProtoSerializer::deserialize(data, settings); + esp::receivedSettingsAtLeastOnce = true; + onGaggiaSettingsReceived(settings); + break; + } + case McuCommsMessageType::MCUC_DATA_MANUAL_BREW_PHASE: { + Phase phase; + ProtoSerializer::deserialize(data, phase); + onManualBrewPhaseReceived(phase); + break; + } + case McuCommsMessageType::MCUC_DATA_BOILER_SETTINGS: { + BoilerSettings boilerSettings; + ProtoSerializer::deserialize(data, boilerSettings); + onBoilerSettingsReceived(boilerSettings); + break; + } + case McuCommsMessageType::MCUC_DATA_LED_SETTINGS: { + LedSettings ledSettings; + ProtoSerializer::deserialize(data, ledSettings); + onLedSettingsReceived(ledSettings); + break; + } + case McuCommsMessageType::MCUC_DATA_SYSTEM_SETTINGS: { + SystemSettings systemSettings; + ProtoSerializer::deserialize(data, systemSettings); + onSystemSettingsReceived(systemSettings); + break; + } + case McuCommsMessageType::MCUC_DATA_BREW_SETTINGS: { + BrewSettings brewSettings; + ProtoSerializer::deserialize(data, brewSettings); + onBrewSettingsReceived(brewSettings); + break; + } + case McuCommsMessageType::MCUC_CMD_UPDATE_SYSTEM_STATE: { + UpdateSystemStateComand command; + ProtoSerializer::deserialize(data, command); + onUpdateSystemStateCommandReceived(command); + break; + } + default: // Ignore message in all other cases + break; + } } diff --git a/src/peripherals/esp_comms.h b/src/peripherals/esp_comms.h index afd4bb6f..94c1e7ef 100644 --- a/src/peripherals/esp_comms.h +++ b/src/peripherals/esp_comms.h @@ -3,17 +3,42 @@ #define ESP_COMMS_H #include "mcu_comms.h" +#include "gaggia_settings.h" +#include "system_state.h" +#include "notification_message.h" void espCommsInit(); void espCommsReadData(); +// Get temp stored values +Phase& espCommsGetManualPhase(); + +// Send to MCU void espCommsSendSensorData(const SensorState& state, uint32_t frequency = 1000); -void espCommsSendShotData(ShotSnapshot& shotData, uint32_t frequency = 100); +void espCommsSendShotData(const ShotSnapshot& shotData, uint32_t frequency = 100); void espCommsSendTareScalesCommand(); +void espCommsSendNotification(const Notification& notification, uint32_t frequency = 1000); +void espCommsSendSystemState(const SystemState& systemState, uint32_t frequency = 1000); +void espCommsRequestData(McuCommsMessageType dataType); +void espCommsSendDescaleProgress(const DescalingProgress& descalingProgress); -void onProfileReceived(Profile& profile); +// MCU updates void onRemoteScalesWeightReceived(float weight); void onRemoteScalesDisconnected(); +// Settings updates +void onGaggiaSettingsReceived(const GaggiaSettings& gaggiaSettings); +void onBoilerSettingsReceived(const BoilerSettings& boilerSettings); +void onLedSettingsReceived(const LedSettings& ledSettings); +void onSystemSettingsReceived(const SystemSettings& systemSettings); +void onBrewSettingsReceived(const BrewSettings& brewSettings); + +// System state updates +void onUpdateSystemStateCommandReceived(const UpdateSystemStateComand& command); + +// Profiling +void onProfileReceived(const Profile& profile); +void onManualBrewPhaseReceived(const Phase& phase); + #endif diff --git a/src/peripherals/internal_watchdog.h b/src/peripherals/internal_watchdog.h index f43d5820..e8fea8fb 100644 --- a/src/peripherals/internal_watchdog.h +++ b/src/peripherals/internal_watchdog.h @@ -3,14 +3,14 @@ #define INTERNALWATCHDOG_H #include #include -#include "../lcd/lcd.h" +#include "esp_comms.h" #include "../log.h" /*Checking whether system is booting after a hard reset initiated by the internal watchdog.*/ static inline void iwdcInit(void) { // IWDC init if(IWatchdog.isReset()) { - lcdShowPopup("WATCHDOG RESTARTED"); + espCommsSendNotification(Notification::error("WATCHDOG RESTARTED")); IWatchdog.clearReset(); } IWatchdog.begin(5000000); diff --git a/src/peripherals/led.h b/src/peripherals/led.h index 867555fa..ca02eeb5 100644 --- a/src/peripherals/led.h +++ b/src/peripherals/led.h @@ -26,6 +26,8 @@ class LED { CLASSIC = 15u, DESCALE = 1000u }; + void setCurrent(uint8_t pwm); + void setPulse(float smoothness_pts, float gamma, float beta); private: uint32_t timer; }; @@ -106,4 +108,17 @@ void LED::setDisco(uint32_t increment) { } } +void LED::setCurrent(uint8_t pwm) { + #if defined LED_NCP5623 || defined LED_PCA9632 + tankLED.setCurrent(pwm); + #endif +} + +void LED::setPulse(float smoothness_pts, float gamma, float beta) { + for (int i=0;i 0 && check <= sizeof(tmp)) { - lcdShowPopup(tmp); + espCommsSendNotification(Notification::error(tmp)); } } @@ -72,7 +73,12 @@ void i2cResetState(void) { char tmp[25]; unsigned int check = snprintf(tmp, sizeof(tmp), "I2C error code: %i", result); if (check > 0 && check <= sizeof(tmp)) { - result == 0 ? adsInit() : lcdShowPopup(tmp); + if (result == 0) { + adsInit(); + } + else { + espCommsSendNotification(Notification::error(tmp)); + } } delay(50); } diff --git a/src/peripherals/pump.cpp b/src/peripherals/pump.cpp index 2baa6e53..1866aeee 100644 --- a/src/peripherals/pump.cpp +++ b/src/peripherals/pump.cpp @@ -4,6 +4,8 @@ #include #include "utils.h" #include "internal_watchdog.h" +#undef round +#include "math.h" PSM pump(zcPin, dimmerPin, PUMP_RANGE, ZC_MODE, 1, 6); @@ -63,7 +65,7 @@ inline float getPumpPct(const float targetPressure, const float flowRestriction, // - pressure direction void setPumpPressure(const float targetPressure, const float flowRestriction, const SensorState ¤tState) { float pumpPct = getPumpPct(targetPressure, flowRestriction, currentState); - setPumpToRawValue((uint8_t)(pumpPct * PUMP_RANGE)); + setPumpToPercentage(pumpPct); } void setPumpOff(void) { @@ -74,8 +76,8 @@ void setPumpFullOn(void) { pump.set(PUMP_RANGE); } -void setPumpToRawValue(const uint8_t val) { - pump.set(val); +void setPumpToPercentage(float pct) { + pump.set((uint8_t) std::round(pct * PUMP_RANGE)); } void pumpStopAfter(const uint8_t val) { @@ -109,7 +111,10 @@ void pumpPhaseShift(void) { // Models the flow per click, follows a compromise between the schematic and recorded findings // plotted: https://www.desmos.com/calculator/eqynzclagu float getPumpFlowPerClick(const float pressure) { + float livepressure = pressure; float fpc = 0.f; + /* If pressure smh ends up being 0 just to avoid any crashes or bs cause of division by 0 */ + if (livepressure <= 0.f ) livepressure = 0.1f; fpc = (pressureInefficiencyCoefficient[5] / pressure + pressureInefficiencyCoefficient[6]) * ( -pressure * pressure ) + ( flowPerClickAtZeroBar - pressureInefficiencyCoefficient[0]) - (pressureInefficiencyCoefficient[1] + (pressureInefficiencyCoefficient[2] - (pressureInefficiencyCoefficient[3] - pressureInefficiencyCoefficient[4] * pressure) * pressure) * pressure) * pressure; return fpc * fpc_multiplier; } @@ -136,6 +141,6 @@ void setPumpFlow(const float targetFlow, const float pressureRestriction, const } else { float pumpPct = getClicksPerSecondForFlow(targetFlow, currentState.smoothedPressure) / (float)maxPumpClicksPerSecond; - setPumpToRawValue(pumpPct * PUMP_RANGE); + setPumpToPercentage(pumpPct); } } diff --git a/src/peripherals/pump.h b/src/peripherals/pump.h index 2ac638e8..51e890e1 100644 --- a/src/peripherals/pump.h +++ b/src/peripherals/pump.h @@ -7,13 +7,13 @@ #define ZC_MODE FALLING -constexpr uint8_t PUMP_RANGE = 100; +constexpr uint8_t PUMP_RANGE = 250; // Push to 250 for less rounding void pumpInit(const int powerLineFrequency, const float pumpFlowAtZero); void setPumpPressure(const float targetPressure, const float flowRestriction, const SensorState ¤tState); void setPumpOff(void); void setPumpFullOn(void); -void setPumpToRawValue(const uint8_t val); +void setPumpToPercentage(const float percentage); // 0.0 - 1.0 long getAndResetClickCounter(void); int getCPS(void); void pumpPhaseShift(void); diff --git a/src/peripherals/remote_scales.cpp b/src/peripherals/remote_scales.cpp index 8a6f511e..2f75b23f 100644 --- a/src/peripherals/remote_scales.cpp +++ b/src/peripherals/remote_scales.cpp @@ -8,7 +8,7 @@ bool remoteScalesPresent = false; float remoteScalesLatestWeight = 0.f; uint32_t remoteScalesLastWeightTime = 0; -const uint16_t REMOTE_SCALES_TARE_DEBOUNCE = 500; +const uint16_t REMOTE_SCALES_TARE_DEBOUNCE = 1000; uint32_t lastRemoteScalesTare = 0; void remoteScalesTare(void) { uint32_t now = millis(); diff --git a/src/peripherals/scales.cpp b/src/peripherals/scales.cpp index c03c1a7c..d81e2d43 100644 --- a/src/peripherals/scales.cpp +++ b/src/peripherals/scales.cpp @@ -2,6 +2,7 @@ #include "scales.h" #include "pindef.h" #include "remote_scales.h" +#include "gaggia_settings.h" #include namespace { @@ -17,6 +18,8 @@ namespace { }; } +ScalesSettings currentScalesSettings; + bool hwScalesPresent = false; #if defined SINGLE_HX711_BOARD @@ -25,41 +28,42 @@ unsigned char scale_clk = OUTPUT; unsigned char scale_clk = OUTPUT_OPEN_DRAIN; #endif -void scalesInit(float scalesF1, float scalesF2) { +void scalesInit(const ScalesSettings& settings) { + currentScalesSettings = settings; hwScalesPresent = false; // Forced predicitve scales in case someone with actual hardware scales wants to use them. - if (FORCE_PREDICTIVE_SCALES) { + if (currentScalesSettings.forcePredictive) { return; } -#ifndef DISABLE_HW_SCALES - auto& loadCells = LoadCellSingleton::getInstance(); - loadCells.begin(HX711_dout_1, HX711_dout_2, HX711_sck_1, 128U, scale_clk); - loadCells.set_scale(scalesF1, scalesF2); - loadCells.power_up(); + if (currentScalesSettings.hwScalesEnabled) { + auto& loadCells = LoadCellSingleton::getInstance(); + loadCells.begin(HX711_dout_1, HX711_dout_2, HX711_sck_1, 128U, scale_clk); + loadCells.set_scale(currentScalesSettings.hwScalesF1, currentScalesSettings.hwScalesF2); + loadCells.power_up(); - if (loadCells.wait_ready_timeout(1000, 10)) { - loadCells.tare(4); - hwScalesPresent = true; - } - else { - loadCells.power_down(); + if (loadCells.wait_ready_timeout(1000, 10)) { + loadCells.tare(4); + hwScalesPresent = true; } -#endif + else { + loadCells.power_down(); + } +} - if (!hwScalesPresent && remoteScalesIsPresent()) { + if (!hwScalesPresent && settings.btScalesEnabled && remoteScalesIsPresent()) { remoteScalesTare(); } } void scalesTare(void) { - if (hwScalesPresent) { + if (currentScalesSettings.hwScalesEnabled && hwScalesPresent) { auto& loadCells = LoadCellSingleton::getInstance(); if (loadCells.wait_ready_timeout(150, 10)) { loadCells.tare(4); } } - else if (remoteScalesIsPresent()) { + else if (currentScalesSettings.btScalesEnabled && remoteScalesIsPresent()) { remoteScalesTare(); } } @@ -71,21 +75,23 @@ Measurement scalesGetWeight(void) { if (loadCells.wait_ready_timeout(150, 10)) { float values[2]; loadCells.get_units(values); - currentWeight = Measurement{ .value=values[0] + values[1], .millis=millis() }; + currentWeight = Measurement{ .value = values[0] + values[1], .millis = static_cast(millis()) }; } } else if (remoteScalesIsPresent()) { currentWeight = remoteScalesGetWeight(); } + currentWeight.setPrecision(1); return currentWeight; } bool scalesIsPresent(void) { // Forced predicitve scales in case someone with actual hardware scales wants to use them. - if (FORCE_PREDICTIVE_SCALES) { + if (currentScalesSettings.forcePredictive) { return false; } - return hwScalesPresent || remoteScalesIsPresent(); + return (currentScalesSettings.hwScalesEnabled && hwScalesPresent) || + (currentScalesSettings.btScalesEnabled && remoteScalesIsPresent()); } float scalesDripTrayWeight() { @@ -95,3 +101,26 @@ float scalesDripTrayWeight() { } return ((float)value[0] + (float)value[1]); } + +void scalesCalibrate(void) { + static unsigned long timer = millis(); + + if (currentScalesSettings.hwScalesEnabled && hwScalesPresent) { + float values[2]; + static float previousFactor1, previousFactor2; + auto& loadCells = LoadCellSingleton::getInstance(); + + if (currentScalesSettings.hwScalesF1 != previousFactor1 || currentScalesSettings.hwScalesF1 != previousFactor2) { + loadCells.set_scale(currentScalesSettings.hwScalesF1, currentScalesSettings.hwScalesF2); + previousFactor1 = currentScalesSettings.hwScalesF1; + previousFactor2 = currentScalesSettings.hwScalesF2; + } + + if (millis() > timer) { + loadCells.get_units(values); + // write vals to the weight boxes + timer = millis() + 100ul; + } + } + +} diff --git a/src/peripherals/scales.h b/src/peripherals/scales.h index 0738376a..c13aed0e 100644 --- a/src/peripherals/scales.h +++ b/src/peripherals/scales.h @@ -1,17 +1,14 @@ /* 09:32 15/03/2023 - change triggering comment */#ifndef SCALES_H #define SCALES_H -// Needs to be here so it pleases so cppcheck is pleased. -#ifndef FORCE_PREDICTIVE_SCALES -#define FORCE_PREDICTIVE_SCALES 0 -#endif - #include "measurements.h" +#include "gaggia_settings.h" -void scalesInit(float scalesF1, float scalesF2); +void scalesInit(const ScalesSettings& settings); void scalesTare(void); Measurement scalesGetWeight(void); bool scalesIsPresent(void); float scalesDripTrayWeight(); +void scalesCalibrate(void); #endif diff --git a/src/peripherals/tof.h b/src/peripherals/tof.h index 9e02d34a..b5053756 100644 --- a/src/peripherals/tof.h +++ b/src/peripherals/tof.h @@ -1,10 +1,12 @@ #ifndef TOF_H #define TOF_H +#include "HardwareTimer.h" #include // for uint8_t #include #include -#include "../../lib/Common/sensors_state.h" +#include "system_state.h" +#include // for std::generate Adafruit_VL53L0X tof_sensor; movingAvg mvAvg(4); @@ -12,41 +14,47 @@ movingAvg mvAvg(4); class TOF { public: TOF(); - void init(SensorState& sensor); + void init(SystemState&); + void setCustomRanges(uint16_t startValue, uint16_t endValue); uint16_t readLvl(); - uint16_t readRangeToPct(uint16_t val); + uint16_t readRangeToPct(uint16_t); private: - // HardwareTimer* hw_timer; - // static void TimerHandler10(void); uint32_t tofReading; + const std::array waterLvl = { 100u, 90u, 80u, 70u, 60u, 50u, 40u, 30u, 20u, 10u }; + std::array ranges; + + // Helper function to calculate ranges + void calculateRanges(uint16_t startValue, uint16_t endValue); }; TOF::TOF() {} -// void TOF::TimerHandler10() { -// if (instance != nullptr && tof_sensor.isRangeComplete()) { -// TOF::tofReading = tof_sensor.readRangeResult(); -// } -// } - -void TOF::init(SensorState& sensor) { +void TOF::init(SystemState& systemState) { #ifdef TOF_VL53L0X - while(!sensor.tofReady) { - sensor.tofReady = tof_sensor.begin(0x29, false, &Wire, Adafruit_VL53L0X::VL53L0X_SENSE_HIGH_ACCURACY); + while(!systemState.tofReady) { + systemState.tofReady = tof_sensor.begin(0x29, false, &Wire, Adafruit_VL53L0X::VL53L0X_SENSE_HIGH_ACCURACY); } tof_sensor.startRangeContinuous(); mvAvg.begin(); - // Configure the hardware timer - // hw_timer = new HardwareTimer(TIM10); - // hw_timer->setCount(100000, MICROSEC_FORMAT); - // hw_timer->setOverflow(100000, MICROSEC_FORMAT); - // hw_timer->setInterruptPriority(1, 1); - // hw_timer->attachInterrupt(TOF::TimerHandler10); // Attach the ISR function to the timer - #endif } +void TOF::setCustomRanges(uint16_t startValue, uint16_t endValue) { + calculateRanges(startValue, endValue); +} + +void TOF::calculateRanges(uint16_t startValue, uint16_t endValue) { + uint16_t step = (endValue - startValue) / (ranges.size() - 1); + + uint16_t current = startValue; + std::generate(ranges.begin(), ranges.end(), [¤t, step]() { + uint16_t value = current; + current += step; + return value; + }); +} + uint16_t TOF::readLvl() { #ifdef TOF_VL53L0X if(tof_sensor.isRangeComplete()) { @@ -57,15 +65,13 @@ uint16_t TOF::readLvl() { } uint16_t TOF::readRangeToPct(uint16_t val) { - static const std::array water_lvl = { 100u, 90u, 80u, 70u, 60u, 50u, 40u, 30u, 20u, 10u }; - static const std::array ranges = { 15u, 30u, 45u, 60u, 75u, 90u, 105u, 115u, 125u }; for (size_t i = 0; i < ranges.size(); i++) { if (val <= ranges[i]) { - return water_lvl[i]; + return TOF::waterLvl[i]; } } - return 9u; + return 4u; } #endif diff --git a/src/pindef.h b/src/pindef.h index b5c30afe..5c9e5202 100644 --- a/src/pindef.h +++ b/src/pindef.h @@ -2,6 +2,10 @@ #ifndef PINDEF_H #define PINDEF_H +#ifdef RUNNING_TESTS +#include "mock.h" +#endif + // STM32F4 pins definitions #define thermoDO PB4 #define thermoDI PA7 // not used @@ -30,8 +34,8 @@ #define HX711_dout_1 PB8 #define HX711_dout_2 PB9 -#define USART_LCD Serial2 // PA2(TX) & PA3(RX) -#define USART_ESP Serial1 // PA9(TX) & PA10(RX) +#define USART_LCD Serial2 // [Active - FULL DUPLEX DATA TRANSFER] - PA2(TX) & PA3(RX) +#define USART_ESP Serial1 // [INACTIVE - SEE BELOW] PA9(TX) & PA10(RX) #define USART_DEBUG Serial // USB-CDC (Takes PA8,PA9,PA10,PA11) #endif diff --git a/test/test_main.cpp b/test/test_main.cpp index b41e893f..ddbf4328 100644 --- a/test/test_main.cpp +++ b/test/test_main.cpp @@ -2,11 +2,13 @@ #include "./tests/test_pressure_profiler.cpp" #include "./tests/test_pump.cpp" #include "./tests/test_profile_serializer.cpp" - +#include "./tests/test_settings_serializer.cpp" +\ int main(int argc, char **argv) { UNITY_BEGIN(); runAllPressureProfilerTests(); runAllPumpTests(); runAllProfileSerializerTests(); + runAllSettingsSerializerTests(); UNITY_END(); } diff --git a/test/tests/test_pressure_profiler.cpp b/test/tests/test_pressure_profiler.cpp index b810fd76..8d88a87f 100644 --- a/test/tests/test_pressure_profiler.cpp +++ b/test/tests/test_pressure_profiler.cpp @@ -1,28 +1,23 @@ #include #include +#include "../../src/functional/shot_profiler.h" +#include "../../src/functional/shot_profiler.cpp" #include -#include <../../src/eeprom_data/eeprom_data.h> #include -Phase pressurePhase(float start, float end, long time) { - Phase phase = Phase{PHASE_TYPE::PHASE_TYPE_PRESSURE, Transition{start, end}, 0.f, PhaseStopConditions{}}; - phase.stopConditions.time = time; - return phase; +Phase pressurePhase(float start, float end, uint32_t time) { + return Phase{ .type = PhaseType::PRESSURE, .target = Transition{start, end}, .restriction = 0.f, .stopConditions = PhaseStopConditions{.time = time} }; } -Phase presurePhaseWithWeightTarget(float start, float end, long time, float weight) { - Phase phase = Phase{PHASE_TYPE::PHASE_TYPE_PRESSURE, Transition{start, end}, 0.f, PhaseStopConditions{}}; - phase.stopConditions.time = time; - phase.stopConditions.weight = weight; - return phase; +Phase presurePhaseWithWeightTarget(float start, float end, uint32_t time, float weight) { + return Phase{ .type = PhaseType::PRESSURE, .target = Transition{start, end}, .restriction = 0.f, .stopConditions = PhaseStopConditions{.time = time, .weight = weight} }; } SensorState state; ShotSnapshot shotSnapshotInstance; ShotSnapshot& shotSnapshotAtStart = shotSnapshotInstance; -void test_current_phase_calculation(void) -{ +void test_current_phase_calculation(void) { Profile profile; profile.phases.push_back(pressurePhase(0, 2, 1000)); profile.phases.push_back(pressurePhase(2, 2, 10000)); @@ -64,8 +59,7 @@ void test_current_phase_calculation(void) TEST_ASSERT_EQUAL(0, phaseProfiler.getCurrentPhase().getTimeInPhase()); } -void test_get_pressure_for_phase(void) -{ +void test_get_pressure_for_phase(void) { Phase phase = pressurePhase(0, 2, 1000); TEST_ASSERT_EQUAL_FLOAT(2.0f, phase.getTarget(0, shotSnapshotAtStart)); // TEST_ASSERT_EQUAL_FLOAT(1.0f, phase.getTarget(500, shotSnapshotAtStart)); @@ -74,28 +68,26 @@ void test_get_pressure_for_phase(void) } -void test_get_pressure_for_phase_with_negative_change(void) -{ +void test_get_pressure_for_phase_with_negative_change(void) { Phase phase = pressurePhase(9, 6, 3000); - TEST_ASSERT_EQUAL_FLOAT(6.0f, phase.getTarget(0,shotSnapshotAtStart)); + TEST_ASSERT_EQUAL_FLOAT(6.0f, phase.getTarget(0, shotSnapshotAtStart)); // TEST_ASSERT_EQUAL_FLOAT(8.0f, phase.getTarget(1000, shotSnapshotAtStart)); // TEST_ASSERT_EQUAL_FLOAT(7.0f, phase.getTarget(2000, shotSnapshotAtStart)); TEST_ASSERT_EQUAL_FLOAT(6.0f, phase.getTarget(3000, shotSnapshotAtStart)); } -void test_get_pressure_for_phase_with_time_larger_than_duration(void) -{ +void test_get_pressure_for_phase_with_time_larger_than_duration(void) { Phase phase = pressurePhase(9, 6, 3000); TEST_ASSERT_EQUAL_FLOAT(6.0f, phase.getTarget(4000, shotSnapshotAtStart)); TEST_ASSERT_EQUAL_FLOAT(6.0f, phase.getTarget(10000, shotSnapshotAtStart)); } -void test_phases_with_zero_duration_are_skipped(void) { +void test_phases_are_skipped_correctly_based_on_skipped_flag(void) { Profile profile; - profile.addPhase(pressurePhase(2, 2, 0)); - profile.addPhase(pressurePhase(2, 5, 0)); - profile.addPhase(pressurePhase(2, 5, 0)); + profile.addPhase(Phase{ .skip = true }); + profile.addPhase(Phase{ .skip = true }); + profile.addPhase(Phase{ .skip = true }); profile.addPhase(pressurePhase(5, 5, 1000)); PhaseProfiler profiler = PhaseProfiler(profile); @@ -113,7 +105,7 @@ void test_phases_with_weight_stop_condition(void) { float weightTarget = 0.4f; Profile profile; - profile.addPhase(presurePhaseWithWeightTarget(2, 2, -1, weightTarget)); + profile.addPhase(presurePhaseWithWeightTarget(2, 2, 0, weightTarget)); profile.addPhase(pressurePhase(5, 5, 1000)); PhaseProfiler profiler = PhaseProfiler(profile); @@ -146,10 +138,10 @@ void test_phases_with_stop_conditions_and_skipped_phases() { Profile profile; profile.addPhase(presurePhaseWithWeightTarget(2, 2, 30000, weightTarget)); - profile.addPhase(pressurePhase(0, 0, 0)); // should be skipped; + profile.addPhase(Phase{ .stopConditions = {.time = 100000}, .skip = true }); // should be skipped; profile.addPhase(pressurePhase(5, 5, 1000)); - PhaseProfiler profiler = PhaseProfiler{profile}; + PhaseProfiler profiler = PhaseProfiler{ profile }; mockedState.shotWeight = 0.5f; profiler.updatePhase(2000, mockedState); @@ -191,7 +183,7 @@ void runAllPressureProfilerTests() { RUN_TEST(test_get_pressure_for_phase); RUN_TEST(test_get_pressure_for_phase_with_negative_change); RUN_TEST(test_get_pressure_for_phase_with_time_larger_than_duration); - RUN_TEST(test_phases_with_zero_duration_are_skipped); + RUN_TEST(test_phases_are_skipped_correctly_based_on_skipped_flag); RUN_TEST(test_phases_with_weight_stop_condition); RUN_TEST(test_phases_with_stop_conditions_and_skipped_phases); RUN_TEST(test_phases_stay_constant); diff --git a/test/tests/test_profile_serializer.cpp b/test/tests/test_profile_serializer.cpp index c3f5500c..e77353b3 100644 --- a/test/tests/test_profile_serializer.cpp +++ b/test/tests/test_profile_serializer.cpp @@ -1,26 +1,43 @@ #include "profiling_phases.h" -#include "mcu_comms.h" +#include "proto/proto_serializer.h" +#include "proto/profile_converters.h" #include "utils/test-utils.h" +#include void test_profile_serializer_works_correctly(void) { - Phase phaseArray[] = { + Profile profile{ .name = "test profile" }; + profile.phases.push_back({ .name = "Fill", .type = PhaseType::PRESSURE, .target = Transition(), .restriction = -1, .stopConditions = PhaseStopConditions{} }); + profile.phases.push_back({ .type = PhaseType::PRESSURE, .target = Transition(10.f, TransitionCurve::EASE_IN_OUT, 1000), .restriction = -1, .stopConditions = PhaseStopConditions{.time = 1000} }); + profile.phases.push_back({ .type = PhaseType::FLOW, .target = Transition(10.f, 5.f, TransitionCurve::EASE_IN), .restriction = 2.f, .stopConditions = PhaseStopConditions{.weight = 10.f}, .waterTemperature = 95.f }); + profile.phases.push_back({ .type = PhaseType::FLOW, .target = Transition(0.f, 5.f, TransitionCurve::EASE_OUT), .restriction = 3.f, .stopConditions = PhaseStopConditions{.pressureAbove = 2.f} }); + profile.phases.push_back({ .type = PhaseType::FLOW, .target = Transition(10.f, 5.f, TransitionCurve::LINEAR), .restriction = 3.f, .stopConditions = PhaseStopConditions{.time = 199, .pressureAbove = 2.f, .pressureBelow = 1.3f, .flowAbove = 1.f, .flowBelow = 2.4f, .weight = 20.f, .waterPumpedInPhase = 29} }); + profile.globalStopConditions.time = 60000; + profile.globalStopConditions.weight = 35.4f; + profile.globalStopConditions.waterPumped = 120.3f; + profile.waterTemperature = 93.f; - }; + std::vector serializedProfile = ProtoSerializer::serialize(profile); + std::cout << "Output size: " << serializedProfile.size() << "(bytes)" << std::endl; + + Profile deserializedProfile; + ProtoSerializer::deserialize(serializedProfile, deserializedProfile); + + TEST_ASSERT_EQUAL_PROFILE(profile, deserializedProfile); +} - Profile profile; - profile.phases.push_back({ PHASE_TYPE::PHASE_TYPE_PRESSURE, Transition(0.f, 10.f, TransitionCurve::EASE_IN_OUT, 1000), -1, PhaseStopConditions{.time = 1000} }); - profile.phases.push_back({ PHASE_TYPE::PHASE_TYPE_FLOW, Transition(10.f, 5.f, TransitionCurve::LINEAR), 2.f, PhaseStopConditions{.weight = 10.f} }); - profile.phases.push_back({ PHASE_TYPE::PHASE_TYPE_FLOW, Transition(10.f, 5.f, TransitionCurve::LINEAR), 3.f, PhaseStopConditions{.pressureAbove = 2.f} }); - ProfileSerializer serializer; - std::vector serializedProfile = serializer.serializeProfile(profile); +void test_profile_serializer_with_empty_profile(void) { + Profile profile{ .name = "empty profile" }; + std::vector serializedProfile = ProtoSerializer::serialize(profile); + std::cout << "Output size: " << serializedProfile.size() << "(bytes)" << std::endl; Profile deserializedProfile; - serializer.deserializeProfile(serializedProfile, deserializedProfile); + ProtoSerializer::deserialize(serializedProfile, deserializedProfile); TEST_ASSERT_EQUAL_PROFILE(profile, deserializedProfile); } void runAllProfileSerializerTests(void) { RUN_TEST(test_profile_serializer_works_correctly); + RUN_TEST(test_profile_serializer_with_empty_profile); } diff --git a/test/tests/test_settings_serializer.cpp b/test/tests/test_settings_serializer.cpp new file mode 100644 index 00000000..20b58cc6 --- /dev/null +++ b/test/tests/test_settings_serializer.cpp @@ -0,0 +1,112 @@ +#include "gaggia_settings.h" +#include "proto/proto_serializer.h" +#include "proto/settings_converters.h" +#include "proto/message_converters.h" +#include "../webserver/src/persistence/default_settings.h" +#include "utils/test-utils.h" +#include "mcu_comms.h" +#include + +void test_settings_serializer_works_correctly(void) { + GaggiaSettings settings = default_settings::getDefaultSettings(); + + std::vector serializedSettings = ProtoSerializer::serialize(settings); + std::cout << "Output size: " << serializedSettings.size() << "(bytes)" << std::endl; + + GaggiaSettings deserializedSettings; + ProtoSerializer::deserialize(serializedSettings, deserializedSettings); + + TEST_ASSERT_EQUAL_GAGGIA_SETTINGS(settings, deserializedSettings); +} + +void test_data_request_is_serialized_correctly(void) { + McuCommsRequestData requestData = { .type = McuCommsMessageType::MCUC_DATA_ALL_SETTINGS }; + + std::vector serializedRequest = ProtoSerializer::serialize(requestData); + std::cout << "Output size: " << serializedRequest.size() << "(bytes)" << std::endl; + + McuCommsRequestData deserializedRequest; + ProtoSerializer::deserialize(serializedRequest, deserializedRequest); + + TEST_ASSERT_EQUAL_MESSAGE(requestData.type, deserializedRequest.type, "not equal"); +} + +void test_shot_snapshot_is_serialized_correctly(void) { + ShotSnapshot snapshot = { + .timeInShot = 13, + .pressure = 1.f, + .pumpFlow = 2.f, + .weightFlow = 3.f, + .temperature = 98.f, + .shotWeight = 3.f, + .waterPumped = 4.f, + + .targetTemperature = 100.f, + .targetPumpFlow = 12.f, + .targetPressure = 5.f, + }; + + std::vector serializedData = ProtoSerializer::serialize(snapshot); + std::cout << "Output size: " << serializedData.size() << "(bytes)" << std::endl; + + ShotSnapshot deserializedSnapshot; + ProtoSerializer::deserialize(serializedData, deserializedSnapshot); + + TEST_ASSERT_EQUAL_SHOT_SNAPSHOT(snapshot, deserializedSnapshot); +} + +void test_system_state_serialized_correctly(void) { + SystemState systemState = { + .startupInitFinished = true, + .operationMode = OperationMode::FLUSH, + .tofReady = true, + .isSteamForgottenON = true, + .scalesPresent = true, + .timeAlive = 123123, + }; + + std::vector serializedData = ProtoSerializer::serialize(systemState); + std::cout << "Output size: " << serializedData.size() << "(bytes)" << std::endl; + + SystemState deserializedSystemState; + ProtoSerializer::deserialize(serializedData, deserializedSystemState); + + TEST_ASSERT_EQUAL_SYSTEM_STATE(systemState, deserializedSystemState); +} + +void test_measurement_serialized_correctly(void) { + Measurement weight = { + .value = 10.3f, + }; + + std::vector serializedData = ProtoSerializer::serialize(weight); + std::cout << "Output size: " << serializedData.size() << "(bytes)" << std::endl; + + Measurement deserializedWeight = {}; + ProtoSerializer::deserialize(serializedData, deserializedWeight); + + TEST_ASSERT_EQUAL_MESSAGE(weight.value, deserializedWeight.value, "value"); + TEST_ASSERT_EQUAL_MESSAGE(weight.millis, deserializedWeight.millis, "millis"); +} + +void test_notification_serialized_correctly(void) { + Notification notification = Notification::info("some notification"); + + std::vector serializedData = ProtoSerializer::serialize(notification); + std::cout << "Output size: " << serializedData.size() << "(bytes)" << std::endl; + + Notification deserializedNotification = {}; + ProtoSerializer::deserialize(serializedData, deserializedNotification); + + TEST_ASSERT_EQUAL_MESSAGE(notification.type, deserializedNotification.type, "type"); + TEST_ASSERT_EQUAL_STRING_MESSAGE(notification.message.c_str(), deserializedNotification.message.c_str(), "message"); +} + +void runAllSettingsSerializerTests(void) { + RUN_TEST(test_settings_serializer_works_correctly); + RUN_TEST(test_data_request_is_serialized_correctly); + RUN_TEST(test_shot_snapshot_is_serialized_correctly); + RUN_TEST(test_system_state_serialized_correctly); + RUN_TEST(test_measurement_serialized_correctly); + RUN_TEST(test_notification_serialized_correctly); +} diff --git a/test/utils/test-utils.cpp b/test/utils/test-utils.cpp index 58aa281f..27015859 100644 --- a/test/utils/test-utils.cpp +++ b/test/utils/test-utils.cpp @@ -1,24 +1,25 @@ #include "test-utils.h" +#include void TEST_ASSERT_EQUAL_FLOAT_ACCURACY(float expected, float actual, int digits) { float mult = pow(10, digits); TEST_ASSERT_EQUAL_FLOAT(roundf(expected * mult) / mult, roundf(actual * mult) / mult); } -void TEST_ASSERT_EQUAL_GLOBAL_STOP_CONDITIONS(GlobalStopConditions& expected, GlobalStopConditions& actual) { +void TEST_ASSERT_EQUAL_GLOBAL_STOP_CONDITIONS(const GlobalStopConditions& expected, const GlobalStopConditions& actual) { TEST_ASSERT_EQUAL_MESSAGE(expected.time, actual.time, "GlobalStopConditions.time"); TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.waterPumped, actual.waterPumped, "GlobalStopConditions.waterPumped"); TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.weight, actual.weight, "GlobalStopConditions.weight"); } -void TEST_ASSERT_EQUAL_TRANSITION(Transition& expected, Transition& actual) { +void TEST_ASSERT_EQUAL_TRANSITION(const Transition& expected, const Transition& actual) { TEST_ASSERT_EQUAL_MESSAGE(expected.time, actual.time, "Transition.time"); TEST_ASSERT_EQUAL_MESSAGE(expected.curve, actual.curve, "Transition.curve"); TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.start, actual.start, "Transition.start"); TEST_ASSERT_EQUAL_MESSAGE(expected.end, actual.end, "Transition.end"); } -void TEST_ASSERT_EQUAL_PHASE_STOP_CONDITIONS(PhaseStopConditions& expected, PhaseStopConditions& actual) { +void TEST_ASSERT_EQUAL_PHASE_STOP_CONDITIONS(const PhaseStopConditions& expected, const PhaseStopConditions& actual) { TEST_ASSERT_EQUAL_MESSAGE(expected.time, actual.time, "PhaseStopConditions.time"); TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.pressureAbove, actual.pressureAbove, "PhaseStopConditions.pressureAbove"); TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.pressureBelow, actual.pressureBelow, "PhaseStopConditions.pressureBelow"); @@ -28,16 +29,130 @@ void TEST_ASSERT_EQUAL_PHASE_STOP_CONDITIONS(PhaseStopConditions& expected, Phas TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.waterPumpedInPhase, actual.waterPumpedInPhase, "PhaseStopConditions.waterPumpedInPhase"); } -void TEST_ASSERT_EQUAL_PHASE(Phase& expected, Phase& actual) { - TEST_ASSERT_EQUAL_MESSAGE(expected.type, actual.type, "Phase.type"); - TEST_ASSERT_EQUAL_TRANSITION(expected.target, actual.target); - TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.restriction, actual.restriction, "Phase.restriction"); - TEST_ASSERT_EQUAL_PHASE_STOP_CONDITIONS(expected.stopConditions, actual.stopConditions); +void TEST_ASSERT_EQUAL_PHASE(const Phase& expected, const Phase& actual) { + TEST_ASSERT_EQUAL_MESSAGE(expected.skip, actual.skip, "Phase.skip"); + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected.name.c_str(), actual.name.c_str(), "Phase.name"); + if (!expected.skip) { // If phase is skipped the other values don't matter semantically + TEST_ASSERT_EQUAL_MESSAGE(expected.type, actual.type, "Phase.type"); + TEST_ASSERT_EQUAL_TRANSITION(expected.target, actual.target); + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.restriction, actual.restriction, "Phase.restriction"); + TEST_ASSERT_EQUAL_PHASE_STOP_CONDITIONS(expected.stopConditions, actual.stopConditions); + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.waterTemperature, actual.waterTemperature, "Phase.waterTemperature"); + } } -void TEST_ASSERT_EQUAL_PROFILE(Profile& expected, Profile& actual) { +void TEST_ASSERT_EQUAL_PROFILE(const Profile& expected, const Profile& actual) { + TEST_ASSERT_EQUAL_STRING_MESSAGE(expected.name.c_str(), actual.name.c_str(), "Profile.name"); + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.waterTemperature, actual.waterTemperature, "Profile.waterTemperature"); TEST_ASSERT_EQUAL_GLOBAL_STOP_CONDITIONS(expected.globalStopConditions, actual.globalStopConditions); for (int i = 0; i < expected.phaseCount(); i++) { TEST_ASSERT_EQUAL_PHASE(expected.phases[i], actual.phases[i]); } + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.recipe.coffeeIn, actual.recipe.coffeeIn, "profile.recipe.coffeeIn"); + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.recipe.coffeeOut, actual.recipe.coffeeOut, "profile.recipe.coffeeOut"); +} + +void TEST_ASSERT_EQUAL_GAGGIA_SETTINGS(const GaggiaSettings& expected, const GaggiaSettings& actual) { + TEST_ASSERT_EQUAL_MESSAGE(expected.boiler.steamSetPoint, actual.boiler.steamSetPoint, "boiler.steamSetPoint"); + TEST_ASSERT_EQUAL_MESSAGE(expected.boiler.offsetTemp, actual.boiler.offsetTemp, "boiler.offsetTemp"); + TEST_ASSERT_EQUAL_MESSAGE(expected.boiler.hpwr, actual.boiler.hpwr, "boiler.hpwr"); + TEST_ASSERT_EQUAL_MESSAGE(expected.boiler.mainDivider, actual.boiler.mainDivider, "boiler.mainDivider"); + TEST_ASSERT_EQUAL_MESSAGE(expected.boiler.brewDivider, actual.boiler.brewDivider, "boiler.brewDivider"); + + TEST_ASSERT_EQUAL_MESSAGE(expected.system.pumpFlowAtZero, actual.system.pumpFlowAtZero, "system.pumpFlowAtZero"); + TEST_ASSERT_EQUAL_MESSAGE(expected.system.warmupState, actual.system.warmupState, "system.warmupState"); + TEST_ASSERT_EQUAL_MESSAGE(expected.system.lcdSleep, actual.system.lcdSleep, "screen.lcdSleep"); + + TEST_ASSERT_EQUAL_MESSAGE(expected.brew.homeOnShotFinish, actual.brew.homeOnShotFinish, "screen.homeOnShotFinish"); + TEST_ASSERT_EQUAL_MESSAGE(expected.brew.brewDeltaState, actual.brew.brewDeltaState, "brew.brewDeltaState"); + TEST_ASSERT_EQUAL_MESSAGE(expected.brew.basketPrefill, actual.brew.basketPrefill, "brew.basketPrefill"); + + TEST_ASSERT_EQUAL_MESSAGE(expected.led.state, actual.led.state, "led.state"); + TEST_ASSERT_EQUAL_MESSAGE(expected.led.disco, actual.led.disco, "led.disco"); + TEST_ASSERT_EQUAL_MESSAGE(expected.led.color.R, actual.led.color.R, "led.color.R"); + TEST_ASSERT_EQUAL_MESSAGE(expected.led.color.G, actual.led.color.G, "led.color.G"); + TEST_ASSERT_EQUAL_MESSAGE(expected.led.color.B, actual.led.color.B, "led.color.B"); + + TEST_ASSERT_EQUAL_MESSAGE(expected.scales.forcePredictive, actual.scales.forcePredictive, "scales.forcePredictive"); + TEST_ASSERT_EQUAL_MESSAGE(expected.scales.hwScalesEnabled, actual.scales.hwScalesEnabled, "scales.hwScalesEnabled"); + TEST_ASSERT_EQUAL_MESSAGE(expected.scales.hwScalesF1, actual.scales.hwScalesF1, "scales.hwScalesF1"); + TEST_ASSERT_EQUAL_MESSAGE(expected.scales.hwScalesF2, actual.scales.hwScalesF2, "scales.hwScalesF2"); + TEST_ASSERT_EQUAL_MESSAGE(expected.scales.btScalesEnabled, actual.scales.btScalesEnabled, "scales.btScalesEnabled"); + TEST_ASSERT_EQUAL_MESSAGE(expected.scales.btScalesAutoConnect, actual.scales.btScalesAutoConnect, "scales.btScalesAutoConnect"); +} + +void TEST_ASSERT_EQUAL_SHOT_SNAPSHOT(const ShotSnapshot& expected, const ShotSnapshot& actual) { + TEST_ASSERT_EQUAL_MESSAGE(expected.timeInShot, actual.timeInShot, "snapshot.timeInShot"); + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.pressure, actual.pressure, "snapshot.pressure"); + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.pumpFlow, actual.pumpFlow, "snapshot.pumpFlow"); + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.weightFlow, actual.weightFlow, "snapshot.weightFlow"); + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.temperature, actual.temperature, "snapshot.temperature"); + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.shotWeight, actual.shotWeight, "snapshot.shotWeight"); + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.waterPumped, actual.waterPumped, "snapshot.waterPumped"); + + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.targetTemperature, actual.targetTemperature, "snapshot.targetTemperature"); + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.targetPumpFlow, actual.targetPumpFlow, "snapshot.targetPumpFlow"); + TEST_ASSERT_EQUAL_FLOAT_MESSAGE(expected.targetPressure, actual.targetPressure, "snapshot.targetPressure"); +} + +void TEST_ASSERT_EQUAL_SYSTEM_STATE(const SystemState& expected, const SystemState& actual) { + TEST_ASSERT_EQUAL_MESSAGE(expected.startupInitFinished, actual.startupInitFinished, "system.startupInitFinished"); + TEST_ASSERT_EQUAL_MESSAGE(expected.operationMode, actual.operationMode, "system.operationMode"); + TEST_ASSERT_EQUAL_MESSAGE(expected.tofReady, actual.tofReady, "system.tofReady"); + TEST_ASSERT_EQUAL_MESSAGE(expected.isSteamForgottenON, actual.isSteamForgottenON, "system.isSteamForgottenON"); + TEST_ASSERT_EQUAL_MESSAGE(expected.scalesPresent, actual.scalesPresent, "system.scalesPresent"); + TEST_ASSERT_EQUAL_MESSAGE(expected.timeAlive, actual.timeAlive, "system.timeAlive"); +} + +#include +#include +#include + +std::string transitionCurveToString(TransitionCurve curve) { + switch (curve) { + case TransitionCurve::EASE_IN_OUT: return "EASE_IN_OUT"; + case TransitionCurve::EASE_IN: return "EASE_IN"; + case TransitionCurve::EASE_OUT: return "EASE_OUT"; + case TransitionCurve::LINEAR: return "LINEAR"; + case TransitionCurve::INSTANT: return "INSTANT"; + default: return "Unknown"; + } +} + +void printProfile(const Profile& profile) { + std::cout << "Profile: \n"; + std::cout << " Name: " << profile.name << "\n"; + std::cout << " Water Temperature: " << profile.waterTemperature << "\n"; + + std::cout << " Recipe: \n"; + std::cout << " Coffee In: " << profile.recipe.coffeeIn << "\n"; + std::cout << " Coffee Out: " << profile.recipe.coffeeOut << "\n"; + std::cout << " Brewing Ratio: " << profile.recipe.ratio << "\n"; + + std::cout << " Global Stop Conditions: \n"; + std::cout << " Time: " << profile.globalStopConditions.time << "\n"; + std::cout << " Weight: " << profile.globalStopConditions.weight << "\n"; + std::cout << " Water Pumped: " << profile.globalStopConditions.waterPumped << "\n"; + + std::cout << " Phases:\n"; + for (const auto& phase : profile.phases) { + std::cout << " Phase: " << phase.name << "\n"; + std::cout << " Type: " << (phase.type == PhaseType::FLOW ? "FLOW" : "PRESSURE") << "\n"; + std::cout << " Skip: " << (phase.skip ? "true" : "false") << "\n"; + std::cout << " Target Start: " << phase.target.start << "\n"; + std::cout << " Target End: " << phase.target.end << "\n"; + std::cout << " Target Time: " << phase.target.time << "\n"; + std::cout << " Transition Curve: " << transitionCurveToString(phase.target.curve) << "\n"; + std::cout << " Restriction: " << phase.restriction << "\n"; + std::cout << " Water Temperature: " << phase.waterTemperature << "\n"; + + std::cout << " Stop Conditions:\n"; + std::cout << " Time: " << phase.stopConditions.time << "\n"; + std::cout << " Pressure Above: " << phase.stopConditions.pressureAbove << "\n"; + std::cout << " Pressure Below: " << phase.stopConditions.pressureBelow << "\n"; + std::cout << " Flow Above: " << phase.stopConditions.flowAbove << "\n"; + std::cout << " Flow Below: " << phase.stopConditions.flowBelow << "\n"; + std::cout << " Weight: " << phase.stopConditions.weight << "\n"; + std::cout << " Water Pumped In Phase: " << phase.stopConditions.waterPumpedInPhase << "\n"; + } } diff --git a/test/utils/test-utils.h b/test/utils/test-utils.h index 5b3eddd6..dbaf8da7 100644 --- a/test/utils/test-utils.h +++ b/test/utils/test-utils.h @@ -3,14 +3,24 @@ #include #include "profiling_phases.h" +#include "gaggia_settings.h" +#include "system_state.h" #include "./test-utils.cpp" void TEST_ASSERT_EQUAL_FLOAT_ACCURACY(float expected, float actual, int digits); -void TEST_ASSERT_EQUAL_GLOBAL_STOP_CONDITIONS(GlobalStopConditions& expected, GlobalStopConditions& actual); -void TEST_ASSERT_EQUAL_TRANSITION(Transition& expected, Transition& actual); -void TEST_ASSERT_EQUAL_PHASE_STOP_CONDITIONS(PhaseStopConditions& expected, PhaseStopConditions& actual); -void TEST_ASSERT_EQUAL_PHASE(Phase& expected, Phase& actual); -void TEST_ASSERT_EQUAL_PROFILE(Profile& expected, Profile& actual); +void TEST_ASSERT_EQUAL_GLOBAL_STOP_CONDITIONS(const GlobalStopConditions& expected, const GlobalStopConditions& actual); +void TEST_ASSERT_EQUAL_TRANSITION(const Transition& expected, const Transition& actual); +void TEST_ASSERT_EQUAL_PHASE_STOP_CONDITIONS(const PhaseStopConditions& expected, const PhaseStopConditions& actual); +void TEST_ASSERT_EQUAL_PHASE(const Phase& expected, const Phase& actual); +void TEST_ASSERT_EQUAL_PROFILE(const Profile& expected, const Profile& actual); + +void TEST_ASSERT_EQUAL_GAGGIA_SETTINGS(const GaggiaSettings& expected, const GaggiaSettings& actual); + +void TEST_ASSERT_EQUAL_SHOT_SNAPSHOT(const ShotSnapshot& expected, const ShotSnapshot& actual); + +void TEST_ASSERT_EQUAL_SYSTEM_STATE(const SystemState& expected, const SystemState& actual); + +void printProfile(const Profile& profile); #endif diff --git a/uart-esp/main.ino b/uart-esp/main.ino deleted file mode 100644 index 787c755f..00000000 --- a/uart-esp/main.ino +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "SerialTransfer.h" -#include "mcu_comms.h" - -#define UART_MCU Serial1 - -TaskHandle_t Task1; -TaskHandle_t Task2; - -McuComms comms; - -Phase phaseArray[12]; -Profile profile{ 6, phaseArray }; - -void setup(void) { - Serial.begin(460800); - - xTaskCreatePinnedToCore( - Task1code, /* Task function. */ - "Task1", /* name of task. */ - 10000, /* Stack size of task */ - NULL, /* parameter of the task */ - 1, /* priority of the task */ - &Task1, /* Task handle to keep track of created task */ - 0 - ); - - xTaskCreatePinnedToCore( - Task2code, /* Task function. */ - "Task2", /* name of task. */ - 10000, /* Stack size of task */ - NULL, /* parameter of the task */ - 1, /* priority of the task */ - &Task2, /* Task handle to keep track of created task */ - 1 - ); - - UART_MCU.setRxBufferSize(256); - UART_MCU.setTxBufferSize(256); - UART_MCU.begin(115200); - // comms.setDebugPort(&Serial); - comms.begin(UART_MCU); - - comms.setShotSnapshotCallback([](ShotSnapshot& snapshot) { - Serial.print("Received shot snapshot {"); - Serial.print("time:"); Serial.print(snapshot.timeInShot); - Serial.print(", pressure:"); Serial.print(snapshot.pressure); - Serial.print(", pumpFlow:"); Serial.print(snapshot.pumpFlow); - Serial.print(", weightFlow:"); Serial.print(snapshot.weightFlow); - Serial.print(", temp:"); Serial.print(snapshot.temperature); - Serial.print(", shotWeight:"); Serial.print(snapshot.shotWeight); - Serial.print(", waterPumped:"); Serial.print(snapshot.waterPumped); - Serial.print(", targetPressure:"); Serial.print(snapshot.targetPressure); - Serial.print(", targetPumpFlow:"); Serial.print(snapshot.targetPumpFlow); - Serial.print(", targetTemperature:"); Serial.print(snapshot.targetTemperature); - Serial.println("}"); - } - ); -} - -long profileTimer = -4000; -long snapshotTimer = 400; - -void loop(void) { -} - - -//Task1code: runs a core 0 bound task -void Task1code( void * pvParameters ){ - comms.readData(); - if (millis() - profileTimer > 100) { - profile.count = 12; - profile.globalStopConditions = GlobalStopConditions { .time=1923, .weight=37.f, .waterPumped=60.f}; - profile.phases[0] = Phase{ PHASE_TYPE_FLOW, Transition(4.5f), 2.f, PhaseStopConditions{ .pressureBelow=2.f } }; - profile.phases[1] = Phase{ PHASE_TYPE_FLOW, Transition(0.f), 2.f, PhaseStopConditions{ .time=3000 } }; - profile.phases[2] = Phase{ PHASE_TYPE_PRESSURE, Transition(9.f), -1, PhaseStopConditions{ .time=30000 } }; - profile.phases[3] = Phase{ PHASE_TYPE_PRESSURE, Transition(9.f, 7.f), 2.f, PhaseStopConditions{ .weight=35.f } }; - profile.phases[4] = Phase{ PHASE_TYPE_PRESSURE, Transition(7.f), 2.f, PhaseStopConditions{ .weight=35.f } }; - profile.phases[5] = Phase{ PHASE_TYPE_PRESSURE, Transition(7.f), 2.f, PhaseStopConditions{ .weight=36.f } }; - profile.phases[6] = Phase{ PHASE_TYPE_PRESSURE, Transition(7.f), 2.f, PhaseStopConditions{ .weight=37.f } }; - profile.phases[7] = Phase{ PHASE_TYPE_PRESSURE, Transition(7.f), 2.f, PhaseStopConditions{ .weight=38.f } }; - profile.phases[8] = Phase{ PHASE_TYPE_PRESSURE, Transition(7.f), 2.f, PhaseStopConditions{ .weight=39.f } }; - profile.phases[9] = Phase{ PHASE_TYPE_PRESSURE, Transition(7.f), 2.f, PhaseStopConditions{ .weight=40.f } }; - profile.phases[10] = Phase{ PHASE_TYPE_PRESSURE, Transition(7.f), 2.f, PhaseStopConditions{ .weight=41.f } }; - profile.phases[11] = Phase{ PHASE_TYPE_PRESSURE, Transition(7.f), 2.f, PhaseStopConditions{ .weight=42.f } }; - - comms.sendProfile(profile); - - profileTimer = millis(); - } -} -//Task1code: runs a core 0 bound task -void Task2code( void * pvParameters ){ - for(;;){ - delay(700); - Serial.print("Task2 running on core "); - } -} diff --git a/uart-stm/main.ino b/uart-stm/main.ino deleted file mode 100644 index 45735760..00000000 --- a/uart-stm/main.ino +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include "SerialTransfer.h" -#include "mcu_comms.h" - -#define UART_MCU Serial1 - -McuComms comms; - -Phase phaseArray[8]; -Profile profile{ 6, phaseArray }; - -void logProfile(Profile& profile) { - Serial.printf("Received profile {count: %d}\n", profile.count); - for (int i = 0; i < profile.count; i++) { - Serial.printf("phase[%d] {type: %d, transition:{", i, profile.phases[i].type); - Serial.print(profile.phases[i].target.start); - Serial.print("->"); - Serial.print(profile.phases[i].target.end); - Serial.print("} restriction:"); - Serial.print(profile.phases[i].restriction); - Serial.print(", stopConditions: {"); - Serial.print("time:"); Serial.print(profile.phases[i].stopConditions.time); - Serial.print(", pressureAbove:"); Serial.print(profile.phases[i].stopConditions.pressureAbove); - Serial.print(", pressureBelow:"); Serial.print(profile.phases[i].stopConditions.pressureBelow); - Serial.print(", weight:"); Serial.print(profile.phases[i].stopConditions.weight); - Serial.print(", flowAbove:"); Serial.print(profile.phases[i].stopConditions.flowAbove); - Serial.print(", flowBelow:"); Serial.print(profile.phases[i].stopConditions.flowBelow); - Serial.print(", waterPumpedInPhase:"); Serial.print(profile.phases[i].stopConditions.waterPumpedInPhase); - Serial.println("}}"); - } - Serial.print("globalStopConditions: {"); - Serial.print("time:"); Serial.print(profile.globalStopConditions.time); - Serial.print(", weight:"); Serial.print(profile.globalStopConditions.weight); - Serial.print(", waterPumped:"); Serial.print(profile.globalStopConditions.waterPumped); - Serial.println("}"); -} - -void setup(void) { - Serial.begin(115200); - - // comms.setDebugPort(&Serial); - UART_MCU.begin(460800); - comms.begin(UART_MCU); - - comms.setProfileReceivedCallback([](Profile& newProfile) { - delete[] profile.phases; - profile = newProfile; - logProfile(profile); - }); -} - -long stateTimer = 400; -long shotTimer = 100; - -void loop(void) { - comms.readData(); - if (millis() - shotTimer > 200) { - ShotSnapshot shotData = ShotSnapshot{ - .timeInShot=millis(), - .pressure=7.f + random(0, 2) / 10.f, - .pumpFlow=2.f + random(0, 5) / 10.f, - .weightFlow=-1, - .temperature=90.f + random(20, 30) / 10.f, - .shotWeight=millis() / 100000.f, - .waterPumped=0.f, - .targetTemperature=93.f, - .targetPumpFlow=2.f, - .targetPressure=9.f - }; - - comms.sendShotData(shotData); - shotTimer = millis(); - } - if (millis() - stateTimer > 1000) { - SensorStateSnapshot stateData = SensorStateSnapshot{ - .brewActive=false, - .steamActive=false, - .temperature=90.f + random(20, 30) / 10.f, - .pressure=7.f + random(0, 2) / 10.f, - .pumpFlow=2.f + random(0, 5) / 10.f, - .weightFlow=-1, - .weight=0.f - }; - comms.sendSensorStateSnapshot(stateData); - stateTimer = millis(); - } -} diff --git a/webserver/src/main.cpp b/webserver/src/main.cpp index 21e9da3a..c763ee52 100644 --- a/webserver/src/main.cpp +++ b/webserver/src/main.cpp @@ -6,34 +6,100 @@ #include "wifi/wifi_setup.h" #include "server/websocket/websocket.h" #include "scales/ble_scales.h" +#include "persistence/persistence.h" +#include "state/state.h" #include "./log/log.h" void setup() { LOG_INIT(); REMOTE_LOG_INIT([](std::string message) {wsSendLog(message);}); initFS(); + persistence::init(); + state::init(); stmCommsInit(Serial1); wifiSetup(); webServerSetup(); - bleScalesInit(); - vTaskDelete(NULL); //Delete own task by passing NULL(task handle can also be used) + blescales::init(); + vTaskDelete(NULL); //Delete own task by passing NULL } void loop() { - vTaskDelete(NULL); //Delete own task by passing NULL(task handle can also be used) + vTaskDelete(NULL); //Delete own task by passing NULL } // ------------------------------------------------------------------------ -// ---------------- Handle STM communication messages --------------------- +// ----------------------- Handle STM callbacks --------------------------- // ------------------------------------------------------------------------ -void onSensorStateSnapshotReceived(SensorStateSnapshot& sensorData) { +void onSensorStateSnapshotReceived(const SensorStateSnapshot& sensorData) { wsSendSensorStateSnapshotToClients(sensorData); } - -void onShotSnapshotReceived(ShotSnapshot& shotData) { +void onShotSnapshotReceived(const ShotSnapshot& shotData) { wsSendShotSnapshotToClients(shotData); } - +void onSystemStateReceived(const SystemState& systemState) { + state::updateSystemState(systemState); +} void onScalesTareReceived() { - bleScalesTare(); + LOG_INFO("STM sent tare command"); + blescales::tare(); +} +void onGaggiaSettingsRequested() { + LOG_INFO("STM request active settings"); + stmCommsSendGaggiaSettings(state::getSettings()); +} +void onProfileRequested() { + LOG_INFO("STM request active profile"); + stmCommsSendProfile(state::getActiveProfile()); +} +void onNotification(const Notification& notification) { + wsSendNotification(notification); +} +void onDescalingProgressReceived(const DescalingProgress& progress) { + wsSendDescalingProgress(progress); +} + +// ------------------------------------------------------------------------ +// ------------------ Handle state updated callbacks ---------------------- +// ------------------------------------------------------------------------ +void state::onActiveProfileUpdated(const Profile& profile) { + stmCommsSendProfile(state::getActiveProfile()); + wsSendActiveProfileUpdated(); +} +void state::onAllSettingsUpdated(const GaggiaSettings& settings) { + stmCommsSendGaggiaSettings(state::getSettings()); + wsSendSettingsUpdated(); +} +void state::onBrewSettingsUpdated(const BrewSettings& settings) { + stmCommsSendBrewSettings(settings); +} +void state::onBoilerSettingsUpdated(const BoilerSettings& settings) { + stmCommsSendBoilerSettings(settings); +} +void state::onLedSettingsUpdated(const LedSettings& settings) { + stmCommsSendLedSettings(settings); +} +void state::onSystemSettingsUpdated(const SystemSettings& settings) { + stmCommsSendSystemSettings(settings); +} +void state::onScalesSettingsUpdated(const ScalesSettings& settings) { + stmCommsSendScalesSettings(settings); +} +void state::onSystemStateUpdated(const SystemState& systemState) { + wsSendSystemStateToClients(systemState); +} +void state::onUpdateSystemStateCommandSubmitted(const UpdateSystemStateComand& command) { + stmCommsSendUpdateSystemState(command); +} +void state::onConnectedBleScalesUpdated(const blescales::Scales& scales) { + if (scales.address.length() == 0) { + stmCommsSendScaleDisconnected(); + } + wsSendConnectedBleScalesUpdated(scales); +} + +// ------------------------------------------------------------------------ +// -------------------- Handle ble scales callbacks ----------------------- +// ------------------------------------------------------------------------ +void blescales::onWeightReceived(float weight) { + stmCommsSendWeight(weight); } diff --git a/webserver/src/persistence/default_settings.h b/webserver/src/persistence/default_settings.h new file mode 100644 index 00000000..ae194ada --- /dev/null +++ b/webserver/src/persistence/default_settings.h @@ -0,0 +1,114 @@ +#ifndef GAGGIA_DEFAULT_SETTINGS_H +#define GAGGIA_DEFAULT_SETTINGS_H + +#include "gaggia_settings.h" +#include "vector" + +namespace default_settings { + std::vector getDefaultProfiles(void); + + GaggiaSettings getDefaultSettings(void) { + GaggiaSettings defaultData; + + // Boiler + defaultData.boiler.steamSetPoint = 155; + defaultData.boiler.offsetTemp = 7; + defaultData.boiler.hpwr = 550; + defaultData.boiler.mainDivider = 5; + defaultData.boiler.brewDivider = 3; + + // Screen + defaultData.brew.homeOnShotFinish = false; + defaultData.brew.brewDeltaState = true; + defaultData.brew.basketPrefill = false; + + // System settings + defaultData.system.pumpFlowAtZero = 0.2401f; + defaultData.system.lcdSleep = 16; + defaultData.system.warmupState = false; + + // LED settings + defaultData.led.state = true; + defaultData.led.disco = true; + defaultData.led.color.R = 9; + defaultData.led.color.G = 0; + defaultData.led.color.B = 9; + + // Scales settings + defaultData.scales.forcePredictive = false; + defaultData.scales.hwScalesF1 = 3920; + defaultData.scales.hwScalesF2 = 4210; + defaultData.scales.hwScalesEnabled = true; + defaultData.scales.btScalesEnabled = false; + defaultData.scales.btScalesAutoConnect = true; + + return defaultData; + } + + std::vector getDefaultProfiles(void) { + return { + Profile{ + .name = "IUIUIU Classic", + .phases = { + /*pre-infuse*/ Phase{.type = PhaseType::FLOW, .target = Transition(3.f), .restriction = 4.f, .stopConditions = {.time = 20000, .pressureAbove = 4.f, .weight = 4.f, .waterPumpedInPhase = 60.f}}, + /*soak */ Phase{.type = PhaseType::FLOW, .target = Transition(0.f), .stopConditions = {.time = 30000, .pressureBelow = 2.f }}, + /*ramp */ Phase{.type = PhaseType::PRESSURE, .target = Transition(7.5, TransitionCurve::EASE_OUT, 5000), .stopConditions = {.time = 5000}}, + /*main-slope*/ Phase{.type = PhaseType::PRESSURE, .target = Transition(7.5f, 6.f, TransitionCurve::EASE_IN_OUT, 4000), .restriction = 3.f}, + }, + .globalStopConditions = GlobalStopConditions{.weight = 36.f}, + .waterTemperature = 93.f, + .recipe = {.coffeeIn = 18.f, .ratio = 2.f}, + }, + Profile{ + .name = "Londinium", + .phases = { + /*pre-infuse*/ Phase{.type = PhaseType::FLOW, .target = Transition(9.f), .restriction = 4.f, .stopConditions = {.time = 10000, .pressureAbove = 4.f, .waterPumpedInPhase = 65.f}}, + /*soak */ Phase{.type = PhaseType::FLOW, .target = Transition(0.f), .stopConditions = {.time = 10000, .pressureBelow = 0.7f }}, + /*ramp */ Phase{.type = PhaseType::PRESSURE, .target = Transition(9.f, TransitionCurve::EASE_OUT, 1000), .stopConditions = {.time = 1000}}, + /*adv-hold */ Phase{.type = PhaseType::PRESSURE, .target = Transition(9.f), .restriction = 3.f, .stopConditions = {.time = 4000}}, + /*main-slope*/ Phase{.type = PhaseType::PRESSURE, .target = Transition(9.f, 3.f, TransitionCurve::EASE_IN_OUT, 20000), .restriction = 3.f}, + }, + .globalStopConditions = GlobalStopConditions{.weight = 40.f}, + .waterTemperature = 92.f, + .recipe = {.coffeeIn = 20.f, .ratio = 2.f}, + }, + Profile{ + .name = "Adaptive", + .phases = { + /*pre-infuse*/ Phase{.type = PhaseType::FLOW, .target = Transition(7.f), .restriction = 3.f, .stopConditions = {.time = 20000, .pressureAbove = 3.f, .waterPumpedInPhase = 60.f}}, + /*soak */ Phase{.type = PhaseType::PRESSURE, .target = Transition(3.f), .stopConditions = {.time = 6000}}, + /*adv-slope */ Phase{.type = PhaseType::PRESSURE, .target = Transition(9.f, TransitionCurve::LINEAR, 5000), .stopConditions = {.time = 5000 }}, + /*main-slope*/ Phase{.type = PhaseType::FLOW, .target = Transition(2.5f, TransitionCurve::LINEAR, 25000), .restriction = 9.f}, + }, + .globalStopConditions = GlobalStopConditions{.weight = 33.f}, + .waterTemperature = 93.f, + .recipe = {.coffeeIn = 15.f, .coffeeOut = 33.f}, + }, + Profile{ + .name = "Filter 2.1", + .phases = { + /*pre-infuse*/ Phase{.type = PhaseType::FLOW, .target = Transition(4.f), .restriction = 1.f, .stopConditions = {.time = 15000, .pressureAbove = 1.f, .waterPumpedInPhase = 60.f}}, + /*soak */ Phase{.type = PhaseType::FLOW, .target = Transition(0.2f), .stopConditions = {.time = 90000, .weight = 45.f}}, + /*main-slope*/ Phase{.type = PhaseType::FLOW, .target = Transition(0.2f, 3.f, TransitionCurve::EASE_IN_OUT, 10000), .restriction = 9.f}, + }, + .globalStopConditions = GlobalStopConditions{.weight = 36.f}, + .waterTemperature = 89.f, + .recipe = {.coffeeIn = 18.f, .ratio = 2.f}, + }, + Profile{ + .name = "Blooming espresso", + .phases = { + /*pre-infuse*/ Phase{.type = PhaseType::FLOW, .target = Transition(4.f), .restriction = 7.f, .stopConditions = {.time = 20000, .pressureAbove = 7.f, .waterPumpedInPhase = 65.f}}, + /*soak */ Phase{.type = PhaseType::FLOW, .target = Transition(0.f), .stopConditions = {.time = 30000, .pressureBelow = 0.6f, .weight = 5.f}}, + /*ramp */ Phase{.type = PhaseType::FLOW, .target = Transition(2.f, TransitionCurve::EASE_OUT, 5000), .stopConditions = {.time = 5000}}, + /*main-slope*/ Phase{.type = PhaseType::FLOW, .target = Transition(2.f), .restriction = 9.f}, + }, + .globalStopConditions = GlobalStopConditions{.weight = 36.f}, + .waterTemperature = 93.f, + .recipe = {.coffeeIn = 18.f, .ratio = 2.f}, + }, + }; + } +} + +#endif diff --git a/webserver/src/persistence/persistence.cpp b/webserver/src/persistence/persistence.cpp new file mode 100644 index 00000000..c42a2464 --- /dev/null +++ b/webserver/src/persistence/persistence.cpp @@ -0,0 +1,284 @@ +#include "persistence.h" + +#include "string" +#include "Preferences.h" +#include "proto/proto_serializer.h" +#include "proto/persistence_converters.h" +#include "proto/settings_converters.h" +#include "proto/profile_converters.h" +#include "default_settings.h" +#include "saved_profiles.h" +#include "../log/log.h" + +#define KEY_PROFILE(id) std::string("p_id_" + std::to_string(id)) + +namespace persistence { + SemaphoreHandle_t persistenceLock = xSemaphoreCreateRecursiveMutex(); + + GaggiaSettings persistedSettings; + SavedProfiles profileDictionary; + ProfileId nextProfileId; + ProfileId activeProfileId; + + const std::string KEY_NEXT_PROFILE_ID = "n_p_id"; + const std::string KEY_NAMESPACE = "gaggiuino_state"; + const std::string KEY_ACTIVE_PROFILE_ID = "act_p_id"; + const std::string KEY_SETTINGS = "g_settings"; + const std::string KEY_SAVED_PROFILES = "p_sum"; + + Preferences preferences; + + // Private helper method definitions + void initProfiles(); + void initSettings(); + void initNextProfileId(); + void initActiveProfileId(); + ProfileId getAndIncrementProfileId(); + std::vector::iterator findProfileById(ProfileId id_to_find); + template void loadProtoResource(std::string key, typename MESSAGE_CONVERTER::LocalType& target); + template void saveProtoResource(std::string key, const typename MESSAGE_CONVERTER::LocalType& source); + + // ---------------------------------------------------------------------------------------- + // --------------------------------- PUBLIC METHODS---------------------------------------- + // ---------------------------------------------------------------------------------------- + + // Initialise settings or load defaults + void init() { + preferences.begin(KEY_NAMESPACE.c_str(), false); + + initSettings(); + LOG_INFO("Initialized settings"); + + initNextProfileId(); + LOG_INFO("Initialized nextProfileId to %d", nextProfileId); + + initProfiles(); + LOG_INFO("Initialized profile dictionary [size=%d]", profileDictionary.profiles.size()); + + initActiveProfileId(); + LOG_INFO("Initialized activeProfileId to %d", activeProfileId); + } + + // Settings methods + bool saveSettings(const GaggiaSettings& settings) { + saveProtoResource(KEY_SETTINGS, settings); + persistedSettings = settings; + return true; + } + + // Save gaggia settings + const GaggiaSettings& getSettings() { + return persistedSettings; + } + + // Returns the list of known profiles with their names/ids. To get the full profile information + // use getActiveProfile() or getProfile(id) + const std::vector& getSavedProfiles() { + return profileDictionary.profiles; + } + + bool profileExists(ProfileId id) { + auto profilePointer = findProfileById(id); + return profilePointer != profileDictionary.profiles.end() && preferences.isKey(KEY_PROFILE(id).c_str()); + } + + // Get a profile by a given id + std::pair getProfile(ProfileId id) { + auto profilePointer = findProfileById(id); + + if (profilePointer == profileDictionary.profiles.end() || !preferences.isKey(KEY_PROFILE(id).c_str())) { // couldn't find profile, perform cleanup and return + deleteProfile(id); + return { false, {} }; + } + + Profile profile; + loadProtoResource(KEY_PROFILE(id), profile); + return { true, profile }; + } + + ProfileId getActiveProfileId() { + return activeProfileId; + } + + bool saveActiveProfileId(ProfileId id) { + if (xSemaphoreTakeRecursive(persistenceLock, portMAX_DELAY) == pdFALSE) return false; + + auto profilePointer = findProfileById(id); + if (profilePointer == profileDictionary.profiles.end()) { // the id does not exist + return false; + } + + activeProfileId = id; + preferences.putUInt(KEY_ACTIVE_PROFILE_ID.c_str(), activeProfileId); + + xSemaphoreGiveRecursive(persistenceLock); + return true; + } + + // Deletes profile with given id. + bool deleteProfile(ProfileId id) { + if (xSemaphoreTakeRecursive(persistenceLock, portMAX_DELAY) == pdFALSE) return false; + + auto profilePointer = findProfileById(id); + if (profilePointer != profileDictionary.profiles.end()) { // the exists + profileDictionary.profiles.erase(profilePointer); // remove it from the dictionary + saveProtoResource(KEY_SAVED_PROFILES, profileDictionary); + } + + if (preferences.isKey(KEY_PROFILE(id).c_str())) { + preferences.remove(KEY_PROFILE(id).c_str()); // remove it from the storage + } + + xSemaphoreGiveRecursive(persistenceLock); + return true; + } + + // Saves profile with a given id. If the profile cannot be found under this id, it will be created. + bool saveProfile(ProfileId id, const Profile& profile) { + if (xSemaphoreTakeRecursive(persistenceLock, portMAX_DELAY) == pdFALSE) return false; + + auto profilePointer = findProfileById(id); + bool updateDictionary = false; + + if (profilePointer == profileDictionary.profiles.end()) { // the id does not exist + profileDictionary.profiles.push_back(SavedProfile{ .id = id, .name = profile.name }); + updateDictionary = true; + } + + if (profilePointer->name != profile.name) { // if name changed update the dictionary + profilePointer->name = profile.name; + updateDictionary = true; + } + + if (updateDictionary) { + saveProtoResource(KEY_SAVED_PROFILES, profileDictionary); + } + saveProtoResource(KEY_PROFILE(id), profile); + + xSemaphoreGiveRecursive(persistenceLock); + return true; + } + + // Creates a new profile and returns its id. + std::pair saveNewProfile(const Profile& profile) { + if (xSemaphoreTakeRecursive(persistenceLock, portMAX_DELAY) == pdFALSE) return { false, {} }; + + SavedProfile newSavedProfile = SavedProfile{ .id = getAndIncrementProfileId(), .name = profile.name }; + + if (!saveProfile(newSavedProfile.id, profile)) { + xSemaphoreGiveRecursive(persistenceLock); + return { false, {} }; + } + + xSemaphoreGiveRecursive(persistenceLock); + return { true, newSavedProfile }; + } + + // ---------------------------------------------------------------------------------------- + // ----------------------------- PRIVATE HELPER METHODS ----------------------------------- + // ---------------------------------------------------------------------------------------- + + // Initializes profile variables (dictionary and saved profile resources) + void initProfiles() { + // Retrieve profile summaries or initialize profiles + if (preferences.isKey(KEY_SAVED_PROFILES.c_str())) { + LOG_INFO("Found profile dictionary"); + loadProtoResource(KEY_SAVED_PROFILES, profileDictionary); + // TODO: Should we perform health check of saved profiles and attempt to recover? + return; + } + + // If saved profiles were not present initialize all resources to defaults + LOG_INFO("Not found profile dictionary"); + std::vector profiles = default_settings::getDefaultProfiles(); + profileDictionary.profiles.clear(); + + for (auto& profile : profiles) { + SavedProfile savedProfile = SavedProfile{ .id = nextProfileId, .name = profile.name }; + profileDictionary.profiles.push_back(savedProfile); + saveProtoResource(KEY_PROFILE(savedProfile.id), profile); + nextProfileId += 1; + } + + saveProtoResource(KEY_SAVED_PROFILES, profileDictionary); + preferences.putUInt(KEY_NEXT_PROFILE_ID.c_str(), nextProfileId); + } + + // Retrieve settings or initialize to defaults if not present + void initSettings() { + if (preferences.isKey(KEY_SETTINGS.c_str())) { + LOG_INFO("Found persisted settings. Will load them"); + GaggiaSettings settings; + loadProtoResource(KEY_SETTINGS, settings); + LOG_INFO("Default boiler.hpwr = %d", settings.boiler.hpwr); + persistedSettings = settings; + return; + } + + LOG_INFO("Not found persisted settings. Will load defaults"); + saveSettings(default_settings::getDefaultSettings()); + LOG_INFO("Default boiler.hpwr = %d", persistedSettings.boiler.hpwr); + loadProtoResource(KEY_SETTINGS, persistedSettings); + LOG_INFO("Reloaded boiler.hpwr = %d", persistedSettings.boiler.hpwr); + } + + // Initialize nextProfileId + void initNextProfileId() { + if (preferences.isKey(KEY_NEXT_PROFILE_ID.c_str())) { + LOG_INFO("Found nextProfileId. Will load it"); + nextProfileId = preferences.getUInt(KEY_NEXT_PROFILE_ID.c_str()); + return; + } + + LOG_INFO("Not nextProfileId. Will default it"); + nextProfileId = 0u; + preferences.putUInt(KEY_NEXT_PROFILE_ID.c_str(), nextProfileId); + } + + // Retrieve active profile index or initialize to zero + void initActiveProfileId() { + if (preferences.isKey(KEY_ACTIVE_PROFILE_ID.c_str())) { + LOG_INFO("Found activeProfileId. Will load it"); + activeProfileId = preferences.getUInt(KEY_ACTIVE_PROFILE_ID.c_str()); + } + else { + LOG_INFO("Not found activeProfileId. Will default it"); + activeProfileId = 0u; + preferences.putUInt(KEY_ACTIVE_PROFILE_ID.c_str(), activeProfileId); + } + } + + ProfileId getAndIncrementProfileId() { + ProfileId result = nextProfileId++; + preferences.putUInt(KEY_NEXT_PROFILE_ID.c_str(), nextProfileId); + return result; + } + + // Searches in the profile dictionary for a profile with the given id and returns + // an iterator pointer to the element of that id, or a pointer to the end of the dictionary + // if the id was not found + std::vector::iterator findProfileById(ProfileId id) { + for (auto it = profileDictionary.profiles.begin(); it != profileDictionary.profiles.end(); ++it) { + if (it->id == id) { + return it; + } + } + return profileDictionary.profiles.end(); + } + + template + void loadProtoResource(std::string key, typename MESSAGE_CONVERTER::LocalType& target) { + size_t size = preferences.getBytesLength(key.c_str()); + std::vector data(size); + LOG_INFO("Retrieving key[%s] from nvs with size=%dbytes", key.c_str(), size); + preferences.getBytes(key.c_str(), data.data(), size); + ProtoSerializer::deserialize(data, target); + } + + template + void saveProtoResource(std::string key, const typename MESSAGE_CONVERTER::LocalType& source) { + std::vector data = ProtoSerializer::serialize(source); + LOG_INFO("Saving key[%s] to nvs with size=%dbytes", key.c_str(), data.size()); + preferences.putBytes(key.c_str(), data.data(), data.size()); + } +} // namespace state diff --git a/webserver/src/persistence/persistence.h b/webserver/src/persistence/persistence.h new file mode 100644 index 00000000..d0192f6d --- /dev/null +++ b/webserver/src/persistence/persistence.h @@ -0,0 +1,28 @@ +#ifndef SETTINGS_STATE_H +#define SETTINGS_STATE_H + +#include "vector" +#include "gaggia_settings.h" +#include "profiling_phases.h" +#include "saved_profiles.h" + +namespace persistence { + void init(); + + const GaggiaSettings& getSettings(); + bool saveSettings(const GaggiaSettings& settings); + + const std::vector& getSavedProfiles(); + + ProfileId getActiveProfileId(); + bool saveActiveProfileId(ProfileId id); + + bool profileExists(ProfileId id); + std::pair getProfile(ProfileId id); + bool deleteProfile(ProfileId id); + bool saveProfile(ProfileId id, const Profile& profile); + std::pair saveNewProfile(const Profile& profile); + +} // namespace state + +#endif diff --git a/webserver/src/persistence/proto/persistence.proto b/webserver/src/persistence/proto/persistence.proto new file mode 100644 index 00000000..d67ad760 --- /dev/null +++ b/webserver/src/persistence/proto/persistence.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +message SavedProfileDto { + uint32 id = 1; + string name = 2; +} + +message SavedProfilesDto { + repeated SavedProfileDto profiles = 1; +} + diff --git a/webserver/src/persistence/proto/persistence_converters.h b/webserver/src/persistence/proto/persistence_converters.h new file mode 100644 index 00000000..2b50e257 --- /dev/null +++ b/webserver/src/persistence/proto/persistence_converters.h @@ -0,0 +1,51 @@ +#ifndef WEB_PERSISTENCE_CONVERTERS_H +#define WEB_PERSISTENCE_CONVERTERS_H + +#include "nanopb_cpp.h" +#include "../saved_profiles.h" +#include "persistence.pb.h" + + +class SavedProfileConverter : public NanoPb::Converter::MessageConverter { +public: + static ProtoType encoderInit(const LocalType& local) { + return SavedProfileDto{ + .id = local.id, + .name = NanoPb::Converter::StringConverter::encoderInit(local.name), + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return SavedProfileDto{ + .name = NanoPb::Converter::StringConverter::decoderInit(local.name), + }; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + local.id = proto.id; + return true; + }; +}; + +class SavedProfilesConverter : public NanoPb::Converter::MessageConverter { +public: + using SavedProfileArrayConverter = NanoPb::Converter::ArrayConverter>; + + static ProtoType encoderInit(const LocalType& local) { + return SavedProfilesDto{ + .profiles = SavedProfileArrayConverter::encoderCallbackInit(local.profiles), + }; + }; + + static ProtoType decoderInit(LocalType& local) { + return SavedProfilesDto{ + .profiles = SavedProfileArrayConverter::decoderCallbackInit(local.profiles), + }; + }; + + static bool decoderApply(const ProtoType& proto, LocalType& local) { + return true; + }; +}; + +#endif diff --git a/webserver/src/persistence/saved_profiles.h b/webserver/src/persistence/saved_profiles.h new file mode 100644 index 00000000..aaff9dd3 --- /dev/null +++ b/webserver/src/persistence/saved_profiles.h @@ -0,0 +1,18 @@ +#ifndef PROFILE_SUMMARY_H +#define PROFILE_SUMMARY_H + +#include "string" +#include "vector" + +using ProfileId = uint32_t; + +struct SavedProfile { + ProfileId id; + std::string name; +}; + +struct SavedProfiles { + std::vector profiles; +}; + +#endif diff --git a/webserver/src/scales/ble_scales.cpp b/webserver/src/scales/ble_scales.cpp index 7f14c66b..1f43aaae 100644 --- a/webserver/src/scales/ble_scales.cpp +++ b/webserver/src/scales/ble_scales.cpp @@ -2,61 +2,111 @@ #include #include "remote_scales.h" #include "scales/acaia.h" -#include "../stm_comms/stm_comms.h" +#include "../state/state.h" #include "../log/log.h" #include "../task_config.h" +#include -namespace { +namespace blescales { RemoteScalesScanner remoteScalesScanner; std::unique_ptr bleScales; -} -void bleScalesTask(void* params); + void bleScalesTask(void* params); -void bleScalesInit() { - AcaiaScalesPlugin::apply(); - BLEDevice::init("Gaggiuino"); + // ---------------------------------------------------------------- + // ------------------------- PUBLIC METHODS ----------------------- + // ---------------------------------------------------------------- - xTaskCreateUniversal(bleScalesTask, "bleScales", configMINIMAL_STACK_SIZE + 4096, NULL, PRIORITY_BLE_SCALES_MAINTAINANCE, NULL, CORE_BLE_SCALES_MAINTAINANCE); -} + void init() { + AcaiaScalesPlugin::apply(); + xTaskCreateUniversal(bleScalesTask, "bleScales", configMINIMAL_STACK_SIZE + 4096, NULL, PRIORITY_BLE_SCALES_MAINTAINANCE, NULL, CORE_BLE_SCALES_MAINTAINANCE); + } + + void tare() { + if (bleScales.get() != nullptr) { + bleScales->tare(); + } + } -void bleScalesTask(void* params) { - remoteScalesScanner.initializeAsyncScan(); + std::vector getAvailableScales() { + auto discoveredScales = remoteScalesScanner.getDiscoveredScales(); + std::vector result(discoveredScales.size()); - LOG_INFO("Remote scales and bluetooth initialized"); + std::transform(discoveredScales.begin(), discoveredScales.end(), result.begin(), [](RemoteScales* input) { + return Scales{ .name = input->getDeviceName(), .address = input->getDeviceAddress() }; + }); - for (;;) { - bleScalesMaintainConnection(); - vTaskDelay(1000 / portTICK_PERIOD_MS); + return result; } -} - -void bleScalesMaintainConnection() { - if (bleScales.get() == nullptr) { // No scale discovered yet. Keep checking scan results to find scales. - std::vector scales = remoteScalesScanner.getDiscoveredScales(); - if (scales.size() > 0) { - LOG_INFO("We have %d discovered scales.", scales.size()); - bleScales.reset(scales[0]); - remoteScalesScanner.stopAsyncScan(); - bleScales->setWeightUpdatedCallback(stmCommsSendWeight); - bleScales->setLogCallback([](std::string message) { LOG_INFO(message.c_str()); }); - bleScales->connect(); + Scales getConnectedScales() { + if (bleScales.get() != nullptr && bleScales->isConnected()) { + return { .name = bleScales->getDeviceName(), .address = bleScales->getDeviceAddress() }; } + return { .name = "", .address = "" }; } - else if (!bleScales->isConnected()) { // Scale discovered but not connected. Make sure it's still reachable. - LOG_INFO("Connection failed. Will retry."); - stmCommsSendScaleDisconnected(); - bleScales.release(); - remoteScalesScanner.restartAsyncScan(); + + // ---------------------------------------------------------------- + // ---------------------- PRIVATE HELPER METHODS ------------------ + // ---------------------------------------------------------------- + + void handleBleDevice(); + void maintainConnection(); + + void bleScalesTask(void* params) { + LOG_INFO("Remote scales and bluetooth initialized"); + + for (;;) { + handleBleDevice(); + maintainConnection(); + vTaskDelay(1000 / portTICK_PERIOD_MS); + } } - else if (bleScales->isConnected()) { // Scale stil connected. Invoke update to keep alive. - bleScales->update(); + + void handleBleDevice() { + if (state::getSettings().scales.btScalesEnabled && !BLEDevice::getInitialized()) { + BLEDevice::init("Gaggiuino"); + } + + if (!state::getSettings().scales.btScalesEnabled && BLEDevice::getInitialized()) { + remoteScalesScanner.stopAsyncScan(); + if (bleScales.get() != nullptr && bleScales->isConnected()) { + bleScales->disconnect(); + bleScales.release(); + state::updateConnectedScales({}); + } + BLEDevice::deinit(); + } } -} -void bleScalesTare() { - if (bleScales.get() != nullptr) { - bleScales->tare(); + void maintainConnection() { + if (!state::getSettings().scales.btScalesEnabled) { + return; + } + + if (bleScales.get() == nullptr) { // No scale discovered yet. Keep checking scan results to find scales. + remoteScalesScanner.initializeAsyncScan(); + + std::vector scales = remoteScalesScanner.getDiscoveredScales(); + + if (scales.size() > 0) { + LOG_INFO("We have discovered %d matching scales.", scales.size()); + bleScales.reset(scales[0]); + bleScales->setWeightUpdatedCallback(onWeightReceived); + bleScales->setLogCallback([](std::string message) { LOG_INFO(message.c_str()); }); + bleScales->connect(); + state::updateConnectedScales({ bleScales->getDeviceName(), bleScales->getDeviceAddress() }); + } + } + else if (!bleScales->isConnected()) { // Scale discovered but not connected. Make sure it's still reachable. + LOG_INFO("Connection failed. Will retry."); + remoteScalesScanner.stopAsyncScan(); + bleScales.release(); + state::updateConnectedScales({}); + } + else if (bleScales->isConnected()) { // Scale stil connected. Invoke update to keep alive. + remoteScalesScanner.stopAsyncScan(); + bleScales->update(); + } } } diff --git a/webserver/src/scales/ble_scales.h b/webserver/src/scales/ble_scales.h index 924a291b..7a113a82 100644 --- a/webserver/src/scales/ble_scales.h +++ b/webserver/src/scales/ble_scales.h @@ -1,8 +1,21 @@ #ifndef BLE_SCALES_H #define BLE_SCALES_H -void bleScalesInit(); -void bleScalesMaintainConnection(); -void bleScalesTare(); +#include +#include + +namespace blescales { + struct Scales { + std::string name; + std::string address; + }; + + void init(); + void tare(); + void onWeightReceived(float weight); + + std::vector getAvailableScales(); + Scales getConnectedScales(); +} #endif diff --git a/webserver/src/server/api/api_profile.cpp b/webserver/src/server/api/api_profile.cpp new file mode 100644 index 00000000..ef5a76cd --- /dev/null +++ b/webserver/src/server/api/api_profile.cpp @@ -0,0 +1,198 @@ +#include "api_settings.h" +#include "../utils/server_utils.h" +#include "ESPAsyncWebServer.h" +#include "AsyncTCP.h" +#include + +#include "../../state/state.h" +#include "../../persistence/persistence.h" +#include "../../log/log.h" +#include "../json/json_profile_converters.h" + +// Gets and updates part of the settings object in memory. + +void handleGetProfileSummaries(AsyncWebServerRequest* request); +void handleGetProfileById(AsyncWebServerRequest* request); +void handleGetActiveProfile(AsyncWebServerRequest* request); + +void handlePostProfile(AsyncWebServerRequest* request, JsonVariant& body); +void handleUpdateProfile(AsyncWebServerRequest* request, JsonVariant& body); +void handleDeleteProfile(AsyncWebServerRequest* request); + +void handleSelectActiveProfileId(AsyncWebServerRequest* request, JsonVariant& body); +void handleGetActiveProfileId(AsyncWebServerRequest* request); +void handlePersistActiveProfile(AsyncWebServerRequest* request); +void handleUpdateActiveProfile(AsyncWebServerRequest* request, JsonVariant& body); + +void setupProfileApi(AsyncWebServer& server) { + server.on("/api/profile-summaries", HTTP_GET, handleGetProfileSummaries); + server.on("^\\/api\\/profiles\\/([0-9]+)$", HTTP_GET, handleGetProfileById); + server.on("^\\/api\\/profiles\\/([0-9]+)$", HTTP_DELETE, handleDeleteProfile); + server.on("^\\/api\\/profiles\\/([0-9]+)$", HTTP_PUT, withJson(handleUpdateProfile, 4096), NULL, onJsonBody); + server.on("/api/profiles", HTTP_POST, withJson(handlePostProfile, 4096), NULL, onJsonBody); + + server.on("/api/profiles/active-profile/id", HTTP_PUT, withJson(handleSelectActiveProfileId, 50), NULL, onJsonBody); + server.on("/api/profiles/active-profile/id", HTTP_GET, handleGetActiveProfileId); + server.on("/api/profiles/active-profile/persist", HTTP_PUT, handlePersistActiveProfile); + server.on("/api/profiles/active-profile", HTTP_GET, handleGetActiveProfile); + server.on("/api/profiles/active-profile", HTTP_PUT, withJson(handleUpdateActiveProfile, 4096), NULL, onJsonBody); +} + +// ------------------------------------------------------------------------------------------ +// ------------------------------- Profile Summaries ---------------------------------------- +// ------------------------------------------------------------------------------------------ + +void handleGetProfileSummaries(AsyncWebServerRequest* request) { + LOG_INFO("Got request to get profile summaries"); + + DynamicJsonDocument json(2048); + JsonArray profileSummariesJson = json.to(); + + auto savedProfiles = persistence::getSavedProfiles(); + for (auto savedProfile : savedProfiles) { + auto savedProfileJson = profileSummariesJson.createNestedObject(); + json::mapProfileSummaryToJson(savedProfile, savedProfileJson); + } + + sendJsonResponse(request, profileSummariesJson); +} + +// ------------------------------------------------------------------------------------------ +// ----------------------------- Profile actions by ID -------------------------------------- +// ------------------------------------------------------------------------------------------ +void handleGetProfileById(AsyncWebServerRequest* request) { + LOG_INFO("Got request to get profile by id"); + + uint32_t id = request->pathArg(0).toInt(); + auto result = persistence::getProfile(id); + if (!result.first) { + request->send(404); + return; + } + + DynamicJsonDocument json(2048); + JsonObject jsonObj = json.to(); + + json::mapProfileToJson(id, result.second, jsonObj); + + sendJsonResponse(request, jsonObj); +} + +void handlePostProfile(AsyncWebServerRequest* request, JsonVariant& body) { + LOG_INFO("Got request to create new profile"); + + Profile newProfile = json::mapJsonToProfile(body); + auto result = persistence::saveNewProfile(newProfile); + if (!result.first) { + request->send(422); + return; + } + SavedProfile savedProfile = result.second; + + JsonObject responseBody = body.to(); + json::mapProfileToJson(savedProfile.id, newProfile, responseBody); + + sendJsonResponse(request, responseBody); +} + +void handleUpdateProfile(AsyncWebServerRequest* request, JsonVariant& body) { + uint32_t id = request->pathArg(0).toInt(); + LOG_INFO("Got request to update profile by id %d", id); + + if (!persistence::profileExists(id)) { + request->send(404); + return; + } + + Profile profile = json::mapJsonToProfile(body); + if (id == state::getActiveProfileId()) { + state::updateActiveProfile(profile); + } + persistence::saveProfile(id, profile); + + JsonObject responseBody = body.to(); + json::mapProfileToJson(id, profile, responseBody); + + sendJsonResponse(request, responseBody); +} + +void handleDeleteProfile(AsyncWebServerRequest* request) { + LOG_INFO("Got request to delete profile by id"); + + uint32_t id = request->pathArg(0).toInt(); + + if (state::getActiveProfileId() == id || persistence::getActiveProfileId() == id) { + request->send(422); + return; + } + + bool result = persistence::deleteProfile(id); + if (!result) { + request->send(422); + return; + } + + request->send(200); +} + +// ------------------------------------------------------------------------------------------ +// ----------------------------- Active profile actions ------------------------------------- +// ------------------------------------------------------------------------------------------ +void handleGetActiveProfile(AsyncWebServerRequest* request) { + LOG_INFO("Got request to get active profile"); + + DynamicJsonDocument json(4096); + JsonObject jsonObj = json.to(); + + json::mapProfileToJson(state::getActiveProfileId(), state::getActiveProfile(), jsonObj); + + sendJsonResponse(request, jsonObj); +} + +void handleSelectActiveProfileId(AsyncWebServerRequest* request, JsonVariant& body) { + LOG_INFO("Got request to select new active profile"); + + uint32_t id = body["id"]; + + bool result = state::updateActiveProfileId(id); + if (!result) { + request->send(422, "application/json", "{}"); + return; + } + + request->send(200); +} + +void handleGetActiveProfileId(AsyncWebServerRequest* request) { + LOG_INFO("Got request to get the active profile id"); + + DynamicJsonDocument json(50); + JsonObject jsonObj = json.to(); + jsonObj["id"] = state::getActiveProfileId(); + + sendJsonResponse(request, jsonObj); +} + +void handlePersistActiveProfile(AsyncWebServerRequest* request) { + LOG_INFO("Got request to persist active profile"); + + bool result = state::persistActiveProfile() && state::persistActiveProfileId(); + + if (result) { + request->send(200); + } + else { + request->send(422); + } +} + +void handleUpdateActiveProfile(AsyncWebServerRequest* request, JsonVariant& body) { + LOG_INFO("Got request to update active profile"); + + state::updateActiveProfile(json::mapJsonToProfile(body)); + + JsonObject responseBody = body.to(); + json::mapProfileToJson(state::getActiveProfileId(), state::getActiveProfile(), responseBody); + + sendJsonResponse(request, responseBody); +} diff --git a/webserver/src/server/api/api_profile.h b/webserver/src/server/api/api_profile.h new file mode 100644 index 00000000..5bc2ec1b --- /dev/null +++ b/webserver/src/server/api/api_profile.h @@ -0,0 +1,8 @@ +#ifndef API_PROFILE_H +#define API_PROFILE_H + +#include "ESPAsyncWebServer.h" + +void setupProfileApi(AsyncWebServer& server); + +#endif diff --git a/webserver/src/server/api/api_scales.cpp b/webserver/src/server/api/api_scales.cpp new file mode 100644 index 00000000..6eebcc35 --- /dev/null +++ b/webserver/src/server/api/api_scales.cpp @@ -0,0 +1,51 @@ +#include "api_settings.h" +#include "../utils/server_utils.h" +#include "ESPAsyncWebServer.h" +#include "AsyncTCP.h" +#include + +#include "../json/json_settings_converters.h" +#include "../../state/state.h" +#include "../../log/log.h" + +// Gets and updates part of the settings object in memory. +void handleGetConnectedScales(AsyncWebServerRequest* request); +void handleGetAvailableScales(AsyncWebServerRequest* request); + +void setupScalesApi(AsyncWebServer& server) { + server.on("/api/bt-scales/connected", HTTP_GET, handleGetConnectedScales); + server.on("/api/bt-scales/available", HTTP_GET, handleGetAvailableScales); +} + +// ------------------------------------------------------------------------------------------ +// ------------------------------------- GET METHODS ---------------------------------------- +// ------------------------------------------------------------------------------------------ + +void handleGetConnectedScales(AsyncWebServerRequest* request) { + auto connectedScales = blescales::getConnectedScales(); + + LOG_INFO("Got request get connected scales"); + + DynamicJsonDocument json(100); + JsonObject jsonObj = json.to(); + + json::mapBleScalesToJson(connectedScales, jsonObj); + + sendJsonResponse(request, jsonObj); +} + +void handleGetAvailableScales(AsyncWebServerRequest* request) { + auto availableScales = blescales::getAvailableScales(); + + LOG_INFO("Got request get available scales"); + + DynamicJsonDocument json(2048); + JsonArray jsonArray = json.to(); + + for (auto scales : availableScales) { + JsonObject scalesJson = jsonArray.createNestedObject(); + json::mapBleScalesToJson(scales, scalesJson); + } + + sendJsonResponse(request, jsonArray); +} diff --git a/webserver/src/server/api/api_scales.h b/webserver/src/server/api/api_scales.h new file mode 100644 index 00000000..402a8a11 --- /dev/null +++ b/webserver/src/server/api/api_scales.h @@ -0,0 +1,8 @@ +#ifndef API_SCALES_H +#define API_SCALES_H + +#include "ESPAsyncWebServer.h" + +void setupScalesApi(AsyncWebServer& server); + +#endif diff --git a/webserver/src/server/api/api_settings.cpp b/webserver/src/server/api/api_settings.cpp new file mode 100644 index 00000000..3ed4cbe9 --- /dev/null +++ b/webserver/src/server/api/api_settings.cpp @@ -0,0 +1,213 @@ +#include "api_settings.h" +#include "../utils/server_utils.h" +#include "ESPAsyncWebServer.h" +#include "AsyncTCP.h" +#include + +#include "../json/json_settings_converters.h" +#include "../../state/state.h" +#include "../../log/log.h" + +// Gets and updates part of the settings object in memory. +void handleGetAllSettings(AsyncWebServerRequest* request); +void handlePutAllSettings(AsyncWebServerRequest* request, JsonVariant& body); + +void handleGetBoilerSettings(AsyncWebServerRequest* request); +void handlePutBoilerSettings(AsyncWebServerRequest* request, JsonVariant& body); + +void handleGetBrewSettings(AsyncWebServerRequest* request); +void handlePutBrewSettings(AsyncWebServerRequest* request, JsonVariant& body); + +void handleGetLedSettings(AsyncWebServerRequest* request); +void handlePutLedSettings(AsyncWebServerRequest* request, JsonVariant& body); + +void handleGetScalesSettings(AsyncWebServerRequest* request); +void handlePutScalesSettings(AsyncWebServerRequest* request, JsonVariant& body); + +void handleGetSystemSettings(AsyncWebServerRequest* request); +void handlePutSystemSettings(AsyncWebServerRequest* request, JsonVariant& body); + +// Only this method persists to non volatile storage +void handlePersistSettings(AsyncWebServerRequest* request); + +void setupSettingsApi(AsyncWebServer& server) { + + server.on("/api/settings/boiler", HTTP_GET, handleGetBoilerSettings); + server.on("/api/settings/boiler", HTTP_PUT, withJson(handlePutBoilerSettings), NULL, onJsonBody); + + server.on("/api/settings/brew", HTTP_GET, handleGetBrewSettings); + server.on("/api/settings/brew", HTTP_PUT, withJson(handlePutBrewSettings), NULL, onJsonBody); + + server.on("/api/settings/led", HTTP_GET, handleGetLedSettings); + server.on("/api/settings/led", HTTP_PUT, withJson(handlePutLedSettings), NULL, onJsonBody); + + server.on("/api/settings/system", HTTP_GET, handleGetSystemSettings); + server.on("/api/settings/system", HTTP_PUT, withJson(handlePutSystemSettings), NULL, onJsonBody); + + server.on("/api/settings/scales", HTTP_GET, handleGetScalesSettings); + server.on("/api/settings/scales", HTTP_PUT, withJson(handlePutScalesSettings), NULL, onJsonBody); + + server.on("/api/settings/persist", HTTP_PUT, handlePersistSettings); + + server.on("/api/settings", HTTP_GET, handleGetAllSettings); + server.on("/api/settings", HTTP_PUT, withJson(handlePutAllSettings), NULL, onJsonBody); +} + +// ------------------------------------------------------------------------------------------ +// ------------------------------------- GET METHODS ---------------------------------------- +// ------------------------------------------------------------------------------------------ + +void handleGetLedSettings(AsyncWebServerRequest* request) { + const LedSettings& led = state::getSettings().led; + + LOG_INFO("Got request get LedSettings"); + + DynamicJsonDocument json(100); + JsonObject jsonObj = json.to(); + json::mapLedSettingsToJson(led, jsonObj); + + sendJsonResponse(request, jsonObj); +} + +void handleGetBoilerSettings(AsyncWebServerRequest* request) { + const BoilerSettings& boiler = state::getSettings().boiler; + + LOG_INFO("Got request get BoilerSettings"); + + DynamicJsonDocument json(100); + JsonObject jsonObj = json.to(); + json::mapBoilerSettingsToJson(boiler, jsonObj); + + sendJsonResponse(request, jsonObj); +} + + +void handleGetBrewSettings(AsyncWebServerRequest* request) { + const BrewSettings& brew = state::getSettings().brew; + + LOG_INFO("Got request get BrewSettings"); + + DynamicJsonDocument json(100); + JsonObject jsonObj = json.to(); + json::mapBrewSettingsToJson(brew, jsonObj); + + sendJsonResponse(request, jsonObj); +} + +void handleGetSystemSettings(AsyncWebServerRequest* request) { + const SystemSettings& system = state::getSettings().system; + + LOG_INFO("Got request get SystemSettings"); + + DynamicJsonDocument json(100); + JsonObject jsonObj = json.to(); + json::mapSystemSettingsToJson(system, jsonObj); + + sendJsonResponse(request, jsonObj); +} + +void handleGetScalesSettings(AsyncWebServerRequest* request) { + const ScalesSettings& scales = state::getSettings().scales; + + LOG_INFO("Got request get ScalesSettings"); + + DynamicJsonDocument json(400); + JsonObject jsonObj = json.to(); + json::mapScalesSettingsToJson(scales, jsonObj); + + sendJsonResponse(request, jsonObj); +} + +void handleGetAllSettings(AsyncWebServerRequest* request) { + const GaggiaSettings& settings = state::getSettings(); + + LOG_INFO("Got request get all settings"); + + DynamicJsonDocument json(500); + JsonObject jsonObj = json.to(); + json::mapAllSettingsToJson(settings, jsonObj); + + sendJsonResponse(request, jsonObj); +} + +// ------------------------------------------------------------------------------------------ +// ------------------------------------- PUT METHODS ---------------------------------------- +// ------------------------------------------------------------------------------------------ + +void handlePutAllSettings(AsyncWebServerRequest* request, JsonVariant& body) { + LOG_INFO("Got request to update all settings"); + + state::updateAllSettings(json::mapJsonToAllSettings(body)); + + JsonObject responseBody = body.to(); + json::mapAllSettingsToJson(state::getSettings(), responseBody); + + sendJsonResponse(request, responseBody); +} + +void handlePutBoilerSettings(AsyncWebServerRequest* request, JsonVariant& body) { + LOG_INFO("Got request to update BoilerSettings"); + + state::updateBoilerSettings(json::mapJsonToBoilerSettings(body)); + + JsonObject responseBody = body.to(); + json::mapBoilerSettingsToJson(state::getSettings().boiler, responseBody); + + sendJsonResponse(request, responseBody); +} + +void handlePutBrewSettings(AsyncWebServerRequest* request, JsonVariant& body) { + LOG_INFO("Got request to update BrewSettings"); + + state::updateBrewSettings(json::mapJsonToBrewSettings(body)); + + JsonObject responseBody = body.to(); + json::mapBrewSettingsToJson(state::getSettings().brew, responseBody); + + sendJsonResponse(request, responseBody); +} + +void handlePutLedSettings(AsyncWebServerRequest* request, JsonVariant& body) { + LOG_INFO("Got request to update LedSettings"); + + state::updateLedSettings(json::mapJsonToLedSettings(body)); + + JsonObject responseBody = body.to(); + json::mapLedSettingsToJson(state::getSettings().led, responseBody); + + sendJsonResponse(request, responseBody); +} + +void handlePutSystemSettings(AsyncWebServerRequest* request, JsonVariant& body) { + LOG_INFO("Got request to update SystemSettings"); + + state::updateSystemSettings(json::mapJsonToSystemSettings(body)); + + JsonObject responseBody = body.to(); + json::mapSystemSettingsToJson(state::getSettings().system, responseBody); + + sendJsonResponse(request, responseBody); +} + +void handlePutScalesSettings(AsyncWebServerRequest* request, JsonVariant& body) { + LOG_INFO("Got request to update ScalesSettings"); + + state::updateScalesSettings(json::mapJsonToScalesSettings(body)); + + JsonObject responseBody = body.to(); + json::mapScalesSettingsToJson(state::getSettings().scales, responseBody); + + sendJsonResponse(request, responseBody); +} + + +void handlePersistSettings(AsyncWebServerRequest* request) { + LOG_INFO("Got request to persist current settings"); + + AsyncResponseStream* response = request->beginResponseStream("application/json"); + + state::persistSettings(); + + response->setCode(200); + request->send(response); +} diff --git a/webserver/src/server/api/api_settings.h b/webserver/src/server/api/api_settings.h new file mode 100644 index 00000000..ad550a10 --- /dev/null +++ b/webserver/src/server/api/api_settings.h @@ -0,0 +1,8 @@ +#ifndef API_SETTINGS_H +#define API_SETTINGS_H + +#include "ESPAsyncWebServer.h" + +void setupSettingsApi(AsyncWebServer& server); + +#endif diff --git a/webserver/src/server/api/api_static_file.cpp b/webserver/src/server/api/api_static_file.cpp index 0b185d99..f40e5908 100644 --- a/webserver/src/server/api/api_static_file.cpp +++ b/webserver/src/server/api/api_static_file.cpp @@ -1,6 +1,9 @@ #include "api_static_files.h" #include +#include "../../log/log.h" void setupStaticFiles(AsyncWebServer& server) { - server.serveStatic("/", LittleFS, "/"); + + server.serveStatic("/", LittleFS, "/") + .setDefaultFile("index.html"); } diff --git a/webserver/src/server/api/api_system_state.cpp b/webserver/src/server/api/api_system_state.cpp new file mode 100644 index 00000000..e09f6950 --- /dev/null +++ b/webserver/src/server/api/api_system_state.cpp @@ -0,0 +1,55 @@ +#include "api_system_state.h" +#include "../utils/server_utils.h" +#include "ESPAsyncWebServer.h" +#include "AsyncTCP.h" +#include + +#include "../json/json_system_state_converters.h" +#include "../../state/state.h" +#include "../../log/log.h" + +// Gets and updates part of the settings object in memory. +void handleGetSystemState(AsyncWebServerRequest* request); +void handleUpdateSystemStateOperationMode(AsyncWebServerRequest* request, JsonVariant& json); +void handleUpdateSystemStateTarePending(AsyncWebServerRequest* request, JsonVariant& json); + +void setupSystemStateApi(AsyncWebServer& server) { + server.on("/api/system-state", HTTP_GET, handleGetSystemState); + server.on("/api/system-state/operation-mode", HTTP_PUT, withJson(handleUpdateSystemStateOperationMode), NULL, onJsonBody); + server.on("/api/system-state/tare-pending", HTTP_PUT, withJson(handleUpdateSystemStateTarePending), NULL, onJsonBody); +} + +// ------------------------------------------------------------------------------------------ +// ------------------------------------- GET METHODS ---------------------------------------- +// ------------------------------------------------------------------------------------------ + +void systemStateResponse(AsyncWebServerRequest* request, JsonObject json, int code = 200) { + json::mapSystemStateToJson(state::getSystemState(), json); + sendJsonResponse(request, json, code); +} + +void handleGetSystemState(AsyncWebServerRequest* request) { + LOG_INFO("Got request get operation mode"); + + DynamicJsonDocument json(512); + JsonObject jsonObj = json.to(); + systemStateResponse(request, jsonObj); +} + +void handleUpdateSystemStateTarePending(AsyncWebServerRequest* request, JsonVariant& body) { + LOG_INFO("Got request to update operation mode"); + + state::updateTarePending(body["tarePending"]); + + JsonObject responseBody = body.to(); + systemStateResponse(request, responseBody); +} + +void handleUpdateSystemStateOperationMode(AsyncWebServerRequest* request, JsonVariant& body) { + LOG_INFO("Got request to update operation mode"); + + state::updateOperationMode(json::mapJsonValueToOperationMode(body["operationMode"])); + + JsonObject responseBody = body.to(); + systemStateResponse(request, responseBody); +} diff --git a/webserver/src/server/api/api_system_state.h b/webserver/src/server/api/api_system_state.h new file mode 100644 index 00000000..f9c5cbfa --- /dev/null +++ b/webserver/src/server/api/api_system_state.h @@ -0,0 +1,8 @@ +#ifndef API_SYSTEM_STATE_H +#define API_SYSTEM_STATE_H + +#include "ESPAsyncWebServer.h" + +void setupSystemStateApi(AsyncWebServer& server); + +#endif diff --git a/webserver/src/server/json/json_notification_converters.cpp b/webserver/src/server/json/json_notification_converters.cpp new file mode 100644 index 00000000..50f7b08e --- /dev/null +++ b/webserver/src/server/json/json_notification_converters.cpp @@ -0,0 +1,21 @@ +#include "json_notification_converters.h" + +namespace json { + // ------------------------------------------------------------------------------------------ + // ------------------------------------- Serializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + std::string mapNotificationTypeToJsonValue(const NotificationType& type) { + switch (type) { + case NotificationType::INFO : return "INFO"; + case NotificationType::WARN: return "WARN"; + case NotificationType::ERROR: return "ERROR"; + case NotificationType::SUCCESS: return "SUCCESS"; + default: return "INFO"; + } + } + + void mapNotificationToJson(const Notification& notification, JsonObject& target) { + target["type"] = mapNotificationTypeToJsonValue(notification.type); + target["message"] = notification.message; + } +} diff --git a/webserver/src/server/json/json_notification_converters.h b/webserver/src/server/json/json_notification_converters.h new file mode 100644 index 00000000..ecb0edaf --- /dev/null +++ b/webserver/src/server/json/json_notification_converters.h @@ -0,0 +1,15 @@ +#ifndef JSON_NOTIFICATION_CONVERTERS_H +#define JSON_NOTIFICATION_CONVERTERS_H + +#include "ArduinoJson.h" +#include "notification_message.h" +#include "string" + +namespace json { + // ------------------------------------------------------------------------------------------ + // ------------------------------------- Serializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + std::string mapNotificationTypeToJsonValue(const NotificationType& type); + void mapNotificationToJson(const Notification& notification, JsonObject& taret); +} +#endif diff --git a/webserver/src/server/json/json_profile_converters.cpp b/webserver/src/server/json/json_profile_converters.cpp new file mode 100644 index 00000000..fd361f8a --- /dev/null +++ b/webserver/src/server/json/json_profile_converters.cpp @@ -0,0 +1,192 @@ +#include "json_profile_converters.h" +#include "string" + +#include "../../state/state.h" + +namespace json { + + // ------------------------------------------------------------------------------------------ + // ------------------------------------- Serializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + + std::string mapTransitionCurveToJsonValue(const TransitionCurve curve) { + switch (curve) { + case TransitionCurve::EASE_IN_OUT: return "EASE_IN_OUT"; + case TransitionCurve::EASE_IN: return "EASE_IN"; + case TransitionCurve::EASE_OUT: return "EASE_OUT"; + case TransitionCurve::LINEAR: return "LINEAR"; + case TransitionCurve::INSTANT: return "INSTANT"; + default: return "EASE_IN_OUT"; + } + } + + std::string mapPhaseTypeToJsonValue(const PhaseType phaseType) { + switch (phaseType) { + case PhaseType::PRESSURE: return "PRESSURE"; + case PhaseType::FLOW: return "FLOW"; + default: return "PRESSURE"; + } + } + + void addSkippable(JsonObject& target, const char* key, uint32_t value) { + if (value != 0) { + target[key] = value; + } + } + + void addSkippable(JsonObject& target, const char* key, float value) { + if (value != 0) { + target[key] = value; + } + } + + void addSkippable(JsonObject& target, const char* key, std::string value) { + if (value.size() != 0) { + target[key] = value; + } + } + + void mapRecipeToJson(const BrewRecipe& recipe, JsonObject& target) { + addSkippable(target, "coffeeIn", recipe.coffeeIn); + addSkippable(target, "coffeeOut", recipe.coffeeOut); + addSkippable(target, "ratio", recipe.ratio); + } + + void mapTransitionToJson(const Transition& transition, JsonObject& target) { + addSkippable(target, "start", transition.start); + target["end"] = transition.end; + target["curve"] = mapTransitionCurveToJsonValue(transition.curve); + addSkippable(target, "time", transition.time); + } + + void mapPhaseStopConditionsToJson(const PhaseStopConditions& stopConditions, JsonObject& target) { + addSkippable(target, "time", stopConditions.time); + addSkippable(target, "pressureAbove", stopConditions.pressureAbove); + addSkippable(target, "pressureBelow", stopConditions.pressureBelow); + addSkippable(target, "flowAbove", stopConditions.flowAbove); + addSkippable(target, "flowBelow", stopConditions.flowBelow); + addSkippable(target, "weight", stopConditions.weight); + addSkippable(target, "waterPumpedInPhase", stopConditions.waterPumpedInPhase); + } + + void mapPhaseToJson(const Phase& phase, JsonObject& target) { + auto targetJson = target.createNestedObject("target"); + auto stopConditionsJson = target.createNestedObject("stopConditions"); + + target["type"] = mapPhaseTypeToJsonValue(phase.type); + target["skip"] = phase.skip; + mapTransitionToJson(phase.target, targetJson); + mapPhaseStopConditionsToJson(phase.stopConditions, stopConditionsJson); + addSkippable(target, "name", phase.name); + addSkippable(target, "restriction", phase.restriction); + addSkippable(target, "waterTemperature", phase.waterTemperature); + } + + void mapGlobalStopConditionsToJson(const GlobalStopConditions& globalStopConditions, JsonObject& target) { + addSkippable(target, "time", globalStopConditions.time); + addSkippable(target, "weight", globalStopConditions.weight); + addSkippable(target, "waterPumped", globalStopConditions.waterPumped); + } + + void mapProfileToJson(ProfileId id, const Profile& profile, JsonObject& target) { + target["id"] = id; + target["name"] = profile.name; + auto phasesJson = target.createNestedArray("phases"); + for (auto& phase : profile.phases) { + auto phaseJson = phasesJson.createNestedObject(); + mapPhaseToJson(phase, phaseJson); + } + auto globalStopConditionsJson = target.createNestedObject("globalStopConditions"); + mapGlobalStopConditionsToJson(profile.globalStopConditions, globalStopConditionsJson); + target["waterTemperature"] = profile.waterTemperature; + auto recipeJson = target.createNestedObject("recipe"); + mapRecipeToJson(profile.recipe, recipeJson); + } + + void mapProfileSummaryToJson(const SavedProfile& summary, JsonObject& target) { + target["id"] = summary.id; + target["name"] = summary.name; + target["active"] = state::getActiveProfileId() == summary.id; + } + + // ------------------------------------------------------------------------------------------ + // ----------------------------------- Deserializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + + TransitionCurve mapJsonValueToTransitionCurve(const std::string& curve) { + if (curve == "EASE_IN_OUT") return TransitionCurve::EASE_IN_OUT; + if (curve == "EASE_IN") return TransitionCurve::EASE_IN; + if (curve == "EASE_OUT") return TransitionCurve::EASE_OUT; + if (curve == "LINEAR") return TransitionCurve::LINEAR; + if (curve == "INSTANT") return TransitionCurve::INSTANT; + return TransitionCurve::EASE_IN_OUT; + } + + PhaseType mapJsonValueToPhaseType(const std::string& phaseType) { + if (phaseType == "PRESSURE") return PhaseType::PRESSURE; + if (phaseType == "FLOW") return PhaseType::FLOW; + return PhaseType::PRESSURE; + } + + BrewRecipe mapJsonToRecipe(const JsonObject& source) { + BrewRecipe recipe; + recipe.coffeeIn = source["coffeeIn"]; + recipe.coffeeOut = source["coffeeOut"]; + recipe.ratio = source["ratio"]; + return recipe; + } + + Transition mapJsonToTransition(const JsonObject& source) { + Transition transition; + transition.start = source["start"]; + transition.end = source["end"]; + transition.curve = mapJsonValueToTransitionCurve(source["curve"]); + transition.time = source["time"]; + return transition; + } + + PhaseStopConditions mapJsonToPhaseStopConditions(const JsonObject& source) { + PhaseStopConditions stopConditions; + stopConditions.time = source["time"]; + stopConditions.pressureAbove = source["pressureAbove"]; + stopConditions.pressureBelow = source["pressureBelow"]; + stopConditions.flowAbove = source["flowAbove"]; + stopConditions.flowBelow = source["flowBelow"]; + stopConditions.weight = source["weight"]; + stopConditions.waterPumpedInPhase = source["waterPumpedInPhase"]; + return stopConditions; + } + + Phase mapJsonToPhase(const JsonObject& source) { + Phase phase; + phase.target = mapJsonToTransition(source["target"].as()); + phase.stopConditions = mapJsonToPhaseStopConditions(source["stopConditions"].as()); + phase.name = source["name"] ? std::string(source["name"]) : ""; + phase.type = mapJsonValueToPhaseType(source["type"]); + phase.restriction = source["restriction"]; + phase.skip = source["skip"]; + phase.waterTemperature = source["waterTemperature"]; + return phase; + } + + GlobalStopConditions mapJsonToGlobalStopConditions(const JsonObject& source) { + GlobalStopConditions globalStopConditions; + globalStopConditions.time = source["time"]; + globalStopConditions.weight = source["weight"]; + globalStopConditions.waterPumped = source["waterPumped"]; + return globalStopConditions; + } + + Profile mapJsonToProfile(const JsonObject& source) { + Profile profile; + profile.name = source["name"] ? std::string(source["name"]) : ""; + JsonArray phasesJson = source["phases"].as(); + for (JsonObject phaseJson : phasesJson) { + profile.phases.push_back(mapJsonToPhase(phaseJson)); + } + profile.globalStopConditions = mapJsonToGlobalStopConditions(source["globalStopConditions"].as()); + profile.waterTemperature = source["waterTemperature"]; + profile.recipe = mapJsonToRecipe(source["recipe"].as()); + return profile; + } +} diff --git a/webserver/src/server/json/json_profile_converters.h b/webserver/src/server/json/json_profile_converters.h new file mode 100644 index 00000000..67028123 --- /dev/null +++ b/webserver/src/server/json/json_profile_converters.h @@ -0,0 +1,38 @@ +#ifndef JSON_PROFILE_CONVERTERS_H +#define JSON_PROFILE_CONVERTERS_H + +#include "ArduinoJson.h" +#include "profiling_phases.h" +#include "../../persistence/saved_profiles.h" + +namespace json { + + // ------------------------------------------------------------------------------------------ + // ------------------------------------- Serializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + + std::string mapTransitionCurveToJsonValue(const TransitionCurve curve); + std::string mapPhaseTypeToJsonValue(const PhaseType phaseType); + void mapRecipeToJson(const BrewRecipe& recipe, JsonObject& target); + void mapTransitionToJson(const Transition& transition, JsonObject& target); + void mapPhaseStopConditionsToJson(const PhaseStopConditions& stopConditions, JsonObject& target); + void mapPhaseToJson(const Phase& phase, JsonObject& target); + void mapGlobalStopConditionsToJson(const GlobalStopConditions& globalStopConditions, JsonObject& target); + void mapProfileToJson(ProfileId id, const Profile& profile, JsonObject& target); + void mapProfileSummaryToJson(const SavedProfile& summary, JsonObject& target); + + // ------------------------------------------------------------------------------------------ + // ----------------------------------- Deserializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + + TransitionCurve mapJsonValueToTransitionCurve(const std::string& curve); + PhaseType mapJsonValueToPhaseType(const std::string& phaseType); + BrewRecipe mapJsonToRecipe(const JsonObject& source); + Transition mapJsonToTransition(const JsonObject& source); + PhaseStopConditions mapJsonToPhaseStopConditions(const JsonObject& source); + Phase mapJsonToPhase(const JsonObject& source); + GlobalStopConditions mapJsonToGlobalStopConditions(const JsonObject& source); + Profile mapJsonToProfile(const JsonObject& source); + +} +#endif diff --git a/webserver/src/server/json/json_settings_converters.cpp b/webserver/src/server/json/json_settings_converters.cpp new file mode 100644 index 00000000..3abeed6c --- /dev/null +++ b/webserver/src/server/json/json_settings_converters.cpp @@ -0,0 +1,140 @@ +#include "json_settings_converters.h" + +namespace json { + + // ------------------------------------------------------------------------------------------ + // ------------------------------------- Serializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + + void mapLedSettingsToJson(const LedSettings& led, JsonObject& target) { + target["state"] = led.state; + target["disco"] = led.disco; + JsonObject ledColor = target.createNestedObject("color"); + ledColor["R"] = led.color.R; + ledColor["G"] = led.color.G; + ledColor["B"] = led.color.B; + } + + void mapBoilerSettingsToJson(const BoilerSettings& boiler, JsonObject& target) { + target["brewDivider"] = boiler.brewDivider; + target["hpwr"] = boiler.hpwr; + target["mainDivider"] = boiler.mainDivider; + target["offsetTemp"] = boiler.offsetTemp; + target["steamSetPoint"] = boiler.steamSetPoint; + } + + void mapSystemSettingsToJson(const SystemSettings& system, JsonObject& target) { + target["warmupState"] = system.warmupState; + target["lcdSleep"] = system.lcdSleep; + target["pumpFlowAtZero"] = system.pumpFlowAtZero; + } + + void mapBrewSettingsToJson(const BrewSettings& brew, JsonObject& target) { + target["basketPrefill"] = brew.basketPrefill; + target["brewDeltaState"] = brew.brewDeltaState; + target["homeOnShotFinish"] = brew.homeOnShotFinish; + } + + void mapScalesSettingsToJson(const ScalesSettings& scales, JsonObject& target) { + target["forcePredictive"] = scales.forcePredictive; + target["hwScalesEnabled"] = scales.hwScalesEnabled; + target["hwScalesF1"] = scales.hwScalesF1; + target["hwScalesF2"] = scales.hwScalesF2; + target["btScalesEnabled"] = scales.btScalesEnabled; + target["btScalesAutoConnect"] = scales.btScalesAutoConnect; + } + + void mapAllSettingsToJson(const GaggiaSettings& settings, JsonObject& target) { + JsonObject systemObj = target.createNestedObject("system"); + JsonObject boilerObj = target.createNestedObject("boiler"); + JsonObject brewObj = target.createNestedObject("brew"); + JsonObject ledObj = target.createNestedObject("led"); + JsonObject scalesObj = target.createNestedObject("scales"); + mapSystemSettingsToJson(settings.system, systemObj); + mapBoilerSettingsToJson(settings.boiler, boilerObj); + mapBrewSettingsToJson(settings.brew, brewObj); + mapLedSettingsToJson(settings.led, ledObj); + mapScalesSettingsToJson(settings.scales, scalesObj); + }; + + void mapBleScalesToJson(const blescales::Scales& scales, JsonObject& target) { + target["name"] = scales.name; + target["address"] = scales.address; + } + + // ------------------------------------------------------------------------------------------ + // ----------------------------------- Deserializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + SystemSettings mapJsonToSystemSettings(const JsonObject& json) { + return SystemSettings{ + .pumpFlowAtZero = json["pumpFlowAtZero"], + .lcdSleep = json["lcdSleep"], + .warmupState = json["warmupState"], + }; + } + + LedSettings mapJsonToLedSettings(const JsonObject& json) { + JsonObject colorObject = json["color"].as(); + return LedSettings{ + .state = json["state"], + .disco = json["disco"], + .color = LedSettings::Color { + .R = colorObject["R"], + .G = colorObject["G"], + .B = colorObject["B"], + } + }; + } + + BrewSettings mapJsonToBrewSettings(const JsonObject& json) { + return BrewSettings{ + .basketPrefill = json["basketPrefill"], + .homeOnShotFinish = json["homeOnShotFinish"], + .brewDeltaState = json["brewDeltaState"], + }; + } + + BoilerSettings mapJsonToBoilerSettings(const JsonObject& json) { + return BoilerSettings{ + .steamSetPoint = json["steamSetPoint"], + .offsetTemp = json["offsetTemp"], + .hpwr = json["hpwr"], + .mainDivider = json["mainDivider"], + .brewDivider = json["brewDivider"], + }; + } + + ScalesSettings mapJsonToScalesSettings(const JsonObject& json) { + return ScalesSettings{ + .forcePredictive = json["forcePredictive"], + .hwScalesEnabled = json["hwScalesEnabled"], + .hwScalesF1 = json["hwScalesF1"], + .hwScalesF2 = json["hwScalesF2"], + .btScalesEnabled = json["btScalesEnabled"], + .btScalesAutoConnect = json["btScalesAutoConnect"], + }; + } + + GaggiaSettings mapJsonToAllSettings(const JsonObject& json) { + JsonObject boilerJson = json["boiler"].as(); + JsonObject systemJson = json["system"].as(); + JsonObject brewJson = json["brew"].as(); + JsonObject ledJson = json["led"].as(); + JsonObject scalesJson = json["scales"].as(); + return GaggiaSettings{ + .boiler = mapJsonToBoilerSettings(boilerJson), + .system = mapJsonToSystemSettings(systemJson), + .brew = mapJsonToBrewSettings(brewJson), + .led = mapJsonToLedSettings(ledJson), + .scales = mapJsonToScalesSettings(scalesJson), + }; + } + + blescales::Scales mapJsonToBleScales(const JsonObject& json) { + return blescales::Scales{ + .name = json["name"], + .address = json["address"], + }; + } + +} diff --git a/webserver/src/server/json/json_settings_converters.h b/webserver/src/server/json/json_settings_converters.h new file mode 100644 index 00000000..409c109c --- /dev/null +++ b/webserver/src/server/json/json_settings_converters.h @@ -0,0 +1,30 @@ +#ifndef JSON_SETTINGS_CONVERTERS_H +#define JSON_SETTINGS_CONVERTERS_H + +#include "ArduinoJson.h" +#include "gaggia_settings.h" +#include "../../scales/ble_scales.h" + +namespace json { + // ------------------------------------------------------------------------------------------ + // ------------------------------------- Serializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + void mapLedSettingsToJson(const LedSettings& led, JsonObject& target); + void mapBoilerSettingsToJson(const BoilerSettings& boiler, JsonObject& target); + void mapSystemSettingsToJson(const SystemSettings& system, JsonObject& target); + void mapBrewSettingsToJson(const BrewSettings& brew, JsonObject& target); + void mapScalesSettingsToJson(const ScalesSettings& brew, JsonObject& target); + void mapAllSettingsToJson(const GaggiaSettings& settings, JsonObject& target); + void mapBleScalesToJson(const blescales::Scales& scales, JsonObject& target); + // ------------------------------------------------------------------------------------------ + // ----------------------------------- Deserializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + SystemSettings mapJsonToSystemSettings(const JsonObject& json); + LedSettings mapJsonToLedSettings(const JsonObject& json); + BrewSettings mapJsonToBrewSettings(const JsonObject& json); + BoilerSettings mapJsonToBoilerSettings(const JsonObject& json); + ScalesSettings mapJsonToScalesSettings(const JsonObject& json); + GaggiaSettings mapJsonToAllSettings(const JsonObject& json); + blescales::Scales mapJsonToBleScales(const JsonObject& json); +} +#endif diff --git a/webserver/src/server/json/json_system_state_converters.cpp b/webserver/src/server/json/json_system_state_converters.cpp new file mode 100644 index 00000000..82e2840a --- /dev/null +++ b/webserver/src/server/json/json_system_state_converters.cpp @@ -0,0 +1,115 @@ +#include "json_system_state_converters.h" + +namespace json { + + // ------------------------------------------------------------------------------------------ + // ------------------------------------- Serializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + void mapSystemStateToJson(const SystemState& systemState, JsonObject& target) { + target["startupInitFinished"] = systemState.startupInitFinished; + target["operationMode"] = mapOperationModeToJsonValue(systemState.operationMode); + target["tofReady"] = systemState.tofReady; + target["isSteamForgottenON"] = systemState.isSteamForgottenON; + target["scalesPresent"] = systemState.scalesPresent; + target["timeAlive"] = systemState.timeAlive; + } + + std::string mapOperationModeToJsonValue(const OperationMode& mode) { + switch (mode) { + case OperationMode::BREW_AUTO: return "BREW_AUTO"; + case OperationMode::BREW_MANUAL: return "BREW_MANUAL"; + case OperationMode::FLUSH: return "FLUSH"; + case OperationMode::DESCALE: return "DESCALE"; + case OperationMode::STEAM: return "STEAM"; + case OperationMode::FLUSH_AUTO: return "FLUSH_AUTO"; + default: return "BREW_AUTO"; + } + } + + void mapUpdateSystemStateCommmandToJson(const UpdateSystemStateComand& command, JsonObject& target) { + target["operationMode"] = mapOperationModeToJsonValue(command.operationMode); + target["tarePending"] = command.tarePending; + } + + void mapSensorStateToJson(const SensorStateSnapshot& sensorState, JsonObject& target) { + target["brewActive"] = sensorState.brewActive; + target["steamActive"] = sensorState.steamActive; + target["hotWaterActive"] = sensorState.hotWaterSwitchState; + target["temperature"] = sensorState.temperature; + target["waterTemperature"] = sensorState.waterTemperature; + target["pressure"] = sensorState.pressure; + target["pumpFlow"] = sensorState.pumpFlow; + target["weightFlow"] = sensorState.weightFlow; + target["weight"] = sensorState.weight; + target["waterLevel"] = sensorState.waterLevel; + } + + void mapShotSnapshotToJson(const ShotSnapshot& shotSnaposhot, JsonObject& target) { + target["timeInShot"] = shotSnaposhot.timeInShot; + target["pressure"] = shotSnaposhot.pressure; + target["pumpFlow"] = shotSnaposhot.pumpFlow; + target["weightFlow"] = shotSnaposhot.weightFlow; + target["temperature"] = shotSnaposhot.temperature; + target["shotWeight"] = shotSnaposhot.shotWeight; + target["waterPumped"] = shotSnaposhot.waterPumped; + target["targetTemperature"] = shotSnaposhot.targetTemperature; + target["targetPumpFlow"] = shotSnaposhot.targetPumpFlow; + target["targetPressure"] = shotSnaposhot.targetPressure; + } + + // ------------------------------------------------------------------------------------------ + // ----------------------------------- Deserializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + + OperationMode mapJsonValueToOperationMode(const std::string& modeValue) { + if (modeValue == "BREW_AUTO") return OperationMode::BREW_AUTO; + if (modeValue == "BREW_MANUAL") return OperationMode::BREW_MANUAL; + if (modeValue == "FLUSH") return OperationMode::FLUSH; + if (modeValue == "DESCALE") return OperationMode::DESCALE; + if (modeValue == "STEAM") return OperationMode::STEAM; + if (modeValue == "FLUSH_AUTO") return OperationMode::FLUSH_AUTO; + return OperationMode::BREW_AUTO; + } + + UpdateSystemStateComand mapJsonToUpdateSystemStateCommand(const JsonObject& json) { + return UpdateSystemStateComand{ + .operationMode = mapJsonValueToOperationMode(json["operationMode"]), + .tarePending = json["tarePending"], + }; + } + + SystemState mapJsonToSystemState(const JsonObject& json) { + SystemState systemState; + systemState.startupInitFinished = json["startupInitFinished"]; + systemState.operationMode = mapJsonValueToOperationMode(json["operationMode"]); + systemState.tofReady = json["tofReady"]; + systemState.isSteamForgottenON = json["isSteamForgottenON"]; + systemState.scalesPresent = json["scalesPresent"]; + systemState.timeAlive = json["timeAlive"]; + systemState.tarePending = json["tarePending"]; + return systemState; + } + + std::string mapDescalingStateToJsonValue(const DescalingState& state) { + switch (state) { + case DescalingState::IDLE: + return "IDLE"; + case DescalingState::PHASE1: + return "PHASE1"; + case DescalingState::PHASE2: + return "PHASE2"; + case DescalingState::PHASE3: + return "PHASE3"; + case DescalingState::FINISHED: + return "FINISHED"; + default: + return "IDLE"; + } + } + + void mapDescalingProgressToJson(const DescalingProgress& progress, JsonObject& target) { + target["state"] = mapDescalingStateToJsonValue(progress.state); + target["time"] = progress.time; + target["progress"] = progress.progess; + } +} diff --git a/webserver/src/server/json/json_system_state_converters.h b/webserver/src/server/json/json_system_state_converters.h new file mode 100644 index 00000000..8a448f51 --- /dev/null +++ b/webserver/src/server/json/json_system_state_converters.h @@ -0,0 +1,28 @@ +#ifndef JSON_SYSTEM_STATE_CONVERTERS_H +#define JSON_SYSTEM_STATE_CONVERTERS_H + +#include "ArduinoJson.h" +#include "system_state.h" +#include "sensors_state.h" +#include "string" + +namespace json { + // ------------------------------------------------------------------------------------------ + // ------------------------------------- Serializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + std::string mapOperationModeToJsonValue(const OperationMode& mode); + void mapSystemStateToJson(const SystemState& systemState, JsonObject& target); + void mapUpdateSystemStateCommmandToJson(const UpdateSystemStateComand& command, JsonObject& target); + void mapSensorStateToJson(const SensorStateSnapshot& sensorState, JsonObject& target); + void mapShotSnapshotToJson(const ShotSnapshot& shotSnaposhot, JsonObject& target); + std::string mapDescalingStateToJsonValue(const DescalingState& state); + void mapDescalingProgressToJson(const DescalingProgress& progress, JsonObject& target); + + // ------------------------------------------------------------------------------------------ + // ----------------------------------- Deserializers ---------------------------------------- + // ------------------------------------------------------------------------------------------ + OperationMode mapJsonValueToOperationMode(const std::string& modeValue); + SystemState mapJsonToSystemState(const JsonObject& json); + UpdateSystemStateComand mapJsonToUpdateSystemStateCommand(const JsonObject& json); +} +#endif diff --git a/webserver/src/server/server_setup.cpp b/webserver/src/server/server_setup.cpp index 80abcc63..0e09be85 100644 --- a/webserver/src/server/server_setup.cpp +++ b/webserver/src/server/server_setup.cpp @@ -1,10 +1,15 @@ #include "server_setup.h" #include "ESPAsyncWebServer.h" #include "AsyncTCP.h" +#include "LittleFS.h" #include "api/api_wifi.h" #include "api/api_static_files.h" #include "api/api_not_found_handler.h" +#include "api/api_settings.h" +#include "api/api_profile.h" +#include "api/api_system_state.h" +#include "api/api_scales.h" #include "websocket/websocket.h" #include "../task_config.h" #include "../log/log.h" @@ -18,6 +23,10 @@ void webServerTask(void* params); void webServerSetup() { setupWifiApi(webserver::server); + setupSettingsApi(webserver::server); + setupProfileApi(webserver::server); + setupSystemStateApi(webserver::server); + setupScalesApi(webserver::server); setupWebSocket(webserver::server); setupStaticFiles(webserver::server); webserver::server.onNotFound(&handleUrlNotFound); diff --git a/webserver/src/server/utils/server_utils.cpp b/webserver/src/server/utils/server_utils.cpp new file mode 100644 index 00000000..4ed74e30 --- /dev/null +++ b/webserver/src/server/utils/server_utils.cpp @@ -0,0 +1,37 @@ +#include "server_utils.h" + +#define WS_MAX_JSON_CONTENT_LENGTH 16384 + +AsyncCallbackJsonWebHandler* jsonHandler(const char* uri, WebRequestMethodComposite method, ArJsonRequestHandlerFunction onRequest) { + AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler(String(uri), onRequest); + handler->setMethod(method); + return handler; +} + +void onJsonBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { + if (total > 0 && request->_tempObject == NULL && total < WS_MAX_JSON_CONTENT_LENGTH) { + request->_tempObject = malloc(total); + } + if (request->_tempObject != NULL) { + memcpy((uint8_t*)(request->_tempObject) + index, data, len); + } +} + +ArRequestHandlerFunction withJson(ArJsonRequestHandlerFunction wrappedHandler, size_t bufferSize) { + return [wrappedHandler, bufferSize](AsyncWebServerRequest *request) { + DynamicJsonDocument jsonBuffer(bufferSize); + DeserializationError error = deserializeJson(jsonBuffer, (uint8_t*)(request->_tempObject)); + if(!error) { + JsonVariant json = jsonBuffer.as(); + wrappedHandler(request, json); + return; + } + }; +} + +void sendJsonResponse(AsyncWebServerRequest *request, JsonVariantConst json, int code) { + AsyncResponseStream* response = request->beginResponseStream("application/json"); + response->setCode(code); + serializeJson(json, *response); + request->send(response); +} diff --git a/webserver/src/server/utils/server_utils.h b/webserver/src/server/utils/server_utils.h index ed408e35..e966dcf7 100644 --- a/webserver/src/server/utils/server_utils.h +++ b/webserver/src/server/utils/server_utils.h @@ -4,11 +4,11 @@ #include "ESPAsyncWebServer.h" #include "AsyncJson.h" -AsyncCallbackJsonWebHandler* jsonHandler(const char* uri, WebRequestMethodComposite method, ArJsonRequestHandlerFunction onRequest) { - AsyncCallbackJsonWebHandler* handler = new AsyncCallbackJsonWebHandler(String(uri), onRequest); - handler->setMethod(method); - return handler; -} +AsyncCallbackJsonWebHandler* jsonHandler(const char* uri, WebRequestMethodComposite method, ArJsonRequestHandlerFunction onRequest); +void onJsonBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total); +ArRequestHandlerFunction withJson(ArJsonRequestHandlerFunction wrappedHandler, size_t bufferSize = 1024); + +void sendJsonResponse(AsyncWebServerRequest *request, JsonVariantConst json, int code = 200); #endif diff --git a/webserver/src/server/websocket/websocket.cpp b/webserver/src/server/websocket/websocket.cpp index 273d34a8..d5b449df 100644 --- a/webserver/src/server/websocket/websocket.cpp +++ b/webserver/src/server/websocket/websocket.cpp @@ -1,14 +1,26 @@ #include "websocket.h" -#include "../../log/log.h" #include #include #include "ESPAsyncWebServer.h" #include "AsyncTCP.h" #include +#include "../../log/log.h" + +#include "../../state/state.h" +#include "../json/json_system_state_converters.h" +#include "../json/json_profile_converters.h" +#include "../json/json_settings_converters.h" +#include "../json/json_notification_converters.h" const std::string WS_MSG_SENSOR_DATA = "sensor_data_update"; const std::string WS_MSG_SHOT_DATA = "shot_data_update"; const std::string WS_MSG_LOG = "log_record"; +const std::string WS_MSG_SYSTEM_STATE = "sys_state"; +const std::string WS_MSG_ACTIVE_PROFILE_UPDATED = "act_prof_update"; +const std::string WS_MSG_SETTINGS_UPDATED = "settings_update"; +const std::string WS_MSG_NOTIFICATION = "notification"; +const std::string WS_MSG_DESCALING_PROGRESS = "descaling_progress"; +const std::string WS_MSG_BLE_SCALES_UPDATED = "ble_scls_upd"; namespace websocket { AsyncWebSocket wsServer("/ws"); @@ -105,22 +117,14 @@ void handleWebSocketMessage(void* arg, uint8_t* data, size_t len) { //-------------------------------------------------------------// //--------------------------OUTGOING---------------------------// //-------------------------------------------------------------// -void wsSendSensorStateSnapshotToClients(SensorStateSnapshot& snapshot) { +void wsSendSensorStateSnapshotToClients(const SensorStateSnapshot& snapshot) { if (!websocket::lockJson()) return; JsonObject root = websocket::jsonDoc.to(); root["action"] = WS_MSG_SENSOR_DATA; JsonObject data = root.createNestedObject("data"); - data["brewActive"] = snapshot.brewActive; - data["steamActive"] = snapshot.steamActive; - data["scalesPresent"] = snapshot.scalesPresent; - data["temperature"] = snapshot.temperature; - data["waterLvl"] = snapshot.waterLvl; - data["pressure"] = snapshot.pressure; - data["pumpFlow"] = snapshot.pumpFlow; - data["weightFlow"] = snapshot.weightFlow; - data["weight"] = snapshot.weight; + json::mapSensorStateToJson(snapshot, data); std::string serializedMsg; // create temp buffer serializeJson(root, serializedMsg); // serialize to buffer @@ -129,23 +133,14 @@ void wsSendSensorStateSnapshotToClients(SensorStateSnapshot& snapshot) { websocket::wsServer.textAll(serializedMsg.c_str(), serializedMsg.length()); } -void wsSendShotSnapshotToClients(ShotSnapshot& snapshot) { +void wsSendShotSnapshotToClients(const ShotSnapshot& snapshot) { if (!websocket::lockJson()) return; JsonObject root = websocket::jsonDoc.to(); root["action"] = WS_MSG_SHOT_DATA; JsonObject data = root.createNestedObject("data"); - data["timeInShot"] = snapshot.timeInShot; - data["pressure"] = snapshot.pressure; - data["pumpFlow"] = snapshot.pumpFlow; - data["weightFlow"] = snapshot.weightFlow; - data["temperature"] = snapshot.temperature; - data["shotWeight"] = snapshot.shotWeight; - data["waterPumped"] = snapshot.waterPumped; - data["targetTemperature"] = snapshot.targetTemperature; - data["targetPumpFlow"] = snapshot.targetPumpFlow; - data["targetPressure"] = snapshot.targetPressure; + json::mapShotSnapshotToJson(snapshot, data); std::string serializedMsg; // create temp buffer serializeJson(root, serializedMsg); // serialize to buffer @@ -170,3 +165,97 @@ void wsSendLog(std::string log, std::string source) { websocket::wsSendWithBuffer(serializedMsg); } + +void wsSendSystemStateToClients(const SystemState& systemState) { + if (!websocket::lockJson()) return; + JsonObject root = websocket::jsonDoc.to(); + + root["action"] = WS_MSG_SYSTEM_STATE; + + JsonObject data = root.createNestedObject("data"); + json::mapSystemStateToJson(systemState, data); + + std::string serializedMsg; // create temp buffer + serializeJson(root, serializedMsg); // serialize to buffer + websocket::unlockJson(); + + websocket::wsServer.textAll(serializedMsg.c_str(), serializedMsg.length()); +} + +void wsSendActiveProfileUpdated() { + if (!websocket::lockJson()) return; + JsonObject root = websocket::jsonDoc.to(); + + root["action"] = WS_MSG_ACTIVE_PROFILE_UPDATED; + JsonObject data = root.createNestedObject("data"); + json::mapProfileToJson(state::getActiveProfileId(), state::getActiveProfile(), data); + + std::string serializedMsg; // create temp buffer + serializeJson(root, serializedMsg); // serialize to buffer + websocket::unlockJson(); + + websocket::wsServer.textAll(serializedMsg.c_str(), serializedMsg.length()); +} + +void wsSendSettingsUpdated() { + if (!websocket::lockJson()) return; + JsonObject root = websocket::jsonDoc.to(); + + root["action"] = WS_MSG_SETTINGS_UPDATED; + JsonObject data = root.createNestedObject("data"); + json::mapAllSettingsToJson(state::getSettings(), data); + + std::string serializedMsg; // create temp buffer + serializeJson(root, serializedMsg); // serialize to buffer + websocket::unlockJson(); + + websocket::wsServer.textAll(serializedMsg.c_str(), serializedMsg.length()); +}; + +void wsSendNotification(const Notification& notification) { + if (!websocket::lockJson()) return; + JsonObject root = websocket::jsonDoc.to(); + + root["action"] = WS_MSG_NOTIFICATION; + JsonObject data = root.createNestedObject("data"); + json::mapNotificationToJson(notification, data); + + std::string serializedMsg; // create temp buffer + serializeJson(root, serializedMsg); // serialize to buffer + + websocket::unlockJson(); + + websocket::wsServer.textAll(serializedMsg.c_str(), serializedMsg.length()); +} + +void wsSendDescalingProgress(const DescalingProgress& progress) { + if (!websocket::lockJson()) return; + JsonObject root = websocket::jsonDoc.to(); + + root["action"] = WS_MSG_DESCALING_PROGRESS; + JsonObject data = root.createNestedObject("data"); + json::mapDescalingProgressToJson(progress, data); + + std::string serializedMsg; // create temp buffer + serializeJson(root, serializedMsg); // serialize to buffer + + websocket::unlockJson(); + + websocket::wsServer.textAll(serializedMsg.c_str(), serializedMsg.length()); +} + +void wsSendConnectedBleScalesUpdated(const blescales::Scales& scles) { + if (!websocket::lockJson()) return; + JsonObject root = websocket::jsonDoc.to(); + + root["action"] = WS_MSG_BLE_SCALES_UPDATED; + JsonObject data = root.createNestedObject("data"); + json::mapBleScalesToJson(scles, data); + + std::string serializedMsg; // create temp buffer + serializeJson(root, serializedMsg); // serialize to buffer + + websocket::unlockJson(); + + websocket::wsServer.textAll(serializedMsg.c_str(), serializedMsg.length()); +} diff --git a/webserver/src/server/websocket/websocket.h b/webserver/src/server/websocket/websocket.h index 96408fca..09402db3 100644 --- a/webserver/src/server/websocket/websocket.h +++ b/webserver/src/server/websocket/websocket.h @@ -3,12 +3,22 @@ #include "ESPAsyncWebServer.h" #include "AsyncTCP.h" -#include "mcu_comms.h" +#include "string" +#include "sensors_state.h" +#include "system_state.h" +#include "notification_message.h" +#include "../../scales/ble_scales.h" void setupWebSocket(AsyncWebServer& server); void wsCleanup(); -void wsSendSensorStateSnapshotToClients(SensorStateSnapshot& snapshot); -void wsSendShotSnapshotToClients(ShotSnapshot& snapshot); +void wsSendSensorStateSnapshotToClients(const SensorStateSnapshot& snapshot); +void wsSendShotSnapshotToClients(const ShotSnapshot& snapshot); void wsSendLog(std::string log, std::string source = "webserver"); +void wsSendSystemStateToClients(const SystemState& systemState); +void wsSendActiveProfileUpdated(); +void wsSendSettingsUpdated(); +void wsSendNotification(const Notification& notification); +void wsSendDescalingProgress(const DescalingProgress& notification); +void wsSendConnectedBleScalesUpdated(const blescales::Scales& scales); #endif diff --git a/webserver/src/state/state.cpp b/webserver/src/state/state.cpp new file mode 100644 index 00000000..96bf2cc9 --- /dev/null +++ b/webserver/src/state/state.cpp @@ -0,0 +1,141 @@ +#include "state.h" +#include "../persistence/persistence.h" +#include "../log/log.h" + +namespace state { + GaggiaSettings currentSettings; + Profile activeProfile; + ProfileId activeProfileId; + SystemState systemState; + blescales::Scales connectedScales; + + void init() { + currentSettings = persistence::getSettings(); + activeProfileId = persistence::getActiveProfileId(); + activeProfile = persistence::getProfile(activeProfileId).second; + + LOG_INFO("Initialized state. s.hpwr=%d, apId=%d, ap.name=%s", currentSettings.boiler.hpwr, activeProfileId, activeProfile.name.c_str()); + } + + // ------------------------------------------------------------------------------ + // -------------------------- SETTINGS RELATED STATE ---------------------------- + // ------------------------------------------------------------------------------ + + const GaggiaSettings& getSettings() { + return currentSettings; + } + + // Updates the instance of `currentSettings`. Does not persist to NVS. + // Call `persistSettings` to persist the running instance of settings + void updateAllSettings(const GaggiaSettings& settings) { + currentSettings = settings; + onAllSettingsUpdated(currentSettings); + } + void updateBrewSettings(const BrewSettings& settings) { + currentSettings.brew = settings; + onBrewSettingsUpdated(currentSettings.brew); + } + void updateBoilerSettings(const BoilerSettings& settings) { + currentSettings.boiler = settings; + onBoilerSettingsUpdated(currentSettings.boiler); + } + void updateLedSettings(const LedSettings& settings) { + currentSettings.led = settings; + onLedSettingsUpdated(currentSettings.led); + } + void updateSystemSettings(const SystemSettings& settings) { + currentSettings.system = settings; + onSystemSettingsUpdated(currentSettings.system); + } + void updateScalesSettings(const ScalesSettings& settings) { + currentSettings.scales = settings; + onScalesSettingsUpdated(currentSettings.scales); + } + + // Persists the `currentSettings` to NVS + bool persistSettings() { + persistence::saveSettings(currentSettings); + return true; + } + + // ------------------------------------------------------------------------------ + // --------------------------- PROFILE RELATED STATE ---------------------------- + // ------------------------------------------------------------------------------ + + const Profile& getActiveProfile() { + return activeProfile; + } + + // Updates the instance of `activeProfile`. Does not persist to NVS. + // Call `persistActiveProfile` to persist the instance of activeProfile + void updateActiveProfile(const Profile& profile) { + activeProfile = profile; + onActiveProfileUpdated(activeProfile); + } + + ProfileId getActiveProfileId() { + return activeProfileId; + } + + bool updateActiveProfileId(ProfileId id) { + auto result = persistence::getProfile(id); + if (!result.first) { + return false; + } + + activeProfileId = id; + activeProfile = result.second; + onActiveProfileUpdated(activeProfile); + return true; + }; + + // Persists the `activeProfile` to NVS + bool persistActiveProfile() { + return persistence::saveProfile(activeProfileId, activeProfile); + }; + + // Persists the `activeProfileId` to NVS + bool persistActiveProfileId() { + return persistence::saveActiveProfileId(activeProfileId); + } + + // --------------------------------------------------------------------------------- + // -------------------------------- SYSTEM_STATE ----------------------------------- + // --------------------------------------------------------------------------------- + SystemState getSystemState() { + return systemState; + } + + void updateSystemState(const SystemState& newState) { + systemState = newState; + onSystemStateUpdated(systemState); + } + + void sumitUpdateSystemStateCommand(const UpdateSystemStateComand& command) { + systemState.tarePending = command.tarePending; + systemState.operationMode = command.operationMode; + onUpdateSystemStateCommandSubmitted(command); + } + + void updateTarePending(bool tarePending) { + sumitUpdateSystemStateCommand({ .operationMode = systemState.operationMode, .tarePending = tarePending }); + } + + void updateOperationMode(OperationMode operationMode) { + sumitUpdateSystemStateCommand({ .operationMode = operationMode, .tarePending = systemState.tarePending }); + } + + // --------------------------------------------------------------------------------- + // ----------------------------------- SCALES -------------------------------------- + // --------------------------------------------------------------------------------- + blescales::Scales getConnectedScales() { + return connectedScales; + } + + void updateConnectedScales(const blescales::Scales& scales) { + if (scales.address == connectedScales.address && scales.name == connectedScales.name) return; + + connectedScales = scales; + onConnectedBleScalesUpdated(connectedScales); + } +} diff --git a/webserver/src/state/state.h b/webserver/src/state/state.h new file mode 100644 index 00000000..3dbbb05a --- /dev/null +++ b/webserver/src/state/state.h @@ -0,0 +1,69 @@ +#ifndef STATE_H +#define STATE_H + +#include "gaggia_settings.h" +#include "profiling_phases.h" +#include "../scales/ble_scales.h" +#include "system_state.h" +#include "../persistence/saved_profiles.h" + +namespace state { + void init(); + +// --------------------------------------------------------------------------------- +// ---------------------------------- SETTINGS ------------------------------------- +// --------------------------------------------------------------------------------- + const GaggiaSettings& getSettings(); + const SensorState& getSensor(); + void updateAllSettings(const GaggiaSettings& settings); + void updateBrewSettings(const BrewSettings& settings); + void updateBoilerSettings(const BoilerSettings& settings); + void updateLedSettings(const LedSettings& settings); + void updateSystemSettings(const SystemSettings& settings); + void updateScalesSettings(const ScalesSettings& settings); + + bool persistSettings(); + +// --------------------------------------------------------------------------------- +// ---------------------------------- PROFILES ------------------------------------- +// --------------------------------------------------------------------------------- + const Profile& getActiveProfile(); + void updateActiveProfile(const Profile& profile); + ProfileId getActiveProfileId(); + bool updateActiveProfileId(ProfileId id); + + bool persistActiveProfileId(); + bool persistActiveProfile(); + +// --------------------------------------------------------------------------------- +// -------------------------------- SYSTEM_STATE ----------------------------------- +// --------------------------------------------------------------------------------- + SystemState getSystemState(); + void updateSystemState(const SystemState& command); + void updateTarePending(bool tarePending); + void updateOperationMode(OperationMode operationMode); + + +// --------------------------------------------------------------------------------- +// ----------------------------------- SCALES -------------------------------------- +// --------------------------------------------------------------------------------- + blescales::Scales getConnectedScales(); + void updateConnectedScales(const blescales::Scales& scales); + +// --------------------------------------------------------------------------------- +// --------------------------------- CALLBACKS ------------------------------------- +// --------------------------------------------------------------------------------- + void onActiveProfileUpdated(const Profile& profile); + void onAllSettingsUpdated(const GaggiaSettings& settings); + void onBrewSettingsUpdated(const BrewSettings& settings); + void onBoilerSettingsUpdated(const BoilerSettings& settings); + void onLedSettingsUpdated(const LedSettings& settings); + void onSystemSettingsUpdated(const SystemSettings& settings); + void onScalesSettingsUpdated(const ScalesSettings& settings); + + void onSystemStateUpdated(const SystemState& systemState); + void onUpdateSystemStateCommandSubmitted(const UpdateSystemStateComand& command); + void onConnectedBleScalesUpdated(const blescales::Scales& scales); +} + +#endif diff --git a/webserver/src/stm_comms/stm_comms.cpp b/webserver/src/stm_comms/stm_comms.cpp index 0f12b5a0..769ba267 100644 --- a/webserver/src/stm_comms/stm_comms.cpp +++ b/webserver/src/stm_comms/stm_comms.cpp @@ -1,5 +1,10 @@ #include "stm_comms.h" #include "../task_config.h" +#include "vector" +#include "proto/proto_serializer.h" +#include "proto/message_converters.h" +#include "proto/profile_converters.h" +#include "proto/settings_converters.h" namespace { McuComms mcuComms; @@ -7,18 +12,17 @@ namespace { } void stmCommsTask(void* params); +void onMessageReceived(McuCommsMessageType type, std::vector& data); void stmCommsInit(HardwareSerial& serial) { serial.setRxBufferSize(256); serial.setTxBufferSize(256); - serial.begin(460800); + serial.begin(921600); // mcuComms.setDebugPort(&Serial); mcuComms.begin(serial); // Set callbacks - mcuComms.setShotSnapshotCallback(onShotSnapshotReceived); - mcuComms.setSensorStateSnapshotCallback(onSensorStateSnapshotReceived); - mcuComms.setRemoteScalesTareCommandCallback(onScalesTareReceived); + mcuComms.setMessageReceivedCallback(onMessageReceived); xTaskCreateUniversal(stmCommsTask, "stmComms", configMINIMAL_STACK_SIZE + 2400, NULL, PRIORITY_STM_COMMS, NULL, CORE_STM_COMMS); } @@ -30,6 +34,67 @@ void stmCommsTask(void* params) { } } +void onDataRequest(McuCommsMessageType requestedDataType) { + switch (requestedDataType) { + case McuCommsMessageType::MCUC_DATA_ALL_SETTINGS: { + onGaggiaSettingsRequested(); + break; + } + case McuCommsMessageType::MCUC_DATA_PROFILE: { + onProfileRequested(); + break; + } + default: // Ignore unhandled request + break; + } +} + +void onMessageReceived(McuCommsMessageType messageType, std::vector& data) { + switch (messageType) { + case McuCommsMessageType::MCUC_DATA_SENSOR_STATE_SNAPSHOT: { + SensorStateSnapshot snapshot; + ProtoSerializer::deserialize(data, snapshot); + onSensorStateSnapshotReceived(snapshot); + break; + } + case McuCommsMessageType::MCUC_DATA_SHOT_SNAPSHOT: { + ShotSnapshot snapshot; + ProtoSerializer::deserialize(data, snapshot); + onShotSnapshotReceived(snapshot); + break; + } + case McuCommsMessageType::MCUC_DATA_SYSTEM_STATE: { + SystemState systemState; + ProtoSerializer::deserialize(data, systemState); + onSystemStateReceived(systemState); + break; + } + case McuCommsMessageType::MCUC_CMD_REMOTE_SCALES_TARE: { + onScalesTareReceived(); + break; + } + case McuCommsMessageType::MCUC_REQ_DATA: { + McuCommsRequestData dataRequest; + ProtoSerializer::deserialize(data, dataRequest); + onDataRequest(dataRequest.type); + } + case McuCommsMessageType::MCUC_DATA_NOTIFICATION: { + Notification notification; + ProtoSerializer::deserialize(data, notification); + onNotification(notification); + } + case McuCommsMessageType::MCUC_DATA_DESCALING_PROGRESS: { + DescalingProgress progress; + ProtoSerializer::deserialize(data, progress); + onDescalingProgressReceived(progress); + break; + } + + default: + break; + } +} + void stmCommsReadData() { if (xSemaphoreTakeRecursive(mcucLock, portMAX_DELAY) == pdFALSE) return; mcuComms.readDataAndTick(); @@ -38,12 +103,91 @@ void stmCommsReadData() { void stmCommsSendWeight(float weight) { if (xSemaphoreTakeRecursive(mcucLock, portMAX_DELAY) == pdFALSE) return; - mcuComms.sendRemoteScalesWeight(weight); + mcuComms.sendMessage( + McuCommsMessageType::MCUC_DATA_REMOTE_SCALES_WEIGHT, + ProtoSerializer::serialize(Measurement{ .value = weight }) + ); xSemaphoreGiveRecursive(mcucLock); } void stmCommsSendScaleDisconnected() { if (xSemaphoreTakeRecursive(mcucLock, portMAX_DELAY) == pdFALSE) return; - mcuComms.sendRemoteScalesDisconnected(); + mcuComms.sendMessage(McuCommsMessageType::MCUC_DATA_REMOTE_SCALES_DISCONNECTED); + xSemaphoreGiveRecursive(mcucLock); +} + +void stmCommsSendGaggiaSettings(const GaggiaSettings& settings) { + if (xSemaphoreTakeRecursive(mcucLock, portMAX_DELAY) == pdFALSE) return; + mcuComms.sendMessage( + McuCommsMessageType::MCUC_DATA_ALL_SETTINGS, + ProtoSerializer::serialize(settings) + ); + xSemaphoreGiveRecursive(mcucLock); +} + +void stmCommsSendProfile(const Profile& profile) { + if (xSemaphoreTakeRecursive(mcucLock, portMAX_DELAY) == pdFALSE) return; + mcuComms.sendMessage( + McuCommsMessageType::MCUC_DATA_PROFILE, + ProtoSerializer::serialize(profile) + ); + xSemaphoreGiveRecursive(mcucLock); +} + +void stmCommsSendBrewSettings(const BrewSettings& settings) { + if (xSemaphoreTakeRecursive(mcucLock, portMAX_DELAY) == pdFALSE) return; + mcuComms.sendMessage( + McuCommsMessageType::MCUC_DATA_BREW_SETTINGS, + ProtoSerializer::serialize(settings) + ); + xSemaphoreGiveRecursive(mcucLock); +} + +void stmCommsSendBoilerSettings(const BoilerSettings& settings) { + if (xSemaphoreTakeRecursive(mcucLock, portMAX_DELAY) == pdFALSE) return; + mcuComms.sendMessage( + McuCommsMessageType::MCUC_DATA_BOILER_SETTINGS, + ProtoSerializer::serialize(settings) + ); + xSemaphoreGiveRecursive(mcucLock); +} + +void stmCommsSendLedSettings(const LedSettings& settings) { + if (xSemaphoreTakeRecursive(mcucLock, portMAX_DELAY) == pdFALSE) return; + mcuComms.sendMessage( + McuCommsMessageType::MCUC_DATA_LED_SETTINGS, + ProtoSerializer::serialize(settings) + ); + xSemaphoreGiveRecursive(mcucLock); +} + +void stmCommsSendSystemSettings(const SystemSettings& settings) { + if (xSemaphoreTakeRecursive(mcucLock, portMAX_DELAY) == pdFALSE) return; + mcuComms.sendMessage( + McuCommsMessageType::MCUC_DATA_SYSTEM_SETTINGS, + ProtoSerializer::serialize(settings) + ); + xSemaphoreGiveRecursive(mcucLock); +} + +void stmCommsSendScalesSettings(const ScalesSettings& settings) { + if (xSemaphoreTakeRecursive(mcucLock, portMAX_DELAY) == pdFALSE) return; + mcuComms.sendMessage( + McuCommsMessageType::MCUC_DATA_SCALES_SETTINGS, + ProtoSerializer::serialize(settings) + ); + xSemaphoreGiveRecursive(mcucLock); +} + +void stmCommsSendUpdateSystemState(const UpdateSystemStateComand& state) { + if (xSemaphoreTakeRecursive(mcucLock, portMAX_DELAY) == pdFALSE) return; + UpdateSystemStateComand command = { + .operationMode = state.operationMode, + .tarePending = state.tarePending + }; + mcuComms.sendMessage( + McuCommsMessageType::MCUC_CMD_UPDATE_SYSTEM_STATE, + ProtoSerializer::serialize(command) + ); xSemaphoreGiveRecursive(mcucLock); } diff --git a/webserver/src/stm_comms/stm_comms.h b/webserver/src/stm_comms/stm_comms.h index fb5b20b0..f4aee186 100644 --- a/webserver/src/stm_comms/stm_comms.h +++ b/webserver/src/stm_comms/stm_comms.h @@ -2,15 +2,31 @@ #define STM_COMMS_H #include "mcu_comms.h" +#include "gaggia_settings.h" +#include "system_state.h" +#include "notification_message.h" void stmCommsInit(HardwareSerial& serial); void stmCommsReadData(); void stmCommsSendWeight(float weight); void stmCommsSendScaleDisconnected(); +void stmCommsSendGaggiaSettings(const GaggiaSettings& settings); +void stmCommsSendProfile(const Profile& profile); +void stmCommsSendBrewSettings(const BrewSettings& settings); +void stmCommsSendBoilerSettings(const BoilerSettings& settings); +void stmCommsSendLedSettings(const LedSettings& settings); +void stmCommsSendSystemSettings(const SystemSettings& settings); +void stmCommsSendScalesSettings(const ScalesSettings& settings); +void stmCommsSendUpdateSystemState(const UpdateSystemStateComand& state); // To be defined elsewhere -void onSensorStateSnapshotReceived(SensorStateSnapshot& snapshot); -void onShotSnapshotReceived(ShotSnapshot& snapshot); +void onSensorStateSnapshotReceived(const SensorStateSnapshot& snapshot); +void onShotSnapshotReceived(const ShotSnapshot& snapshot); void onScalesTareReceived(); +void onGaggiaSettingsRequested(); +void onProfileRequested(); +void onSystemStateReceived(const SystemState& systemState); +void onNotification(const Notification& notification); +void onDescalingProgressReceived(const DescalingProgress& progress); #endif diff --git a/webserver/src/wifi/wifi_setup.cpp b/webserver/src/wifi/wifi_setup.cpp index 609b82d3..8b2de565 100644 --- a/webserver/src/wifi/wifi_setup.cpp +++ b/webserver/src/wifi/wifi_setup.cpp @@ -2,6 +2,7 @@ #include #include "../task_config.h" #include "../log/log.h" +#include "esp_task_wdt.h" /** * Helper class for persisting and retrieving WiFi connection credentials @@ -96,10 +97,23 @@ bool wifiConnect(String ssid, String pass, const uint32_t timeout) { WiFi.begin(ssid.c_str(), pass.c_str()); LOG_INFO("Connecting to WiFi [%s]", ssid.c_str()); - if (WiFi.waitForConnectResult(timeout) != WL_CONNECTED) { - LOG_INFO("Failed to connect. Check password."); - xSemaphoreGiveRecursive(wifi::lock); - return false; + uint32_t wifiStartTimer = millis(); + while (WiFi.status() != WL_CONNECTED) { + + if (WiFi.status() == WL_CONNECT_FAILED) { + LOG_INFO("\nFailed to connect. Check password."); + xSemaphoreGiveRecursive(wifi::lock); + return false; + } + + if (millis() - wifiStartTimer >= timeout) { + LOG_INFO("\nFailed to connect after %ld seconds.\n", timeout / 1000); + xSemaphoreGiveRecursive(wifi::lock); + return false; + } + + esp_task_wdt_reset(); + delay(100); } LOG_INFO("Connected to WiFi [%s] with IP:[%s]", WiFi.SSID().c_str(), WiFi.localIP().toString().c_str()); diff --git a/webserver/web-interface/.eslintrc.js b/webserver/web-interface/.eslintrc.js index 94c42517..a077a92b 100644 --- a/webserver/web-interface/.eslintrc.js +++ b/webserver/web-interface/.eslintrc.js @@ -64,6 +64,13 @@ module.exports = { // allow JSX with any file extension 'import/extensions': [2, { extensions }], 'react/jsx-filename-extension': [2, { extensions }], + 'no-shadow': 'off', + '@typescript-eslint/no-shadow': ['error'], + 'react/require-default-props': ['error', { + forbidDefaultForRequired: true, + classes: 'defaultProps', + functions: 'defaultArguments', + }], }, ignorePatterns: ['build/*.js', 'scripts/*.js', 'config/*.js'], }; diff --git a/webserver/web-interface/.gitignore b/webserver/web-interface/.gitignore index 800f3a80..a522f4c4 100644 --- a/webserver/web-interface/.gitignore +++ b/webserver/web-interface/.gitignore @@ -22,3 +22,5 @@ npm-debug.log* yarn-debug.log* yarn-error.log* + +local.config.ts diff --git a/webserver/web-interface/package-lock.json b/webserver/web-interface/package-lock.json index a1823f18..6d61f3f5 100644 --- a/webserver/web-interface/package-lock.json +++ b/webserver/web-interface/package-lock.json @@ -1,7 +1,7 @@ { "name": "web-interface", "version": "0.1.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -14,13 +14,13 @@ "@mui/icons-material": "^5.11.16", "@mui/material": "^5.13.2", "@mui/x-data-grid": "^5.17.26", + "@uiw/react-color": "^1.3.1", "axios": "^1.4.0", "bfj": "^7.0.2", "camelcase": "^6.3.0", "chart.js": "^4.3.0", "fs-extra": "^10.1.0", - "prop-types": "^15.8.1", - "react": "^18.2.0", + "react": "^15.0.0 || ^17.0.0 || ^18.0.0", "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", "react-icons": "^4.8.0", @@ -28,17 +28,21 @@ "react-refresh": "^0.14.0", "react-router-dom": "^6.11.2", "react-use-websocket": "^4.3.1", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "zustand": "^4.3.9" }, "devDependencies": { "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", - "@typescript-eslint/eslint-plugin": "^5.60.0", - "@typescript-eslint/parser": "^5.60.0", + "@types/node": "^20.4.1", + "@typescript-eslint/eslint-plugin": "^5.61.0", + "@typescript-eslint/parser": "^5.61.0", "@vitejs/plugin-react": "^4.0.1", "babel-jest": "^27.5.1", "browserslist": "^4.21.5", + "compress-create-react-app": "^1.4.2", + "compression-webpack-plugin": "^10.0.0", "dotenv": "^10.0.0", "dotenv-expand": "^5.1.0", "eslint": "^8.43.0", @@ -54,12 +58,23 @@ "jest-watch-typeahead": "^1.1.0", "react-dev-utils": "^12.0.1", "semver": "^7.5.1", - "typescript": "^5.1.3", + "typescript": "^5.1.6", "vite": "^4.3.9", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-pwa": "^0.16.4", "vite-plugin-svgr": "^3.2.0", "vite-tsconfig-paths": "^4.2.0" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@adobe/css-tools": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz", @@ -101,35 +116,35 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz", - "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", - "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", + "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helpers": "^7.22.5", - "@babel/parser": "^7.22.5", + "@babel/generator": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-module-transforms": "^7.22.9", + "@babel/helpers": "^7.22.6", + "@babel/parser": "^7.22.7", "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", + "@babel/traverse": "^7.22.8", "@babel/types": "^7.22.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.2", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -140,23 +155,23 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/eslint-parser": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.21.8.tgz", - "integrity": "sha512-HLhI+2q+BP3sf78mFUZNCGc10KEmoUqtUT1OCdMZsN+qr4qFeLUod62/zAnF3jNQstwyasDkZnVXwfK2Bml7MQ==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.22.9.tgz", + "integrity": "sha512-xdMkt39/nviO/4vpVdrEYPwXCsYIXSSAr6mC7WQsNIlGnuxKyKE7GZjalcnbSWiC4OXGNNN3UQPeHfjSC6sTDA==", "dev": true, "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": "^10.13.0 || ^12.13.0 || >=14.0.0" @@ -176,18 +191,18 @@ } }, "node_modules/@babel/eslint-parser/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", - "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", + "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", "dev": true, "dependencies": { "@babel/types": "^7.22.5", @@ -200,40 +215,40 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", + "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.21.5.tgz", - "integrity": "sha512-uNrjKztPLkUk7bpCNC0jEKDJzzkvel/W+HguzbN8krA+LPfC1CEobJEvAvGka2A/M+ViOqXdcRL0GqPUJSjx9g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", + "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", "dev": true, "dependencies": { - "@babel/types": "^7.21.5" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz", - "integrity": "sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", + "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.5", + "@babel/compat-data": "^7.22.9", "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.3", + "browserslist": "^4.21.9", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -243,29 +258,29 @@ } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.8.tgz", - "integrity": "sha512-+THiN8MqiH2AczyuZrnrKL6cAxFRRQDKW9h1YkBvbgKmAm6mwiacig1qT73DHIWMGo40GRnsEfN3LA+E6NtmSw==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz", + "integrity": "sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-member-expression-to-functions": "^7.21.5", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.21.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/helper-split-export-declaration": "^7.18.6", - "semver": "^6.3.0" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@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" @@ -275,23 +290,23 @@ } }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.8.tgz", - "integrity": "sha512-zGuSdedkFtsFHGbexAvNuipg1hbtitDLo2XE8/uf6Y9sOQV1xsYX/2pNbtedp/X0eU1pIt+kGvaqHCowkRbS5g==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz", + "integrity": "sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-annotate-as-pure": "^7.22.5", "regexpu-core": "^5.3.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -301,40 +316,30 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.1.tgz", + "integrity": "sha512-kX4oXixDxG197yhX+J3Wp+NpL2wuCFjWQAr6yX2jtCnflK9ulMI51ULFGIrWiX1jGfvAxdHp+XQCcP2bZGPs9A==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", + "@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", - "semver": "^6.1.2" + "resolve": "^1.14.2" }, "peerDependencies": { "@babel/core": "^7.4.0-0" } }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-environment-visitor": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", @@ -370,12 +375,12 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.5.tgz", - "integrity": "sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", + "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", "dev": true, "dependencies": { - "@babel/types": "^7.21.5" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -393,31 +398,31 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", - "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", + "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-module-imports": "^7.22.5", "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.5" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -433,15 +438,14 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", - "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz", + "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-wrap-function": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-wrap-function": "^7.22.9" }, "engines": { "node": ">=6.9.0" @@ -451,20 +455,20 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.21.5.tgz", - "integrity": "sha512-/y7vBgsr9Idu4M6MprbOVUfH3vs7tsIfnVWv/Ml2xgwvyH6LTngdfbf5AdsKwkJy4zgy1X/kuNrEKvhhK28Yrg==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", + "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-member-expression-to-functions": "^7.21.5", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@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": { @@ -480,21 +484,21 @@ } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", - "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", "dev": true, "dependencies": { - "@babel/types": "^7.20.0" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", - "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { "@babel/types": "^7.22.5" @@ -529,28 +533,27 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", - "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz", + "integrity": "sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.19.0", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5" + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz", - "integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", + "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", "dev": true, "dependencies": { "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", + "@babel/traverse": "^7.22.6", "@babel/types": "^7.22.5" }, "engines": { @@ -571,9 +574,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", - "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==", + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", + "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -583,12 +586,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", - "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", + "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -598,14 +601,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", - "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", + "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-proposal-optional-chaining": "^7.20.7" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -614,24 +617,6 @@ "@babel/core": "^7.13.0" } }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@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==", - "dev": true, - "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.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", @@ -648,98 +633,17 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.21.0.tgz", - "integrity": "sha512-MfgX49uRrFUTL/HvWtmx3zmpyzMMr4MTj3d527MLlr/4RTT9G/ytFFP7qet2uM2Ve03b+BkpWUpK+lRXnQ+v9w==", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/plugin-syntax-decorators": "^7.21.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.22.7.tgz", + "integrity": "sha512-omXqPF7Onq4Bb7wHxXjM3jSMSJvUUbvDvmmds7KI5n9Cq6Ln5I05I1W2nRlRof1rGdiUxJrxwe285WF96XlBXQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-create-class-features-plugin": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/plugin-syntax-decorators": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -780,41 +684,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dev": true, - "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.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "dev": true, - "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.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", @@ -849,16 +718,10 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", - "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@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==", "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, "engines": { "node": ">=6.9.0" }, @@ -934,12 +797,12 @@ } }, "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.21.0.tgz", - "integrity": "sha512-tIoPpGBR8UuM4++ccWN3gifhVvQu7ZizuR1fklhRJrd5ewgbkUS+0KVFeWWxELtn18NTLoW32XV7zyOgIAiz+w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.22.5.tgz", + "integrity": "sha512-avpUOBS7IU6al8MmF1XpAyj9QYeLPuSDJI5D4pVMSMdL7xQokKqJPYQC67RCT0aCTashUXPiGwMJ0DEXXCEmMA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -973,12 +836,12 @@ } }, "node_modules/@babel/plugin-syntax-flow": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz", - "integrity": "sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz", + "integrity": "sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -988,12 +851,27 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", - "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "dev": true, + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", + "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1027,12 +905,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", - "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1144,12 +1022,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", - "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1158,30 +1036,29 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz", - "integrity": "sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA==", + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", - "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1190,13 +1067,16 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", - "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz", + "integrity": "sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5", + "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { "node": ">=6.9.0" @@ -1205,13 +1085,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", - "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", + "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1220,21 +1102,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", - "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", - "globals": "^11.1.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1243,14 +1117,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz", - "integrity": "sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q==", + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", + "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/template": "^7.20.7" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1259,13 +1132,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz", - "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==", + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", + "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1274,29 +1148,38 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", - "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz", + "integrity": "sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@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.0.0-0" + "@babel/core": "^7.12.0" } }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", - "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "node_modules/@babel/plugin-transform-classes": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", + "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" }, "engines": { "node": ">=6.9.0" @@ -1305,14 +1188,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", - "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1321,14 +1204,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz", - "integrity": "sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==", + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz", + "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-flow": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1337,13 +1219,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz", - "integrity": "sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ==", + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1352,15 +1235,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", - "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1369,13 +1250,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", - "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz", + "integrity": "sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1384,13 +1266,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", - "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1399,14 +1282,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", - "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz", + "integrity": "sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1415,15 +1298,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz", - "integrity": "sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ==", + "node_modules/@babel/plugin-transform-flow-strip-types": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.22.5.tgz", + "integrity": "sha512-tujNbZdxdG0/54g/oua8ISToaXTFBf8EnSb5PgQSciIXWOWKX3S4+JR7ZE9ol8FZwf9kxitzkGQ+QWeov/mCiA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.21.5", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-simple-access": "^7.21.5" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-flow": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1432,16 +1314,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", - "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", + "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", "dev": true, "dependencies": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-identifier": "^7.19.1" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1450,14 +1329,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", - "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1466,29 +1346,29 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", - "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz", + "integrity": "sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.20.5", - "@babel/helper-plugin-utils": "^7.20.2" + "@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" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", - "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "node_modules/@babel/plugin-transform-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1497,14 +1377,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", - "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz", + "integrity": "sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { "node": ">=6.9.0" @@ -1513,13 +1393,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz", - "integrity": "sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==", + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1528,13 +1408,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", - "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", + "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1543,13 +1424,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", - "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", + "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1558,17 +1441,16 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.5.tgz", - "integrity": "sha512-ELdlq61FpoEkHO6gFRpfj0kUgSwQTGoaEU8eMRoS8Dv3v6e7BjEAj5WMtIBRdHUeAioMhKP5HyxNzNnP+heKbA==", + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", + "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/types": "^7.21.5" + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1577,13 +1459,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", - "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", "dev": true, "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.18.6" + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1592,25 +1475,26 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-jsx-self": { + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz", - "integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", "dev": true, "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-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-react-jsx-source": { + "node_modules/@babel/plugin-transform-new-target": { "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz", - "integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1622,14 +1506,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", - "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz", + "integrity": "sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1638,14 +1522,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz", - "integrity": "sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w==", + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz", + "integrity": "sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "regenerator-transform": "^0.15.1" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "engines": { "node": ">=6.9.0" @@ -1654,13 +1538,17 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", - "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz", + "integrity": "sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/compat-data": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1669,18 +1557,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz", - "integrity": "sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA==", + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-plugin-utils": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "semver": "^6.3.0" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1689,22 +1573,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", - "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz", + "integrity": "sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1713,14 +1589,15 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", - "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz", + "integrity": "sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" + "@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" @@ -1729,13 +1606,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", - "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", + "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1744,13 +1621,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", - "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", + "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1759,13 +1637,16 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", - "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz", + "integrity": "sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { "node": ">=6.9.0" @@ -1774,16 +1655,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", - "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-typescript": "^7.20.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1792,13 +1670,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz", - "integrity": "sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg==", + "node_modules/@babel/plugin-transform-react-display-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz", + "integrity": "sha512-PVk3WPYudRF5z4GKMEYUrLjPl38fJSKNaEOkFuoprioowGuWN6w2RKznuFNSlJx7pzzXXStPUnNSOEO0jL5EVw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1807,14 +1685,17 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", - "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz", + "integrity": "sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1823,88 +1704,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-env": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.5.tgz", - "integrity": "sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.21.5", - "@babel/helper-compilation-targets": "^7.21.5", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.20.7", - "@babel/plugin-proposal-async-generator-functions": "^7.20.7", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.21.0", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.20.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.21.0", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.21.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@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-transform-arrow-functions": "^7.21.5", - "@babel/plugin-transform-async-to-generator": "^7.20.7", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.21.0", - "@babel/plugin-transform-classes": "^7.21.0", - "@babel/plugin-transform-computed-properties": "^7.21.5", - "@babel/plugin-transform-destructuring": "^7.21.3", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.21.5", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.20.11", - "@babel/plugin-transform-modules-commonjs": "^7.21.5", - "@babel/plugin-transform-modules-systemjs": "^7.20.11", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.20.5", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.21.3", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.21.5", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.20.7", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.21.5", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.21.5", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" + "node_modules/@babel/plugin-transform-react-jsx-development": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", + "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", + "dev": true, + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1913,43 +1719,28 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz", + "integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-react": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", - "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz", + "integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-react-display-name": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-pure-annotations": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1958,17 +1749,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-typescript": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.5.tgz", - "integrity": "sha512-iqe3sETat5EOrORXiQ6rWfoOg2y68Cs75B9wNxdPW4kixJxh7aXQE1KPdWLDniC24T/6dSnguF33W9j/ZZQcmA==", + "node_modules/@babel/plugin-transform-react-pure-annotations": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz", + "integrity": "sha512-gP4k85wx09q+brArVinTXhWiyzLl9UpmGva0+mWyKxk6JZequ05x3eUcIUE+FyttPKJFRRVtAvQaJ6YF9h1ZpA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/plugin-transform-modules-commonjs": "^7.21.5", - "@babel/plugin-transform-typescript": "^7.21.3" + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1977,57 +1765,432 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true - }, - "node_modules/@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", + "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", + "dev": true, "dependencies": { - "regenerator-runtime": "^0.13.11" + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.1" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/template": { + "node_modules/@babel/plugin-transform-reserved-words": { "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/traverse": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz", - "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==", + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.9.tgz", + "integrity": "sha512-9KjBH61AGJetCPYp/IEyLEp47SyybZb0nDRpBvmtEkm+rUIwxdlKpyNHI1TmsGkeuLclJdleQHRZ8XLBnnh8CQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5", - "debug": "^4.1.0", - "globals": "^11.1.0" + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.4", + "babel-plugin-polyfill-corejs3": "^0.8.2", + "babel-plugin-polyfill-regenerator": "^0.5.1", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - } + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", + "dev": true, + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "dev": true, + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "dev": true, + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "dev": true, + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "dev": true, + "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.22.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.9.tgz", + "integrity": "sha512-BnVR1CpKiuD0iobHPaM1iLvcwPYN2uVFAqoLVSpEDKWuOikoCv5HbKLxclhKYUXlWkX86DoZGtqI4XhbOsyrMg==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.9", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/plugin-syntax-typescript": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", + "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", + "dev": true, + "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.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", + "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "dev": true, + "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-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "dev": true, + "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-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", + "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "dev": true, + "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/preset-env": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.9.tgz", + "integrity": "sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@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.22.5", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@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.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.22.7", + "@babel/plugin-transform-async-to-generator": "^7.22.5", + "@babel/plugin-transform-block-scoped-functions": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.5", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.5", + "@babel/plugin-transform-classes": "^7.22.6", + "@babel/plugin-transform-computed-properties": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.5", + "@babel/plugin-transform-dotall-regex": "^7.22.5", + "@babel/plugin-transform-duplicate-keys": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.5", + "@babel/plugin-transform-exponentiation-operator": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.5", + "@babel/plugin-transform-for-of": "^7.22.5", + "@babel/plugin-transform-function-name": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.5", + "@babel/plugin-transform-literals": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", + "@babel/plugin-transform-member-expression-literals": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-modules-systemjs": "^7.22.5", + "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", + "@babel/plugin-transform-numeric-separator": "^7.22.5", + "@babel/plugin-transform-object-rest-spread": "^7.22.5", + "@babel/plugin-transform-object-super": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.6", + "@babel/plugin-transform-parameters": "^7.22.5", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.5", + "@babel/plugin-transform-property-literals": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.5", + "@babel/plugin-transform-reserved-words": "^7.22.5", + "@babel/plugin-transform-shorthand-properties": "^7.22.5", + "@babel/plugin-transform-spread": "^7.22.5", + "@babel/plugin-transform-sticky-regex": "^7.22.5", + "@babel/plugin-transform-template-literals": "^7.22.5", + "@babel/plugin-transform-typeof-symbol": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.5", + "@babel/plugin-transform-unicode-property-regex": "^7.22.5", + "@babel/plugin-transform-unicode-regex": "^7.22.5", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.4", + "babel-plugin-polyfill-corejs3": "^0.8.2", + "babel-plugin-polyfill-regenerator": "^0.5.1", + "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-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-react": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.22.5.tgz", + "integrity": "sha512-M+Is3WikOpEJHgR385HbuCITPTaPRaNkibTEa9oiofmJvIsrceb4yp9RL9Kb+TE8LznmeyZqpP+Lopwcx59xPQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-transform-react-display-name": "^7.22.5", + "@babel/plugin-transform-react-jsx": "^7.22.5", + "@babel/plugin-transform-react-jsx-development": "^7.22.5", + "@babel/plugin-transform-react-pure-annotations": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz", + "integrity": "sha512-YbPaal9LxztSGhmndR46FmAbkJ/1fAsw293tSU+I5E5h+cnJ3d4GTwyUgGYmOXJYdGA+uNePle4qbaRzj2NISQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-syntax-jsx": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-typescript": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, + "node_modules/@babel/runtime": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", + "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.22.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", + "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.7", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/types": "^7.22.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } }, "node_modules/@babel/types": { "version": "7.22.5", @@ -2097,9 +2260,9 @@ "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" }, "node_modules/@emotion/react": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.0.tgz", - "integrity": "sha512-ZSK3ZJsNkwfjT3JpDAWJZlrGD81Z3ytNDsxw1LKq1o+xkmO5pnWfr6gmCC8gHEFf3nSSX/09YrG67jybNPxSUw==", + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", + "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.11.0", @@ -2182,9 +2345,9 @@ "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, "node_modules/@esbuild/android-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", - "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.14.tgz", + "integrity": "sha512-blODaaL+lngG5bdK/t4qZcQvq2BBqrABmYwqPPcS5VRxrCSGHb9R/rA3fqxh7R18I7WU4KKv+NYkt22FDfalcg==", "cpu": [ "arm" ], @@ -2198,9 +2361,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", - "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.14.tgz", + "integrity": "sha512-rZ2v+Luba5/3D6l8kofWgTnqE+qsC/L5MleKIKFyllHTKHrNBMqeRCnZI1BtRx8B24xMYxeU32iIddRQqMsOsg==", "cpu": [ "arm64" ], @@ -2214,9 +2377,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", - "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.14.tgz", + "integrity": "sha512-qSwh8y38QKl+1Iqg+YhvCVYlSk3dVLk9N88VO71U4FUjtiSFylMWK3Ugr8GC6eTkkP4Tc83dVppt2n8vIdlSGg==", "cpu": [ "x64" ], @@ -2230,9 +2393,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", - "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.14.tgz", + "integrity": "sha512-9Hl2D2PBeDYZiNbnRKRWuxwHa9v5ssWBBjisXFkVcSP5cZqzZRFBUWEQuqBHO4+PKx4q4wgHoWtfQ1S7rUqJ2Q==", "cpu": [ "arm64" ], @@ -2246,9 +2409,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", - "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.14.tgz", + "integrity": "sha512-ZnI3Dg4ElQ6tlv82qLc/UNHtFsgZSKZ7KjsUNAo1BF1SoYDjkGKHJyCrYyWjFecmXpvvG/KJ9A/oe0H12odPLQ==", "cpu": [ "x64" ], @@ -2262,9 +2425,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", - "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.14.tgz", + "integrity": "sha512-h3OqR80Da4oQCIa37zl8tU5MwHQ7qgPV0oVScPfKJK21fSRZEhLE4IIVpmcOxfAVmqjU6NDxcxhYaM8aDIGRLw==", "cpu": [ "arm64" ], @@ -2278,9 +2441,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", - "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.14.tgz", + "integrity": "sha512-ha4BX+S6CZG4BoH9tOZTrFIYC1DH13UTCRHzFc3GWX74nz3h/N6MPF3tuR3XlsNjMFUazGgm35MPW5tHkn2lzQ==", "cpu": [ "x64" ], @@ -2294,9 +2457,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", - "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.14.tgz", + "integrity": "sha512-5+7vehI1iqru5WRtJyU2XvTOvTGURw3OZxe3YTdE9muNNIdmKAVmSHpB3Vw2LazJk2ifEdIMt/wTWnVe5V98Kg==", "cpu": [ "arm" ], @@ -2310,9 +2473,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", - "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.14.tgz", + "integrity": "sha512-IXORRe22In7U65NZCzjwAUc03nn8SDIzWCnfzJ6t/8AvGx5zBkcLfknI+0P+hhuftufJBmIXxdSTbzWc8X/V4w==", "cpu": [ "arm64" ], @@ -2326,9 +2489,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", - "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.14.tgz", + "integrity": "sha512-BfHlMa0nibwpjG+VXbOoqJDmFde4UK2gnW351SQ2Zd4t1N3zNdmUEqRkw/srC1Sa1DRBE88Dbwg4JgWCbNz/FQ==", "cpu": [ "ia32" ], @@ -2342,9 +2505,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", - "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.14.tgz", + "integrity": "sha512-j2/Ex++DRUWIAaUDprXd3JevzGtZ4/d7VKz+AYDoHZ3HjJzCyYBub9CU1wwIXN+viOP0b4VR3RhGClsvyt/xSw==", "cpu": [ "loong64" ], @@ -2358,9 +2521,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", - "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.14.tgz", + "integrity": "sha512-qn2+nc+ZCrJmiicoAnJXJJkZWt8Nwswgu1crY7N+PBR8ChBHh89XRxj38UU6Dkthl2yCVO9jWuafZ24muzDC/A==", "cpu": [ "mips64el" ], @@ -2374,9 +2537,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", - "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.14.tgz", + "integrity": "sha512-aGzXzd+djqeEC5IRkDKt3kWzvXoXC6K6GyYKxd+wsFJ2VQYnOWE954qV2tvy5/aaNrmgPTb52cSCHFE+Z7Z0yg==", "cpu": [ "ppc64" ], @@ -2390,9 +2553,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", - "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.14.tgz", + "integrity": "sha512-8C6vWbfr0ygbAiMFLS6OPz0BHvApkT2gCboOGV76YrYw+sD/MQJzyITNsjZWDXJwPu9tjrFQOVG7zijRzBCnLw==", "cpu": [ "riscv64" ], @@ -2406,9 +2569,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", - "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.14.tgz", + "integrity": "sha512-G/Lf9iu8sRMM60OVGOh94ZW2nIStksEcITkXdkD09/T6QFD/o+g0+9WVyR/jajIb3A0LvBJ670tBnGe1GgXMgw==", "cpu": [ "s390x" ], @@ -2422,9 +2585,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", - "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.14.tgz", + "integrity": "sha512-TBgStYBQaa3EGhgqIDM+ECnkreb0wkcKqL7H6m+XPcGUoU4dO7dqewfbm0mWEQYH3kzFHrzjOFNpSAVzDZRSJw==", "cpu": [ "x64" ], @@ -2438,9 +2601,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", - "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.14.tgz", + "integrity": "sha512-stvCcjyCQR2lMTroqNhAbvROqRjxPEq0oQ380YdXxA81TaRJEucH/PzJ/qsEtsHgXlWFW6Ryr/X15vxQiyRXVg==", "cpu": [ "x64" ], @@ -2454,9 +2617,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", - "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.14.tgz", + "integrity": "sha512-apAOJF14CIsN5ht1PA57PboEMsNV70j3FUdxLmA2liZ20gEQnfTG5QU0FhENo5nwbTqCB2O3WDsXAihfODjHYw==", "cpu": [ "x64" ], @@ -2470,9 +2633,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", - "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.14.tgz", + "integrity": "sha512-fYRaaS8mDgZcGybPn2MQbn1ZNZx+UXFSUoS5Hd2oEnlsyUcr/l3c6RnXf1bLDRKKdLRSabTmyCy7VLQ7VhGdOQ==", "cpu": [ "x64" ], @@ -2486,9 +2649,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", - "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.14.tgz", + "integrity": "sha512-1c44RcxKEJPrVj62XdmYhxXaU/V7auELCmnD+Ri+UCt+AGxTvzxl9uauQhrFso8gj6ZV1DaORV0sT9XSHOAk8Q==", "cpu": [ "arm64" ], @@ -2502,9 +2665,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", - "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.14.tgz", + "integrity": "sha512-EXAFttrdAxZkFQmpvcAQ2bywlWUsONp/9c2lcfvPUhu8vXBBenCXpoq9YkUvVP639ld3YGiYx0YUQ6/VQz3Maw==", "cpu": [ "ia32" ], @@ -2518,9 +2681,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", - "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.14.tgz", + "integrity": "sha512-K0QjGbcskx+gY+qp3v4/940qg8JitpXbdxFhRDA1aYoNaPff88+aEwoq45aqJ+ogpxQxmU0ZTjgnrQD/w8iiUg==", "cpu": [ "x64" ], @@ -2558,14 +2721,14 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", + "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -2626,9 +2789,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", - "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", + "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2692,58 +2855,6 @@ "node": ">=6" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -3013,9 +3124,9 @@ } }, "node_modules/@jest/expect-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", - "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.1.tgz", + "integrity": "sha512-o319vIf5pEMx0LmzSxxkYYxo4wrRLKHq9dP1yJU7FoPTB0LfAKSz8SWD6D/6U3v/O52t9cF5t+MeJiRsfk7zMw==", "dev": true, "dependencies": { "jest-get-type": "^29.4.3" @@ -3422,12 +3533,12 @@ } }, "node_modules/@jest/schemas": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", - "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "version": "29.6.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", + "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", "dev": true, "dependencies": { - "@sinclair/typebox": "^0.25.16" + "@sinclair/typebox": "^0.27.8" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -3710,11 +3821,10 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", - "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dev": true, - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -3748,15 +3858,15 @@ "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" }, "node_modules/@mui/base": { - "version": "5.0.0-beta.2", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.2.tgz", - "integrity": "sha512-R9R+aqrl1QhZJaO05rhvooqxOaf7SKpQ+EjW80sbP3ticTVmLmrn4YBLQS7/ML+WXdrkrPtqSmKFdSE5Ik3gBQ==", + "version": "5.0.0-beta.8", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.8.tgz", + "integrity": "sha512-b4vVjMZx5KzzEMf4arXKoeV5ZegAMOoPwoy1vfUBwhvXc2QtaaAyBp50U7OA2L06Leubc1A+lEp3eqwZoFn87g==", "dependencies": { - "@babel/runtime": "^7.21.0", + "@babel/runtime": "^7.22.6", "@emotion/is-prop-valid": "^1.2.1", "@mui/types": "^7.2.4", - "@mui/utils": "^5.13.1", - "@popperjs/core": "^2.11.7", + "@mui/utils": "^5.14.1", + "@popperjs/core": "^2.11.8", "clsx": "^1.2.1", "prop-types": "^15.8.1", "react-is": "^18.2.0" @@ -3780,20 +3890,20 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.13.2.tgz", - "integrity": "sha512-aOLCXMCySMFL2WmUhnz+DjF84AoFVu8rn35OsL759HXOZMz8zhEwVf5w/xxkWx7DycM2KXDTgAvYW48nTfqTLA==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.1.tgz", + "integrity": "sha512-mIa1WmDmNr1LoupV1Rbxt9bTFKMbIn10RHG1bnZ/FJCkAYpuU/D4n+R+ttiycgcZNngU++zyh/OQeJblzbQPzg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui" } }, "node_modules/@mui/icons-material": { - "version": "5.11.16", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.11.16.tgz", - "integrity": "sha512-oKkx9z9Kwg40NtcIajF9uOXhxiyTZrrm9nmIJ4UjkU2IdHpd4QVLbCc/5hZN/y0C6qzi2Zlxyr9TGddQx2vx2A==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.14.1.tgz", + "integrity": "sha512-xV/f26muQqtWzerzOIdGPrXoxp/OKaE2G2Wp9gnmG47mHua5Slup/tMc3fA4ZYUreGGrK6+tT81TEvt1Wsng8Q==", "dependencies": { - "@babel/runtime": "^7.21.0" + "@babel/runtime": "^7.22.6" }, "engines": { "node": ">=12.0.0" @@ -3814,16 +3924,16 @@ } }, "node_modules/@mui/material": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.13.2.tgz", - "integrity": "sha512-Pfke1l0GG2OJb/Nr10aVr8huoBFcBTdWKV5iFSTEHqf9c2C1ZlyYMISn7ui6X3Gix8vr+hP5kVqH1LAWwQSb6w==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.1.tgz", + "integrity": "sha512-WtsgYuageTunLfxH3Ri+o1RuQTFImtRHxMcVNyD0Hhd2/znjW6KODNz0XfjvLRnNCAynBxZNiflcoIBW40h9PQ==", "dependencies": { - "@babel/runtime": "^7.21.0", - "@mui/base": "5.0.0-beta.2", - "@mui/core-downloads-tracker": "^5.13.2", - "@mui/system": "^5.13.2", + "@babel/runtime": "^7.22.6", + "@mui/base": "5.0.0-beta.8", + "@mui/core-downloads-tracker": "^5.14.1", + "@mui/system": "^5.14.1", "@mui/types": "^7.2.4", - "@mui/utils": "^5.13.1", + "@mui/utils": "^5.14.1", "@types/react-transition-group": "^4.4.6", "clsx": "^1.2.1", "csstype": "^3.1.2", @@ -3858,12 +3968,12 @@ } }, "node_modules/@mui/private-theming": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.13.1.tgz", - "integrity": "sha512-HW4npLUD9BAkVppOUZHeO1FOKUJWAwbpy0VQoGe3McUYTlck1HezGHQCfBQ5S/Nszi7EViqiimECVl9xi+/WjQ==", + "version": "5.13.7", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.13.7.tgz", + "integrity": "sha512-qbSr+udcij5F9dKhGX7fEdx2drXchq7htLNr2Qg2Ma+WJ6q0ERlEqGSBiPiVDJkptcjeVL4DGmcf1wl5+vD4EA==", "dependencies": { - "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.13.1", + "@babel/runtime": "^7.22.5", + "@mui/utils": "^5.13.7", "prop-types": "^15.8.1" }, "engines": { @@ -3915,15 +4025,15 @@ } }, "node_modules/@mui/system": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.13.2.tgz", - "integrity": "sha512-TPyWmRJPt0JPVxacZISI4o070xEJ7ftxpVtu6LWuYVOUOINlhoGOclam4iV8PDT3EMQEHuUrwU49po34UdWLlw==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.1.tgz", + "integrity": "sha512-u+xlsU34Jdkgx1CxmBnIC4Y08uPdVX5iEd3S/1dggDFtOGp+Lj8xmKRJAQ8PJOOJLOh8pDwaZx4AwXikL4l1QA==", "dependencies": { - "@babel/runtime": "^7.21.0", - "@mui/private-theming": "^5.13.1", + "@babel/runtime": "^7.22.6", + "@mui/private-theming": "^5.13.7", "@mui/styled-engine": "^5.13.2", "@mui/types": "^7.2.4", - "@mui/utils": "^5.13.1", + "@mui/utils": "^5.14.1", "clsx": "^1.2.1", "csstype": "^3.1.2", "prop-types": "^15.8.1" @@ -3967,13 +4077,13 @@ } }, "node_modules/@mui/utils": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz", - "integrity": "sha512-6lXdWwmlUbEU2jUI8blw38Kt+3ly7xkmV9ljzY4Q20WhsJMWiNry9CX8M+TaP/HbtuyR8XKsdMgQW7h7MM3n3A==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.1.tgz", + "integrity": "sha512-39KHKK2JeqRmuUcLDLwM+c2XfVC136C5/yUyQXmO2PVbOb2Bol4KxtkssEqCbTwg87PSCG3f1Tb0keRsK7cVGw==", "dependencies": { - "@babel/runtime": "^7.21.0", + "@babel/runtime": "^7.22.6", "@types/prop-types": "^15.7.5", - "@types/react-is": "^18.2.0", + "@types/react-is": "^18.2.1", "prop-types": "^15.8.1", "react-is": "^18.2.0" }, @@ -4022,26 +4132,13 @@ "eslint-scope": "5.1.1" } }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/@nicolo-ribaudo/semver-v6": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz", + "integrity": "sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg==", "dev": true, - "engines": { - "node": ">=4.0" + "bin": { + "semver": "bin/semver.js" } }, "node_modules/@nodelib/fs.scandir": { @@ -4080,32 +4177,54 @@ } }, "node_modules/@popperjs/core": { - "version": "2.11.7", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz", - "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, "node_modules/@remix-run/router": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.2.tgz", - "integrity": "sha512-LzqpSrMK/3JBAVBI9u3NWtOhWNw5AMQfrUFYB0+bDHTSw17z++WJLsPsxAuK+oSddsxk4d7F/JcdDPM1M5YAhA==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.2.tgz", + "integrity": "sha512-7Lcn7IqGMV+vizMPoEl5F0XDshcdDYtMI6uJLQdQz5CfZAwy3vvGKYSUk789qndt5dEC4HfSjviSYlSoHGL2+A==", "engines": { "node": ">=14" } }, + "node_modules/@rollup/pluginutils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", + "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rushstack/eslint-patch": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.0.tgz", - "integrity": "sha512-IthPJsJR85GhOkp3Hvp8zFOPK5ynKn6STyHa/WZpioK7E1aYDiBzpqQPrngc14DszIUkIrdd3k9Iu0XSzlP/1w==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz", + "integrity": "sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw==", "dev": true }, "node_modules/@sinclair/typebox": { - "version": "0.25.24", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", - "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, "node_modules/@sinonjs/commons": { @@ -4126,233 +4245,289 @@ "@sinonjs/commons": "^1.7.0" } }, - "node_modules/@testing-library/dom": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.0.tgz", - "integrity": "sha512-Dffe68pGwI6WlLRYR2I0piIkyole9cSBH5jGQKCGMRpHW5RHCqAUaqc2Kv0tUyd4dU4DLPKhJIjyKOnjv4tuUw==", + "node_modules/@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", "dev": true, - "peer": true, "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "^5.0.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=14" + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" } }, - "node_modules/@testing-library/dom/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@svgr/babel-plugin-add-jsx-attribute": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-7.0.0.tgz", + "integrity": "sha512-khWbXesWIP9v8HuKCl2NU2HNAyqpSQ/vkIl36Nbn4HIwEYSRWL0H7Gs6idJdha2DkpFDWlsqMELvoCE8lfFY6Q==", "dev": true, - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=14" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@testing-library/dom/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@svgr/babel-plugin-remove-jsx-attribute": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-7.0.0.tgz", + "integrity": "sha512-iiZaIvb3H/c7d3TH2HBeK91uI2rMhZNwnsIrvd7ZwGLkFw6mmunOCoVnjdYua662MqGFxlN9xTq4fv9hgR4VXQ==", "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@testing-library/dom/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "peer": true, - "dependencies": { - "color-name": "~1.1.4" + "type": "github", + "url": "https://github.com/sponsors/gregberge" }, - "engines": { - "node": ">=7.0.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@testing-library/dom/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "peer": true - }, - "node_modules/@testing-library/dom/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-7.0.0.tgz", + "integrity": "sha512-sQQmyo+qegBx8DfFc04PFmIO1FP1MHI1/QEpzcIcclo5OAISsOJPW76ZIs0bDyO/DBSJEa/tDa1W26pVtt0FRw==", "dev": true, - "peer": true, "engines": { - "node": ">=8" + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@testing-library/dom/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-7.0.0.tgz", + "integrity": "sha512-i6MaAqIZXDOJeikJuzocByBf8zO+meLwfQ/qMHIjCcvpnfvWf82PFvredEZElErB5glQFJa2KVKk8N2xV6tRRA==", "dev": true, - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@testing-library/jest-dom": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", - "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "node_modules/@svgr/babel-plugin-svg-dynamic-title": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-7.0.0.tgz", + "integrity": "sha512-BoVSh6ge3SLLpKC0pmmN9DFlqgFy4NxNgdZNLPNJWBUU7TQpDWeBuyVuDW88iXydb5Cv0ReC+ffa5h3VrKfk1w==", "dev": true, - "dependencies": { - "@adobe/css-tools": "^4.0.1", - "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.5.6", - "lodash": "^4.17.15", - "redent": "^3.0.0" - }, "engines": { - "node": ">=8", - "npm": ">=6", - "yarn": ">=1" + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@testing-library/jest-dom/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@svgr/babel-plugin-svg-em-dimensions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-7.0.0.tgz", + "integrity": "sha512-tNDcBa+hYn0gO+GkP/AuNKdVtMufVhU9fdzu+vUQsR18RIJ9RWe7h/pSBY338RO08wArntwbDk5WhQBmhf2PaA==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=14" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "node_modules/@svgr/babel-plugin-transform-react-native-svg": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-7.0.0.tgz", + "integrity": "sha512-qw54u8ljCJYL2KtBOjI5z7Nzg8LnSvQOP5hPKj77H4VQL4+HdKbAT5pnkkZLmHKYwzsIHSYKXxHouD8zZamCFQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-plugin-transform-svg-component": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-7.0.0.tgz", + "integrity": "sha512-CcFECkDj98daOg9jE3Bh3uyD9kzevCAnZ+UtzG6+BQG/jOQ2OA3jHnX6iG4G1MCJkUQFnUvEv33NvQfqrb/F3A==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@svgr/babel-preset": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-7.0.0.tgz", + "integrity": "sha512-EX/NHeFa30j5UjldQGVQikuuQNHUdGmbh9kEpBKofGUtF0GUPJ4T4rhoYiqDAOmBOxojyot36JIFiDUHUK1ilQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@svgr/babel-plugin-add-jsx-attribute": "^7.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "^7.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "^7.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "^7.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "^7.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "^7.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "^7.0.0", + "@svgr/babel-plugin-transform-svg-component": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@testing-library/jest-dom/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@svgr/core": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-7.0.0.tgz", + "integrity": "sha512-ztAoxkaKhRVloa3XydohgQQCb0/8x9T63yXovpmHzKMkHO6pkjdsIAWKOS4bE95P/2quVh1NtjSKlMRNzSBffw==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "^7.0.0", + "camelcase": "^6.2.0", + "cosmiconfig": "^8.1.3" }, "engines": { - "node": ">=7.0.0" + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@testing-library/jest-dom/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/@svgr/core/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/@testing-library/jest-dom/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/@svgr/core/node_modules/cosmiconfig": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", + "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", "dev": true, + "dependencies": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + }, "engines": { - "node": ">=8" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" } }, - "node_modules/@testing-library/jest-dom/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@svgr/core/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@svgr/hast-util-to-babel-ast": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-7.0.0.tgz", + "integrity": "sha512-42Ej9sDDEmsJKjrfQ1PHmiDiHagh/u9AHO9QWbeNx4KmD9yS5d1XHmXUNINfUcykAU+4431Cn+k6Vn5mWBYimQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.3", + "entities": "^4.4.0" }, "engines": { - "node": ">=8" + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@testing-library/react": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", - "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", + "node_modules/@svgr/plugin-jsx": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-7.0.0.tgz", + "integrity": "sha512-SWlTpPQmBUtLKxXWgpv8syzqIU8XgFRvyhfkam2So8b3BE0OS0HPe5UfmlJ2KIC+a7dpuuYovPR2WAQuSyMoPw==", "dev": true, "dependencies": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.5.0", - "@types/react-dom": "^18.0.0" + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "^7.0.0", + "@svgr/hast-util-to-babel-ast": "^7.0.0", + "svg-parser": "^2.0.4" }, "engines": { - "node": ">=12" + "node": ">=14" }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" } }, - "node_modules/@testing-library/react/node_modules/@testing-library/dom": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz", - "integrity": "sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA==", + "node_modules/@testing-library/dom": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.1.tgz", + "integrity": "sha512-0DGPd9AR3+iDTjGoMpxIkAsUihHZ3Ai6CneU6bRRrffXMgzCdlNk43jTrD2/5LT6CBb3MWTP8v510JzYtahD2w==", "dev": true, + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", - "aria-query": "^5.0.0", + "aria-query": "5.1.3", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.4.4", + "lz-string": "^1.5.0", "pretty-format": "^27.0.2" }, "engines": { - "node": ">=12" + "node": ">=14" } }, - "node_modules/@testing-library/react/node_modules/ansi-styles": { + "node_modules/@testing-library/dom/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -4363,11 +4538,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@testing-library/react/node_modules/chalk": { + "node_modules/@testing-library/dom/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4379,11 +4555,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@testing-library/react/node_modules/color-convert": { + "node_modules/@testing-library/dom/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -4391,26 +4568,29 @@ "node": ">=7.0.0" } }, - "node_modules/@testing-library/react/node_modules/color-name": { + "node_modules/@testing-library/dom/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "peer": true }, - "node_modules/@testing-library/react/node_modules/has-flag": { + "node_modules/@testing-library/dom/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "peer": true, "engines": { "node": ">=8" } }, - "node_modules/@testing-library/react/node_modules/supports-color": { + "node_modules/@testing-library/dom/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -4418,105 +4598,278 @@ "node": ">=8" } }, - "node_modules/@testing-library/user-event": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", - "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "node_modules/@testing-library/jest-dom": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", + "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", "dev": true, "dependencies": { - "@babel/runtime": "^7.12.5" + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" }, "engines": { - "node": ">=10", - "npm": ">=6" - }, - "peerDependencies": { - "@testing-library/dom": ">=7.21.4" + "node": ">=8", + "npm": ">=6", + "yarn": ">=1" } }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "node_modules/@testing-library/jest-dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">= 6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@types/aria-query": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", - "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", - "dev": true - }, - "node_modules/@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", "dev": true, "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "node_modules/@testing-library/jest-dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "@babel/types": "^7.0.0" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "node_modules/@testing-library/jest-dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@testing-library/jest-dom/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/babel__traverse": { - "version": "7.18.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.5.tgz", - "integrity": "sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q==", + "node_modules/@testing-library/react": { + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", + "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", "dev": true, "dependencies": { - "@babel/types": "^7.3.0" + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, - "node_modules/@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "node_modules/@testing-library/react/node_modules/@testing-library/dom": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", + "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", "dev": true, - "optional": true, - "peer": true, "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "node_modules/@testing-library/react/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "optional": true, - "peer": true, "dependencies": { - "@types/node": "*" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/react/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/react/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/react/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@testing-library/react/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/user-event": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", + "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", + "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "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.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" } }, "node_modules/@types/eslint": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.0.tgz", - "integrity": "sha512-nbq2mvc/tBrK9zQQuItvjJl++GTN5j06DaPtp3hZCpngmG6Q3xoyEmd0TwZI0gAy/G1X0zhGBbr2imsGFdFV0g==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.0.tgz", + "integrity": "sha512-gsF+c/0XOguWgaOgvFs+xnnRqt9GwgTvIks36WpE6ueeI4KCEHHd8K/CKHqhOqrJKsYH8m27kRzQEvWXAwXUTw==", "dev": true, "peer": true, "dependencies": { @@ -4541,34 +4894,6 @@ "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", "dev": true }, - "node_modules/@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, "node_modules/@types/graceful-fs": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", @@ -4612,9 +4937,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.1", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.1.tgz", - "integrity": "sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==", + "version": "29.5.3", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.3.tgz", + "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -4634,12 +4959,12 @@ } }, "node_modules/@types/jest/node_modules/pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.1.tgz", + "integrity": "sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==", "dev": true, "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.0", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -4648,9 +4973,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, "node_modules/@types/json5": { @@ -4659,18 +4984,10 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, - "node_modules/@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/@types/node": { - "version": "20.2.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.3.tgz", - "integrity": "sha512-pg9d0yC4rVNWQzX8U7xb4olIOFuuVL9za3bzMT2pu2SU0SNEi66i2qrvhE2qt0HvkhuCaWJu7pLNOt/Pj8BIrw==", + "version": "20.4.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", + "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==", "dev": true }, "node_modules/@types/parse-json": { @@ -4679,9 +4996,9 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" }, "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "dev": true }, "node_modules/@types/prop-types": { @@ -4689,26 +5006,10 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, - "node_modules/@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true, - "optional": true, - "peer": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/@types/react": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.7.tgz", - "integrity": "sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==", + "version": "18.2.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.15.tgz", + "integrity": "sha512-oEjE7TQt1fFTFSbf8kkNuc798ahTUzn3Le67/PWjE8MAfYAD/qB7O8hSTcromLFqHCt9bcdOg5GXMokzTjJ5SA==", "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -4716,18 +5017,18 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz", - "integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==", + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", + "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", "dev": true, "dependencies": { "@types/react": "*" } }, "node_modules/@types/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-1vz2yObaQkLL7YFe/pme2cpvDsCwI1WXIfL+5eLz0MI9gFG24Re16RzUsI8t9XZn9ZWvgLNDrJBmrqXJO7GNQQ==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.1.tgz", + "integrity": "sha512-wyUkmaaSZEzFZivD8F2ftSyAfk6L+DfFliVj/mYdOXbVjRcS87fQJLTnhk6dRZPuJjI+9g6RZJO4PNCngUrmyw==", "dependencies": { "@types/react": "*" } @@ -4740,6 +5041,15 @@ "@types/react": "*" } }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", @@ -4751,30 +5061,6 @@ "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, - "node_modules/@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", - "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", - "dev": true, - "optional": true, - "peer": true, - "dependencies": { - "@types/mime": "*", - "@types/node": "*" - } - }, "node_modules/@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -4782,14 +5068,20 @@ "dev": true }, "node_modules/@types/testing-library__jest-dom": { - "version": "5.14.6", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.6.tgz", - "integrity": "sha512-FkHXCb+ikSoUP4Y4rOslzTdX5sqYwMxfefKh1GmZ8ce1GOkEHntSp6b5cGadmNfp5e4BMEWOMx+WSKd5/MqlDA==", + "version": "5.14.8", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.8.tgz", + "integrity": "sha512-NRfJE9Cgpmu4fx716q9SYmU4jxxhYRU1BQo239Txt/9N3EC745XZX1Yl7h/SBIDlo1ANVOCRB4YDXjaQdoKCHQ==", "dev": true, "dependencies": { "@types/jest": "*" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", + "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==", + "dev": true + }, "node_modules/@types/yargs": { "version": "16.0.5", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", @@ -4806,17 +5098,17 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.0.tgz", - "integrity": "sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@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.60.0", - "@typescript-eslint/type-utils": "5.60.0", - "@typescript-eslint/utils": "5.60.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", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", "semver": "^7.3.7", @@ -4839,14 +5131,13 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz", - "integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==", + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", + "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0" + "@typescript-eslint/utils": "5.62.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4854,41 +5145,31 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", - "integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==", + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@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" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz", - "integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -4896,78 +5177,14 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.0.tgz", - "integrity": "sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==", - "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.60.0", - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/typescript-estree": "5.60.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", - "integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.60.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.59.7.tgz", - "integrity": "sha512-jqM0Cjfvta/sBlY1MxdXYv853/dJUC2wmUWnKoG2srwp0njNGQ6Zu/XLWoRFiLvocQbzBbpHkPFwKgC2UqyovA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "5.59.7" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4975,21 +5192,18 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.0.tgz", - "integrity": "sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==", + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/typescript-estree": "5.60.0", - "debug": "^4.3.4" + "@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" @@ -4999,7 +5213,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "*" }, "peerDependenciesMeta": { "typescript": { @@ -5007,27 +5221,10 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz", - "integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", - "integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==", + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@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" @@ -5037,14 +5234,14 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz", - "integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0", + "@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", @@ -5064,14 +5261,20 @@ } } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", - "integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==", + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.60.0", - "eslint-visitor-keys": "^3.3.0" + "@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" @@ -5079,16 +5282,19 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz", - "integrity": "sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/visitor-keys": "5.59.7" + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -5098,264 +5304,323 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.0.tgz", - "integrity": "sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "5.60.0", - "@typescript-eslint/utils": "5.60.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, + "node_modules/@uiw/color-convert": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/color-convert/-/color-convert-1.3.3.tgz", + "integrity": "sha512-lCnDyNnHEYY1flsjSBkTodrEUG/YemWrzSwr7r7h8ZZdjk0RRMdIwbgMerOEODYQDh8vtoaorbJncshJ2Z2CSQ==", "peerDependencies": { - "eslint": "*" + "@babel/runtime": ">=7.19.0" + } + }, + "node_modules/@uiw/react-color": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color/-/react-color-1.3.3.tgz", + "integrity": "sha512-/Ny6RzAc0X3kp2EoRa0N70ZizgXifYsmFzi1DxQdM+WeHil6Nk5i2S7wrsXonvdyP1V4OrPNNh2YE7WQtQ1Jcg==", + "dependencies": { + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-alpha": "1.3.3", + "@uiw/react-color-block": "1.3.3", + "@uiw/react-color-chrome": "1.3.3", + "@uiw/react-color-circle": "1.3.3", + "@uiw/react-color-colorful": "1.3.3", + "@uiw/react-color-compact": "1.3.3", + "@uiw/react-color-editable-input": "1.3.3", + "@uiw/react-color-editable-input-hsla": "1.3.3", + "@uiw/react-color-editable-input-rgba": "1.3.3", + "@uiw/react-color-github": "1.3.3", + "@uiw/react-color-hue": "1.3.3", + "@uiw/react-color-material": "1.3.3", + "@uiw/react-color-saturation": "1.3.3", + "@uiw/react-color-shade-slider": "1.3.3", + "@uiw/react-color-sketch": "1.3.3", + "@uiw/react-color-slider": "1.3.3", + "@uiw/react-color-swatch": "1.3.3", + "@uiw/react-color-wheel": "1.3.3" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz", - "integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==", - "dev": true, + "node_modules/@uiw/react-color-alpha": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-alpha/-/react-color-alpha-1.3.3.tgz", + "integrity": "sha512-GmK/Dyj5TlLzt5Epcwry+9K4BPsqyPPhV09rsKKyiVa+/4XHRQRj97fjJ0GHgae6+71BPoIC9ulAR/VnmK3lrg==", "dependencies": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "@uiw/color-convert": "1.3.3", + "@uiw/react-drag-event-interactive": "1.3.3" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", - "integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node_modules/@uiw/react-color-block": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-block/-/react-color-block-1.3.3.tgz", + "integrity": "sha512-6fSCNzmXwSjaiRe3GfOuU+rMl1NcAwPLPz/+4eYwW1QrH/fl2JYbyQ+8cDFpsnnGCxk7fTwlvIg9pYC2Slu11A==", + "dependencies": { + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-editable-input": "1.3.3", + "@uiw/react-color-swatch": "1.3.3" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz", - "integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==", - "dev": true, + "node_modules/@uiw/react-color-chrome": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-chrome/-/react-color-chrome-1.3.3.tgz", + "integrity": "sha512-M/+svpgxmOVO/S/Nh0i/9ZWa+ATr+5DSYXGl5Ki/YvviyvBW4pHRmjXkOuL6j1U82xrxnOgxJVy2KJqVibMFPg==", "dependencies": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-alpha": "1.3.3", + "@uiw/react-color-editable-input": "1.3.3", + "@uiw/react-color-editable-input-hsla": "1.3.3", + "@uiw/react-color-editable-input-rgba": "1.3.3", + "@uiw/react-color-github": "1.3.3", + "@uiw/react-color-hue": "1.3.3", + "@uiw/react-color-saturation": "1.3.3" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.0.tgz", - "integrity": "sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==", - "dev": true, + "node_modules/@uiw/react-color-circle": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-circle/-/react-color-circle-1.3.3.tgz", + "integrity": "sha512-VX/vszuJqaZ8F/fYkYL8i+vqbm36PYy6Tbx2R1f/nIol4068h21hCHXfa+6Vjd0doBV3qBuuwIDAeROiyNSBtQ==", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/typescript-estree": "5.60.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-swatch": "1.3.3" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", - "integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==", - "dev": true, + "node_modules/@uiw/react-color-colorful": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-colorful/-/react-color-colorful-1.3.3.tgz", + "integrity": "sha512-ib13E672x0NAKW10ygyMqJDan35Rb0dGv/QxVgB8ch/52Ho2D4U7C4tl0fgAxDbapAUW1ZyOssngdjnwUExQhQ==", "dependencies": { - "@typescript-eslint/types": "5.60.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-alpha": "1.3.3", + "@uiw/react-color-hue": "1.3.3", + "@uiw/react-color-saturation": "1.3.3" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, + "node_modules/@uiw/react-color-compact": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-compact/-/react-color-compact-1.3.3.tgz", + "integrity": "sha512-EDgcB0ENc0jrkZQHxJh1mBuNGaY/0aP4QxOgLrpHIJY7wLP/h5tl0sDUJdewJH559fGnvUHcjcX7HfyjeGnXhw==", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-editable-input": "1.3.3", + "@uiw/react-color-editable-input-rgba": "1.3.3", + "@uiw/react-color-swatch": "1.3.3" }, - "engines": { - "node": ">=8.0.0" + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" + "node_modules/@uiw/react-color-editable-input": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input/-/react-color-editable-input-1.3.3.tgz", + "integrity": "sha512-n7RrVxpIDBueqCfwpx1zaKTqdk5DWTKWNii1NErM7VlLa2PSA2zZrdMeB8JkhwQAgCPKvEfv2f67tO5wkY/uTg==", + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/@typescript-eslint/types": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.7.tgz", - "integrity": "sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node_modules/@uiw/react-color-editable-input-hsla": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-hsla/-/react-color-editable-input-hsla-1.3.3.tgz", + "integrity": "sha512-9bvzRLMZudIcGjs6R2K5M9W1Hj23gMuiZFKErneJ+QqxrFtZ2da/jMyfFUAfLA9fI4c6EaqKsKYuEKTu62jW8w==", + "dependencies": { + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-editable-input-rgba": "1.3.3" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz", - "integrity": "sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==", - "dev": true, + "node_modules/@uiw/react-color-editable-input-rgba": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-rgba/-/react-color-editable-input-rgba-1.3.3.tgz", + "integrity": "sha512-FtbBLAQHHaNHM0ksagi6wCYV9rNjVI66cVHmzfMFtUm7XlXROSD5WTZsNJShY7o5yYcwWPTxACnKe7IXCdK4Eg==", "dependencies": { - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/visitor-keys": "5.59.7", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-editable-input": "1.3.3" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/@typescript-eslint/utils": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.7.tgz", - "integrity": "sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==", - "dev": true, + "node_modules/@uiw/react-color-github": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-github/-/react-color-github-1.3.3.tgz", + "integrity": "sha512-D6qc594DUg6ZZ1Ap2pDrxS/WtAwB3f4s3qFWP1T/Ylfpuoq0OuR3xJJzi7Y73h1tdLuQ598g86kwU0w298tHqw==", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.7", - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/typescript-estree": "5.59.7", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-swatch": "1.3.3" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, + "node_modules/@uiw/react-color-hue": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-hue/-/react-color-hue-1.3.3.tgz", + "integrity": "sha512-TywdH89nxRG3pVmUTOzyEzg6nI0EI8VSA6Vh0Uh+gGZZQjgDM1ptCa7V5KyeKEfWCvaeo3RBAHPijFOKQrHqow==", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-alpha": "1.3.3" }, - "engines": { - "node": ">=8.0.0" + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" + "node_modules/@uiw/react-color-material": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-material/-/react-color-material-1.3.3.tgz", + "integrity": "sha512-J9EmqykMl3WQ6xKfkbQhuVBmfmPEexIVp+yys0+I7iVk8EbwOqLlin5X3Zc3aLFEtKSOyGjPVwJQlHzMwgypdw==", + "dependencies": { + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-editable-input": "1.3.3", + "@uiw/react-color-editable-input-rgba": "1.3.3" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz", - "integrity": "sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==", - "dev": true, + "node_modules/@uiw/react-color-saturation": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-saturation/-/react-color-saturation-1.3.3.tgz", + "integrity": "sha512-gQNPaYgGbvE0Hq4pJjUNOfYuYFSV9QSWwPC23l9R/0WKdHci3ObHk8rycAkg9C6muV1ttBicqt9zqDVSwRHPrw==", "dependencies": { - "@typescript-eslint/types": "5.59.7", - "eslint-visitor-keys": "^3.3.0" + "@uiw/color-convert": "1.3.3", + "@uiw/react-drag-event-interactive": "1.3.3" }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-shade-slider": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-shade-slider/-/react-color-shade-slider-1.3.3.tgz", + "integrity": "sha512-LjPPmuqeSCEVAeThZZ6hVuYhawXc5yUC1/UCjrwoiH3DrBckTf5Xf4gL7QlQO58iSsRgi2yK0rQy8jhnhxvldw==", + "dependencies": { + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-alpha": "1.3.3" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-sketch": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-sketch/-/react-color-sketch-1.3.3.tgz", + "integrity": "sha512-CEGiYs22KRN66YrHxJZNe1xqS7RQF0XUiP8nYnvHwOxtycoPssgyX8KN8rpjFtBcHA+Y4hkN2i7cq9ApL6Vrsg==", + "dependencies": { + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-alpha": "1.3.3", + "@uiw/react-color-editable-input": "1.3.3", + "@uiw/react-color-editable-input-rgba": "1.3.3", + "@uiw/react-color-hue": "1.3.3", + "@uiw/react-color-saturation": "1.3.3", + "@uiw/react-color-swatch": "1.3.3" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-slider": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-slider/-/react-color-slider-1.3.3.tgz", + "integrity": "sha512-bfKYFpz1KrLkdeNaaUNu+cRW7OpLX0PzCInvpIUkGVgUfwcinm+pziI1DpQYTys06sqXBsEMviRu1C/O+C54EQ==", + "dependencies": { + "@uiw/color-convert": "1.3.3", + "@uiw/react-color-alpha": "1.3.3" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-swatch": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-swatch/-/react-color-swatch-1.3.3.tgz", + "integrity": "sha512-4RPHZO4mNmCLhbGoeCltauuGJSrSu15aR9mmUxa4gn8VdlnlxmWCG+Kq0jRQAN/pgvWuHbItYVR6a96TAq1UPA==", + "dependencies": { + "@uiw/color-convert": "1.3.3" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-color-wheel": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-color-wheel/-/react-color-wheel-1.3.3.tgz", + "integrity": "sha512-tKHR5S8qNqzgs+vZ1EbMFDQkuuZ7GFd3JVcdwyEbajwz5sUsowLD/+ykJ3C1VNOC1pFKl0vgFdNzUp4yuHHJPQ==", + "dependencies": { + "@uiw/color-convert": "1.3.3", + "@uiw/react-drag-event-interactive": "1.3.3" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@uiw/react-drag-event-interactive": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@uiw/react-drag-event-interactive/-/react-drag-event-interactive-1.3.3.tgz", + "integrity": "sha512-dHHr19i4sa21bTFAd5xufsSYGCDqLBttIRmfQSzIpI18NtjmxKVCGyPL0ysxinWBjFomTUczszY4+fTOXnq1jQ==", + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, "node_modules/@vitejs/plugin-react": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.1.tgz", - "integrity": "sha512-g25lL98essfeSj43HJ0o4DMp0325XK0ITkxpgChzJU/CyemgyChtlxfnRbjfwxDGCTRxTiXtQAsdebQXKMRSOA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.3.tgz", + "integrity": "sha512-pwXDog5nwwvSIzwrvYYmA2Ljcd/ZNlcsSG2Q9CNDBwnsd55UGAyr2doXtB5j+2uymRCnCfExlznzzSFbBRcoCg==", "dev": true, "dependencies": { "@babel/core": "^7.22.5", @@ -5552,9 +5817,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -5650,6 +5915,45 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -5707,6 +6011,15 @@ "node": ">= 8" } }, + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "dev": true, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -5815,12 +6128,38 @@ "get-intrinsic": "^1.1.3" } }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", + "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", "dev": true }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -5848,9 +6187,9 @@ } }, "node_modules/axe-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.1.tgz", - "integrity": "sha512-sCXXUhA+cljomZ3ZAwb8i1p3oOlkABzPy08ZDAoGcYuvtBPlQ1Ytde129ArXyHWDhfeewq7rlx9F+cUx2SSlkg==", + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.2.tgz", + "integrity": "sha512-zIURGIS1E1Q4pcrMjp+nnEh+16G56eG/MUllJH8yEvw7asDo7Ac9uhC9KIH5jzpITueEZolfYglnCGIuSBz39g==", "dev": true, "engines": { "node": ">=4" @@ -5867,12 +6206,12 @@ } }, "node_modules/axobject-query": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", - "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", + "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", "dev": true, "dependencies": { - "deep-equal": "^2.0.5" + "dequal": "^2.0.3" } }, "node_modules/babel-jest": { @@ -6013,48 +6352,39 @@ } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.4.tgz", + "integrity": "sha512-9WeK9snM1BfxB38goUEv2FLnA6ja07UMfazFHzCXUb3NyDZAwfXvQiURQ6guTTMeHcOsdknULm1PDhs4uWtKyA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.4.1", + "@nicolo-ribaudo/semver-v6": "^6.3.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.2.tgz", + "integrity": "sha512-Cid+Jv1BrY9ReW9lIfNlNpsI53N+FN7gE+f73zLAUbr9C52W4gKLWSByx47pfDJsEysojKArqOtOKZSVIIUTuQ==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" + "@babel/helper-define-polyfill-provider": "^0.4.1", + "core-js-compat": "^3.31.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.1.tgz", + "integrity": "sha512-L8OyySuI6OSQ5hFy9O+7zFjyr4WhAfRjLIOkhQGYl+emwJkd/S4XXT1JpfrgR1jrQ1NcGiOh+yAdGlF8pnC3Jw==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3" + "@babel/helper-define-polyfill-provider": "^0.4.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" @@ -6192,9 +6522,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "funding": [ { @@ -6204,13 +6534,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" @@ -6234,19 +6568,16 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/bufferutil": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", - "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", "dev": true, - "hasInstallScript": true, - "optional": true, - "peer": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, "engines": { - "node": ">=6.14.2" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/call-bind": { @@ -6282,9 +6613,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001489", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz", - "integrity": "sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==", + "version": "1.0.30001517", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz", + "integrity": "sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==", "dev": true, "funding": [ { @@ -6412,9 +6743,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, "node_modules/cliui": { @@ -6447,9 +6778,9 @@ } }, "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, "node_modules/color-convert": { @@ -6476,6 +6807,149 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/compress-create-react-app": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/compress-create-react-app/-/compress-create-react-app-1.4.2.tgz", + "integrity": "sha512-Ss40UBgYrVy90uJ4FPy3rIqdM3r2Gktfr4sw9mtUylmaFoiJpQotbi7d7tdhkL6IvZtovVePV1Q65vE8y88P5A==", + "dev": true, + "dependencies": { + "app-root-path": "^3.1.0", + "yargs": "^17.5.1" + }, + "bin": { + "compress-cra": "index.js", + "compress-create-react-app": "index.js" + } + }, + "node_modules/compress-create-react-app/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/compress-create-react-app/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/compress-create-react-app/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/compression-webpack-plugin": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/compression-webpack-plugin/-/compression-webpack-plugin-10.0.0.tgz", + "integrity": "sha512-wLXLIBwpul/ALcm7Aj+69X0pYT3BYt6DdPn3qrgBIh9YejV9Bju9ShhlAsjujLyWMo6SAweFIWaUoFmXZNuNrg==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/compression-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/compression-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/compression-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/compression-webpack-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -6494,12 +6968,12 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, "node_modules/core-js-compat": { - "version": "3.30.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.2.tgz", - "integrity": "sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA==", + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.1.tgz", + "integrity": "sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==", "dev": true, "dependencies": { - "browserslist": "^4.21.5" + "browserslist": "^4.21.9" }, "funding": { "type": "opencollective", @@ -6535,6 +7009,15 @@ "node": ">= 8" } }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", @@ -6721,15 +7204,15 @@ "dev": true }, "node_modules/deep-equal": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz", - "integrity": "sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.2.tgz", + "integrity": "sha512-xjVyBf0w5vH0I42jdAZzOKVldmPgSulmiyPRywoyq7HXC9qdgo17kxJE+rdnif5Tz6+pIrpJI8dCpMNLIGkUiA==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.2", "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.0", + "get-intrinsic": "^1.2.1", "is-arguments": "^1.1.1", "is-array-buffer": "^3.0.2", "is-date-object": "^1.0.5", @@ -6797,6 +7280,15 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -6928,10 +7420,25 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, + "node_modules/ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { - "version": "1.4.407", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.407.tgz", - "integrity": "sha512-5smEvFSFYMv90tICOzRVP7Opp98DAC4KW7RRipg3BuNpGbbV3N+x24Zh3sbLb1T5haGtOSy/hrBfXsWnIM9aCg==", + "version": "1.4.464", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.464.tgz", + "integrity": "sha512-guZ84yoou4+ILNdj0XEbmGs6DEWj6zpVOWYpY09GU66yEb0DSYvP/biBPzHn0GuW/3RC/pnaYNUWlQE1fJYtgA==", "dev": true }, "node_modules/emittery": { @@ -6953,9 +7460,9 @@ "dev": true }, "node_modules/enhanced-resolve": { - "version": "5.14.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz", - "integrity": "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==", + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", "dev": true, "peer": true, "dependencies": { @@ -6966,6 +7473,16 @@ "node": ">=10.13.0" } }, + "node_modules/enhanced-resolve/node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -6987,18 +7504,19 @@ } }, "node_modules/es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", + "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.1", "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", + "get-intrinsic": "^1.2.1", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", @@ -7018,14 +7536,18 @@ "object-inspect": "^1.12.3", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", + "regexp.prototype.flags": "^1.5.0", + "safe-array-concat": "^1.0.0", "safe-regex-test": "^1.0.0", "string.prototype.trim": "^1.2.7", "string.prototype.trimend": "^1.0.6", "string.prototype.trimstart": "^1.0.6", + "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.9" + "which-typed-array": "^1.1.10" }, "engines": { "node": ">= 0.4" @@ -7055,9 +7577,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", - "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", + "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", "dev": true, "peer": true }, @@ -7102,9 +7624,9 @@ } }, "node_modules/esbuild": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", - "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "version": "0.18.14", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.14.tgz", + "integrity": "sha512-uNPj5oHPYmj+ZhSQeYQVFZ+hAlJZbAGOmmILWIqrGvPVlNLbyOvU5Bu6Woi8G8nskcx0vwY0iFoMPrzT86Ko+w==", "dev": true, "hasInstallScript": true, "bin": { @@ -7114,28 +7636,28 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.17.19", - "@esbuild/android-arm64": "0.17.19", - "@esbuild/android-x64": "0.17.19", - "@esbuild/darwin-arm64": "0.17.19", - "@esbuild/darwin-x64": "0.17.19", - "@esbuild/freebsd-arm64": "0.17.19", - "@esbuild/freebsd-x64": "0.17.19", - "@esbuild/linux-arm": "0.17.19", - "@esbuild/linux-arm64": "0.17.19", - "@esbuild/linux-ia32": "0.17.19", - "@esbuild/linux-loong64": "0.17.19", - "@esbuild/linux-mips64el": "0.17.19", - "@esbuild/linux-ppc64": "0.17.19", - "@esbuild/linux-riscv64": "0.17.19", - "@esbuild/linux-s390x": "0.17.19", - "@esbuild/linux-x64": "0.17.19", - "@esbuild/netbsd-x64": "0.17.19", - "@esbuild/openbsd-x64": "0.17.19", - "@esbuild/sunos-x64": "0.17.19", - "@esbuild/win32-arm64": "0.17.19", - "@esbuild/win32-ia32": "0.17.19", - "@esbuild/win32-x64": "0.17.19" + "@esbuild/android-arm": "0.18.14", + "@esbuild/android-arm64": "0.18.14", + "@esbuild/android-x64": "0.18.14", + "@esbuild/darwin-arm64": "0.18.14", + "@esbuild/darwin-x64": "0.18.14", + "@esbuild/freebsd-arm64": "0.18.14", + "@esbuild/freebsd-x64": "0.18.14", + "@esbuild/linux-arm": "0.18.14", + "@esbuild/linux-arm64": "0.18.14", + "@esbuild/linux-ia32": "0.18.14", + "@esbuild/linux-loong64": "0.18.14", + "@esbuild/linux-mips64el": "0.18.14", + "@esbuild/linux-ppc64": "0.18.14", + "@esbuild/linux-riscv64": "0.18.14", + "@esbuild/linux-s390x": "0.18.14", + "@esbuild/linux-x64": "0.18.14", + "@esbuild/netbsd-x64": "0.18.14", + "@esbuild/openbsd-x64": "0.18.14", + "@esbuild/sunos-x64": "0.18.14", + "@esbuild/win32-arm64": "0.18.14", + "@esbuild/win32-ia32": "0.18.14", + "@esbuild/win32-x64": "0.18.14" } }, "node_modules/escalade": { @@ -7159,15 +7681,14 @@ } }, "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" + "esutils": "^2.0.2" }, "bin": { "escodegen": "bin/escodegen.js", @@ -7180,77 +7701,26 @@ "source-map": "~0.6.1" } }, - "node_modules/escodegen/node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true, + "optional": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/escodegen/node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/eslint": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", - "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", + "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.43.0", + "@eslint/eslintrc": "^2.1.0", + "@eslint/js": "8.44.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -7262,7 +7732,7 @@ "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.0", "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "espree": "^9.6.0", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -7272,7 +7742,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -7282,9 +7751,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -7338,9 +7806,9 @@ } }, "node_modules/eslint-config-airbnb-base/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -7489,9 +7957,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -7552,9 +8020,9 @@ } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -7631,9 +8099,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -7656,19 +8124,25 @@ } }, "node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "estraverse": "^4.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=8.0.0" + } + }, + "node_modules/eslint-scope/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" } }, "node_modules/eslint-visitor-keys": { @@ -7738,6 +8212,38 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.1.tgz", + "integrity": "sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/globals": { "version": "13.20.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", @@ -7774,6 +8280,51 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -7799,12 +8350,12 @@ } }, "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -7861,6 +8412,12 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -7919,28 +8476,29 @@ } }, "node_modules/expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.1.tgz", + "integrity": "sha512-XEdDLonERCU1n9uR56/Stx9OqojaLAQtZf9PrCHH9Hl8YXiEIka3H4NXJ3NOIBmQJTg7+j7buh34PMHfJujc8g==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.5.0", + "@jest/expect-utils": "^29.6.1", + "@types/node": "*", "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0" + "jest-matcher-utils": "^29.6.1", + "jest-message-util": "^29.6.1", + "jest-util": "^29.6.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/expect/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz", + "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.0", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -8019,12 +8577,12 @@ } }, "node_modules/expect/node_modules/jest-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", - "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.1.tgz", + "integrity": "sha512-NRFCcjc+/uO3ijUVyNOQJluf8PtGCe/W6cix36+M3cTFgiYqFOOW5MgN4JOOcvbUhcKTYVd1CvHz/LWi8d16Mg==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.1", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -8054,9 +8612,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -8123,6 +8681,36 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/filesize": { "version": "8.0.7", "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", @@ -8150,19 +8738,16 @@ "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" }, "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "locate-path": "^6.0.0", + "locate-path": "^5.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/flat-cache": { @@ -8340,24 +8925,6 @@ "node": ">=8" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - }, - "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -8370,15 +8937,6 @@ "node": ">=8" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -8406,9 +8964,9 @@ } }, "node_modules/fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.4.tgz", + "integrity": "sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==", "dev": true }, "node_modules/fs.realpath": { @@ -8496,6 +9054,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -8677,12 +9241,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -8896,6 +9454,24 @@ "node": ">=10.17.0" } }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "dev": true + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -8909,7 +9485,7 @@ "version": "9.0.21", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "dev": true, + "devOptional": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -9148,6 +9724,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-generator-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", @@ -9178,6 +9763,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -9214,6 +9805,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -9257,6 +9857,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-root": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", @@ -9330,16 +9939,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.11" }, "engines": { "node": ">= 0.4" @@ -9438,9 +10043,9 @@ } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -9517,34 +10122,122 @@ "node": ">=8" } }, - "node_modules/jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", "dev": true, "dependencies": { - "@jest/core": "^27.5.1", - "import-local": "^3.0.2", - "jest-cli": "^27.5.1" + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" }, "bin": { - "jest": "bin/jest.js" + "jake": "bin/cli.js" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=10" } }, - "node_modules/jest-changed-files": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jake/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", + "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", + "dev": true, + "dependencies": { + "@jest/core": "^27.5.1", + "import-local": "^3.0.2", + "jest-cli": "^27.5.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", "dev": true, "dependencies": { @@ -9966,15 +10659,15 @@ } }, "node_modules/jest-diff": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", - "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.1.tgz", + "integrity": "sha512-FsNCvinvl8oVxpNLttNQX7FAq7vR+gMDGj90tiP7siWw1UdakWUGqrylpsYrpvj908IYckm5Y0Q7azNAozU1Kg==", "dev": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.4.3", "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "pretty-format": "^29.6.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -10039,12 +10732,12 @@ } }, "node_modules/jest-diff/node_modules/pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.1.tgz", + "integrity": "sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==", "dev": true, "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.0", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -10457,15 +11150,15 @@ } }, "node_modules/jest-matcher-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", - "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.1.tgz", + "integrity": "sha512-SLaztw9d2mfQQKHmJXKM0HCbl2PPVld/t9Xa6P9sgiExijviSp7TnZZpw2Fpt+OI3nwUO/slJbOfzfUMKKC5QA==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.5.0", + "jest-diff": "^29.6.1", "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "pretty-format": "^29.6.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -10530,12 +11223,12 @@ } }, "node_modules/jest-matcher-utils/node_modules/pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.1.tgz", + "integrity": "sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==", "dev": true, "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.0", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -10568,18 +11261,18 @@ } }, "node_modules/jest-message-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", - "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.1.tgz", + "integrity": "sha512-KoAW2zAmNSd3Gk88uJ56qXUWbFk787QKmjjJVOjtGFmmGSZgDBrlIL4AfQw1xyMYPNVD7dNInfIbur9B2rd/wQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.1", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", + "pretty-format": "^29.6.1", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -10588,12 +11281,12 @@ } }, "node_modules/jest-message-util/node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz", + "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.0", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -10672,12 +11365,12 @@ } }, "node_modules/jest-message-util/node_modules/pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.1.tgz", + "integrity": "sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==", "dev": true, "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.0", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -11820,9 +12513,9 @@ } }, "node_modules/jest-watch-typeahead/node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { "ansi-regex": "^6.0.1" @@ -12079,6 +12772,12 @@ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -12114,14 +12813,25 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.4.tgz", + "integrity": "sha512-fX2TVdCViod6HwKEtSWGHs57oFhVfCMwieb9PuRDgjDPh5XeqJiHFFFJCHxU5cnTc3Bu/GRL+kPiFmw8XWOfKw==", "dev": true, "dependencies": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" + "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" @@ -12197,19 +12907,25 @@ "node": ">=6.11.5" } }, + "node_modules/loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "p-locate": "^5.0.0" + "p-locate": "^4.1.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/lodash": { @@ -12230,6 +12946,12 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -12259,6 +12981,15 @@ "lz-string": "bin/bin.js" } }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -12275,9 +13006,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -12293,12 +13024,12 @@ } }, "node_modules/memfs": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.1.tgz", - "integrity": "sha512-UWbFJKvj5k+nETdteFndTpYxdeTMox/ULeqX5k/dpaQJCCFmj5EeKv3dBcyO2xmkRAx2vppRu5dVG7SOtsGOzA==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dev": true, "dependencies": { - "fs-monkey": "^1.0.3" + "fs-monkey": "^1.0.4" }, "engines": { "node": ">= 4.0.0" @@ -12433,19 +13164,6 @@ "dev": true, "peer": true }, - "node_modules/node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", - "dev": true, - "optional": true, - "peer": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -12453,9 +13171,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", - "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, "node_modules/normalize-path": { @@ -12480,9 +13198,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.4.tgz", - "integrity": "sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", "dev": true }, "node_modules/object-assign": { @@ -12648,50 +13366,47 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/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", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" + "p-limit": "^2.2.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/p-try": { @@ -12796,9 +13511,9 @@ } }, "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "engines": { "node": ">= 6" @@ -12816,58 +13531,6 @@ "node": ">=8" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/pkg-up": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", @@ -12905,21 +13568,6 @@ "node": ">=6" } }, - "node_modules/pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/pkg-up/node_modules/p-locate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", @@ -12942,9 +13590,9 @@ } }, "node_modules/postcss": { - "version": "8.4.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", - "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", + "version": "8.4.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.26.tgz", + "integrity": "sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==", "dev": true, "funding": [ { @@ -12978,6 +13626,18 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-bytes": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", + "dev": true, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", @@ -13089,7 +13749,6 @@ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "peer": true, "dependencies": { "safe-buffer": "^5.1.0" } @@ -13198,6 +13857,22 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/react-dev-utils/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/react-dev-utils/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -13207,13 +13882,49 @@ "node": ">=8" } }, - "node_modules/react-dev-utils/node_modules/loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "node_modules/react-dev-utils/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, "engines": { - "node": ">= 12.13.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/react-dev-utils/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/react-dev-utils/node_modules/supports-color": { @@ -13247,9 +13958,9 @@ "dev": true }, "node_modules/react-icons": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.8.0.tgz", - "integrity": "sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.10.1.tgz", + "integrity": "sha512-/ngzDP/77tlCfqthiiGNZeYFACw85fUjZtLbedmJ5DTlNDIwETxhwBzdOJ21zj4iJdvc0J3y7yOsX3PpxAJzrw==", "peerDependencies": { "react": "*" } @@ -13288,11 +13999,11 @@ } }, "node_modules/react-router": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.11.2.tgz", - "integrity": "sha512-74z9xUSaSX07t3LM+pS6Un0T55ibUE/79CzfZpy5wsPDZaea1F8QkrsiyRnA2YQ7LwE/umaydzXZV80iDCPkMg==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.14.2.tgz", + "integrity": "sha512-09Zss2dE2z+T1D03IheqAFtK4UzQyX8nFPWx6jkwdYzGLXd5ie06A6ezS2fO6zJfEb/SpG6UocN2O1hfD+2urQ==", "dependencies": { - "@remix-run/router": "1.6.2" + "@remix-run/router": "1.7.2" }, "engines": { "node": ">=14" @@ -13302,12 +14013,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.11.2.tgz", - "integrity": "sha512-JNbKtAeh1VSJQnH6RvBDNhxNwemRj7KxCzc5jb7zvDSKRnPWIFj9pO+eXqjM69gQJ0r46hSz1x4l9y0651DKWw==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.14.2.tgz", + "integrity": "sha512-5pWX0jdKR48XFZBuJqHosX3AAHjRAzygouMTyimnBPOLdY3WjzUSKhus2FVMihUFWzeLebDgr4r8UeQFAct7Bg==", "dependencies": { - "@remix-run/router": "1.6.2", - "react-router": "6.11.2" + "@remix-run/router": "1.7.2", + "react-router": "6.14.2" }, "engines": { "node": ">=14" @@ -13474,6 +14185,15 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -13557,17 +14277,16 @@ } }, "node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", + "version": "3.26.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.3.tgz", + "integrity": "sha512-7Tin0C8l86TkpcMtXvQu6saWH93nhG3dGQ1/+l5V2TDMceTxO7kDiK6GzbfLWNNxqJXm591PcEZUozZm51ogwQ==", "dev": true, - "optional": true, - "peer": true, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=10.0.0" + "node": ">=14.18.0", + "npm": ">=8.0.0" }, "optionalDependencies": { "fsevents": "~2.3.2" @@ -13596,12 +14315,43 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, - "peer": true + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/safe-regex-test": { "version": "1.0.0", @@ -13644,18 +14394,17 @@ } }, "node_modules/schema-utils": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", - "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", "dev": true, - "peer": true, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 8.9.0" }, "funding": { "type": "opencollective", @@ -13663,9 +14412,9 @@ } }, "node_modules/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -13700,7 +14449,6 @@ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "dev": true, - "peer": true, "dependencies": { "randombytes": "^2.1.0" } @@ -13806,6 +14554,13 @@ "node": ">=0.10.0" } }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -13884,15 +14639,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/string-width/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/string.prototype.matchall": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", @@ -13957,6 +14703,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -13978,6 +14738,15 @@ "node": ">=8" } }, + "node_modules/strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -14085,15 +14854,53 @@ "dev": true }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true, - "peer": true, "engines": { "node": ">=6" } }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "dev": true, + "dependencies": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", @@ -14111,14 +14918,13 @@ } }, "node_modules/terser": { - "version": "5.17.6", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.6.tgz", - "integrity": "sha512-V8QHcs8YuyLkLHsJO5ucyff1ykrLVsR4dNnS//L5Y3NiSXpbK1J+WMVUs67eI0KTxs9JtHhgEQpXQVHlHI92DQ==", + "version": "5.19.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.1.tgz", + "integrity": "sha512-27hxBUVdV6GoNg1pKQ7Z5cbR6V9txPVyBA+FQw3BaZ1Wuzvztce5p156DaP0NVZNrMZZ+6iG9Syf7WgMNKDg2Q==", "dev": true, - "peer": true, "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -14164,12 +14970,24 @@ } } }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, - "peer": true + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } }, "node_modules/test-exclude": { "version": "6.0.0", @@ -14224,9 +15042,9 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", - "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "dependencies": { "psl": "^1.1.33", @@ -14265,9 +15083,9 @@ "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" }, "node_modules/tsconfck": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-2.1.1.tgz", - "integrity": "sha512-ZPCkJBKASZBmBUNqGHmRhdhM8pJYDdOXp4nRgj/O0JwUwsMq50lCDRQP/M5GBNAA0elPrq4gAeu4dkaVCuKWww==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-2.1.2.tgz", + "integrity": "sha512-ghqN1b0puy3MhhviwO2kGF8SeMDNhEbnKxjK7h6+fvY9JAxqvXi8y5NAHSQv687OVboS2uZIByzGd45/YxrRHg==", "dev": true, "bin": { "tsconfck": "bin/tsconfck.js" @@ -14317,6 +15135,12 @@ "node": ">=4" } }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, "node_modules/tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", @@ -14332,12 +15156,6 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -14371,6 +15189,57 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/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.npmjs.org/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" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/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" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typed-array-length": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", @@ -14395,9 +15264,9 @@ } }, "node_modules/typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -14462,6 +15331,18 @@ "node": ">=4" } }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -14470,6 +15351,16 @@ "node": ">= 10.0.0" } }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", @@ -14519,19 +15410,12 @@ "requires-port": "^1.0.0" } }, - "node_modules/utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "peer": true, - "dependencies": { - "node-gyp-build": "^4.3.0" - }, - "engines": { - "node": ">=6.14.2" + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/v8-to-istanbul": { @@ -14558,14 +15442,14 @@ } }, "node_modules/vite": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", - "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.4.tgz", + "integrity": "sha512-4mvsTxjkveWrKDJI70QmelfVqTm+ihFAb6+xf4sjEU2TmUCTlVX87tmg/QooPEMQb/lM9qGHT99ebqPziEd3wg==", "dev": true, "dependencies": { - "esbuild": "^0.17.5", - "postcss": "^8.4.23", - "rollup": "^3.21.0" + "esbuild": "^0.18.10", + "postcss": "^8.4.25", + "rollup": "^3.25.2" }, "bin": { "vite": "bin/vite.js" @@ -14573,12 +15457,16 @@ "engines": { "node": "^14.18.0 || >=16.0.0" }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", + "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", @@ -14591,6 +15479,9 @@ "less": { "optional": true }, + "lightningcss": { + "optional": true + }, "sass": { "optional": true }, @@ -14605,371 +15496,190 @@ } } }, - "node_modules/vite-plugin-svgr": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-3.2.0.tgz", - "integrity": "sha512-Uvq6niTvhqJU6ga78qLKBFJSDvxWhOnyfQSoKpDPMAGxJPo5S3+9hyjExE5YDj6Lpa4uaLkGc1cBgxXov+LjSw==", + "node_modules/vite-plugin-compression": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz", + "integrity": "sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==", "dev": true, "dependencies": { - "@rollup/pluginutils": "^5.0.2", - "@svgr/core": "^7.0.0", - "@svgr/plugin-jsx": "^7.0.0" + "chalk": "^4.1.2", + "debug": "^4.3.3", + "fs-extra": "^10.0.0" }, "peerDependencies": { - "vite": "^2.6.0 || 3 || 4" + "vite": ">=2.0.0" } }, - "node_modules/vite-plugin-svgr/node_modules/@rollup/pluginutils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", - "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "node_modules/vite-plugin-compression/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0" + "color-convert": "^2.0.1" }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-7.0.0.tgz", - "integrity": "sha512-khWbXesWIP9v8HuKCl2NU2HNAyqpSQ/vkIl36Nbn4HIwEYSRWL0H7Gs6idJdha2DkpFDWlsqMELvoCE8lfFY6Q==", - "dev": true, "engines": { - "node": ">=14" + "node": ">=8" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-remove-jsx-attribute": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-7.0.0.tgz", - "integrity": "sha512-iiZaIvb3H/c7d3TH2HBeK91uI2rMhZNwnsIrvd7ZwGLkFw6mmunOCoVnjdYua662MqGFxlN9xTq4fv9hgR4VXQ==", + "node_modules/vite-plugin-compression/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-7.0.0.tgz", - "integrity": "sha512-sQQmyo+qegBx8DfFc04PFmIO1FP1MHI1/QEpzcIcclo5OAISsOJPW76ZIs0bDyO/DBSJEa/tDa1W26pVtt0FRw==", + "node_modules/vite-plugin-compression/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "dependencies": { + "color-name": "~1.1.4" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=7.0.0" } }, - "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-7.0.0.tgz", - "integrity": "sha512-i6MaAqIZXDOJeikJuzocByBf8zO+meLwfQ/qMHIjCcvpnfvWf82PFvredEZElErB5glQFJa2KVKk8N2xV6tRRA==", + "node_modules/vite-plugin-compression/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/vite-plugin-compression/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "node": ">=8" } }, - "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-7.0.0.tgz", - "integrity": "sha512-BoVSh6ge3SLLpKC0pmmN9DFlqgFy4NxNgdZNLPNJWBUU7TQpDWeBuyVuDW88iXydb5Cv0ReC+ffa5h3VrKfk1w==", + "node_modules/vite-plugin-compression/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "dependencies": { + "has-flag": "^4.0.0" }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "engines": { + "node": ">=8" } }, - "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-7.0.0.tgz", - "integrity": "sha512-tNDcBa+hYn0gO+GkP/AuNKdVtMufVhU9fdzu+vUQsR18RIJ9RWe7h/pSBY338RO08wArntwbDk5WhQBmhf2PaA==", + "node_modules/vite-plugin-pwa": { + "version": "0.16.4", + "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.16.4.tgz", + "integrity": "sha512-lmwHFIs9zI2H9bXJld/zVTbCqCQHZ9WrpyDMqosICDV0FVnCJwniX1NMDB79HGTIZzOQkY4gSZaVTJTw6maz/Q==", "dev": true, + "dependencies": { + "debug": "^4.3.4", + "fast-glob": "^3.2.12", + "pretty-bytes": "^6.0.0", + "workbox-build": "^7.0.0", + "workbox-window": "^7.0.0" + }, "engines": { - "node": ">=14" + "node": ">=16.0.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "url": "https://github.com/sponsors/antfu" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "vite": "^3.1.0 || ^4.0.0", + "workbox-build": "^7.0.0", + "workbox-window": "^7.0.0" } }, - "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-7.0.0.tgz", - "integrity": "sha512-qw54u8ljCJYL2KtBOjI5z7Nzg8LnSvQOP5hPKj77H4VQL4+HdKbAT5pnkkZLmHKYwzsIHSYKXxHouD8zZamCFQ==", + "node_modules/vite-plugin-svgr": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-3.2.0.tgz", + "integrity": "sha512-Uvq6niTvhqJU6ga78qLKBFJSDvxWhOnyfQSoKpDPMAGxJPo5S3+9hyjExE5YDj6Lpa4uaLkGc1cBgxXov+LjSw==", "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "dependencies": { + "@rollup/pluginutils": "^5.0.2", + "@svgr/core": "^7.0.0", + "@svgr/plugin-jsx": "^7.0.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "vite": "^2.6.0 || 3 || 4" } }, - "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-7.0.0.tgz", - "integrity": "sha512-CcFECkDj98daOg9jE3Bh3uyD9kzevCAnZ+UtzG6+BQG/jOQ2OA3jHnX6iG4G1MCJkUQFnUvEv33NvQfqrb/F3A==", + "node_modules/vite-tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-jGpus0eUy5qbbMVGiTxCL1iB9ZGN6Bd37VGLJU39kTDD6ZfULTTb1bcc5IeTWqWJKiWV5YihCaibeASPiGi8kw==", "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "dependencies": { + "debug": "^4.1.1", + "globrex": "^0.1.2", + "tsconfck": "^2.1.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } } }, - "node_modules/vite-plugin-svgr/node_modules/@svgr/babel-preset": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-7.0.0.tgz", - "integrity": "sha512-EX/NHeFa30j5UjldQGVQikuuQNHUdGmbh9kEpBKofGUtF0GUPJ4T4rhoYiqDAOmBOxojyot36JIFiDUHUK1ilQ==", + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", "dev": true, "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "^7.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^7.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^7.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^7.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "^7.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "^7.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "^7.0.0", - "@svgr/babel-plugin-transform-svg-component": "^7.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" + "browser-process-hrtime": "^1.0.0" } }, - "node_modules/vite-plugin-svgr/node_modules/@svgr/core": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-7.0.0.tgz", - "integrity": "sha512-ztAoxkaKhRVloa3XydohgQQCb0/8x9T63yXovpmHzKMkHO6pkjdsIAWKOS4bE95P/2quVh1NtjSKlMRNzSBffw==", + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", "dev": true, "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "^7.0.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3" + "xml-name-validator": "^3.0.0" }, "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "node": ">=10" } }, - "node_modules/vite-plugin-svgr/node_modules/@svgr/hast-util-to-babel-ast": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-7.0.0.tgz", - "integrity": "sha512-42Ej9sDDEmsJKjrfQ1PHmiDiHagh/u9AHO9QWbeNx4KmD9yS5d1XHmXUNINfUcykAU+4431Cn+k6Vn5mWBYimQ==", + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, "dependencies": { - "@babel/types": "^7.21.3", - "entities": "^4.4.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" + "makeerror": "1.0.12" } }, - "node_modules/vite-plugin-svgr/node_modules/@svgr/plugin-jsx": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-7.0.0.tgz", - "integrity": "sha512-SWlTpPQmBUtLKxXWgpv8syzqIU8XgFRvyhfkam2So8b3BE0OS0HPe5UfmlJ2KIC+a7dpuuYovPR2WAQuSyMoPw==", + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", "dev": true, + "peer": true, "dependencies": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "^7.0.0", - "@svgr/hast-util-to-babel-ast": "^7.0.0", - "svg-parser": "^2.0.4" + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" }, "engines": { - "node": ">=14" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/gregberge" - } - }, - "node_modules/vite-plugin-svgr/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/vite-plugin-svgr/node_modules/cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", - "dev": true, - "dependencies": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - } - }, - "node_modules/vite-plugin-svgr/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "node_modules/vite-plugin-svgr/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/vite-tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-jGpus0eUy5qbbMVGiTxCL1iB9ZGN6Bd37VGLJU39kTDD6ZfULTTb1bcc5IeTWqWJKiWV5YihCaibeASPiGi8kw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^2.1.0" - }, - "peerDependencies": { - "vite": "*" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/rollup": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.25.1.tgz", - "integrity": "sha512-tywOR+rwIt5m2ZAWSe5AIJcTat8vGlnPFAv15ycCrw33t6iFsXZ6mzHVFh2psSjxQPmI+xgzMZZizUAukBI4aQ==", - "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=14.18.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", - "dev": true, - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "dependencies": { - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dev": true, - "peer": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" + "node": ">=10.13.0" } }, "node_modules/web-vitals": { @@ -14987,9 +15697,9 @@ } }, "node_modules/webpack": { - "version": "5.84.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.84.0.tgz", - "integrity": "sha512-XezNK3kwJq6IyeoZmZ1uEqQs+42nTqIi4jYM/YjLwaJedUC1N3bwnCC0+UcnHJPfqWX0kGrQnMIvZZyWYaIZrA==", + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", "dev": true, "peer": true, "dependencies": { @@ -15002,7 +15712,7 @@ "acorn-import-assertions": "^1.9.0", "browserslist": "^4.14.5", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.14.1", + "enhanced-resolve": "^5.15.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -15012,7 +15722,7 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.2", + "schema-utils": "^3.2.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.7", "watchpack": "^2.4.0", @@ -15044,28 +15754,33 @@ "node": ">=10.13.0" } }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "peer": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">=8.0.0" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/webpack/node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, "peer": true, "engines": { - "node": ">=4.0" + "node": ">=6" } }, "node_modules/whatwg-encoding": { @@ -15077,18 +15792,6 @@ "iconv-lite": "0.4.24" } }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/whatwg-mimetype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", @@ -15156,17 +15859,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", "dev": true, "dependencies": { "available-typed-arrays": "^1.0.5", "call-bind": "^1.0.2", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -15175,11279 +15877,591 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "node_modules/workbox-background-sync": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.0.0.tgz", + "integrity": "sha512-S+m1+84gjdueM+jIKZ+I0Lx0BDHkk5Nu6a3kTVxP4fdj3gKouRNmhO8H290ybnJTOPfBDtTMXSQA/QLTvr7PeA==", "dev": true, - "engines": { - "node": ">=0.10.0" + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "7.0.0" } }, - "node_modules/wrap-ansi": { + "node_modules/workbox-broadcast-update": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-7.0.0.tgz", + "integrity": "sha512-oUuh4jzZrLySOo0tC0WoKiSg90bVAcnE98uW7F8GFiSOXnhogfNDGZelPJa+6KpGBO5+Qelv04Hqx2UD+BJqNQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "workbox-core": "7.0.0" + } + }, + "node_modules/workbox-build": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-7.0.0.tgz", + "integrity": "sha512-CttE7WCYW9sZC+nUYhQg3WzzGPr4IHmrPnjKiu3AMXsiNQKx+l4hHl63WTrnicLmKEKHScWDH8xsGBdrYgtBzg==", + "dev": true, + "dependencies": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.11.1", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^11.2.1", + "@rollup/plugin-replace": "^2.4.1", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "rollup-plugin-terser": "^7.0.0", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "7.0.0", + "workbox-broadcast-update": "7.0.0", + "workbox-cacheable-response": "7.0.0", + "workbox-core": "7.0.0", + "workbox-expiration": "7.0.0", + "workbox-google-analytics": "7.0.0", + "workbox-navigation-preload": "7.0.0", + "workbox-precaching": "7.0.0", + "workbox-range-requests": "7.0.0", + "workbox-recipes": "7.0.0", + "workbox-routing": "7.0.0", + "workbox-strategies": "7.0.0", + "workbox-streams": "7.0.0", + "workbox-sw": "7.0.0", + "workbox-window": "7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/workbox-build/node_modules/@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "dev": true, + "dependencies": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" }, "engines": { "node": ">=10" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "peerDependencies": { + "ajv": ">=8" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/workbox-build/node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" }, "engines": { - "node": ">=8" + "node": ">= 10.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } } }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/workbox-build/node_modules/@rollup/plugin-node-resolve": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz", + "integrity": "sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.19.0" }, "engines": { - "node": ">=7.0.0" + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" } }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "node_modules/workbox-build/node_modules/@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", "dev": true, "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" } }, - "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "node_modules/workbox-build/node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", "dev": true, + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, "engines": { - "node": ">=8.3.0" + "node": ">= 8.0.0" }, "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "rollup": "^1.20.0||^2.0.0" } }, - "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "node_modules/workbox-build/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "dev": true }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/workbox-build/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "node_modules/workbox-build/node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", "dev": true }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/workbox-build/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": 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" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { "node": ">=10" } }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "node_modules/workbox-build/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "node_modules/workbox-build/node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", "dev": true, - "engines": { - "node": ">=10" + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@adobe/css-tools": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.2.0.tgz", - "integrity": "sha512-E09FiIft46CmH5Qnjb0wsW54/YQd69LsxeKUOWawmws1XWvyFGURnAChH0mlr7YPFR1ofwvUQfcL0J3lMxXqPA==", - "dev": true - }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@anatoliygatt/dark-mode-toggle": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@anatoliygatt/dark-mode-toggle/-/dark-mode-toggle-1.0.1.tgz", - "integrity": "sha512-zOjGHCeTGXxuWuh02NMTHAJ7VrptoVKmGHr/Pb4r+rsUBUz7OEI0x6HXTFZ15RFOAG2pfKjMmQQaOgfhfbyv1w==", - "requires": {} - }, - "@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", - "requires": { - "@babel/highlight": "^7.22.5" + "engines": { + "node": ">= 10.13.0" } }, - "@babel/compat-data": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz", - "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==", + "node_modules/workbox-build/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "@babel/core": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", - "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", + "node_modules/workbox-build/node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helpers": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "engines": { + "node": ">=6" }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "@babel/eslint-parser": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.21.8.tgz", - "integrity": "sha512-HLhI+2q+BP3sf78mFUZNCGc10KEmoUqtUT1OCdMZsN+qr4qFeLUod62/zAnF3jNQstwyasDkZnVXwfK2Bml7MQ==", + "node_modules/workbox-build/node_modules/rollup": { + "version": "2.79.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", + "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", "dev": true, - "requires": { - "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.0" + "bin": { + "rollup": "dist/bin/rollup" }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", - "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.21.5.tgz", - "integrity": "sha512-uNrjKztPLkUk7bpCNC0jEKDJzzkvel/W+HguzbN8krA+LPfC1CEobJEvAvGka2A/M+ViOqXdcRL0GqPUJSjx9g==", - "dev": true, - "requires": { - "@babel/types": "^7.21.5" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz", - "integrity": "sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "engines": { + "node": ">=10.0.0" }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "@babel/helper-create-class-features-plugin": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.8.tgz", - "integrity": "sha512-+THiN8MqiH2AczyuZrnrKL6cAxFRRQDKW9h1YkBvbgKmAm6mwiacig1qT73DHIWMGo40GRnsEfN3LA+E6NtmSw==", + "node_modules/workbox-build/node_modules/rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser", "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-member-expression-to-functions": "^7.21.5", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.21.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/helper-split-export-declaration": "^7.18.6", - "semver": "^6.3.0" - }, "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.8.tgz", - "integrity": "sha512-zGuSdedkFtsFHGbexAvNuipg1hbtitDLo2XE8/uf6Y9sOQV1xsYX/2pNbtedp/X0eU1pIt+kGvaqHCowkRbS5g==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.3.1", - "semver": "^6.3.0" + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "peerDependencies": { + "rollup": "^2.0.0" } }, - "@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "node_modules/workbox-build/node_modules/serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", - "dev": true, - "requires": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.5.tgz", - "integrity": "sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg==", - "dev": true, - "requires": { - "@babel/types": "^7.21.5" - } - }, - "@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-module-transforms": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", - "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", - "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", - "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-wrap-function": "^7.18.9", - "@babel/types": "^7.18.9" - } - }, - "@babel/helper-replace-supers": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.21.5.tgz", - "integrity": "sha512-/y7vBgsr9Idu4M6MprbOVUfH3vs7tsIfnVWv/Ml2xgwvyH6LTngdfbf5AdsKwkJy4zgy1X/kuNrEKvhhK28Yrg==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-member-expression-to-functions": "^7.21.5", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" - } - }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", - "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", - "dev": true, - "requires": { - "@babel/types": "^7.20.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", - "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" - }, - "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==" - }, - "@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", - "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.19.0", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.20.5", - "@babel/types": "^7.20.5" - } - }, - "@babel/helpers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz", - "integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==", - "dev": true, - "requires": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.5", - "@babel/types": "^7.22.5" - } - }, - "@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", - "requires": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", - "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==", - "dev": true - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", - "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", - "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-proposal-optional-chaining": "^7.20.7" - } - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@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==", - "dev": true, - "requires": { - "@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" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-proposal-decorators": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.21.0.tgz", - "integrity": "sha512-MfgX49uRrFUTL/HvWtmx3zmpyzMMr4MTj3d527MLlr/4RTT9G/ytFFP7qet2uM2Ve03b+BkpWUpK+lRXnQ+v9w==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/plugin-syntax-decorators": "^7.21.0" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dev": true, - "requires": { - "@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" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", - "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-decorators": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.21.0.tgz", - "integrity": "sha512-tIoPpGBR8UuM4++ccWN3gifhVvQu7ZizuR1fklhRJrd5ewgbkUS+0KVFeWWxELtn18NTLoW32XV7zyOgIAiz+w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-flow": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.21.4.tgz", - "integrity": "sha512-l9xd3N+XG4fZRxEP3vXdK6RW7vN1Uf5dxzRC/09wV86wqZ/YYQooBIGNsiRdfNR3/q2/5pPzV4B54J/9ctX5jw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", - "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.19.0" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", - "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", - "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz", - "integrity": "sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.21.5" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", - "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", - "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", - "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", - "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-replace-supers": "^7.20.7", - "@babel/helper-split-export-declaration": "^7.18.6", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz", - "integrity": "sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/template": "^7.20.7" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz", - "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", - "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", - "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", - "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-flow-strip-types": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.21.0.tgz", - "integrity": "sha512-FlFA2Mj87a6sDkW4gfGrQQqwY/dLlBAyJa2dJEZ+FHXUVHBflO2wyKvg+OOEzXfrKYIa4HWl0mgmbCzt0cMb7w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-flow": "^7.18.6" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz", - "integrity": "sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.21.5" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", - "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", - "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", - "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", - "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz", - "integrity": "sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.21.5", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-simple-access": "^7.21.5" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.20.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", - "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-identifier": "^7.19.1" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", - "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", - "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.20.5", - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", - "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", - "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.6" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz", - "integrity": "sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", - "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-react-display-name": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", - "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.5.tgz", - "integrity": "sha512-ELdlq61FpoEkHO6gFRpfj0kUgSwQTGoaEU8eMRoS8Dv3v6e7BjEAj5WMtIBRdHUeAioMhKP5HyxNzNnP+heKbA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/types": "^7.21.5" - } - }, - "@babel/plugin-transform-react-jsx-development": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", - "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", - "dev": true, - "requires": { - "@babel/plugin-transform-react-jsx": "^7.18.6" - } - }, - "@babel/plugin-transform-react-jsx-self": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz", - "integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-react-jsx-source": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz", - "integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-react-pure-annotations": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", - "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz", - "integrity": "sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.21.5", - "regenerator-transform": "^0.15.1" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", - "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.4.tgz", - "integrity": "sha512-1J4dhrw1h1PqnNNpzwxQ2UBymJUF8KuPjAAnlLwZcGhHAIqUigFW7cdK6GHoB64ubY4qXQNYknoUeks4Wz7CUA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-plugin-utils": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", - "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", - "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", - "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", - "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", - "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", - "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-typescript": "^7.20.0" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz", - "integrity": "sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.21.5" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", - "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/preset-env": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.5.tgz", - "integrity": "sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.21.5", - "@babel/helper-compilation-targets": "^7.21.5", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.20.7", - "@babel/plugin-proposal-async-generator-functions": "^7.20.7", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.21.0", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.20.7", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.7", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.21.0", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.21.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@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-transform-arrow-functions": "^7.21.5", - "@babel/plugin-transform-async-to-generator": "^7.20.7", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.21.0", - "@babel/plugin-transform-classes": "^7.21.0", - "@babel/plugin-transform-computed-properties": "^7.21.5", - "@babel/plugin-transform-destructuring": "^7.21.3", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.21.5", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.20.11", - "@babel/plugin-transform-modules-commonjs": "^7.21.5", - "@babel/plugin-transform-modules-systemjs": "^7.20.11", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.20.5", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.21.3", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.21.5", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.20.7", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.21.5", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.21.5", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/preset-modules": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", - "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-react": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.18.6.tgz", - "integrity": "sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-transform-react-display-name": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.18.6", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-pure-annotations": "^7.18.6" - } - }, - "@babel/preset-typescript": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.5.tgz", - "integrity": "sha512-iqe3sETat5EOrORXiQ6rWfoOg2y68Cs75B9wNxdPW4kixJxh7aXQE1KPdWLDniC24T/6dSnguF33W9j/ZZQcmA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/plugin-transform-modules-commonjs": "^7.21.5", - "@babel/plugin-transform-typescript": "^7.21.3" - } - }, - "@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true - }, - "@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, - "@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" - } - }, - "@babel/traverse": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz", - "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@emotion/babel-plugin": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", - "requires": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/serialize": "^1.1.2", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" - } - }, - "@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", - "requires": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "stylis": "4.2.0" - } - }, - "@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" - }, - "@emotion/is-prop-valid": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", - "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", - "requires": { - "@emotion/memoize": "^0.8.1" - } - }, - "@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" - }, - "@emotion/react": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.0.tgz", - "integrity": "sha512-ZSK3ZJsNkwfjT3JpDAWJZlrGD81Z3ytNDsxw1LKq1o+xkmO5pnWfr6gmCC8gHEFf3nSSX/09YrG67jybNPxSUw==", - "requires": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "hoist-non-react-statics": "^3.3.1" - } - }, - "@emotion/serialize": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", - "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", - "requires": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", - "csstype": "^3.0.2" - } - }, - "@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" - }, - "@emotion/styled": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", - "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", - "requires": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/is-prop-valid": "^1.2.1", - "@emotion/serialize": "^1.1.2", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1" - } - }, - "@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" - }, - "@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", - "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", - "requires": {} - }, - "@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" - }, - "@emotion/weak-memoize": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" - }, - "@esbuild/android-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", - "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", - "dev": true, - "optional": true - }, - "@esbuild/android-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", - "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", - "dev": true, - "optional": true - }, - "@esbuild/android-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", - "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", - "dev": true, - "optional": true - }, - "@esbuild/darwin-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", - "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", - "dev": true, - "optional": true - }, - "@esbuild/darwin-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", - "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", - "dev": true, - "optional": true - }, - "@esbuild/freebsd-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", - "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", - "dev": true, - "optional": true - }, - "@esbuild/freebsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", - "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", - "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", - "dev": true, - "optional": true - }, - "@esbuild/linux-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", - "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", - "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-loong64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", - "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", - "dev": true, - "optional": true - }, - "@esbuild/linux-mips64el": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", - "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", - "dev": true, - "optional": true - }, - "@esbuild/linux-ppc64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", - "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", - "dev": true, - "optional": true - }, - "@esbuild/linux-riscv64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", - "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", - "dev": true, - "optional": true - }, - "@esbuild/linux-s390x": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", - "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", - "dev": true, - "optional": true - }, - "@esbuild/linux-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", - "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", - "dev": true, - "optional": true - }, - "@esbuild/netbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", - "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", - "dev": true, - "optional": true - }, - "@esbuild/openbsd-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", - "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", - "dev": true, - "optional": true - }, - "@esbuild/sunos-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", - "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", - "dev": true, - "optional": true - }, - "@esbuild/win32-arm64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", - "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", - "dev": true, - "optional": true - }, - "@esbuild/win32-ia32": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", - "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", - "dev": true, - "optional": true - }, - "@esbuild/win32-x64": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", - "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", - "dev": true, - "optional": true - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.5.2", - "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" - }, - "dependencies": { - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "@eslint/js": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", - "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", - "dev": true - }, - "@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", - "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/core": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", - "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", - "dev": true, - "requires": { - "@jest/console": "^27.5.1", - "@jest/reporters": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^27.5.1", - "jest-config": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-resolve-dependencies": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "jest-watcher": "^27.5.1", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1" - } - }, - "@jest/expect-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", - "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", - "dev": true, - "requires": { - "jest-get-type": "^29.4.3" - } - }, - "@jest/fake-timers": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", - "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/globals": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", - "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/types": "^27.5.1", - "expect": "^27.5.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "dev": true - }, - "expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - }, - "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/reporters": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", - "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/schemas": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", - "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.25.16" - } - }, - "@jest/source-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", - "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "@jest/test-result": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", - "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", - "dev": true, - "requires": { - "@jest/console": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", - "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", - "dev": true, - "requires": { - "@jest/test-result": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-runtime": "^27.5.1" - } - }, - "@jest/transform": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", - "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.5.1", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-util": "^27.5.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/source-map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", - "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", - "dev": true, - "peer": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - }, - "dependencies": { - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - } - } - }, - "@kurkle/color": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", - "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" - }, - "@mui/base": { - "version": "5.0.0-beta.2", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.2.tgz", - "integrity": "sha512-R9R+aqrl1QhZJaO05rhvooqxOaf7SKpQ+EjW80sbP3ticTVmLmrn4YBLQS7/ML+WXdrkrPtqSmKFdSE5Ik3gBQ==", - "requires": { - "@babel/runtime": "^7.21.0", - "@emotion/is-prop-valid": "^1.2.1", - "@mui/types": "^7.2.4", - "@mui/utils": "^5.13.1", - "@popperjs/core": "^2.11.7", - "clsx": "^1.2.1", - "prop-types": "^15.8.1", - "react-is": "^18.2.0" - } - }, - "@mui/core-downloads-tracker": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.13.2.tgz", - "integrity": "sha512-aOLCXMCySMFL2WmUhnz+DjF84AoFVu8rn35OsL759HXOZMz8zhEwVf5w/xxkWx7DycM2KXDTgAvYW48nTfqTLA==" - }, - "@mui/icons-material": { - "version": "5.11.16", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.11.16.tgz", - "integrity": "sha512-oKkx9z9Kwg40NtcIajF9uOXhxiyTZrrm9nmIJ4UjkU2IdHpd4QVLbCc/5hZN/y0C6qzi2Zlxyr9TGddQx2vx2A==", - "requires": { - "@babel/runtime": "^7.21.0" - } - }, - "@mui/material": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.13.2.tgz", - "integrity": "sha512-Pfke1l0GG2OJb/Nr10aVr8huoBFcBTdWKV5iFSTEHqf9c2C1ZlyYMISn7ui6X3Gix8vr+hP5kVqH1LAWwQSb6w==", - "requires": { - "@babel/runtime": "^7.21.0", - "@mui/base": "5.0.0-beta.2", - "@mui/core-downloads-tracker": "^5.13.2", - "@mui/system": "^5.13.2", - "@mui/types": "^7.2.4", - "@mui/utils": "^5.13.1", - "@types/react-transition-group": "^4.4.6", - "clsx": "^1.2.1", - "csstype": "^3.1.2", - "prop-types": "^15.8.1", - "react-is": "^18.2.0", - "react-transition-group": "^4.4.5" - } - }, - "@mui/private-theming": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.13.1.tgz", - "integrity": "sha512-HW4npLUD9BAkVppOUZHeO1FOKUJWAwbpy0VQoGe3McUYTlck1HezGHQCfBQ5S/Nszi7EViqiimECVl9xi+/WjQ==", - "requires": { - "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.13.1", - "prop-types": "^15.8.1" - } - }, - "@mui/styled-engine": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.13.2.tgz", - "integrity": "sha512-VCYCU6xVtXOrIN8lcbuPmoG+u7FYuOERG++fpY74hPpEWkyFQG97F+/XfTQVYzlR2m7nPjnwVUgATcTCMEaMvw==", - "requires": { - "@babel/runtime": "^7.21.0", - "@emotion/cache": "^11.11.0", - "csstype": "^3.1.2", - "prop-types": "^15.8.1" - } - }, - "@mui/system": { - "version": "5.13.2", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.13.2.tgz", - "integrity": "sha512-TPyWmRJPt0JPVxacZISI4o070xEJ7ftxpVtu6LWuYVOUOINlhoGOclam4iV8PDT3EMQEHuUrwU49po34UdWLlw==", - "requires": { - "@babel/runtime": "^7.21.0", - "@mui/private-theming": "^5.13.1", - "@mui/styled-engine": "^5.13.2", - "@mui/types": "^7.2.4", - "@mui/utils": "^5.13.1", - "clsx": "^1.2.1", - "csstype": "^3.1.2", - "prop-types": "^15.8.1" - } - }, - "@mui/types": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.4.tgz", - "integrity": "sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==", - "requires": {} - }, - "@mui/utils": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz", - "integrity": "sha512-6lXdWwmlUbEU2jUI8blw38Kt+3ly7xkmV9ljzY4Q20WhsJMWiNry9CX8M+TaP/HbtuyR8XKsdMgQW7h7MM3n3A==", - "requires": { - "@babel/runtime": "^7.21.0", - "@types/prop-types": "^15.7.5", - "@types/react-is": "^18.2.0", - "prop-types": "^15.8.1", - "react-is": "^18.2.0" - } - }, - "@mui/x-data-grid": { - "version": "5.17.26", - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-5.17.26.tgz", - "integrity": "sha512-eGJq9J0g9cDGLFfMmugOadZx0mJeOd/yQpHwEa5gUXyONS6qF0OhXSWyDOhDdA3l2TOoQzotMN5dY/T4Wl1KYA==", - "requires": { - "@babel/runtime": "^7.18.9", - "@mui/utils": "^5.10.3", - "clsx": "^1.2.1", - "prop-types": "^15.8.1", - "reselect": "^4.1.6" - } - }, - "@nicolo-ribaudo/eslint-scope-5-internals": { - "version": "5.1.1-v1", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", - "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", - "dev": true, - "requires": { - "eslint-scope": "5.1.1" - }, - "dependencies": { - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - } - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@popperjs/core": { - "version": "2.11.7", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz", - "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==" - }, - "@remix-run/router": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.2.tgz", - "integrity": "sha512-LzqpSrMK/3JBAVBI9u3NWtOhWNw5AMQfrUFYB0+bDHTSw17z++WJLsPsxAuK+oSddsxk4d7F/JcdDPM1M5YAhA==" - }, - "@rushstack/eslint-patch": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.0.tgz", - "integrity": "sha512-IthPJsJR85GhOkp3Hvp8zFOPK5ynKn6STyHa/WZpioK7E1aYDiBzpqQPrngc14DszIUkIrdd3k9Iu0XSzlP/1w==", - "dev": true - }, - "@sinclair/typebox": { - "version": "0.25.24", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", - "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@testing-library/dom": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.0.tgz", - "integrity": "sha512-Dffe68pGwI6WlLRYR2I0piIkyole9cSBH5jGQKCGMRpHW5RHCqAUaqc2Kv0tUyd4dU4DLPKhJIjyKOnjv4tuUw==", - "dev": true, - "peer": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "^5.0.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "peer": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "peer": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "peer": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "peer": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "peer": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "peer": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@testing-library/jest-dom": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", - "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", - "dev": true, - "requires": { - "@adobe/css-tools": "^4.0.1", - "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.5.6", - "lodash": "^4.17.15", - "redent": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@testing-library/react": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", - "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.5.0", - "@types/react-dom": "^18.0.0" - }, - "dependencies": { - "@testing-library/dom": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz", - "integrity": "sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "^5.0.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.4.4", - "pretty-format": "^27.0.2" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "@testing-library/user-event": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz", - "integrity": "sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.12.5" - } - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true - }, - "@types/aria-query": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", - "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==", - "dev": true - }, - "@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", - "dev": true, - "requires": { - "@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.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.18.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.5.tgz", - "integrity": "sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@types/node": "*" - } - }, - "@types/eslint": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.0.tgz", - "integrity": "sha512-nbq2mvc/tBrK9zQQuItvjJl++GTN5j06DaPtp3hZCpngmG6Q3xoyEmd0TwZI0gAy/G1X0zhGBbr2imsGFdFV0g==", - "dev": true, - "peer": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", - "dev": true, - "peer": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", - "dev": true - }, - "@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.35", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz", - "integrity": "sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/http-proxy": { - "version": "1.17.11", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.11.tgz", - "integrity": "sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "29.5.1", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.1.tgz", - "integrity": "sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==", - "dev": true, - "requires": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - } - } - }, - "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "@types/mime": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true, - "optional": true, - "peer": true - }, - "@types/node": { - "version": "20.2.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.3.tgz", - "integrity": "sha512-pg9d0yC4rVNWQzX8U7xb4olIOFuuVL9za3bzMT2pu2SU0SNEi66i2qrvhE2qt0HvkhuCaWJu7pLNOt/Pj8BIrw==", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, - "@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", - "dev": true - }, - "@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" - }, - "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true, - "optional": true, - "peer": true - }, - "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true, - "optional": true, - "peer": true - }, - "@types/react": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.7.tgz", - "integrity": "sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==", - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "@types/react-dom": { - "version": "18.2.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz", - "integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-1vz2yObaQkLL7YFe/pme2cpvDsCwI1WXIfL+5eLz0MI9gFG24Re16RzUsI8t9XZn9ZWvgLNDrJBmrqXJO7GNQQ==", - "requires": { - "@types/react": "*" - } - }, - "@types/react-transition-group": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", - "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==", - "requires": { - "@types/react": "*" - } - }, - "@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" - }, - "@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", - "dev": true - }, - "@types/send": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz", - "integrity": "sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/serve-static": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz", - "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@types/mime": "*", - "@types/node": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/testing-library__jest-dom": { - "version": "5.14.6", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.6.tgz", - "integrity": "sha512-FkHXCb+ikSoUP4Y4rOslzTdX5sqYwMxfefKh1GmZ8ce1GOkEHntSp6b5cGadmNfp5e4BMEWOMx+WSKd5/MqlDA==", - "dev": true, - "requires": { - "@types/jest": "*" - } - }, - "@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.0.tgz", - "integrity": "sha512-78B+anHLF1TI8Jn/cD0Q00TBYdMgjdOn980JfAVa9yw5sop8nyTfVOQAv6LWywkOGLclDBtv5z3oxN4w7jxyNg==", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/type-utils": "5.60.0", - "@typescript-eslint/utils": "5.60.0", - "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz", - "integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0" - } - }, - "@typescript-eslint/types": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", - "integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz", - "integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.0.tgz", - "integrity": "sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/typescript-estree": "5.60.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", - "integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.60.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - } - } - }, - "@typescript-eslint/experimental-utils": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.59.7.tgz", - "integrity": "sha512-jqM0Cjfvta/sBlY1MxdXYv853/dJUC2wmUWnKoG2srwp0njNGQ6Zu/XLWoRFiLvocQbzBbpHkPFwKgC2UqyovA==", - "dev": true, - "requires": { - "@typescript-eslint/utils": "5.59.7" - } - }, - "@typescript-eslint/parser": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.0.tgz", - "integrity": "sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/typescript-estree": "5.60.0", - "debug": "^4.3.4" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz", - "integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0" - } - }, - "@typescript-eslint/types": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", - "integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz", - "integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", - "integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.60.0", - "eslint-visitor-keys": "^3.3.0" - } - } - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz", - "integrity": "sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/visitor-keys": "5.59.7" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.0.tgz", - "integrity": "sha512-X7NsRQddORMYRFH7FWo6sA9Y/zbJ8s1x1RIAtnlj6YprbToTiQnM6vxcMu7iYhdunmoC0rUWlca13D5DVHkK2g==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.60.0", - "@typescript-eslint/utils": "5.60.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz", - "integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0" - } - }, - "@typescript-eslint/types": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz", - "integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz", - "integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/visitor-keys": "5.60.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.0.tgz", - "integrity": "sha512-ba51uMqDtfLQ5+xHtwlO84vkdjrqNzOnqrnwbMHMRY8Tqeme8C2Q8Fc7LajfGR+e3/4LoYiWXUM6BpIIbHJ4hQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.60.0", - "@typescript-eslint/types": "5.60.0", - "@typescript-eslint/typescript-estree": "5.60.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.60.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz", - "integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.60.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - } - } - }, - "@typescript-eslint/types": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.7.tgz", - "integrity": "sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz", - "integrity": "sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/visitor-keys": "5.59.7", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.7.tgz", - "integrity": "sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.7", - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/typescript-estree": "5.59.7", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "dependencies": { - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.59.7", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz", - "integrity": "sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.59.7", - "eslint-visitor-keys": "^3.3.0" - } - }, - "@vitejs/plugin-react": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.1.tgz", - "integrity": "sha512-g25lL98essfeSj43HJ0o4DMp0325XK0ITkxpgChzJU/CyemgyChtlxfnRbjfwxDGCTRxTiXtQAsdebQXKMRSOA==", - "dev": true, - "requires": { - "@babel/core": "^7.22.5", - "@babel/plugin-transform-react-jsx-self": "^7.22.5", - "@babel/plugin-transform-react-jsx-source": "^7.22.5", - "react-refresh": "^0.14.0" - } - }, - "@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true, - "peer": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true, - "peer": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", - "dev": true, - "peer": true - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true, - "peer": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", - "dev": true, - "peer": true, - "requires": { - "@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": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dev": true, - "peer": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dev": true, - "peer": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true, - "peer": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", - "dev": true, - "peer": true, - "requires": { - "@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": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", - "dev": true, - "peer": true, - "requires": { - "@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": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", - "dev": true, - "peer": true, - "requires": { - "@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": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", - "dev": true, - "peer": true, - "requires": { - "@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": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "peer": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "peer": true - }, - "abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "dev": true - }, - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - } - } - }, - "acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "peer": true, - "requires": {} - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true - }, - "address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", - "dev": true, - "requires": { - "deep-equal": "^2.0.5" - } - }, - "array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - } - }, - "array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.tosorted": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", - "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" - } - }, - "ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true - }, - "axe-core": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.1.tgz", - "integrity": "sha512-sCXXUhA+cljomZ3ZAwb8i1p3oOlkABzPy08ZDAoGcYuvtBPlQ1Ytde129ArXyHWDhfeewq7rlx9F+cUx2SSlkg==", - "dev": true - }, - "axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", - "requires": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "axobject-query": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", - "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", - "dev": true, - "requires": { - "deep-equal": "^2.0.5" - } - }, - "babel-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", - "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", - "dev": true, - "requires": { - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", - "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "requires": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - } - }, - "babel-plugin-transform-react-remove-prop-types": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", - "integrity": "sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==", - "dev": true - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", - "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^27.5.1", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "babel-preset-react-app": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz", - "integrity": "sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==", - "dev": true, - "requires": { - "@babel/core": "^7.16.0", - "@babel/plugin-proposal-class-properties": "^7.16.0", - "@babel/plugin-proposal-decorators": "^7.16.4", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.0", - "@babel/plugin-proposal-numeric-separator": "^7.16.0", - "@babel/plugin-proposal-optional-chaining": "^7.16.0", - "@babel/plugin-proposal-private-methods": "^7.16.0", - "@babel/plugin-transform-flow-strip-types": "^7.16.0", - "@babel/plugin-transform-react-display-name": "^7.16.0", - "@babel/plugin-transform-runtime": "^7.16.4", - "@babel/preset-env": "^7.16.4", - "@babel/preset-react": "^7.16.0", - "@babel/preset-typescript": "^7.16.0", - "@babel/runtime": "^7.16.3", - "babel-plugin-macros": "^3.1.0", - "babel-plugin-transform-react-remove-prop-types": "^0.4.24" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "bfj": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz", - "integrity": "sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==", - "requires": { - "bluebird": "^3.5.5", - "check-types": "^11.1.1", - "hoopy": "^0.1.4", - "tryer": "^1.0.1" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "bufferutil": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", - "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "node-gyp-build": "^4.3.0" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" - }, - "caniuse-lite": { - "version": "1.0.30001489", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz", - "integrity": "sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" - } - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "chart.js": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.0.tgz", - "integrity": "sha512-ynG0E79xGfMaV2xAHdbhwiPLczxnNNnasrmPEXriXsPJGjmhOBYzFVEsB65w2qMDz+CaBJJuJD0inE/ab/h36g==", - "requires": { - "@kurkle/color": "^0.3.0" - } - }, - "check-types": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/check-types/-/check-types-11.2.2.tgz", - "integrity": "sha512-HBiYvXvn9Z70Z88XKjz3AEKd4HJhBXsa3j7xFnITAzoS8+q6eIGi8qDB8FKPBAjtuxjI/zFpwuiCb8oDtKOYrA==" - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "peer": true - }, - "ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", - "dev": true - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "core-js-compat": { - "version": "3.30.2", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.2.tgz", - "integrity": "sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA==", - "dev": true, - "requires": { - "browserslist": "^4.21.5" - } - }, - "cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "dev": true - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } - } - }, - "csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" - }, - "d3-array": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", - "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" - }, - "d3-collection": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", - "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" - }, - "d3-color": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", - "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" - }, - "d3-dispatch": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", - "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" - }, - "d3-ease": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", - "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" - }, - "d3-format": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", - "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" - }, - "d3-interpolate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", - "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", - "requires": { - "d3-color": "1" - } - }, - "d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" - }, - "d3-scale": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz", - "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==", - "requires": { - "d3-array": "^1.2.0", - "d3-collection": "1", - "d3-color": "1", - "d3-format": "1", - "d3-interpolate": "1", - "d3-time": "1", - "d3-time-format": "2" - } - }, - "d3-selection": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", - "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==" - }, - "d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", - "requires": { - "d3-path": "1" - } - }, - "d3-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", - "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" - }, - "d3-time-format": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", - "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", - "requires": { - "d3-time": "1" - } - }, - "d3-timer": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", - "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" - }, - "d3-transition": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz", - "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==", - "requires": { - "d3-color": "1", - "d3-dispatch": "1", - "d3-ease": "1", - "d3-interpolate": "1", - "d3-selection": "^1.1.0", - "d3-timer": "1" - } - }, - "damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "deep-equal": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz", - "integrity": "sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.0", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - } - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true - }, - "define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true - }, - "define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dev": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "detect-port-alt": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz", - "integrity": "sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==", - "dev": true, - "requires": { - "address": "^1.0.1", - "debug": "^2.6.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true - }, - "dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "requires": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, - "dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "dev": true - }, - "dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", - "dev": true - }, - "duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.4.407", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.407.tgz", - "integrity": "sha512-5smEvFSFYMv90tICOzRVP7Opp98DAC4KW7RRipg3BuNpGbbV3N+x24Zh3sbLb1T5haGtOSy/hrBfXsWnIM9aCg==", - "dev": true - }, - "emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "enhanced-resolve": { - "version": "5.14.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz", - "integrity": "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==", - "dev": true, - "peer": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "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.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - } - }, - "es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - } - }, - "es-module-lexer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.2.1.tgz", - "integrity": "sha512-9978wrXM50Y4rTMmW5kXIC09ZdXQZqkE4mxhwkd8VbzsGkXGPgV4zWuqQJgCEzYngdo2dYDa0l8xhX4fkSwJSg==", - "dev": true, - "peer": true - }, - "es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - } - }, - "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "esbuild": { - "version": "0.17.19", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", - "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", - "dev": true, - "requires": { - "@esbuild/android-arm": "0.17.19", - "@esbuild/android-arm64": "0.17.19", - "@esbuild/android-x64": "0.17.19", - "@esbuild/darwin-arm64": "0.17.19", - "@esbuild/darwin-x64": "0.17.19", - "@esbuild/freebsd-arm64": "0.17.19", - "@esbuild/freebsd-x64": "0.17.19", - "@esbuild/linux-arm": "0.17.19", - "@esbuild/linux-arm64": "0.17.19", - "@esbuild/linux-ia32": "0.17.19", - "@esbuild/linux-loong64": "0.17.19", - "@esbuild/linux-mips64el": "0.17.19", - "@esbuild/linux-ppc64": "0.17.19", - "@esbuild/linux-riscv64": "0.17.19", - "@esbuild/linux-s390x": "0.17.19", - "@esbuild/linux-x64": "0.17.19", - "@esbuild/netbsd-x64": "0.17.19", - "@esbuild/openbsd-x64": "0.17.19", - "@esbuild/sunos-x64": "0.17.19", - "@esbuild/win32-arm64": "0.17.19", - "@esbuild/win32-ia32": "0.17.19", - "@esbuild/win32-x64": "0.17.19" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, - "escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - } - } - }, - "eslint": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", - "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.43.0", - "@humanwhocodes/config-array": "^0.11.10", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", - "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", - "import-fresh": "^3.0.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.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "eslint-config-airbnb": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", - "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", - "dev": true, - "requires": { - "eslint-config-airbnb-base": "^15.0.0", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5" - } - }, - "eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", - "dev": true, - "requires": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "eslint-config-react-app": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz", - "integrity": "sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==", - "dev": true, - "requires": { - "@babel/core": "^7.16.0", - "@babel/eslint-parser": "^7.16.3", - "@rushstack/eslint-patch": "^1.1.0", - "@typescript-eslint/eslint-plugin": "^5.5.0", - "@typescript-eslint/parser": "^5.5.0", - "babel-preset-react-app": "^10.0.1", - "confusing-browser-globals": "^1.0.11", - "eslint-plugin-flowtype": "^8.0.3", - "eslint-plugin-import": "^2.25.3", - "eslint-plugin-jest": "^25.3.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.27.1", - "eslint-plugin-react-hooks": "^4.3.0", - "eslint-plugin-testing-library": "^5.0.1" - } - }, - "eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "requires": { - "debug": "^3.2.7" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-plugin-flowtype": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", - "integrity": "sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==", - "dev": true, - "requires": { - "lodash": "^4.17.21", - "string-natural-compare": "^3.0.1" - } - }, - "eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", - "dev": true, - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "eslint-plugin-jest": { - "version": "25.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz", - "integrity": "sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==", - "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "^5.0.0" - } - }, - "eslint-plugin-jsx-a11y": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz", - "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.20.7", - "aria-query": "^5.1.3", - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.6.2", - "axobject-query": "^3.1.1", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.3", - "language-tags": "=1.0.5", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "eslint-plugin-react": { - "version": "7.32.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", - "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", - "dev": true, - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.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.0", - "string.prototype.matchall": "^4.0.8" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "dev": true, - "requires": {} - }, - "eslint-plugin-testing-library": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.0.tgz", - "integrity": "sha512-ELY7Gefo+61OfXKlQeXNIDVVLPcvKTeiQOoMZG9TeuWa7Ln4dUNRv8JdRWBQI9Mbb427XGlVB1aa1QPZxBJM8Q==", - "dev": true, - "requires": { - "@typescript-eslint/utils": "^5.58.0" - } - }, - "eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true - }, - "espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", - "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "peer": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.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" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", - "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "filesize": { - "version": "8.0.7", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", - "integrity": "sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "fork-ts-checker-webpack-plugin": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", - "integrity": "sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@types/json-schema": "^7.0.5", - "chalk": "^4.1.0", - "chokidar": "^3.4.2", - "cosmiconfig": "^6.0.0", - "deepmerge": "^4.2.2", - "fs-extra": "^9.0.0", - "glob": "^7.1.6", - "memfs": "^3.1.2", - "minimatch": "^3.0.4", - "schema-utils": "2.7.0", - "semver": "^7.3.2", - "tapable": "^1.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", - "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" - } - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "schema-utils": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", - "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.4", - "ajv": "^6.12.2", - "ajv-keywords": "^3.4.1" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "dev": true - } - } - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs-monkey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz", - "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - } - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "peer": true - }, - "global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "requires": { - "global-prefix": "^3.0.0" - } - }, - "global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - }, - "dependencies": { - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "gzip-size": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", - "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", - "dev": true, - "requires": { - "duplexer": "^0.1.2" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "requires": { - "react-is": "^16.7.0" - }, - "dependencies": { - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } - } - }, - "hoopy": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", - "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==" - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "requires": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", - "dev": true, - "requires": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - } - }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true - }, - "immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - } - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true - }, - "is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true - }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-root": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz", - "integrity": "sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==", - "dev": true - }, - "is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "dev": true - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", - "dev": true, - "requires": { - "@jest/core": "^27.5.1", - "import-local": "^3.0.2", - "jest-cli": "^27.5.1" - } - }, - "jest-changed-files": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", - "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "execa": "^5.0.0", - "throat": "^6.0.1" - } - }, - "jest-circus": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "dev": true - }, - "expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - }, - "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-cli": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", - "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", - "dev": true, - "requires": { - "@jest/core": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-config": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", - "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", - "dev": true, - "requires": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.5.1", - "@jest/types": "^27.5.1", - "babel-jest": "^27.5.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.9", - "jest-circus": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-jasmine2": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-diff": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", - "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-docblock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" - } - }, - "jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - } - }, - "jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", - "dev": true - }, - "jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - } - }, - "jest-jasmine2": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", - "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "throat": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "dev": true - }, - "expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - }, - "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", - "dev": true, - "requires": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - }, - "dependencies": { - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - } - } - }, - "jest-matcher-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", - "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-message-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", - "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.5.0", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.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" - } - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", - "dev": true - }, - "jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" - } - }, - "jest-runner": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", - "dev": true, - "requires": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-runtime": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", - "dev": true, - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - } - }, - "jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", - "dev": true, - "requires": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "dev": true - }, - "expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - }, - "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "leven": "^3.1.0", - "pretty-format": "^27.5.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-watch-typeahead": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz", - "integrity": "sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.1", - "chalk": "^4.0.0", - "jest-regex-util": "^28.0.0", - "jest-watcher": "^28.0.0", - "slash": "^4.0.0", - "string-length": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "dependencies": { - "@jest/console": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", - "dev": true - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", - "dev": true - }, - "jest-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-watcher": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", - "dev": true, - "requires": { - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" - }, - "dependencies": { - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - } - } - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true - }, - "string-length": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz", - "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==", - "dev": true, - "requires": { - "char-regex": "^2.0.0", - "strip-ansi": "^7.0.1" - }, - "dependencies": { - "char-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz", - "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==", - "dev": true - } - } - }, - "strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - } - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", - "dev": true, - "requires": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.5.1", - "string-length": "^4.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "requires": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", - "dev": true, - "requires": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", - "dev": true - }, - "language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", - "dev": true, - "requires": { - "language-subtag-registry": "~0.3.2" - } - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "peer": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "memfs": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.1.tgz", - "integrity": "sha512-UWbFJKvj5k+nETdteFndTpYxdeTMox/ULeqX5k/dpaQJCCFmj5EeKv3dBcyO2xmkRAx2vppRu5dVG7SOtsGOzA==", - "dev": true, - "requires": { - "fs-monkey": "^1.0.3" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "peer": true - }, - "node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", - "dev": true, - "optional": true, - "peer": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node-releases": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", - "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "nwsapi": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.4.tgz", - "integrity": "sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g==", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true - }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", - "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "object.hasown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", - "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", - "dev": true, - "requires": { - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dev": true, - "requires": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true - } - } - }, - "postcss": { - "version": "8.4.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", - "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", - "dev": true, - "requires": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - } - } - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - }, - "dependencies": { - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } - } - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true - }, - "querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "peer": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "react-chartjs-2": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", - "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", - "requires": {} - }, - "react-dev-utils": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", - "integrity": "sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.16.0", - "address": "^1.1.2", - "browserslist": "^4.18.1", - "chalk": "^4.1.2", - "cross-spawn": "^7.0.3", - "detect-port-alt": "^1.1.6", - "escape-string-regexp": "^4.0.0", - "filesize": "^8.0.6", - "find-up": "^5.0.0", - "fork-ts-checker-webpack-plugin": "^6.5.0", - "global-modules": "^2.0.0", - "globby": "^11.0.4", - "gzip-size": "^6.0.0", - "immer": "^9.0.7", - "is-root": "^2.1.0", - "loader-utils": "^3.2.0", - "open": "^8.4.0", - "pkg-up": "^3.1.0", - "prompts": "^2.4.2", - "react-error-overlay": "^6.0.11", - "recursive-readdir": "^2.2.2", - "shell-quote": "^1.7.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "requires": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - } - }, - "react-error-overlay": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", - "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==", - "dev": true - }, - "react-icons": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.8.0.tgz", - "integrity": "sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg==", - "requires": {} - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" - }, - "react-liquid-gauge": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/react-liquid-gauge/-/react-liquid-gauge-1.2.4.tgz", - "integrity": "sha512-GBhex9mag2gL4t8sfFbqMYRpGFWN/scJ3p05FIiuNu89l0LSUFem85m5SrGV2qORC0Zn6DyS/VGctMII6piFAA==", - "requires": { - "d3-color": "^1.0.2", - "d3-ease": "^1.0.2", - "d3-interpolate": "^1.1.5", - "d3-scale": "^1.0.6", - "d3-selection": "^1.1.0", - "d3-shape": "^1.2.0", - "d3-timer": "^1.0.3", - "d3-transition": "^1.1.0", - "prop-types": "^15.5.8" - } - }, - "react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==" - }, - "react-router": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.11.2.tgz", - "integrity": "sha512-74z9xUSaSX07t3LM+pS6Un0T55ibUE/79CzfZpy5wsPDZaea1F8QkrsiyRnA2YQ7LwE/umaydzXZV80iDCPkMg==", - "requires": { - "@remix-run/router": "1.6.2" - } - }, - "react-router-dom": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.11.2.tgz", - "integrity": "sha512-JNbKtAeh1VSJQnH6RvBDNhxNwemRj7KxCzc5jb7zvDSKRnPWIFj9pO+eXqjM69gQJ0r46hSz1x4l9y0651DKWw==", - "requires": { - "@remix-run/router": "1.6.2", - "react-router": "6.11.2" - } - }, - "react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "requires": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - } - }, - "react-use-websocket": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/react-use-websocket/-/react-use-websocket-4.3.1.tgz", - "integrity": "sha512-zHPLWrgcqydJaak2O5V9hiz4q2dwkwqNQqpgFVmSuPxLZdsZlnDs8DVHy3WtHH+A6ms/8aHIyX7+7ulOcrnR0Q==", - "requires": {} - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "dev": true, - "requires": { - "minimatch": "^3.0.5" - } - }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, - "regenerator-transform": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", - "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - } - }, - "regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "dev": true, - "requires": { - "@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" - } - }, - "regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "reselect": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", - "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" - }, - "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "requires": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve.exports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", - "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "fsevents": "~2.3.2" - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "peer": true - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, - "scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "requires": { - "loose-envify": "^1.1.0" - } - }, - "schema-utils": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.2.tgz", - "integrity": "sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==", - "dev": true, - "peer": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dev": true, - "peer": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" - }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dev": true, - "requires": { - "internal-slot": "^1.0.4" - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-natural-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", - "integrity": "sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==", - "dev": true - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - } - } - }, - "string.prototype.matchall": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", - "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4" - } - }, - "string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "svg-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", - "dev": true - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "peer": true - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "terser": { - "version": "5.17.6", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.6.tgz", - "integrity": "sha512-V8QHcs8YuyLkLHsJO5ucyff1ykrLVsR4dNnS//L5Y3NiSXpbK1J+WMVUs67eI0KTxs9JtHhgEQpXQVHlHI92DQ==", - "dev": true, - "peer": true, - "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "peer": true - } - } - }, - "terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", - "dev": true, - "peer": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.17", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "throat": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", - "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", - "dev": true - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tough-cookie": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", - "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", - "dev": true, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "dependencies": { - "universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true - } - } - }, - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "tryer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", - "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==" - }, - "tsconfck": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-2.1.1.tgz", - "integrity": "sha512-ZPCkJBKASZBmBUNqGHmRhdhM8pJYDdOXp4nRgj/O0JwUwsMq50lCDRQP/M5GBNAA0elPrq4gAeu4dkaVCuKWww==", - "dev": true, - "requires": {} - }, - "tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - } - } - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - }, - "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - } - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - }, - "update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "utf-8-validate": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", - "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "node-gyp-build": "^4.3.0" - } - }, - "v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true - } - } - }, - "vite": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", - "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", - "dev": true, - "requires": { - "esbuild": "^0.17.5", - "fsevents": "~2.3.2", - "postcss": "^8.4.23", - "rollup": "^3.21.0" - }, - "dependencies": { - "rollup": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.25.1.tgz", - "integrity": "sha512-tywOR+rwIt5m2ZAWSe5AIJcTat8vGlnPFAv15ycCrw33t6iFsXZ6mzHVFh2psSjxQPmI+xgzMZZizUAukBI4aQ==", - "dev": true, - "requires": { - "fsevents": "~2.3.2" - } - } - } - }, - "vite-plugin-svgr": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/vite-plugin-svgr/-/vite-plugin-svgr-3.2.0.tgz", - "integrity": "sha512-Uvq6niTvhqJU6ga78qLKBFJSDvxWhOnyfQSoKpDPMAGxJPo5S3+9hyjExE5YDj6Lpa4uaLkGc1cBgxXov+LjSw==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^5.0.2", - "@svgr/core": "^7.0.0", - "@svgr/plugin-jsx": "^7.0.0" - }, - "dependencies": { - "@rollup/pluginutils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", - "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", - "dev": true, - "requires": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - } - }, - "@svgr/babel-plugin-add-jsx-attribute": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-7.0.0.tgz", - "integrity": "sha512-khWbXesWIP9v8HuKCl2NU2HNAyqpSQ/vkIl36Nbn4HIwEYSRWL0H7Gs6idJdha2DkpFDWlsqMELvoCE8lfFY6Q==", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-remove-jsx-attribute": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-7.0.0.tgz", - "integrity": "sha512-iiZaIvb3H/c7d3TH2HBeK91uI2rMhZNwnsIrvd7ZwGLkFw6mmunOCoVnjdYua662MqGFxlN9xTq4fv9hgR4VXQ==", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-remove-jsx-empty-expression": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-7.0.0.tgz", - "integrity": "sha512-sQQmyo+qegBx8DfFc04PFmIO1FP1MHI1/QEpzcIcclo5OAISsOJPW76ZIs0bDyO/DBSJEa/tDa1W26pVtt0FRw==", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-7.0.0.tgz", - "integrity": "sha512-i6MaAqIZXDOJeikJuzocByBf8zO+meLwfQ/qMHIjCcvpnfvWf82PFvredEZElErB5glQFJa2KVKk8N2xV6tRRA==", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-svg-dynamic-title": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-7.0.0.tgz", - "integrity": "sha512-BoVSh6ge3SLLpKC0pmmN9DFlqgFy4NxNgdZNLPNJWBUU7TQpDWeBuyVuDW88iXydb5Cv0ReC+ffa5h3VrKfk1w==", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-svg-em-dimensions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-7.0.0.tgz", - "integrity": "sha512-tNDcBa+hYn0gO+GkP/AuNKdVtMufVhU9fdzu+vUQsR18RIJ9RWe7h/pSBY338RO08wArntwbDk5WhQBmhf2PaA==", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-transform-react-native-svg": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-7.0.0.tgz", - "integrity": "sha512-qw54u8ljCJYL2KtBOjI5z7Nzg8LnSvQOP5hPKj77H4VQL4+HdKbAT5pnkkZLmHKYwzsIHSYKXxHouD8zZamCFQ==", - "dev": true, - "requires": {} - }, - "@svgr/babel-plugin-transform-svg-component": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-7.0.0.tgz", - "integrity": "sha512-CcFECkDj98daOg9jE3Bh3uyD9kzevCAnZ+UtzG6+BQG/jOQ2OA3jHnX6iG4G1MCJkUQFnUvEv33NvQfqrb/F3A==", - "dev": true, - "requires": {} - }, - "@svgr/babel-preset": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-7.0.0.tgz", - "integrity": "sha512-EX/NHeFa30j5UjldQGVQikuuQNHUdGmbh9kEpBKofGUtF0GUPJ4T4rhoYiqDAOmBOxojyot36JIFiDUHUK1ilQ==", - "dev": true, - "requires": { - "@svgr/babel-plugin-add-jsx-attribute": "^7.0.0", - "@svgr/babel-plugin-remove-jsx-attribute": "^7.0.0", - "@svgr/babel-plugin-remove-jsx-empty-expression": "^7.0.0", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^7.0.0", - "@svgr/babel-plugin-svg-dynamic-title": "^7.0.0", - "@svgr/babel-plugin-svg-em-dimensions": "^7.0.0", - "@svgr/babel-plugin-transform-react-native-svg": "^7.0.0", - "@svgr/babel-plugin-transform-svg-component": "^7.0.0" - } - }, - "@svgr/core": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-7.0.0.tgz", - "integrity": "sha512-ztAoxkaKhRVloa3XydohgQQCb0/8x9T63yXovpmHzKMkHO6pkjdsIAWKOS4bE95P/2quVh1NtjSKlMRNzSBffw==", - "dev": true, - "requires": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "^7.0.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3" - } - }, - "@svgr/hast-util-to-babel-ast": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-7.0.0.tgz", - "integrity": "sha512-42Ej9sDDEmsJKjrfQ1PHmiDiHagh/u9AHO9QWbeNx4KmD9yS5d1XHmXUNINfUcykAU+4431Cn+k6Vn5mWBYimQ==", - "dev": true, - "requires": { - "@babel/types": "^7.21.3", - "entities": "^4.4.0" - } - }, - "@svgr/plugin-jsx": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-7.0.0.tgz", - "integrity": "sha512-SWlTpPQmBUtLKxXWgpv8syzqIU8XgFRvyhfkam2So8b3BE0OS0HPe5UfmlJ2KIC+a7dpuuYovPR2WAQuSyMoPw==", - "dev": true, - "requires": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "^7.0.0", - "@svgr/hast-util-to-babel-ast": "^7.0.0", - "svg-parser": "^2.0.4" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", - "dev": true, - "requires": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - } - }, - "estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - } - } - }, - "vite-tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-jGpus0eUy5qbbMVGiTxCL1iB9ZGN6Bd37VGLJU39kTDD6ZfULTTb1bcc5IeTWqWJKiWV5YihCaibeASPiGi8kw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^2.1.0" + "randombytes": "^2.1.0" } }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "node_modules/workbox-build/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" } }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "node_modules/workbox-build/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { - "xml-name-validator": "^3.0.0" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "node_modules/workbox-build/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", "dev": true, - "requires": { - "makeerror": "1.0.12" + "dependencies": { + "punycode": "^2.1.0" } }, - "watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "node_modules/workbox-build/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "node_modules/workbox-build/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", "dev": true, - "peer": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" } }, - "web-vitals": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", - "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==" + "node_modules/workbox-cacheable-response": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-7.0.0.tgz", + "integrity": "sha512-0lrtyGHn/LH8kKAJVOQfSu3/80WDc9Ma8ng0p2i/5HuUndGttH+mGMSvOskjOdFImLs2XZIimErp7tSOPmu/6g==", + "dev": true, + "dependencies": { + "workbox-core": "7.0.0" + } }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "node_modules/workbox-core": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-7.0.0.tgz", + "integrity": "sha512-81JkAAZtfVP8darBpfRTovHg8DGAVrKFgHpOArZbdFd78VqHr5Iw65f2guwjE2NlCFbPFDoez3D3/6ZvhI/rwQ==", "dev": true }, - "webpack": { - "version": "5.84.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.84.0.tgz", - "integrity": "sha512-XezNK3kwJq6IyeoZmZ1uEqQs+42nTqIi4jYM/YjLwaJedUC1N3bwnCC0+UcnHJPfqWX0kGrQnMIvZZyWYaIZrA==", + "node_modules/workbox-expiration": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-7.0.0.tgz", + "integrity": "sha512-MLK+fogW+pC3IWU9SFE+FRStvDVutwJMR5if1g7oBJx3qwmO69BNoJQVaMXq41R0gg3MzxVfwOGKx3i9P6sOLQ==", "dev": true, - "peer": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@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.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.14.1", - "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.1.2", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, "dependencies": { - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "peer": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "peer": true - } + "idb": "^7.0.1", + "workbox-core": "7.0.0" } }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "node_modules/workbox-google-analytics": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-7.0.0.tgz", + "integrity": "sha512-MEYM1JTn/qiC3DbpvP2BVhyIH+dV/5BjHk756u9VbwuAhu0QHyKscTnisQuz21lfRpOwiS9z4XdqeVAKol0bzg==", "dev": true, - "peer": true + "dependencies": { + "workbox-background-sync": "7.0.0", + "workbox-core": "7.0.0", + "workbox-routing": "7.0.0", + "workbox-strategies": "7.0.0" + } }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "node_modules/workbox-navigation-preload": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-7.0.0.tgz", + "integrity": "sha512-juWCSrxo/fiMz3RsvDspeSLGmbgC0U9tKqcUPZBCf35s64wlaLXyn2KdHHXVQrb2cqF7I0Hc9siQalainmnXJA==", "dev": true, - "requires": { - "iconv-lite": "0.4.24" - }, "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } + "workbox-core": "7.0.0" } }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true + "node_modules/workbox-precaching": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-7.0.0.tgz", + "integrity": "sha512-EC0vol623LJqTJo1mkhD9DZmMP604vHqni3EohhQVwhJlTgyKyOkMrZNy5/QHfOby+39xqC01gv4LjOm4HSfnA==", + "dev": true, + "dependencies": { + "workbox-core": "7.0.0", + "workbox-routing": "7.0.0", + "workbox-strategies": "7.0.0" + } }, - "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "node_modules/workbox-range-requests": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-7.0.0.tgz", + "integrity": "sha512-SxAzoVl9j/zRU9OT5+IQs7pbJBOUOlriB8Gn9YMvi38BNZRbM+RvkujHMo8FOe9IWrqqwYgDFBfv6sk76I1yaQ==", "dev": true, - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" + "dependencies": { + "workbox-core": "7.0.0" } }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/workbox-recipes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-7.0.0.tgz", + "integrity": "sha512-DntcK9wuG3rYQOONWC0PejxYYIDHyWWZB/ueTbOUDQgefaeIj1kJ7pdP3LZV2lfrj8XXXBWt+JDRSw1lLLOnww==", "dev": true, - "requires": { - "isexe": "^2.0.0" + "dependencies": { + "workbox-cacheable-response": "7.0.0", + "workbox-core": "7.0.0", + "workbox-expiration": "7.0.0", + "workbox-precaching": "7.0.0", + "workbox-routing": "7.0.0", + "workbox-strategies": "7.0.0" } }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "node_modules/workbox-routing": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-7.0.0.tgz", + "integrity": "sha512-8YxLr3xvqidnbVeGyRGkaV4YdlKkn5qZ1LfEePW3dq+ydE73hUUJJuLmGEykW3fMX8x8mNdL0XrWgotcuZjIvA==", "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "dependencies": { + "workbox-core": "7.0.0" } }, - "which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "node_modules/workbox-strategies": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-7.0.0.tgz", + "integrity": "sha512-dg3qJU7tR/Gcd/XXOOo7x9QoCI9nk74JopaJaYAQ+ugLi57gPsXycVdBnYbayVj34m6Y8ppPwIuecrzkpBVwbA==", "dev": true, - "requires": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" + "dependencies": { + "workbox-core": "7.0.0" } }, - "which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "node_modules/workbox-streams": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-7.0.0.tgz", + "integrity": "sha512-moVsh+5to//l6IERWceYKGiftc+prNnqOp2sgALJJFbnNVpTXzKISlTIsrWY+ogMqt+x1oMazIdHj25kBSq/HQ==", "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "dependencies": { + "workbox-core": "7.0.0", + "workbox-routing": "7.0.0" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "node_modules/workbox-sw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-7.0.0.tgz", + "integrity": "sha512-SWfEouQfjRiZ7GNABzHUKUyj8pCoe+RwjfOIajcx6J5mtgKkN+t8UToHnpaJL5UVVOf5YhJh+OHhbVNIHe+LVA==", "dev": true }, - "wrap-ansi": { + "node_modules/workbox-window": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-7.0.0.tgz", + "integrity": "sha512-j7P/bsAWE/a7sxqTzXo3P2ALb1reTfZdvVp6OJ/uLr/C2kZAMvjeWGm8V4htQhor7DOvYg0sSbFN2+flT5U0qA==", + "dev": true, + "dependencies": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "7.0.0" + } + }, + "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "wrappy": { + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "write-file-atomic": { + "node_modules/write-file-atomic": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, - "requires": { + "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", "signal-exit": "^3.0.2", "typedarray-to-buffer": "^3.1.5" } }, - "ws": { + "node_modules/ws": { "version": "7.5.9", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", "dev": true, - "requires": {} + "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 + } + } }, - "xml-name-validator": { + "node_modules/xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "dev": true }, - "xmlchars": { + "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "dev": true }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "yallist": { + "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "yaml": { + "node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } }, - "yargs": { + "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, - "requires": { + "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", @@ -26455,19 +16469,54 @@ "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "yocto-queue": { + "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zustand": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.3.9.tgz", + "integrity": "sha512-Tat5r8jOMG1Vcsj8uldMyqYKC5IZvQif8zetmLHs9WoZlntTHmIoNM8TpLRY31ExncuUvUOXehd0kvahkuHjDw==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/webserver/web-interface/package.json b/webserver/web-interface/package.json index d77797a1..b7734e1b 100644 --- a/webserver/web-interface/package.json +++ b/webserver/web-interface/package.json @@ -9,13 +9,13 @@ "@mui/icons-material": "^5.11.16", "@mui/material": "^5.13.2", "@mui/x-data-grid": "^5.17.26", + "@uiw/react-color": "^1.3.1", "axios": "^1.4.0", "bfj": "^7.0.2", "camelcase": "^6.3.0", "chart.js": "^4.3.0", "fs-extra": "^10.1.0", - "prop-types": "^15.8.1", - "react": "^18.2.0", + "react": "^15.0.0 || ^17.0.0 || ^18.0.0", "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", "react-icons": "^4.8.0", @@ -23,17 +23,21 @@ "react-refresh": "^0.14.0", "react-router-dom": "^6.11.2", "react-use-websocket": "^4.3.1", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "zustand": "^4.3.9" }, "devDependencies": { "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", - "@typescript-eslint/eslint-plugin": "^5.60.0", - "@typescript-eslint/parser": "^5.60.0", + "@types/node": "^20.4.1", + "@typescript-eslint/eslint-plugin": "^5.61.0", + "@typescript-eslint/parser": "^5.61.0", "@vitejs/plugin-react": "^4.0.1", "babel-jest": "^27.5.1", "browserslist": "^4.21.5", + "compress-create-react-app": "^1.4.2", + "compression-webpack-plugin": "^10.0.0", "dotenv": "^10.0.0", "dotenv-expand": "^5.1.0", "eslint": "^8.43.0", @@ -49,17 +53,20 @@ "jest-watch-typeahead": "^1.1.0", "react-dev-utils": "^12.0.1", "semver": "^7.5.1", - "typescript": "^5.1.3", + "typescript": "^5.1.6", "vite": "^4.3.9", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-pwa": "^0.16.4", "vite-plugin-svgr": "^3.2.0", "vite-tsconfig-paths": "^4.2.0" }, "scripts": { "start": "vite", "build": "tsc && vite build", + "postbuild": "node postbuild.js", "serve": "vite preview", "test": "jest --watch", - "lint": "eslint src --ext .js,.jsx,.ts,.tsx" + "lint": "eslint src --ext .js,.jsx,.ts,.tsx --fix" }, "browserslist": { "production": [ diff --git a/webserver/web-interface/postbuild.js b/webserver/web-interface/postbuild.js new file mode 100644 index 00000000..e6593676 --- /dev/null +++ b/webserver/web-interface/postbuild.js @@ -0,0 +1,36 @@ +const fs = require('fs'); +const path = require('path'); +// Assets folder regex +const filenameRegex = /^index-[a-zA-Z0-9]+\.js$/; + +const deleteData = (filePath) => { + if (fs.existsSync(filePath)) { + if (fs.lstatSync(filePath).isDirectory()) { + fs.readdirSync(filePath).forEach((file) => { + const fullPath = path.join(filePath, file); + deleteData(fullPath); + }); + fs.rmdirSync(filePath); + } else { + fs.unlinkSync(filePath); + } + } +}; + +const matchesRegex = (filePath) => filenameRegex.test(path.basename(filePath)); +const deleteAssetsMatchingRegex = (dirPath) => { + if (fs.existsSync(dirPath)) { + fs.readdirSync(dirPath).forEach((file) => { + const fullPath = path.join(dirPath, file); + if (matchesRegex(fullPath)) { + deleteData(fullPath); + } + }); + } +}; + + +deleteAssetsMatchingRegex('../data/assets'); +deleteData('../data/index.html'); + +console.log('Files deleted successfully.'); diff --git a/webserver/web-interface/public/logo.png b/webserver/web-interface/public/logo.png new file mode 100644 index 00000000..ad79e8ed Binary files /dev/null and b/webserver/web-interface/public/logo.png differ diff --git a/webserver/web-interface/public/manifest.json b/webserver/web-interface/public/manifest.json deleted file mode 100644 index 92022971..00000000 --- a/webserver/web-interface/public/manifest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "short_name": "Gaggiuino", - "name": "Gaggiuino web portal", - "icons": [ - { - "src": "favicon.png", - "sizes": "64x64 32x32 24x24 16x16", - "type": "image/png" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#000000", - "background_color": "#ffffff" -} diff --git a/webserver/web-interface/public/spalsh.png b/webserver/web-interface/public/spalsh.png new file mode 100644 index 00000000..ad79e8ed Binary files /dev/null and b/webserver/web-interface/public/spalsh.png differ diff --git a/webserver/web-interface/src/App.tsx b/webserver/web-interface/src/App.tsx index 3e168883..5fc05590 100644 --- a/webserver/web-interface/src/App.tsx +++ b/webserver/web-interface/src/App.tsx @@ -6,11 +6,12 @@ import { Route, Outlet, } from 'react-router-dom'; -import Home from './pages/home/Home'; -import Profiles from './pages/home/Profiles'; -import Settings from './pages/home/Settings'; +import Home from './pages/Home'; +import Profiles from './pages/Profiles'; +import Settings from './pages/Settings'; import MainAppBar from './components/appbar/MainAppBar'; import ThemeWrapper from './components/theme/ThemeWrapper'; +import useWebSocket from './api/websocket'; function Layout() { return ( @@ -32,7 +33,17 @@ const router = createBrowserRouter( ); function App() { - return ; + return ( + <> + + + + ); +} + +function WebSocket() { + useWebSocket(`ws://${window.location.host}/ws`); + return null; } export default App; diff --git a/webserver/web-interface/src/api/websocket.ts b/webserver/web-interface/src/api/websocket.ts new file mode 100644 index 00000000..f371e565 --- /dev/null +++ b/webserver/web-interface/src/api/websocket.ts @@ -0,0 +1,134 @@ +import { useWebSocket as reactUseWebSocket } from 'react-use-websocket/dist/lib/use-websocket'; +import { + useCallback, useEffect, useState, +} from 'react'; +import { Options } from 'react-use-websocket/dist/lib/types'; +import useSensorStateStore from '../state/SensorStateStore'; +import { + BleScales, + DescalingProgress, + GaggiaSettings, + LogMessage, Notification, SensorState, ShotSnapshot, SystemState, +} from '../models/models'; +import useSystemStateStore from '../state/SystemStateStore'; +import useLogMessageStore from '../state/LogStore'; +import useShotDataStore from '../state/ShotDataStore'; +import useProfileStore from '../state/ProfileStore'; +import { Profile } from '../models/profile'; +import useSettingsStore from '../state/SettingsStore'; +import useNotificationStore from '../state/NotificationDataStore'; +import useDescalingProgressStore from '../state/DescalingProgressDataStore'; +import useBleScalesStore from '../state/BleScalesDatastor'; + +enum WsActionType { + SensorStateUpdate = 'sensor_data_update', + ShotSnapshotUpdate = 'shot_data_update', + LogRecordUpdate = 'log_record', + SystemStateUpdate = 'sys_state', + ActiveProfileUpdated ='act_prof_update', + SettingsUpdated ='settings_update', + NotificationReceived = 'notification', + DescalingProgressReceived = 'descaling_progress', + BleScalesUpdated = 'ble_scls_upd', +} + +// Time after which, if we didn't receive any data, the websocket will try to reconnect +const TIMEOUT_INTERVAL = 4000; + +const WS_OPTIONS: Options = { + share: true, + shouldReconnect: () => true, + reconnectAttempts: 1000000, + reconnectInterval: 3, + retryOnError: true, +}; + +interface MessageData { + action: WsActionType, + data: unknown, +} + +const useWebSocket = (url:string) => { + const updateLocalSensorState = useSensorStateStore((state) => state.updateLocalSensorState); + const updateLocalSystemState = useSystemStateStore((state) => state.updateLocalSystemState); + const addMessage = useLogMessageStore((state) => state.addMessage); + const setShotRunning = useShotDataStore((state) => state.setShotRunning); + const addShotDatapoint = useShotDataStore((state) => state.addShotDatapoint); + const updateLocalActiveProfile = useProfileStore((state) => state.updateLocalActiveProfile); + const updateLocalSettings = useSettingsStore((state) => state.updateLocalSettings); + const updateLatestNotification = useNotificationStore((state) => state.updateLatestNotification); + const updateLocalDescalingProgress = useDescalingProgressStore((state) => state.updateLocalDescalingProgress); + const updateBleScales = useBleScalesStore((state) => state.updateBleScales); + + const [connected, setConnected] = useState(true); + const [, setMessageTimeoutId] = useState(); + const { lastJsonMessage, getWebSocket } = reactUseWebSocket(url, WS_OPTIONS, connected); + + // Function to reset connection + const resetConnection = useCallback(() => { + const webSocketInstance = getWebSocket(); + if (webSocketInstance && webSocketInstance.readyState === WebSocket.OPEN) { + setConnected(false); + setTimeout(() => setConnected(true), 1000); + } + }, [getWebSocket]); + + // If no message in {TIMEOUT_INTERVAL} seconds, reset connection + useEffect(() => { + let timeoutId: NodeJS.Timeout; + + setMessageTimeoutId((previousTimeoutId) => { + if (previousTimeoutId) { + clearTimeout(previousTimeoutId); + } + timeoutId = setTimeout(resetConnection, TIMEOUT_INTERVAL); + return timeoutId; + }); + return () => clearTimeout(timeoutId); + }, [lastJsonMessage, resetConnection]); + + useEffect(() => { + if (!lastJsonMessage) return; + + const messageData = lastJsonMessage as unknown as MessageData; + switch (messageData.action) { + case WsActionType.SensorStateUpdate: { + const sensorState = messageData.data as SensorState; + updateLocalSensorState(sensorState); + if (!sensorState.brewActive) { + setShotRunning(false); + } + break; + } + case WsActionType.ShotSnapshotUpdate: + addShotDatapoint(messageData.data as ShotSnapshot); + break; + case WsActionType.LogRecordUpdate: + addMessage(messageData.data as LogMessage); + break; + case WsActionType.SystemStateUpdate: + updateLocalSystemState(messageData.data as SystemState); + break; + case WsActionType.ActiveProfileUpdated: + updateLocalActiveProfile(messageData.data as Profile); + break; + case WsActionType.SettingsUpdated: + updateLocalSettings(messageData.data as GaggiaSettings); + break; + case WsActionType.NotificationReceived: + updateLatestNotification(messageData.data as Notification); + break; + case WsActionType.DescalingProgressReceived: + updateLocalDescalingProgress(messageData.data as DescalingProgress); + break; + case WsActionType.BleScalesUpdated: + updateBleScales(messageData.data as BleScales); + break; + } + }, [lastJsonMessage, updateLocalSettings, updateLocalSensorState, + updateLocalSystemState, addMessage, addShotDatapoint, updateLocalActiveProfile, + setShotRunning, updateLatestNotification, updateLocalDescalingProgress, + updateBleScales]); +}; + +export default useWebSocket; diff --git a/webserver/web-interface/src/components/alert/SnackMessage.tsx b/webserver/web-interface/src/components/alert/SnackMessage.tsx new file mode 100644 index 00000000..dc393d2e --- /dev/null +++ b/webserver/web-interface/src/components/alert/SnackMessage.tsx @@ -0,0 +1,80 @@ +import { + Alert, AlertColor, Snackbar, SnackbarCloseReason, useTheme, +} from '@mui/material'; +import React, { + ReactNode, useCallback, useEffect, useState, +} from 'react'; +import { Notification, NotificationType } from '../../models/models'; + +interface SnackMessage { + content: ReactNode; + level: AlertColor; +} + +export interface SnackNotificationProps { + notification?: Notification; +} + +function typeToAlertColor(type: NotificationType): AlertColor { + switch (type) { + case NotificationType.INFO: return 'info'; + case NotificationType.ERROR: return 'error'; + case NotificationType.WARN: return 'warning'; + case NotificationType.SUCCESS: return 'success'; + default: return 'info'; + } +} + +function notificationToSnackMessage(notification?: Notification): SnackMessage | undefined { + return notification + ? { content: notification.message, level: typeToAlertColor(notification.type) } + : undefined; +} + +function SnackNotification({ notification = undefined }: SnackNotificationProps) { + const [messageInternal, setMessageInternal] = useState(); + const [open, setOpen] = useState(false); + const [key, setKey] = useState(0); + const theme = useTheme(); + + useEffect(() => { + setKey((prevKey) => prevKey + 1); + setMessageInternal(notificationToSnackMessage(notification)); + setOpen(notification !== undefined); + }, [notification]); + + const handleClose = useCallback((event: React.SyntheticEvent | Event, reason?: SnackbarCloseReason) => { + if (reason === 'clickaway') { + return; + } + + setOpen(false); + }, []); + + return ( + + + {messageInternal?.content} + + + + ); +} + +export default SnackNotification; diff --git a/webserver/web-interface/src/components/alert/alert.tsx b/webserver/web-interface/src/components/alert/alert.tsx new file mode 100644 index 00000000..7819961b --- /dev/null +++ b/webserver/web-interface/src/components/alert/alert.tsx @@ -0,0 +1,76 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Modal from '@mui/material/Modal'; + +const style = { + position: 'absolute' as const, + bottom: '0%', + left: '70%', + transform: 'translate(-0%, -0%)', + width: '30%', + bgcolor: 'background.paper', + border: '2px solid', + boxShadow: 50, + p: 1, + justifyContent: 'center', + borderRadius: '16px', +}; + +export interface BasicModalProps { + text: string; + level: string; +} + +export default function ShowAlert({ level, text }: BasicModalProps) { + const [open, setOpen] = React.useState(false); + const handleOpen = () => setOpen(true); + const handleClose = () => setOpen(false); + + React.useEffect(() => { + if (level && text) { + handleOpen(); + setTimeout(handleClose, 3000); + } + }, [level, text]); + + const getBorderColor = () => { + const alertMapToColour: { [key: string]: string } = { + INFO: '#178bca', + WARN: 'orange', + ERROR: '#FF5733', + }; + + if (level in alertMapToColour) { + return alertMapToColour[level]; + } + + return 'gray'; + }; + + const dynamicStyle = { + ...style, + border: `2px solid ${getBorderColor()}`, // Set the border color dynamically + }; + + return ( +
+ {/* */} + + + + {level} + + + {text} + + + +
+ ); +} diff --git a/webserver/web-interface/src/components/appbar/MainAppBar.jsx b/webserver/web-interface/src/components/appbar/MainAppBar.tsx similarity index 51% rename from webserver/web-interface/src/components/appbar/MainAppBar.jsx rename to webserver/web-interface/src/components/appbar/MainAppBar.tsx index be5713dd..f16b7143 100644 --- a/webserver/web-interface/src/components/appbar/MainAppBar.jsx +++ b/webserver/web-interface/src/components/appbar/MainAppBar.tsx @@ -7,31 +7,44 @@ import TuneIcon from '@mui/icons-material/Tune'; import SettingsIcon from '@mui/icons-material/Settings'; import Box from '@mui/material/Box'; import { - useTheme, Stack, AppBar, Toolbar, Fab, useMediaQuery, MenuItem, Button, Menu, + useTheme, + Stack, + AppBar, + Toolbar, + Fab, + useMediaQuery, + MenuItem, + Button, + Menu, } from '@mui/material'; -import PropTypes from 'prop-types'; -import { useWebSocket } from 'react-use-websocket/dist/lib/use-websocket'; import Logo from '../icons/Logo'; -import ThemeModeToggle from '../theme/ThemeModeToggle'; -import ShotDialog from '../../pages/home/ShotDialog'; -import { - MSG_TYPE_SHOT_DATA, apiHost, filterJsonMessage, filterSocketMessage, -} from '../../models/api'; +import ShotDialog from '../../pages/ShotDialog'; +import useShotDataStore from '../../state/ShotDataStore'; +import useSystemStateStore from '../../state/SystemStateStore'; +import formatTime from '../../models/time_format'; +import SnackNotification from '../alert/SnackMessage'; +import useNotificationStore from '../../state/NotificationDataStore'; -const menuItems = { +const menuItems: { [key: string]: { label: string; icon: JSX.Element } } = { '/': { label: 'Home', icon: }, '/profiles': { label: 'Profiles', icon: }, '/settings': { label: 'Settings', icon: }, }; -function LinkTab(props) { +interface LinkTabProps { + value: string; +} + +function LinkTab(props: LinkTabProps) { const { value: path } = props; const navigate = useNavigate(); const location = useLocation(); const theme = useTheme(); - const textColor = theme.palette.text.secondary; - const activeColor = theme.palette.mode === 'light' ? theme.palette.primary.contrastText : theme.palette.primary.main; + const textColor = theme.palette.text.primary; + const activeColor = theme.palette.mode === 'light' + ? theme.palette.primary.contrastText + : theme.palette.primary.main; const id = path.replace('/', ''); const { label, icon } = menuItems[path]; @@ -44,16 +57,17 @@ function LinkTab(props) { onClick={() => navigate(path)} label={label} icon={icon} - > - + /> ); } -LinkTab.propTypes = { - value: PropTypes.string.isRequired, -}; +interface TabMenuProps { + activeItem: string; + onChange: (value: string) => void; + activeColor: string; +} -function TabMenu({ activeItem, onChange, activeColor }) { +function TabMenu({ activeItem, onChange, activeColor }: TabMenuProps) { return ( - {Object.keys(menuItems).map((item) => )} + {Object.keys(menuItems).map((item) => ( + + ))} ); } -function NavMenu({ activeItem, onChange }) { +interface NavMenuProps { + activeItem: string; + onChange: (value: string) => void; +} + +function NavMenu({ activeItem, onChange }: NavMenuProps) { const navigate = useNavigate(); - const [anchorEl, setAnchorEl] = useState(null); + const [anchorEl, setAnchorEl] = useState(null); const theme = useTheme(); - const handleOpenMenu = (event) => { + const handleOpenMenu = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); }; @@ -139,32 +160,29 @@ function NavMenu({ activeItem, onChange }) { function MainAppBar() { const theme = useTheme(); const location = useLocation(); - const [activeTab, setActiveTab] = useState(location.pathname || '/'); - const [shotDialogOpen, setShotDialogOpen] = useState(false); const isBiggerScreen = useMediaQuery(theme.breakpoints.up('sm')); - const { lastJsonMessage } = useWebSocket(`ws://${apiHost}/ws`, { - share: true, - retryOnError: true, - shouldReconnect: () => true, - reconnectAttempts: 1000, - filter: (message) => filterSocketMessage(message, MSG_TYPE_SHOT_DATA), - }); - useEffect(() => { - if (lastJsonMessage !== null && filterJsonMessage(lastJsonMessage, MSG_TYPE_SHOT_DATA)) { - setShotDialogOpen(true); - } - }, [lastJsonMessage]); + const [activeTab, setActiveTab] = useState(location.pathname || '/'); + const [shotDialogOpen, setShotDialogOpen] = useState(false); - const activeColor = theme.palette.mode === 'light' ? theme.palette.primary.contrastText : theme.palette.primary.main; + const activeColor = theme.palette.mode === 'light' + ? theme.palette.primary.contrastText + : theme.palette.primary.main; return ( - + - + + + {isBiggerScreen && ( )} - {!isBiggerScreen && ()} - + {!isBiggerScreen && } + - {shotDialogOpen && } + + ); } +function TimeCounter() { + const timeAlive = useSystemStateStore((state) => state.systemState.timeAlive); + return ( +
+ {formatTime({ time: timeAlive * 1000 })} +
+ ); +} + +function ShotDialogObserver({ open, setOpen }: {open:boolean, setOpen: (value: boolean) => void}) { + const latestShotDatapoint = useShotDataStore((state) => state.latestShotDatapoint); + + useEffect(() => { + if (latestShotDatapoint.timeInShot > 0) setOpen(true); + }, [latestShotDatapoint, setOpen]); + + if (open) { + return ; + } + return null; +} + +function AppNotification() { + const latestNotification = useNotificationStore((state) => state.latestNotification); + return ; +} + export default MainAppBar; diff --git a/webserver/web-interface/src/components/chart/ChartConfig.js b/webserver/web-interface/src/components/chart/ChartConfig.js deleted file mode 100644 index 6033ace3..00000000 --- a/webserver/web-interface/src/components/chart/ChartConfig.js +++ /dev/null @@ -1,70 +0,0 @@ -import { alpha } from '@mui/material'; - -export default function getShotChartConfig(theme) { - return { - animation: false, - responsive: true, - maintainAspectRatio: false, - interaction: { - mode: 'index', - intersect: false, - }, - stacked: false, - plugins: { - legend: { - display: true, - position: 'bottom', - labels: { - color: theme.palette.text.secondary, - }, - }, - }, - datasets: { - line: { - pointRadius: 0, - }, - }, - scales: { - x: { - axis: 'x', - type: 'linear', - ticks: { - color: theme.palette.text.secondary, - }, - grid: { - color: theme.palette.divider, - }, - min: 0, - suggestedMax: 60, - }, - y1: { - type: 'linear', - display: true, - position: 'left', - min: 0, - suggestedMax: 100, - grid: { - color: alpha(theme.palette.temperature.main, 0.5), - borderDash: [3, 3], - }, - ticks: { - color: theme.palette.temperature.main, - }, - }, - y2: { - type: 'linear', - display: true, - position: 'right', - min: 0, - suggestedMax: 16, - grid: { - color: alpha(theme.palette.pressure.main, 0.5), - borderDash: [3, 3], - }, - ticks: { - color: theme.palette.pressure.main, - }, - }, - }, - }; -} diff --git a/webserver/web-interface/src/components/chart/ChartConfig.ts b/webserver/web-interface/src/components/chart/ChartConfig.ts new file mode 100644 index 00000000..346a5251 --- /dev/null +++ b/webserver/web-interface/src/components/chart/ChartConfig.ts @@ -0,0 +1,153 @@ +import { Theme, alpha } from '@mui/material'; +import { ChartOptions } from 'chart.js'; + +export function getShotChartConfig(theme: Theme, onHover?: (index: number) => void): ChartOptions<'line'> { + return { + animation: false, + responsive: true, + maintainAspectRatio: false, + onHover: (event, elements) => { + if (onHover && elements && elements.length > 0 && elements[0].index) { + const { index } = elements[0]; // get the index of the hovered element + onHover(index); + } + }, + interaction: { + mode: 'index', + intersect: false, + }, + plugins: { + legend: { + display: false, + }, + tooltip: { + enabled: false, + }, + }, + datasets: { + line: { + pointRadius: 0, + }, + }, + scales: { + x: { + axis: 'x', + type: 'linear', + ticks: { + color: theme.palette.text.secondary, + callback: (tickValue) => `${tickValue as number / 1000}`, + }, + grid: { + color: theme.palette.divider, + }, + min: 0, + suggestedMax: 60000, + }, + y1: { + type: 'linear', + display: true, + position: 'left', + min: 0, + suggestedMax: 100, + grid: { + color: alpha(theme.palette.temperature.main, 0.5), + tickBorderDash: [3, 3], + }, + ticks: { + color: theme.palette.temperature.main, + }, + }, + y2: { + type: 'linear', + display: true, + position: 'right', + min: 0, + suggestedMax: 16, + grid: { + color: alpha(theme.palette.pressure.main, 0.5), + tickBorderDash: [3, 3], + }, + ticks: { + color: theme.palette.pressure.main, + }, + }, + }, + }; +} + +export function getProfilePreviewChartConfig({ theme, onClick, max }: {theme: Theme, onClick: (dataIndex: number) => void, max?: number}): ChartOptions<'line'> { + return { + animation: false, + responsive: true, + maintainAspectRatio: false, + onClick: (event, elements) => { + if (onClick && elements && elements.length > 0 && elements[0].index) { + const { index } = elements[0]; // get the index of the clicked element + onClick(index); + } + }, + layout: { + padding: 0, + }, + interaction: { + mode: 'index', + intersect: false, + }, + plugins: { + tooltip: { + enabled: false, + }, + legend: { + display: false, + }, + }, + datasets: { + line: { + pointRadius: 0, + borderWidth: 1.5, + tension: 0, + }, + }, + scales: { + x: { + axis: 'x', + type: 'linear', + ticks: { + display: false, + }, + grid: { + display: false, + color: theme.palette.divider, + }, + min: 0, + max, + }, + y1: { + type: 'linear', + display: false, + position: 'left', + min: 0, + suggestedMax: 105, + grid: { + display: false, + }, + ticks: { + display: false, + }, + }, + y2: { + type: 'linear', + display: false, + position: 'right', + min: -0.5, + max: 15, + grid: { + display: false, + }, + ticks: { + display: false, + }, + }, + }, + }; +} diff --git a/webserver/web-interface/src/components/chart/GaugeCentralTextPlugin.js b/webserver/web-interface/src/components/chart/GaugeCentralTextPlugin.ts similarity index 54% rename from webserver/web-interface/src/components/chart/GaugeCentralTextPlugin.js rename to webserver/web-interface/src/components/chart/GaugeCentralTextPlugin.ts index aaca2767..4f513319 100644 --- a/webserver/web-interface/src/components/chart/GaugeCentralTextPlugin.js +++ b/webserver/web-interface/src/components/chart/GaugeCentralTextPlugin.ts @@ -1,19 +1,41 @@ -const GaugeCentralTextPlugin = { +import { FontStyle } from '@mui/material/styles/createTypography'; +import { Plugin, ChartType } from 'chart.js'; + +declare module 'chart.js' { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface PluginOptionsByType { + center?: { + unit: string, + decimals?: number, + color?: string | CanvasGradient | CanvasPattern, + minFontSize?: number, + maxFontSize?: number, + fontStyle?: FontStyle, + sidePadding?: number, + lineHeight?: number, + unitFontRatio?: number, + }, + } +} + +const GaugeCentralTextPlugin: Plugin = { id: 'center', afterDraw(chart) { - if (chart.config.options.plugins && chart.config.options.plugins.center) { + if (chart.config.options?.plugins?.center) { // Get ctx from string const { ctx } = chart; // Get options from the center object in options const centerConfig = chart.config.options.plugins.center; - const fontStyle = centerConfig.fontStyle || 'Arial'; - const txt = centerConfig.text; + const fontStyle = centerConfig?.fontStyle || 'Arial'; + const value = (chart.data.datasets[0].data[0] as number).toFixed(centerConfig.decimals || 0) || ''; + const unit = centerConfig.unit || ''; const color = centerConfig.color || '#000'; const maxFontSize = centerConfig.maxFontSize || 75; const sidePadding = centerConfig.sidePadding || 20; - // eslint-disable-next-line no-underscore-dangle - const { innerRadius } = chart._metasets[chart._metasets.length - 1].data[0]; + const unitFontRatio = centerConfig.unitFontRatio || 0.7; + // eslint-disable-next-line @typescript-eslint/no-explicit-any, no-underscore-dangle + const { innerRadius } = (chart as any)._metasets[(chart as any)._metasets.length - 1].data[0]; const sidePaddingCalculated = (sidePadding / 100) * (innerRadius * 2); // Start with a base font of 30px ctx.font = `30px ${fontStyle}`; @@ -21,7 +43,7 @@ const GaugeCentralTextPlugin = { // Get the width of the string and also the width of the element minus 10 to // give it 5px side padding - const stringWidth = ctx.measureText(txt).width; + const stringWidth = ctx.measureText(`${value}${unit}`).width; const elementWidth = (innerRadius * 2) - sidePaddingCalculated; // Find out how much the font can grow in width. @@ -45,20 +67,32 @@ const GaugeCentralTextPlugin = { wrapText = true; } - // Set font settings to draw it correctly. - ctx.textAlign = 'center'; - ctx.textBaseline = 'middle'; - const centerX = ((chart.chartArea.left + chart.chartArea.right) / 2); - let centerY = ((chart.chartArea.top + chart.chartArea.bottom) / 2); + // Get the text metrics ctx.font = `${fontSizeToUse}px ${fontStyle}`; - ctx.fillStyle = color; + let metrics = ctx.measureText(value); + const valueWidth = metrics.width; + const textHeight = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent; + ctx.font = `${fontSizeToUse * unitFontRatio}px ${fontStyle}`; + const unitWidth = ctx.measureText(unit).width; + + // Set font settings to draw it correctly. + ctx.textAlign = 'left'; + ctx.textBaseline = 'alphabetic'; + let centerX = ((chart.chartArea.left + chart.chartArea.right) / 2) - (valueWidth + unitWidth) / 2; + let centerY = ((chart.chartArea.top + chart.chartArea.bottom) / 2) + textHeight / 3; + ctx.fillStyle = color as string | CanvasGradient | CanvasPattern; if (!wrapText) { - ctx.fillText(txt, centerX, centerY); + ctx.font = `${fontSizeToUse}px ${fontStyle}`; + ctx.fillText(value, centerX, centerY); + centerX += valueWidth; + + ctx.font = `${fontSizeToUse * unitFontRatio}px ${fontStyle}`; + ctx.fillText(unit, centerX, centerY); return; } - const words = txt.split(' '); + const words = value.split(' '); let line = ''; const lines = []; @@ -66,7 +100,7 @@ const GaugeCentralTextPlugin = { // eslint-disable-next-line no-plusplus for (let n = 0; n < words.length; n++) { const testLine = `${line + words[n]} `; - const metrics = ctx.measureText(testLine); + metrics = ctx.measureText(testLine); const testWidth = metrics.width; if (testWidth > elementWidth && n > 0) { lines.push(line); diff --git a/webserver/web-interface/src/components/chart/GaugeChart.jsx b/webserver/web-interface/src/components/chart/GaugeChart.jsx deleted file mode 100644 index 9c7a859a..00000000 --- a/webserver/web-interface/src/components/chart/GaugeChart.jsx +++ /dev/null @@ -1,70 +0,0 @@ -import { Doughnut } from 'react-chartjs-2'; -import PropTypes from 'prop-types'; -import { - Chart as ChartJS, - ArcElement, - Title, -} from 'chart.js'; -import { useTheme } from '@mui/material'; -import GaugeCentralTextPlugin from './GaugeCentralTextPlugin'; - -ChartJS.register(ArcElement, Title, GaugeCentralTextPlugin); - -function GaugeChart({ - value, - maxValue = 0, - primaryColor, - unit, - title, - maintainAspectRatio = false, -}) { - const theme = useTheme(); - - const options = { - cutout: '90%', - borderWidth: 0, - responsive: true, - updateMode: 'resize', - maintainAspectRatio: {maintainAspectRatio}, - plugins: { - center: { - text: value.toFixed(1) + unit, - color: primaryColor, - maxFontSize: 50, - }, - title: { - display: title && title.length > 0, - text: title, - }, - }, - }; - - const data = { - cutout: '90%', - datasets: [{ - data: [value, Math.max(0, maxValue - value)], - backgroundColor: [ - primaryColor, - theme.palette.divider, - ], - }], - }; - return ; -} - -export default GaugeChart; - -GaugeChart.propTypes = { - value: PropTypes.number.isRequired, - maxValue: PropTypes.number, - primaryColor: PropTypes.string, - unit: PropTypes.string, - title: PropTypes.string, -}; - -GaugeChart.defaultProps = { - maxValue: 100, - primaryColor: '#6296C5', - unit: '', - title: '', -}; diff --git a/webserver/web-interface/src/components/chart/GaugeChart.tsx b/webserver/web-interface/src/components/chart/GaugeChart.tsx new file mode 100644 index 00000000..225aaef6 --- /dev/null +++ b/webserver/web-interface/src/components/chart/GaugeChart.tsx @@ -0,0 +1,111 @@ +import React, { + useMemo, useRef, +} from 'react'; +import { Doughnut } from 'react-chartjs-2'; +import { + Chart as ChartJS, + ArcElement, + Title, + ChartOptions, + ChartData, +} from 'chart.js'; +import { + Typography, TypographyProps, lighten, useTheme, +} from '@mui/material'; +import GaugeCentralTextPlugin from './GaugeCentralTextPlugin'; +import AspectRatioBox from '../layout/AspectRatioBox'; + +ChartJS.register(ArcElement, Title, GaugeCentralTextPlugin); + +export function GaugeTitle({ children, sx }: TypographyProps) { + const theme = useTheme(); + return ( + + {children} + + ); +} + +type GaugeChartProps = { + value: number; + maxValue?: number; + primaryColor: string; + unit?: string; + title?: string; + flashAfterValue?: number, +} + +export function GaugeChart({ + value, + maxValue = 100, + primaryColor, + unit = '', + title = '', + flashAfterValue = undefined, +}: GaugeChartProps) { + const theme = useTheme(); + const shouldFlash = useMemo(() => flashAfterValue !== undefined && value > flashAfterValue, [value, flashAfterValue]); + const chartRef = useRef>(); + + const options: ChartOptions<'doughnut'> = useMemo(() => ({ + cutout: '89%', + responsive: true, + maintainAspectRatio: true, + animation: { + animateScale: false, + animateRotate: false, + }, + borderWidth: 0, + plugins: { + tooltip: { + enabled: false, + }, + center: { + unit, + decimals: 1, + color: primaryColor, + maxFontSize: 55, + minFontSize: 1, + sidePadding: 10, + }, + }, + borderColor: primaryColor, + backgroundColor: (ctx) => (ctx.dataIndex === 0 ? primaryColor : theme.palette.divider), + animations: { + backgroundColor: shouldFlash ? { + duration: 500, + easing: 'linear', + type: 'color', + from: (ctx) => (ctx.dataIndex === 0 ? primaryColor : theme.palette.divider), + to: (ctx) => (ctx.dataIndex === 0 ? lighten(primaryColor, 0.5) : theme.palette.divider), + loop: true, + } : false, + }, + + }), [primaryColor, unit, shouldFlash, theme]); + + const data: ChartData<'doughnut'> = useMemo(() => { + const newValue = value > 100 ? Math.round(value) : value; + return { + datasets: [{ + data: [newValue, Math.max(0, maxValue - newValue)], + }], + }; + }, [value, maxValue]); + + return ( + <> + {title && {title}} + + + ); +} + +export default GaugeChart; diff --git a/webserver/web-interface/src/components/chart/GaugeLiquid.jsx b/webserver/web-interface/src/components/chart/GaugeLiquid.jsx deleted file mode 100644 index 43e3e6fd..00000000 --- a/webserver/web-interface/src/components/chart/GaugeLiquid.jsx +++ /dev/null @@ -1,36 +0,0 @@ -import React, { Component } from "react"; -import GaugeLiquid from "react-liquid-gauge"; - -function GaugeLiquidComponent({value=0, radius=50}) { - - return ( -
-
Water Level
-

- -
- ); -}; - -export default GaugeLiquidComponent; diff --git a/webserver/web-interface/src/components/chart/GaugeLiquid.tsx b/webserver/web-interface/src/components/chart/GaugeLiquid.tsx new file mode 100644 index 00000000..eb0653a7 --- /dev/null +++ b/webserver/web-interface/src/components/chart/GaugeLiquid.tsx @@ -0,0 +1,67 @@ +import React, { + useCallback, + useEffect, useRef, useState, +} from 'react'; +import { + darken, debounce, lighten, useTheme, +} from '@mui/material'; +import LiquidFillGauge from 'react-liquid-gauge'; +import AspectRatioBox from '../layout/AspectRatioBox'; +import { GaugeTitle } from './GaugeChart'; + +export default function GaugeLiquidComponent({ value }: { value: number}) { + const theme = useTheme(); + const gaugeRef = useRef(null); + const [gaugeSize, setGaugeSize] = useState({ width: 0, height: 0 }); + + const calculateGaugeSize = useCallback(() => { + const debouncedCalculateSize = debounce(() => { + if (gaugeRef.current) { + const { width, height } = gaugeRef.current.getBoundingClientRect(); + setGaugeSize({ width, height }); + } + }, 300); + debouncedCalculateSize(); + }, []); + + useEffect(() => { + calculateGaugeSize(); + window.addEventListener('resize', calculateGaugeSize); + + return () => { + window.removeEventListener('resize', calculateGaugeSize); + }; + }, [calculateGaugeSize]); + + return ( + <> + Water + + {gaugeSize.width > 10 && ( + + )} + + + ); +} diff --git a/webserver/web-interface/src/components/chart/ProfileChart.jsx b/webserver/web-interface/src/components/chart/ProfileChart.jsx deleted file mode 100644 index 05d9a237..00000000 --- a/webserver/web-interface/src/components/chart/ProfileChart.jsx +++ /dev/null @@ -1,123 +0,0 @@ -import React, { - useRef, useMemo, -} from 'react'; -import { Line } from 'react-chartjs-2'; -import { - Chart as ChartJS, - CategoryScale, - LinearScale, - PointElement, - TimeScale, - LineElement, - Title, - Tooltip, - Legend, -} from 'chart.js'; -import { useTheme, alpha } from '@mui/material'; -import getShotChartConfig from './ChartConfig'; -import { ProfilePropType } from '../../models/propTypes'; -import { PhaseTypes } from '../../models/profile'; - -ChartJS.register( - CategoryScale, - LinearScale, - TimeScale, - PointElement, - LineElement, - Title, - Tooltip, - Legend, -); - -function getPressureTarget(phase) { - if (phase.type === PhaseTypes.FLOW) { - return [phase.restriction, phase.restriction]; - } - return [phase.target.start, phase.target.end || phase.target.start]; -} - -function getFlowTarget(phase) { - if (phase.type === PhaseTypes.PRESSURE) { - return [phase.restriction, phase.restriction]; - } - return [phase.target.start, phase.target.end || phase.target.start]; -} - -function profileToDatasets(profile) { - const data = { - labels: [], - pressureData: [], - flowData: [], - }; - - let phaseStartTime = 0; - profile.phases.forEach((phase) => { - const phaseTime = phase.stopConditions?.time || 5000; - const transitionTime = phase.target.time || phaseTime; - const pressureTargets = getPressureTarget(phase); - const flowTargets = getFlowTarget(phase); - - data.labels.push(phaseStartTime / 1000); - data.flowData.push(flowTargets[0]); - data.pressureData.push(pressureTargets[0]); - - if (transitionTime < phaseTime) { - data.labels.push((phaseStartTime + transitionTime) / 1000); - data.flowData.push(flowTargets[1]); - data.pressureData.push(pressureTargets[1]); - } - data.labels.push((phaseStartTime + phaseTime) / 1000); - data.flowData.push(flowTargets[1]); - data.pressureData.push(pressureTargets[1]); - - phaseStartTime += phaseTime + 500; - }); - - return data; -} - -function mapToChartData(profile, theme) { - const data = profileToDatasets(profile); - return { - labels: data.labels, - datasets: [ - { - label: 'Pressure', - data: data.pressureData, - backgroundColor: alpha(theme.palette.pressure.main, 0.8), - borderColor: theme.palette.pressure.main, - tension: 0.11, - yAxisID: 'y2', - }, - { - label: 'Flow', - data: data.flowData, - backgroundColor: alpha(theme.palette.flow.main, 0.8), - borderColor: theme.palette.flow.main, - tension: 0, - yAxisID: 'y2', - }, - ], - }; -} - -function ProfileChart({ profile }) { - const chartRef = useRef(null); - const theme = useTheme(); - const config = useMemo(() => getShotChartConfig(theme), [theme]); - const chartData = mapToChartData(profile, theme); - - return ( - - ); -} - -export default ProfileChart; - -ProfileChart.propTypes = { - profile: ProfilePropType.isRequired, -}; diff --git a/webserver/web-interface/src/components/chart/ProfileChart.tsx b/webserver/web-interface/src/components/chart/ProfileChart.tsx new file mode 100644 index 00000000..95144c83 --- /dev/null +++ b/webserver/web-interface/src/components/chart/ProfileChart.tsx @@ -0,0 +1,347 @@ +import React, { + useRef, useMemo, useCallback, +} from 'react'; +import { Line } from 'react-chartjs-2'; +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + PointElement, + TimeScale, + LineElement, + Title, + Tooltip, + Legend, + ChartData, + ChartDataset, +} from 'chart.js'; +import { useTheme, alpha, Theme } from '@mui/material'; +import { getProfilePreviewChartConfig } from './ChartConfig'; +import { + PhaseType, Phase, Profile, CurveStyle, +} from '../../models/profile'; + +ChartJS.register( + CategoryScale, + LinearScale, + TimeScale, + PointElement, + LineElement, + Title, + Tooltip, + Legend, +); + +const POINTS_PER_PHASE = 10; + +interface DataPoints { + labels: number[]; + pressureTargetsData: (number | null)[], + pressureLimitsData: (number | null)[], + flowTargetsData: (number | null)[], + flowLimitsData: (number | null)[], + tempData: (number | null)[], + phaseIds: number[], +} + +function profileToDatasets(profile: Profile): DataPoints { + const data: DataPoints = { + labels: [], + pressureTargetsData: [], + pressureLimitsData: [], + flowTargetsData: [], + flowLimitsData: [], + tempData: [], + phaseIds: [], + }; + + let phaseStartTime = 0; + let currentSegment = 0; + let phaseId = 0; + profile.phases.forEach((phase) => { + if (!phase.skip) { + const lastDataIdx = data.labels.length - 1; + const previousPressure = data.pressureTargetsData[lastDataIdx] || data.pressureLimitsData[lastDataIdx] || 0; + const previousFlow = data.flowTargetsData[lastDataIdx] || data.flowLimitsData[lastDataIdx] || 0; + const phaseTemp = (phase.waterTemperature) ? phase.waterTemperature : profile.waterTemperature; + const previousTemp = currentSegment === 0 ? phaseTemp : data.tempData[lastDataIdx]; + + const pressureTargets = getPressureTargets(phase, previousPressure); + const pressureLimits = getPressureLimits(phase, previousPressure); + const flowTargets = getFlowTargets(phase, previousFlow); + const flowLimits = getFlowLimits(phase, previousFlow); + const labels = getPointsInTime(phase, phaseStartTime); + const temp = [previousTemp, ...Array(POINTS_PER_PHASE + 1).fill(phaseTemp)]; + const phaseIds = Array(POINTS_PER_PHASE + 1).fill(phaseId); + + data.labels.push(...labels); + data.flowTargetsData.push(...flowTargets); + data.flowLimitsData.push(...flowLimits); + data.pressureTargetsData.push(...pressureTargets); + data.pressureLimitsData.push(...pressureLimits); + data.tempData.push(...temp); + data.phaseIds.push(...phaseIds); + + phaseStartTime = labels[labels.length - 1]; + currentSegment += 1; + } + phaseId += 1; + }); + + return data; +} + +function buildEmptyChartData(theme: Theme): ChartData<'line'> { + return { + labels: [], + datasets: [ + { + label: 'Pressure Target', + data: [], + backgroundColor: alpha(theme.palette.pressure.main, 0.8), + borderColor: theme.palette.pressure.main, + yAxisID: 'y2', + }, + { + label: 'Flow Target', + data: [], + backgroundColor: alpha(theme.palette.flow.main, 0.8), + borderColor: theme.palette.flow.main, + yAxisID: 'y2', + }, + { + label: 'Temp Target', + data: [], + backgroundColor: alpha(theme.palette.temperature.main, 0.8), + borderColor: theme.palette.temperature.main, + yAxisID: 'y1', + }, + { + label: 'Pressure Limit', + data: [], + backgroundColor: alpha(theme.palette.pressure.main, 0.8), + borderColor: theme.palette.pressure.main, + borderDash: [3, 3], + yAxisID: 'y2', + }, + { + label: 'Flow Limit', + data: [], + backgroundColor: alpha(theme.palette.flow.main, 0.8), + borderColor: theme.palette.flow.main, + borderDash: [3, 3], + yAxisID: 'y2', + }, + ], + }; +} + +function mapToChartData(data: DataPoints, theme: Theme, selectedPhaseIndex?: number): ChartData<'line'> { + const nullSelected = (d: number | null, i: number) => ( + (selectedPhaseIndex !== undefined && selectedPhaseIndex === data.phaseIds[i]) ? null : d + ); + const nullUnselected = (d: number | null, i: number) => ( + (selectedPhaseIndex !== undefined && selectedPhaseIndex === data.phaseIds[i]) ? d : null + ); + const chartData = buildEmptyChartData(theme); + chartData.labels = data.labels; + chartData.datasets[0].data = data.pressureTargetsData.map(nullSelected); + chartData.datasets[1].data = data.flowTargetsData.map(nullSelected); + chartData.datasets[2].data = data.tempData.map(nullSelected); + chartData.datasets[3].data = data.pressureLimitsData.map(nullSelected); + chartData.datasets[4].data = data.flowLimitsData.map(nullSelected); + + if (selectedPhaseIndex !== undefined) { + const selectedDataSets:ChartDataset<'line'>[] = chartData.datasets.map((dataSet) => ({ + ...dataSet, data: [], label: `Selected ${dataSet.label}`, borderWidth: 3, + })); + selectedDataSets[0].data = data.pressureTargetsData.map(nullUnselected); + selectedDataSets[1].data = data.flowTargetsData.map(nullUnselected); + selectedDataSets[2].data = data.tempData.map(nullUnselected); + selectedDataSets[3].data = data.pressureLimitsData.map(nullUnselected); + selectedDataSets[4].data = data.flowLimitsData.map(nullUnselected); + chartData.datasets.push(...selectedDataSets); + } + + return chartData; +} + +export interface ProfileChartProps { + profile: Profile; + selectedPhaseIndex?: number; + onSelectPhase?: (phaseIndex: number) => void; +} + +function ProfileChart({ profile, selectedPhaseIndex = undefined, onSelectPhase = undefined }: ProfileChartProps) { + const chartRef = useRef(null); + const theme = useTheme(); + + const datapoints = useMemo(() => profileToDatasets(profile), [profile]); + + const chartData = useMemo( + () => mapToChartData(datapoints, theme, selectedPhaseIndex), + [datapoints, selectedPhaseIndex, theme], + ); + + const onClickHandler = useCallback((dataIndex: number) => { + onSelectPhase && onSelectPhase(datapoints.phaseIds[dataIndex]); + }, [datapoints, onSelectPhase]); + + const config = useMemo( + () => getProfilePreviewChartConfig({ + theme, + onClick: onClickHandler, + max: datapoints.labels[datapoints.labels.length - 1], + }), + [theme, datapoints, onClickHandler], + ); + + return ( + + ); +} + +export default ProfileChart; + +function easeIn(pct: number) { + return pct ** 1.675; +} + +function easeOut(pct: number) { + return 1 - (1 - pct) ** 1.675; +} + +function easeInOut(pct: number) { + return 0.5 * (Math.sin((pct - 0.5) * Math.PI) + 1); +} + +function percentageWithCurve(pct: number, curve:CurveStyle) { + switch (curve) { + case CurveStyle.LINEAR: + return pct; + case CurveStyle.EASE_IN: + return easeIn(pct); + case CurveStyle.EASE_OUT: + return easeOut(pct); + case CurveStyle.EASE_IN_OUT: + return easeInOut(pct); + default: + return 1; + } +} + +function mapRange( + refNumber: number, + refStart: number, + refEnd: number, + targetStart: number, + targetEnd: number, + curve: CurveStyle, +): number { + const deltaRef = refEnd - refStart; + const deltaTarget = targetEnd - targetStart; + + if (deltaRef === 0) { + return targetEnd; + } + + const pct = Math.max(0, Math.min(1, Math.abs((refNumber - refStart) / deltaRef))); + + return targetStart + deltaTarget * percentageWithCurve(pct, curve); +} + +// Essentially this calculates the x axis(time) for a phase +// Breaks up the transition into `POINTS_PER_PHASE` points in time and returns these +// points in time. +function getPointsInTime(phase: Phase, phaseStartTime:number) { + const times = getPhaseTimes(phase); + const pointCounts = getPointCounts(phase); + + const pointsInTime = [phaseStartTime]; // Adding one extra point to connect to precious phase + for (let i = 0; i < pointCounts.forTransition; i++) { + const pct = i / (pointCounts.forTransition - 1); + pointsInTime.push(phaseStartTime + pct * Math.min(times.transition, times.totalTime)); + } + for (let i = 0; i < pointCounts.forRestOfPhase; i++) { + const pct = i / (pointCounts.forRestOfPhase - 1); + pointsInTime.push(phaseStartTime + times.transition + pct * times.restOfPhase); + } + return pointsInTime; +} + +// Essentially this calculates the y axis for a metric (pressure / flow) +// It breaks up the transition into `POINTS_PER_PHASE` points in time and returns the +// value the target is expected to have for these points in time. +function getTargetValues(phase: Phase, previousValue: number): number[] { + const startingValue = (phase.target.start) ? phase.target.start : previousValue; + const endValue = phase.target.end || 0; + const times = getPhaseTimes(phase); + const pointsInTime = getPointsInTime(phase, 0); + + const targetValues = [previousValue]; // Adding one extra point to connect to precious phase + for (let i = 1; i < pointsInTime.length; i++) { // Skip first element as it's manually set to point to previousValue above + targetValues.push( + mapRange(pointsInTime[i], 0, times.transition, startingValue, endValue, phase.target.curve), + ); + } + + return targetValues; +} + +function getPhaseTimes(phase: Phase) { + const transition = phase.target.time || 0; + const totalTime = phase.stopConditions.time || transition + 5000; + + return { + transition, + totalTime, + restOfPhase: totalTime - transition, + }; +} + +// Tries to split the phase into two equal froups of points for the transition and the rest (if necessary) +function getPointCounts(phase: Phase) { + const times = getPhaseTimes(phase); + + let pointsForTransition = (times.restOfPhase > 0) ? POINTS_PER_PHASE / 2 : POINTS_PER_PHASE; + if (times.transition === 0) { + pointsForTransition = 0; + } + const pointsForRestOfPhase = POINTS_PER_PHASE - pointsForTransition; + return { + forTransition: pointsForTransition, + forRestOfPhase: pointsForRestOfPhase, + }; +} + +function getPressureTargets(phase: Phase, previousPressure: number): (number | null)[] { + if (phase.type === PhaseType.PRESSURE) { + return getTargetValues(phase, previousPressure); + } + return [null, ...Array(POINTS_PER_PHASE).fill(null)]; +} + +function getPressureLimits(phase: Phase, previousPressure: number): (number | null)[] { + if (phase.type === PhaseType.FLOW) { + return [previousPressure, ...Array(POINTS_PER_PHASE).fill(phase.restriction || 0)]; + } + return [null, ...Array(POINTS_PER_PHASE).fill(null)]; +} + +function getFlowTargets(phase: Phase, previousFlow: number): (number | null)[] { + if (phase.type === PhaseType.FLOW) { + return getTargetValues(phase, previousFlow); + } + return [null, ...Array(POINTS_PER_PHASE).fill(null)]; +} + +function getFlowLimits(phase: Phase, previousFlow: number): (number | null)[] { + if (phase.type === PhaseType.PRESSURE) { + return [previousFlow, ...Array(POINTS_PER_PHASE).fill(phase.restriction || 0)]; + } + return [null, ...Array(POINTS_PER_PHASE).fill(null)]; +} diff --git a/webserver/web-interface/src/components/chart/ShotChart.jsx b/webserver/web-interface/src/components/chart/ShotChart.tsx similarity index 64% rename from webserver/web-interface/src/components/chart/ShotChart.jsx rename to webserver/web-interface/src/components/chart/ShotChart.tsx index ea74c313..c4926f0c 100644 --- a/webserver/web-interface/src/components/chart/ShotChart.jsx +++ b/webserver/web-interface/src/components/chart/ShotChart.tsx @@ -1,7 +1,6 @@ import React, { - useEffect, useRef, useState, useMemo, + useEffect, useRef, useState, useMemo, useCallback, } from 'react'; -import PropTypes from 'prop-types'; import { Line } from 'react-chartjs-2'; import { Chart as ChartJS, @@ -13,9 +12,12 @@ import { Title, Tooltip, Legend, + ChartData, } from 'chart.js'; -import { useTheme, alpha } from '@mui/material'; -import getShotChartConfig from './ChartConfig'; +import { useTheme, alpha, Theme } from '@mui/material'; +import { getShotChartConfig } from './ChartConfig'; +import { ShotSnapshot } from '../../models/models'; +import { isNewShotStarted } from '../../state/ShotDataStore'; ChartJS.register( CategoryScale, @@ -28,23 +30,19 @@ ChartJS.register( Legend, ); -function mapDataPointToLabel(dataPoint) { - if (!dataPoint.timeInShot) { - return 0; - } - - return dataPoint.timeInShot / 1000; +function mapDataPointToLabel(dataPoint:ShotSnapshot) { + return dataPoint.timeInShot; } -function getLabels(input) { +function getLabels(input: ShotSnapshot[]) { return input.map(mapDataPointToLabel); } -function getDataset(input, key) { +function getDataset(input:ShotSnapshot[], key: keyof ShotSnapshot) { return input.map((dp) => dp[key]); } -function mapToChartData(input, theme) { +function mapToChartData(input: ShotSnapshot[], theme: Theme) { return { labels: getLabels(input), datasets: [ @@ -110,8 +108,8 @@ function mapToChartData(input, theme) { }; } -function popDataFromChartData(chartData) { - chartData.labels.shift(); +function popDataFromChartData(chartData: ChartData<'line'>) { + chartData.labels?.shift(); chartData.datasets.forEach((dataset) => dataset.data.shift()); } @@ -119,14 +117,13 @@ function popDataFromChartData(chartData) { * Compares the timeInShot prop of the dataPoint to the last dataPoint in chartData. * If it's before it must mean a new shot was started. */ -function newShotStarted(dataPoint, chartData) { - const newTimeLabel = mapDataPointToLabel(dataPoint); - const previousMaxTimeLabel = chartData.labels[chartData.labels.length - 1] || 0; - return previousMaxTimeLabel > newTimeLabel; +function newShotStarted(newDataPoint:ShotSnapshot, existingDatapoints: ShotSnapshot[]) { + return isNewShotStarted({ time: 0, datapoints: existingDatapoints }, newDataPoint); } -function addDataPointToChartData(chartData, dataPoint, maxLength, chartRef) { - while (!Number.isNaN(maxLength) && chartData.labels.length >= maxLength) { +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function addDataPointToChartData(chartData: ChartData<'line'>, dataPoint: ShotSnapshot, maxLength: number | undefined, chartRef:React.MutableRefObject) { + while (maxLength && chartData.labels && chartData.labels.length >= maxLength) { popDataFromChartData(chartData); } @@ -134,7 +131,7 @@ function addDataPointToChartData(chartData, dataPoint, maxLength, chartRef) { return; } - chartData.labels.push(mapDataPointToLabel(dataPoint)); + chartData.labels?.push(mapDataPointToLabel(dataPoint)); chartData.datasets[0].data.push(dataPoint.temperature); chartData.datasets[1].data.push(dataPoint.pressure); chartData.datasets[2].data.push(dataPoint.pumpFlow); @@ -152,10 +149,29 @@ function addDataPointToChartData(chartData, dataPoint, maxLength, chartRef) { /* eslint-enable no-param-reassign */ } -function Chart({ data, newDataPoint, maxLength }) { - const chartRef = useRef(null); +export interface ShotChartProps { + data?: ShotSnapshot[], + newDataPoint?: ShotSnapshot, + maxLength?: number, + onHover?: (datapoint: ShotSnapshot) => void, +} + +function ShotChart({ + data = undefined, + newDataPoint = undefined, + maxLength = undefined, + onHover = undefined, +}: ShotChartProps) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const chartRef = useRef(); const theme = useTheme(); - const config = useMemo(() => getShotChartConfig(theme), [theme]); + const [datapoints, setDatapoints] = useState([]); + + const onHoverInternal = useCallback((hoverIndex: number) => { + onHover && onHover(datapoints[hoverIndex]); + }, [onHover, datapoints]); + + const config = useMemo(() => getShotChartConfig(theme, onHoverInternal), [theme, onHoverInternal]); const [chartData, setChartData] = useState(mapToChartData([], theme)); if (newDataPoint && data) { @@ -167,20 +183,24 @@ function Chart({ data, newDataPoint, maxLength }) { if (data === undefined || data === null) { return; } + setDatapoints(data); setChartData(mapToChartData(data, theme)); - }, [data]); + }, [data, theme, setDatapoints, setChartData]); // Adds newDataPoint to the end of the chart unless it detects that a new shot was started. More efficient. useEffect(() => { - if (!newDataPoint === undefined || newDataPoint === null) { + if (newDataPoint === undefined || newDataPoint === null) { return; } - if (newShotStarted(newDataPoint, chartData)) { + if (newShotStarted(newDataPoint, datapoints)) { setChartData(mapToChartData([newDataPoint], theme)); + setDatapoints([newDataPoint]); } else { addDataPointToChartData(chartData, newDataPoint, maxLength, chartRef); + setDatapoints([...datapoints, newDataPoint]); } - }, [newDataPoint]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [newDataPoint, theme, maxLength, setDatapoints, setChartData]); return ( 0 ? value.toFixed(decimals) : undefined; +} +export interface StatBoxProps { + label: string; + color: string; + stat?: string; + statTarget?: string; + icon?: ReactElement; + unit?: string; + sx?: SxProps; +} + export function StatBox({ - label, color, stat, icon, statTarget, unit, sx, style, -}) { + label, color, stat = undefined, icon = undefined, statTarget = undefined, unit = '', sx = {}, +}: StatBoxProps) { const theme = useTheme(); return ( - - + + {icon && ( - {icon} + {icon} )} - + {label} - - - {`${stat} ${unit || ''}`} - + + {`${stat} ${unit || ''}`} - {statTarget && statTarget >= 0 && ( + {statTarget && statTarget >= '0' && ( - + {`${statTarget} ${unit}`} )} @@ -49,7 +69,7 @@ export function StatBox({ ); } -export function TimeStatBox({ timeInShot, sx, style }) { +export function TimeStatBox({ timeInShot, sx = {} }: {timeInShot: number, sx?: SxProps}) { const theme = useTheme(); return ( @@ -57,16 +77,15 @@ export function TimeStatBox({ timeInShot, sx, style }) { label="Time" icon={} color={theme.palette.text.primary} - stat={formatTimeInShot(timeInShot)} + stat={formatTime({ time: timeInShot })} sx={sx} - style={style} /> ); } export function WeightStatBox({ - shotWeight, target, sx, style, -}) { + shotWeight, target = undefined, sx = {}, +}: {shotWeight: number, target?: number, sx?: SxProps }) { const theme = useTheme(); return ( @@ -75,17 +94,16 @@ export function WeightStatBox({ icon={} color={theme.palette.weight.main} stat={formatNumber(shotWeight)} - statTarget={formatNumber(target)} + statTarget={formatTarget(target)} unit="g" sx={sx} - style={style} /> ); } export function TemperatureStatBox({ - temperature, target, sx, style, -}) { + temperature, target, sx = {}, +}: {temperature: number, target: number, sx?: SxProps}) { const theme = useTheme(); return ( @@ -94,17 +112,15 @@ export function TemperatureStatBox({ icon={} color={theme.palette.temperature.main} stat={formatNumber(temperature)} - statTarget={formatNumber(target)} + statTarget={formatTarget(target)} unit="°C" sx={sx} - style={style} /> ); } - export function PumpFlowStatBox({ - pumpFlow, target, sx, style, -}) { + pumpFlow, target, sx = undefined, +}: {pumpFlow: number, target: number, sx?: SxProps}) { const theme = useTheme(); return ( @@ -113,17 +129,16 @@ export function PumpFlowStatBox({ icon={} color={theme.palette.flow.main} stat={formatNumber(pumpFlow)} - statTarget={formatNumber(target)} + statTarget={formatTarget(target)} unit="ml/s" sx={sx} - style={style} /> ); } export function WeightFlowStatBox({ - flow, target, sx, style, -}) { + flow, target, sx = undefined, +}: {flow: number, target: number, sx?: SxProps}) { const theme = useTheme(); return ( @@ -132,17 +147,16 @@ export function WeightFlowStatBox({ icon={} color={theme.palette.weightFlow.main} stat={formatNumber(flow)} - statTarget={formatNumber(target)} + statTarget={formatTarget(target)} unit="ml/s" sx={sx} - style={style} /> ); } export function PressureStatBox({ - pressure, target, sx, style, -}) { + pressure, target, sx = undefined, +}: {pressure: number, target: number, sx?: SxProps}) { const theme = useTheme(); return ( @@ -151,10 +165,9 @@ export function PressureStatBox({ label="Pressure" color={theme.palette.pressure.main} stat={formatNumber(pressure)} - statTarget={formatNumber(target)} + statTarget={formatTarget(target)} unit="bar" sx={sx} - style={style} /> ); } diff --git a/webserver/web-interface/src/components/chart/react-liquid-gauge.d.ts b/webserver/web-interface/src/components/chart/react-liquid-gauge.d.ts new file mode 100644 index 00000000..de4c1259 --- /dev/null +++ b/webserver/web-interface/src/components/chart/react-liquid-gauge.d.ts @@ -0,0 +1,43 @@ +import React from 'react'; + +interface GradientStop { + offset: string; + stopColor: string; + stopOpacity: string; +} + +interface LiquidFillGaugeProps { + id?: string; + width?: number; + height?: number; + value?: number; + percent?: string | React.ReactNode; + textSize?: number; + textOffsetX?: number; + textOffsetY?: number; + textRenderer?: (percentage: number) => React.ReactNode; + riseAnimation?: boolean; + riseAnimationTime?: number; + riseAnimationEasing?: string; + riseAnimationOnProgress?: () => void; + riseAnimationOnComplete?: () => void; + waveAnimation?: boolean; + waveAnimationTime?: number; + waveAnimationEasing?: string; + waveAmplitude?: number; + waveFrequency?: number; + gradient?: boolean; + gradientStops?: GradientStop[] | React.ReactNode; + onClick?: () => void; + innerRadius?: number; + outerRadius?: number; + margin?: number; + circleStyle?: React.CSSProperties; + waveStyle?: React.CSSProperties; + textStyle?: React.CSSProperties; + waveTextStyle?: React.CSSProperties; +} + +declare const LiquidFillGauge: React.ComponentType; + +export default LiquidFillGauge; diff --git a/webserver/web-interface/src/components/client/BleScalesClient.ts b/webserver/web-interface/src/components/client/BleScalesClient.ts new file mode 100644 index 00000000..102d23f0 --- /dev/null +++ b/webserver/web-interface/src/components/client/BleScalesClient.ts @@ -0,0 +1,16 @@ +import axios, { AxiosRequestConfig } from 'axios'; +import { BleScales } from '../../models/models'; + +const config:AxiosRequestConfig = { + timeout: 4000, +}; + +export async function getConnectedBleScales(): Promise { + return axios.get('/api/bt-scales/connected', config) + .then(({ data }) => data); +} + +export async function getAvailableBleScales(): Promise { + return axios.get('/api/bt-scales/available', config) + .then(({ data }) => data); +} diff --git a/webserver/web-interface/src/components/client/ProfileClient.ts b/webserver/web-interface/src/components/client/ProfileClient.ts new file mode 100644 index 00000000..ba624136 --- /dev/null +++ b/webserver/web-interface/src/components/client/ProfileClient.ts @@ -0,0 +1,48 @@ +import axios, { AxiosRequestConfig } from 'axios'; +import { Profile, ProfileSummary } from '../../models/profile'; + +const config:AxiosRequestConfig = { + timeout: 4000, +}; + +export async function getActiveProfile(): Promise { + return axios.get('/api/profiles/active-profile', config) + .then(({ data }) => data); +} + +export async function getAvailableProfiles(): Promise> { + return axios.get('/api/profile-summaries', config) + .then(({ data }) => data); +} + +export async function selectActiveProfile(id: number): Promise { + return axios.put('/api/profiles/active-profile/id', { id }, config); +} + +export async function persistActiveProfileChanges(): Promise { + return axios.put('/api/profiles/active-profile/persist', config); +} + +export async function updateActiveProfile(profile: Profile): Promise { + return axios.put('/api/profiles/active-profile', profile, config); +} + +export async function getProfileById(id: number): Promise { + return axios.get(`/api/profiles/${id}`, config).then(({ data }) => data); +} + +export async function createProfile(profile: Profile): Promise { + return axios.post('/api/profiles', profile, config) + .then(({ data }) => data); +} + +export async function deleteProfileById(id: number): Promise { + return axios.delete(`/api/profiles/${id}`, config); +} + +export async function updateProfile(profile: Profile): Promise { + if (!profile.id) { + throw Error('Attempting to update a profile without id. Create a profile first.'); + } + return axios.put(`/api/profiles/${profile.id}`, profile, config); +} diff --git a/webserver/web-interface/src/components/client/SettingsClient.ts b/webserver/web-interface/src/components/client/SettingsClient.ts new file mode 100644 index 00000000..ed05767d --- /dev/null +++ b/webserver/web-interface/src/components/client/SettingsClient.ts @@ -0,0 +1,20 @@ +import axios, { AxiosRequestConfig } from 'axios'; +import { GaggiaSettings } from '../../models/models'; + +const config:AxiosRequestConfig = { + timeout: 4000, +}; + +export async function getSettings(): Promise { + return axios.get('/api/settings', config) + .then(({ data }) => data); +} + +export async function updateSettings(settings: GaggiaSettings): Promise { + return axios.put('/api/settings', settings, config) + .then(({ data }) => data); +} + +export async function persistSettings(): Promise { + return axios.put('/api/settings/persist', config); +} diff --git a/webserver/web-interface/src/components/client/SystemStateClient.ts b/webserver/web-interface/src/components/client/SystemStateClient.ts new file mode 100644 index 00000000..f9e89818 --- /dev/null +++ b/webserver/web-interface/src/components/client/SystemStateClient.ts @@ -0,0 +1,21 @@ +import axios, { AxiosRequestConfig } from 'axios'; +import { OperationMode, SystemState } from '../../models/models'; + +const config:AxiosRequestConfig = { + timeout: 4000, +}; + +export async function getSystemState(): Promise { + return axios.get('/api/system-state', config) + .then(({ data }) => data); +} + +export async function updateOperationMode(operationMode: OperationMode): Promise { + return axios.put('/api/system-state/operation-mode', { operationMode }, config) + .then(({ data }) => data); +} + +export async function updateTarePending(tarePending: boolean): Promise { + return axios.put('/api/system-state/tare-pending', { tarePending }, config) + .then(({ data }) => data); +} diff --git a/webserver/web-interface/src/components/client/WifiClient.js b/webserver/web-interface/src/components/client/WifiClient.ts similarity index 79% rename from webserver/web-interface/src/components/client/WifiClient.js rename to webserver/web-interface/src/components/client/WifiClient.ts index 5d9fca11..e3eed7be 100644 --- a/webserver/web-interface/src/components/client/WifiClient.js +++ b/webserver/web-interface/src/components/client/WifiClient.ts @@ -1,4 +1,5 @@ import axios from 'axios'; +import { Network } from '../wifi/NetworkTypes'; export async function getWifiStatus() { return axios.get('/api/wifi/status') @@ -6,7 +7,7 @@ export async function getWifiStatus() { } export async function getAvailableNetworks() { - return axios.get('/api/wifi/networks') + return axios.get('/api/wifi/networks') .then(({ data }) => { const networks = data.filter((network) => network.ssid !== null && network.ssid.length > 0); const networksUniqueByKey = [...new Map(networks.map((item) => [item.ssid, item])).values()]; @@ -18,7 +19,7 @@ export async function disconnectFromWifi() { return axios.delete('/api/wifi/selected-network'); } -export async function connectToWifi({ ssid, pass }) { +export async function connectToWifi({ ssid, pass }: { ssid: string; pass: string; }) { return axios.put('/api/wifi/selected-network', { ssid, pass }, { timeout: 10000, }); diff --git a/webserver/web-interface/src/components/gauges/PressureGauge.tsx b/webserver/web-interface/src/components/gauges/PressureGauge.tsx new file mode 100644 index 00000000..fa961351 --- /dev/null +++ b/webserver/web-interface/src/components/gauges/PressureGauge.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { useTheme } from '@mui/material'; +import useSensorStateStore from '../../state/SensorStateStore'; +import GaugeChart from '../chart/GaugeChart'; + +export default function PressureGauge() { + const theme = useTheme(); + const pressure = useSensorStateStore((state) => state.sensorState.pressure); + + return ( + + ); +} diff --git a/webserver/web-interface/src/components/gauges/TemperatureGauge.tsx b/webserver/web-interface/src/components/gauges/TemperatureGauge.tsx new file mode 100644 index 00000000..3cf6750c --- /dev/null +++ b/webserver/web-interface/src/components/gauges/TemperatureGauge.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { useTheme } from '@mui/material'; +import useSensorStateStore from '../../state/SensorStateStore'; +import GaugeChart from '../chart/GaugeChart'; + +export default function TemperatureGauge({ targetTemperature }: { targetTemperature: number}) { + const theme = useTheme(); + const temperature = useSensorStateStore((state) => state.sensorState.waterTemperature); + + return ( + + ); +} diff --git a/webserver/web-interface/src/components/gauges/WaterLevelGauge.tsx b/webserver/web-interface/src/components/gauges/WaterLevelGauge.tsx new file mode 100644 index 00000000..b8845822 --- /dev/null +++ b/webserver/web-interface/src/components/gauges/WaterLevelGauge.tsx @@ -0,0 +1,9 @@ +import React from 'react'; +import GaugeLiquid from '../chart/GaugeLiquid'; +import useSensorStateStore from '../../state/SensorStateStore'; + +export default function WaterLevelGauge() { + const waterLevel = useSensorStateStore((state) => state.sensorState.waterLevel); + + return ; +} diff --git a/webserver/web-interface/src/components/icons/Logo.jsx b/webserver/web-interface/src/components/icons/Logo.tsx similarity index 95% rename from webserver/web-interface/src/components/icons/Logo.jsx rename to webserver/web-interface/src/components/icons/Logo.tsx index f7e312bf..7360dc6b 100644 --- a/webserver/web-interface/src/components/icons/Logo.jsx +++ b/webserver/web-interface/src/components/icons/Logo.tsx @@ -1,9 +1,8 @@ import React from 'react'; -import PropTypes from 'prop-types'; export default function Logo({ size, -}) { +}: { size: string | number }) { return ( @@ -13,7 +12,3 @@ export default function Logo({ ); } - -Logo.propTypes = { - size: PropTypes.number.isRequired, -}; diff --git a/webserver/web-interface/src/components/inputs/AddPhase.jsx b/webserver/web-interface/src/components/inputs/AddPhase.tsx similarity index 80% rename from webserver/web-interface/src/components/inputs/AddPhase.jsx rename to webserver/web-interface/src/components/inputs/AddPhase.tsx index 56165cca..5fe368ce 100644 --- a/webserver/web-interface/src/components/inputs/AddPhase.jsx +++ b/webserver/web-interface/src/components/inputs/AddPhase.tsx @@ -13,11 +13,11 @@ const options = ['Restriction', 'Pressure', 'Flow']; export default function SplitButton() { const [open, setOpen] = React.useState(false); - const anchorRef = React.useRef(null); + const anchorRef = React.useRef(null); const [selectedIndex, setSelectedIndex] = React.useState(1); const handleClick = () => options[selectedIndex]; - const handleMenuItemClick = (event, index) => { + const handleMenuItemClick = (event: React.MouseEvent, index: number) => { setSelectedIndex(index); setOpen(false); }; @@ -26,10 +26,14 @@ export default function SplitButton() { setOpen((prevOpen) => !prevOpen); }; - const handleClose = (event) => { - if (anchorRef.current && anchorRef.current.contains(event.target)) { - return; - } + const handleClose = (event: MouseEvent | TouchEvent) => { + // Check if the event's target is null (for TypeScript safety) --- Doesn't seem to be used ata all, commented just in case + // const targetNode = (event as MouseEvent)?.target || (event as TouchEvent)?.target; + + // // If anchorRef exists and contains the event's target, return early (do nothing) + // if (anchorRef.current && anchorRef.current.contains(targetNode)) { + // return; + // } setOpen(false); }; diff --git a/webserver/web-interface/src/components/inputs/ProgressBar.jsx b/webserver/web-interface/src/components/inputs/ProgressBar.tsx similarity index 87% rename from webserver/web-interface/src/components/inputs/ProgressBar.jsx rename to webserver/web-interface/src/components/inputs/ProgressBar.tsx index d5fd5bf5..0ec87522 100644 --- a/webserver/web-interface/src/components/inputs/ProgressBar.jsx +++ b/webserver/web-interface/src/components/inputs/ProgressBar.tsx @@ -1,10 +1,17 @@ import * as React from 'react'; -import PropTypes from 'prop-types'; import LinearProgress from '@mui/material/LinearProgress'; import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; -function LinearProgressWithLabel(props) { +type LinearProgressWithLabelProps = { + /** + * The value of the progress indicator for the determinate and buffer variants. + * Value between 0 and 100. + */ + value: number; +} + +function LinearProgressWithLabel(props: LinearProgressWithLabelProps) { return ( @@ -19,14 +26,6 @@ function LinearProgressWithLabel(props) { ); } -LinearProgressWithLabel.propTypes = { - /** - * The value of the progress indicator for the determinate and buffer variants. - * Value between 0 and 100. - */ - value: PropTypes.number.isRequired, -}; - export default function LinearWithValueLabel() { const [progress, setProgress] = React.useState(10); diff --git a/webserver/web-interface/src/components/inputs/RangeSlider.jsx b/webserver/web-interface/src/components/inputs/RangeSlider.tsx similarity index 87% rename from webserver/web-interface/src/components/inputs/RangeSlider.jsx rename to webserver/web-interface/src/components/inputs/RangeSlider.tsx index 1b381a7e..9a50a292 100644 --- a/webserver/web-interface/src/components/inputs/RangeSlider.jsx +++ b/webserver/web-interface/src/components/inputs/RangeSlider.tsx @@ -3,7 +3,7 @@ import Box from '@mui/material/Box'; import Slider from '@mui/material/Slider'; export default function VerticalSlider() { - function preventHorizontalKeyboardNavigation(event) { + function preventHorizontalKeyboardNavigation(event: React.KeyboardEvent) { if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') { event.preventDefault(); } diff --git a/webserver/web-interface/src/components/inputs/ShotSpeedDial.tsx b/webserver/web-interface/src/components/inputs/ShotSpeedDial.tsx new file mode 100644 index 00000000..5ae72b8d --- /dev/null +++ b/webserver/web-interface/src/components/inputs/ShotSpeedDial.tsx @@ -0,0 +1,31 @@ +/* eslint-disable no-console */ +import React, { useMemo } from 'react'; +import SpeedDial from '@mui/material/SpeedDial'; +import MenuOpenIcon from '@mui/icons-material/MenuOpenOutlined'; +import SpeedDialAction from '@mui/material/SpeedDialAction'; +import ShareIcon from '@mui/icons-material/Share'; +import SaveIcon from '@mui/icons-material/Save'; +import DeleteIcon from '@mui/icons-material/Delete'; +import CloudQueueIcon from '@mui/icons-material/CloudQueue'; +import { Shot } from '../../models/models'; +import useShotDataStore from '../../state/ShotDataStore'; + +export interface ShotSpeedDialProps { + shot: Shot; + onSave: (shot: Shot) => void; + onDelete: (shot: Shot) => void; +} + +export default function ShotSpeedDial({ shot, onSave, onDelete }: ShotSpeedDialProps) { + const { shotHistory } = useShotDataStore(); + const shotAlreadyInHistory = useMemo(() => shotHistory.find((s) => s.time === shot.time), [shot, shotHistory]); + + return ( + } direction="left"> + {shotAlreadyInHistory && } tooltipTitle="Delete" onClick={() => onDelete(shot)} />} + {!shotAlreadyInHistory && } tooltipTitle="Save" onClick={() => onSave(shot)} />} + } tooltipTitle="Cloud Upload" /> + } tooltipTitle="Share" /> + + ); +} diff --git a/webserver/web-interface/src/components/inputs/SwitchLedButton.tsx b/webserver/web-interface/src/components/inputs/SwitchLedButton.tsx new file mode 100644 index 00000000..4ecaaae2 --- /dev/null +++ b/webserver/web-interface/src/components/inputs/SwitchLedButton.tsx @@ -0,0 +1,151 @@ +import React, { ReactNode, useCallback, useState } from 'react'; +import { + Box, ButtonBase, alpha, useTheme, +} from '@mui/material'; +import AspectRatioBox from '../layout/AspectRatioBox'; + +const LED_COLOR_ON = '#ef4e2b'; +const GLOW_ON = alpha(LED_COLOR_ON, 0.5); +const LED_COLOR_AUTO = '#00b9ff'; +const GLOW_AUTO = alpha(LED_COLOR_AUTO, 0.5); +const LED_COLOR_OFF = '#822714'; +const SWITCH_BACKGROUND_ON = 'linear-gradient(to top, rgba(127, 127, 127, 0.1) 0%, rgba(0, 0, 0, 0.1) 100%)'; +const SWITCH_BACKGROUND_OFF = 'linear-gradient(to top, rgba(255, 255, 255, 0.1) 0%, rgba(80, 80, 80, 0.1) 100%)'; +const SWITCH_SHADOW = '0px 1px 2px -1px rgba(0,0,0,0.2), 0px 3px 3px 0px rgba(0,0,0,0.14), 0px 1px 5px 0px rgba(0,0,0,0.12)'; + +export enum SwitchLedState { + ON, + AUTO, + OFF +} + +export interface SwitchLedButtonProps { + state: SwitchLedState; + icon?: ReactNode; + label?: ReactNode; + supportsAuto?: boolean; + longPressDuration?: number; + onChange?: (state: SwitchLedState) => void; +} + +function stateToLedColor(state:SwitchLedState) { + if (state === SwitchLedState.ON) return LED_COLOR_ON; + if (state === SwitchLedState.AUTO) return LED_COLOR_AUTO; + return LED_COLOR_OFF; +} + +function stateToGlowColor(state:SwitchLedState) { + if (state === SwitchLedState.ON) return GLOW_ON; + if (state === SwitchLedState.AUTO) return GLOW_AUTO; + return ''; +} + +export function SwitchLedButton({ + state, + icon = undefined, + label = undefined, + supportsAuto = false, + longPressDuration = 1500, + onChange = undefined, +}: SwitchLedButtonProps) { + const theme = useTheme(); + const background = (state === SwitchLedState.OFF) ? SWITCH_BACKGROUND_OFF : SWITCH_BACKGROUND_ON; + + const [pressTimeoutId, setPressTimeoutId] = useState(null); + const [longPressActive, setLongPressActive] = useState(false); + + const handleLongPress = useCallback(() => { + if (!supportsAuto) return; + if (state === SwitchLedState.AUTO) onChange?.(SwitchLedState.OFF); + else onChange?.(SwitchLedState.AUTO); + }, [state, onChange, supportsAuto]); + + const handleShortPress = useCallback(() => { + if (state === SwitchLedState.OFF) onChange?.(SwitchLedState.ON); + else onChange?.(SwitchLedState.OFF); + }, [state, onChange]); + + const handlePressStart = useCallback((event: React.MouseEvent | React.TouchEvent) => { + if (event.type === 'touchstart') { + event.preventDefault(); + } + + if (!supportsAuto) return; + + const id = setTimeout(() => { + setLongPressActive(true); + }, longPressDuration); + setPressTimeoutId(id); + }, [longPressDuration, supportsAuto]); + + const handlePressEnd = useCallback((event: React.MouseEvent | React.TouchEvent) => { + if (event.type === 'touchend') { + event.preventDefault(); + } + + if (pressTimeoutId) { + clearTimeout(pressTimeoutId); + } + if (longPressActive) { + handleLongPress(); + } else { + handleShortPress(); + } + setLongPressActive(false); + }, [pressTimeoutId, longPressActive, handleShortPress, handleLongPress]); + + return ( + + + + + + {icon} + + {label} + + + + + + ); +} diff --git a/webserver/web-interface/src/components/inputs/settings_inputs.tsx b/webserver/web-interface/src/components/inputs/settings_inputs.tsx new file mode 100644 index 00000000..b7e5069a --- /dev/null +++ b/webserver/web-interface/src/components/inputs/settings_inputs.tsx @@ -0,0 +1,420 @@ +import AddIcon from '@mui/icons-material/Add'; +import RemoveIcon from '@mui/icons-material/Remove'; +import { + Box, + IconButton, Switch, SxProps, TextField, Theme, Typography, useTheme, +} from '@mui/material'; +import Grid from '@mui/material/Unstable_Grid2'; +import React, { + forwardRef, + useCallback, useEffect, useMemo, useRef, useState, +} from 'react'; +import { Colorful, RgbaColor, rgbaToHex } from '@uiw/react-color'; +import { Variant } from '@mui/material/styles/createTypography'; +import { LedColor } from '../../models/models'; +import { same, sanitizeNumberString } from '../../models/utils'; + +export interface SettingsToggleInputProps { + label: string; + value: boolean; + onChange: (value: boolean) => void; +} + +export function SettingsToggleInput({ label, value, onChange }: SettingsToggleInputProps) { + return ( + + {label} + + onChange(e.target.checked)} /> + + + ); +} + +export interface SettingsInputFieldNumberProps { + value: number; + onChange: (value: number) => void; + maxDecimals?: number; + sx?: SxProps; + readOnly?: boolean; + textAlign?: string; + onBlur?: () => void; + disabled?: boolean +} + +export const SettingsInputFieldNumber = forwardRef(({ + value, + onChange, + maxDecimals = 1, + sx = {}, + readOnly = false, + textAlign = 'left', + onBlur = undefined, + disabled = false, +}: SettingsInputFieldNumberProps, ref) => { + const theme = useTheme(); + const round = useMemo(() => (x: number) => { + if (maxDecimals === undefined) return x; + const scale = 10 ** maxDecimals; + return Math.round(x * scale) / scale; + }, [maxDecimals]); + + const [inputValue, setInputValue] = useState(sanitizeNumberString(round(value).toString())); + + const handleValueChange = useCallback((newNumericValue: number) => { + const roundedNumber = round(newNumericValue); + + // Ignore update if the value hasn't changed + if (!same(value, roundedNumber)) { + onChange(roundedNumber); + } + }, [onChange, value, round]); + + useEffect(() => setInputValue(round(value).toString()), [value, round]); + + useEffect( + () => { + const number = parseFloat(inputValue || ''); + if (Number.isNaN(number) || same(number, value)) return; + handleValueChange(number); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [inputValue], // Remove handleValueChange dependency. Important to avoid infinite loop. + ); + + const handleFocusLost = useCallback(() => { + setInputValue(sanitizeNumberString(round(value).toString())); + onBlur && onBlur(); + }, [value, round, onBlur]); + + const handleInputValueChanged = useCallback((newValue: string) => { + setInputValue(sanitizeNumberString(newValue, maxDecimals)); + }, [setInputValue, maxDecimals]); + + return ( + handleInputValueChanged(e.target.value)} + onBlur={handleFocusLost} + variant="outlined" + sx={{ + '& fieldset': { border: 'none' }, + '& input': { textAlign, py: theme.spacing(0.5), px: 0 }, + ...sx, + }} + InputProps={{ readOnly }} + /> + ); +}); + +SettingsInputFieldNumber.defaultProps = { + maxDecimals: undefined, + sx: {}, + readOnly: false, + textAlign: 'left', + onBlur: undefined, + disabled: false, +}; + +export interface SettingsNumberIncrementButtonsProps { + value: number; + onChange: (val: number) => void; + buttonIncrements?: number; + fontSize?: string; +} + +export function SettingsNumberIncrementButtons( + { + value, onChange, buttonIncrements = 1, fontSize = undefined, + } : SettingsNumberIncrementButtonsProps, +) { + return ( + + onChange(value - buttonIncrements)}> + + + onChange(value + buttonIncrements)}> + + + + ); +} + +export interface SettingsNumberInputProps { + label: string; + value: number; + onChange: (value: number) => void; + buttonIncrements?: number; + maxDecimals?: number; + optional?: boolean; +} + +export function SettingsNumberInput({ + label, value, onChange, maxDecimals = undefined, buttonIncrements = 1, optional = false, +}: SettingsNumberInputProps) { + const [justEnabled, setJustEnabled] = useState(false); + const inputRef = useRef(null); // Create a ref + const handleInputChanged = (newValue: number) => { + onChange(newValue); + setJustEnabled(false); + }; + const handleToggleSwitch = (newValue: boolean) => { + setJustEnabled(newValue); + setTimeout(() => inputRef.current && inputRef.current.focus(), 200); + }; + const handleInputLostFocus = () => { + if (justEnabled) { + setTimeout(() => setJustEnabled(false), 200); + } + }; + + return ( + + {label} + + + + + {(optional && value === 0 && !justEnabled) + && ( + + )} + {(!optional || value !== 0 || justEnabled) && ( + + )} + + + ); +} + +export function SettingsNumber({ + label, value, onChange, maxDecimals = undefined, buttonIncrements = 1, optional = false, +}: SettingsNumberInputProps) { + const [justEnabled, setJustEnabled] = useState(false); + const inputRef = useRef(null); // Create a ref + const handleInputChanged = (newValue: number) => { + onChange(newValue); + setJustEnabled(false); + }; + const handleInputLostFocus = () => { + if (justEnabled) { + setTimeout(() => setJustEnabled(false), 200); + } + }; + + return ( + + {label} + + + + + ); +} + +interface DelayedSwitchProps { + checked: boolean; + onChange: (checked: boolean) => void; +} + +export function DelayedSwitch({ checked, onChange }: DelayedSwitchProps) { + const [checkedInternal, setCheckedInternal] = useState(checked); + useEffect(() => setCheckedInternal(checked), [checked]); + + const handleToggle = useCallback((value: boolean) => { + setCheckedInternal(value); + const timeoutId = setTimeout(() => { + onChange(value); + }, 150); // approximate duration of the MUI switch animation + + return () => { + clearTimeout(timeoutId); + }; + }, [onChange]); + + return ( + + handleToggle(e.target.checked)} + /> + + ); +} + +export interface LedColorInputProps { + label: string; + value: LedColor; + onChange: (value: LedColor) => void; +} + +export function LedColorPickerInput({ label, value, onChange }: LedColorInputProps) { + const color = { + r: value.R, g: value.G, b: value.B, a: 1, + }; + + const updateLedColor = useCallback( + (inputColor: RgbaColor) => { + onChange({ R: inputColor.r, G: inputColor.g, B: inputColor.b }); + }, + [onChange], + ); + + return ( + + {label} + + { + updateLedColor(newColor.rgba); + }} + /> + + + ); +} + +export function SettingsInputWrapper({ children }: {children: React.ReactNode}) { + const childrenArray = React.Children.toArray(children); + + // Check if child is a valid React element and its type is SettingsInputLabel + const borderLabel = childrenArray.find((c) => React.isValidElement(c) && c.type === SettingsInputBorderLabel); + const inlineLabel = childrenArray.find((c) => React.isValidElement(c) && c.type === SettingsInputInlineLabel); + const field = childrenArray.find((c) => React.isValidElement(c) && c.type === SettingsInputField); + const actions = childrenArray.find((c) => React.isValidElement(c) && c.type === SettingsInputActions); + const otherChildern = childrenArray.filter((c) => [borderLabel, inlineLabel, field, actions].indexOf(c) === -1); + + const theme = useTheme(); + + // See https://css-tricks.com/snippets/css/complete-guide-grid for CSS Grid + const gridTemplateColunns = `${inlineLabel ? '1fr ' : ''}${field ? '1fr ' : ''}${actions ? 'auto' : ''}`; + + return ( + + {borderLabel && {borderLabel}} + + {inlineLabel} + {field} + {actions} + + {otherChildern} +
+ {borderLabel && ( + + {borderLabel} + + )} +
+
+ ); +} + +export interface SettingsInputInlineLabelProps { + children?: React.ReactNode; + variant?: Variant | 'inherit'; +} + +export function SettingsInputInlineLabel( + { variant = 'body2', children = undefined }: SettingsInputInlineLabelProps, +) { + return ( + + { children } + + ); +} + +export interface GridItemProps { + children?: React.ReactNode; +} + +export function SettingsInputBorderLabel({ children = undefined }: GridItemProps) { + const theme = useTheme(); + return ( + + { children } + + + ); +} + +export function SettingsInputField({ children = undefined }: GridItemProps) { + return { children }; +} + +export function SettingsInputActions({ children = undefined }: GridItemProps) { + return { children }; +} diff --git a/webserver/web-interface/src/components/layout/AspectRatioBox.tsx b/webserver/web-interface/src/components/layout/AspectRatioBox.tsx new file mode 100644 index 00000000..60970fd2 --- /dev/null +++ b/webserver/web-interface/src/components/layout/AspectRatioBox.tsx @@ -0,0 +1,35 @@ +import * as React from 'react'; +import { Box, BoxProps } from '@mui/material'; + +interface AspectRatioBoxProps extends BoxProps { + children: React.ReactNode; + ratio?: number; +} + +const AspectRatioBox = React.forwardRef(({ + children, ratio = 1, sx, +}: AspectRatioBoxProps, ref) => ( + + *': { height: '100%', width: '100%' }, + ...sx, + }} + > + {children} + +
+ +)); + +// This is required somehow still by linter even though a default argument is passed. +// Seems like a known issue with forwardRef. https://github.com/jsx-eslint/eslint-plugin-react/issues/2856 +AspectRatioBox.defaultProps = { ratio: 1 }; + +export default AspectRatioBox; diff --git a/webserver/web-interface/src/components/loader/Loader.jsx b/webserver/web-interface/src/components/loader/Loader.tsx similarity index 100% rename from webserver/web-interface/src/components/loader/Loader.jsx rename to webserver/web-interface/src/components/loader/Loader.tsx diff --git a/webserver/web-interface/src/components/log/LogContainer.jsx b/webserver/web-interface/src/components/log/LogContainer.jsx deleted file mode 100644 index c7a90818..00000000 --- a/webserver/web-interface/src/components/log/LogContainer.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import { - Box, FormControlLabel, Paper, Stack, Switch, Typography, useTheme, -} from '@mui/material'; -import PropTypes from 'prop-types'; -import { useWebSocket } from 'react-use-websocket/dist/lib/use-websocket'; -import { MSG_TYPE_LOG, apiHost, filterSocketMessage } from '../../models/api'; - -function LogContainer({ maxLines }) { - const theme = useTheme(); - const [logLines, setLogLines] = useState([]); - const bottomRef = useRef(null); - const [followLogs, setFollowLogs] = useState(false); - - const { lastJsonMessage } = useWebSocket(`ws://${apiHost}/ws`, { - share: true, - retryOnError: true, - shouldReconnect: () => true, - reconnectAttempts: 1000, - filter: (message) => filterSocketMessage(message, MSG_TYPE_LOG), - }); - - useEffect(() => { - if (lastJsonMessage) { - const logLine = lastJsonMessage.data; - setLogLines((prevLogLines) => { - while (prevLogLines.length > maxLines) { - prevLogLines.shift(); - } - return [...prevLogLines, logLine]; - }); - } - }, [lastJsonMessage]); - - useEffect(() => { - if (followLogs) { - bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); - } - }, [logLines, followLogs]); - - const onChangeSwitch = (event, newValue) => { - setFollowLogs(newValue); - }; - - return ( - - - Logs - } - label="Follow logs" - labelPlacement="start" - /> - - - {logLines.map((line, index) => ( - // eslint-disable-next-line react/no-array-index-key - {`[${line.source}] ${line.log}`} - ))} -
- - - ); -} - -LogContainer.propTypes = { - maxLines: PropTypes.number, -}; - -LogContainer.defaultProps = { - maxLines: 200, -}; - -export default LogContainer; diff --git a/webserver/web-interface/src/components/log/LogContainer.tsx b/webserver/web-interface/src/components/log/LogContainer.tsx new file mode 100644 index 00000000..2ba9279b --- /dev/null +++ b/webserver/web-interface/src/components/log/LogContainer.tsx @@ -0,0 +1,45 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { + Box, FormControlLabel, Paper, Stack, Switch, Typography, useTheme, +} from '@mui/material'; +import useLogMessageStore from '../../state/LogStore'; + +function LogContainer() { + const theme = useTheme(); + const bottomRef = useRef(null); + const [followLogs, setFollowLogs] = useState(false); + + const { logs } = useLogMessageStore(); + + useEffect(() => { + if (followLogs) { + bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); + } + }, [logs, followLogs]); + + const onChangeSwitch = (event: React.ChangeEvent, newValue: boolean) => { + setFollowLogs(newValue); + }; + + return ( + + + Logs + } + label="Follow logs" + labelPlacement="start" + /> + + + {logs.map((line, index) => ( + // eslint-disable-next-line react/no-array-index-key + {`[${line.source}] ${line.log}`} + ))} +
+ + + ); +} + +export default LogContainer; diff --git a/webserver/web-interface/src/components/profile/AvailableProfileSelector.tsx b/webserver/web-interface/src/components/profile/AvailableProfileSelector.tsx new file mode 100644 index 00000000..497b6f43 --- /dev/null +++ b/webserver/web-interface/src/components/profile/AvailableProfileSelector.tsx @@ -0,0 +1,56 @@ +import { + Avatar, + ButtonBase, + List, ListItem, ListItemAvatar, + ListItemText, + alpha, + useTheme, +} from '@mui/material'; +import React, { useCallback } from 'react'; +import useProfileStore from '../../state/ProfileStore'; + +interface AvailableProfileSelectorProps { + selectedProfileId?: number; + onSelected?: (profileId: number) => void; +} + +export default function AvailableProfileSelector({ + onSelected = undefined, selectedProfileId = undefined, +}: AvailableProfileSelectorProps) { + const { availableProfiles } = useProfileStore(); + + const handleProfileSelected = useCallback(async (profileId: number) => { + onSelected && onSelected(profileId); + }, [onSelected]); + + const theme = useTheme(); + return ( + + {availableProfiles.map((profileSummary) => ( + + handleProfileSelected(profileSummary.id)} + > + + + { profileSummary.name + .split(' ') + .map((word) => word.replace(/[^a-zA-Z0-9]/g, '')[0]) // Ignore everything that's not a letter or a number + .join('') // Initials + .substring(0, 2) // Keep 2 + .toLocaleUpperCase() } + + + + + + ))} + + ); +} diff --git a/webserver/web-interface/src/components/profile/GlobalRestrictions.tsx b/webserver/web-interface/src/components/profile/GlobalRestrictions.tsx new file mode 100644 index 00000000..a0d5ed9d --- /dev/null +++ b/webserver/web-interface/src/components/profile/GlobalRestrictions.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { + Box, Typography, useTheme, +} from '@mui/material'; +import ScaleIcon from '@mui/icons-material/Scale'; +import TimerIcon from '@mui/icons-material/Timer'; +import WaterIcon from '@mui/icons-material/Water'; +import { Profile } from '../../models/profile'; +import formatTime from '../../models/time_format'; + +export function GlobalRestrictions({ profile }: { profile: Profile }) { + const theme = useTheme(); + const { weight, waterPumped, time } = profile.globalStopConditions || {}; + return ( + + STOP ON + + + + + + + ); +} + +export interface GlobalRestrictionProps { + icon: React.ElementType, value: string | undefined, color?: string +} + +export function GlobalRestriction({ icon: Icon, value, color = undefined }: GlobalRestrictionProps) { + const theme = useTheme(); + const finalColor = !value ? theme.palette.text.disabled : color || theme.palette.text.primary; + + return ( + + + {value} + + ); +} + +export interface RestrictionProps { + value: number | undefined +} + +export function TimeRestriction({ value }: RestrictionProps) { + return ; +} + +export function WeightRestriction({ value }: RestrictionProps) { + const theme = useTheme(); + return ; +} + +export function WaterPumpedRestriction({ value }: RestrictionProps) { + const theme = useTheme(); + return ; +} diff --git a/webserver/web-interface/src/components/profile/ProfilePreview.tsx b/webserver/web-interface/src/components/profile/ProfilePreview.tsx new file mode 100644 index 00000000..af6970eb --- /dev/null +++ b/webserver/web-interface/src/components/profile/ProfilePreview.tsx @@ -0,0 +1,56 @@ +import React, { useCallback, useState } from 'react'; +import { + Box, IconButton, Typography, useTheme, +} from '@mui/material'; +import SaveIcon from '@mui/icons-material/Save'; +import EditIcon from '@mui/icons-material/Edit'; +import { Profile } from '../../models/profile'; +import ProfileChart from '../chart/ProfileChart'; +import { GlobalRestrictions } from './GlobalRestrictions'; +import ProfileEditDialog from './edit/ProfileEditDialog'; + +export interface ProfileReviewProps { + profile: Profile; + onSave?: () => void; + onChange?: (profile: Profile) => void; +} + +export function ProfileReview({ profile, onSave = undefined, onChange = undefined }: ProfileReviewProps) { + const theme = useTheme(); + const [editOpen, setEditOpen] = useState(false); + + const handleDone = useCallback((newProfile: Profile) => { + setEditOpen(false); + onChange?.(newProfile); + }, [onChange]); + + return ( + + + {profile.name} + {(onSave || onChange) && ( + + {onSave && } + {onChange && setEditOpen(true)}>} + + )} + + + + {onChange && ( + setEditOpen(false)} profile={profile} onDone={handleDone} /> + )} + + ); +} diff --git a/webserver/web-interface/src/components/profile/edit/PhaseEdit.tsx b/webserver/web-interface/src/components/profile/edit/PhaseEdit.tsx new file mode 100644 index 00000000..5a51801c --- /dev/null +++ b/webserver/web-interface/src/components/profile/edit/PhaseEdit.tsx @@ -0,0 +1,206 @@ +import React, {} from 'react'; +import { + Box, MenuItem, TextField, ToggleButton, ToggleButtonGroup, Typography, useTheme, +} from '@mui/material'; +import Grid from '@mui/material/Unstable_Grid2/Grid2'; +import { + CurveStyle, Phase, PhaseStopConditions, PhaseType, Transition, +} from '../../../models/profile'; +import { SettingsNumberInput } from '../../inputs/settings_inputs'; +import { constrain } from '../../../models/utils'; + +export interface PhaseEditProps { + phase: Phase; + onChange: (phase: Phase) => void; +} + +function getNewTargetForTime(target:Transition, time: number): Transition { + if (time === 0 || target.curve === CurveStyle.INSTANT) { + return { ...target, time: 0, curve: CurveStyle.INSTANT }; + } + return { ...target, time }; +} + +export function PhaseEdit({ phase, onChange }: PhaseEditProps) { + const theme = useTheme(); + const handleTypeChange = (newType: PhaseType) => onChange({ ...phase, type: newType }); + const handleRestrictionChange = (restriction: number) => onChange({ ...phase, restriction }); + const handleTargetValueChange = (value: number | CurveStyle, key: keyof Transition) => onChange( + { ...phase, target: { ...phase.target, [key]: value } }, + ); + const handleStopConditionChange = (value: number, key: keyof PhaseStopConditions) => onChange( + { ...phase, stopConditions: { ...phase.stopConditions, [key]: value } }, + ); + + const handleCurveChange = (value: CurveStyle) => { + const time = value === CurveStyle.INSTANT ? 0 : phase.target.time || phase.stopConditions.time || 0; + onChange({ ...phase, target: { ...phase.target, time, curve: value } }); + }; + + // Managing curve style when transition time changes to/from zero + const handleTransitionTimeChange = (newTime: number) => { + onChange({ ...phase, target: getNewTargetForTime(phase.target, newTime * 1000) }); + }; + + const handleStopTimeChange = (newTime: number) => { + const time = newTime * 1000; + + // If there is a stop on time, also update the transition time + if (time !== 0) { + onChange({ + ...phase, + target: getNewTargetForTime(phase.target, time), + stopConditions: { ...phase.stopConditions, time }, + }); + } else { + handleStopConditionChange(time, 'time'); + } + }; + + return ( + + + + + + Targets + + handleTargetValueChange(constrain(v, 0, 15), 'end')} + /> + + + handleRestrictionChange(constrain(v, 0, 10))} + /> + + + handleStopTimeChange(constrain(v, 0, 1000))} + /> + + Transition + + div': { py: theme.spacing(0.6) } }} + value={phase.target.curve} + onChange={(e) => handleCurveChange(e.target.value as CurveStyle)} + > + {Object.values(CurveStyle).map((value) => ( + + {value} + + ))} + + + {phase.target.curve !== CurveStyle.INSTANT && ( + + handleTransitionTimeChange(constrain(v, 0, 1000))} + /> + + )} + + + + Stop conditions + + handleStopConditionChange(constrain(v, 0, 15), 'pressureAbove')} + /> + + + handleStopConditionChange(constrain(v, 0, 15), 'pressureBelow')} + /> + + + handleStopConditionChange(constrain(v, 0, 15), 'flowAbove')} + /> + + + handleStopConditionChange(constrain(v, 0, 15), 'flowBelow')} + /> + + + handleStopConditionChange(constrain(v, 0, 15), 'weight')} + /> + + + handleStopConditionChange(constrain(v, 0, 15), 'waterPumpedInPhase')} + /> + + + + ); +} + +export default PhaseEdit; + +interface PhaseTypeToggleProps { + value: PhaseType; + onChange: (value: PhaseType) => void; +} + +function PhaseTypeToggle({ value, onChange }: PhaseTypeToggleProps) { + return ( + + onChange(newValue)} + fullWidth + > + PRESSURE + FLOW + + + ); +} diff --git a/webserver/web-interface/src/components/profile/edit/ProfileEdit.tsx b/webserver/web-interface/src/components/profile/edit/ProfileEdit.tsx new file mode 100644 index 00000000..12d4d1f3 --- /dev/null +++ b/webserver/web-interface/src/components/profile/edit/ProfileEdit.tsx @@ -0,0 +1,283 @@ +import React, { + useCallback, useEffect, useState, +} from 'react'; +import { + Box, Button, IconButton, TextField, Typography, alpha, useTheme, +} from '@mui/material'; +import DoneIcon from '@mui/icons-material/Check'; +import CancelIcon from '@mui/icons-material/CancelOutlined'; +import CloseIcon from '@mui/icons-material/Close'; +import NextIcon from '@mui/icons-material/NavigateNext'; +import DeleteIcon from '@mui/icons-material/Delete'; +import CreateIcon from '@mui/icons-material/AddCircle'; +import GlobeIcon from '@mui/icons-material/Public'; +import PreviousIcon from '@mui/icons-material/NavigateBefore'; +import { + CurveStyle, Phase, PhaseType, Profile, +} from '../../../models/profile'; +import ProfileChart from '../../chart/ProfileChart'; +import { GlobalRestrictions } from '../GlobalRestrictions'; +import PhaseEdit from './PhaseEdit'; +import { getIndexInRange } from '../../../models/utils'; +import { ProfileGlobalsEdit } from './ProfileGlobalsEdit'; + +export interface ProfileEditProps { + profile: Profile, + onDone?: (profile: Profile) => void, + onCancel?: () => void, +} + +export function ProfileEdit({ profile, onDone = undefined, onCancel = undefined }: ProfileEditProps) { + const [editingProfile, setEditingProfile] = useState(profile); + const [phaseIndexSelected, setPhaseIndexSelected] = useState(undefined); + const [globalsSectionVisible, setGlobalsSectionVisible] = useState(true); + + const handlePhaseIndexSelected = useCallback((index: number | undefined) => { + setPhaseIndexSelected(index); + if (index !== undefined) { + setGlobalsSectionVisible(false); + } + }, []); + + const handleGlobalsVisibleChange = useCallback((state: boolean) => { + setGlobalsSectionVisible(state); + if (state) { + setPhaseIndexSelected(undefined); + } + }, []); + + useEffect(() => { + setEditingProfile(profile); + handlePhaseIndexSelected(undefined); + }, [profile, setEditingProfile, handlePhaseIndexSelected]); + + const deletePhase = useCallback( + (phaseIndex: number) => { + const newPhases = [...editingProfile.phases]; + newPhases.splice(phaseIndex, 1); + setEditingProfile({ ...editingProfile, phases: newPhases }); + handlePhaseIndexSelected(getIndexInRange(phaseIndex - 1, newPhases)); + }, + [setEditingProfile, editingProfile, handlePhaseIndexSelected], + ); + + const createNewPhase = useCallback( + (creationIndex: number) => { + const index = Math.min(editingProfile.phases.length, Math.max(0, creationIndex)); // ensure valid range + const newPhase:Phase = { + skip: false, + type: PhaseType.PRESSURE, + target: { end: 9, curve: CurveStyle.INSTANT, time: 0 }, + stopConditions: { time: 5000 }, + }; + const newPhases = [...editingProfile.phases.slice(0, index), newPhase, ...editingProfile.phases.slice(index)]; + setEditingProfile({ ...editingProfile, phases: newPhases }); + handlePhaseIndexSelected(getIndexInRange(index, newPhases)); + }, + [setEditingProfile, editingProfile, handlePhaseIndexSelected], + ); + + const updatePhase = useCallback((index: number, phase:Phase) => { + if (index < 0 || index >= editingProfile.phases.length) return; + const phases = [...editingProfile.phases]; + phases[index] = phase; + setEditingProfile({ ...editingProfile, phases }); + }, [editingProfile, setEditingProfile]); + + return ( + <> + + <> + + + + + + handleGlobalsVisibleChange(!globalsSectionVisible)} + onUpdatePhaseIndexSelected={handlePhaseIndexSelected} + /> + {globalsSectionVisible && ( + handleGlobalsVisibleChange(false)} + /> + )} + {phaseIndexSelected !== undefined && ( + handlePhaseIndexSelected(undefined)} + onUpdatePhase={(newPhase) => updatePhase(phaseIndexSelected, newPhase)} + /> + )} + + ); +} + +interface PhaseManagementBarProps { + profile: Profile; + phaseIndexSelected: number | undefined; + onUpdatePhaseIndexSelected: (newValue: number | undefined) => void; + onCreatePhase: (index: number) => void; + onDeletePhase: (index: number) => void; + onGlobalClicked: () => void; +} + +function PhaseManagementBar( + { + profile, phaseIndexSelected, onUpdatePhaseIndexSelected, onCreatePhase, onDeletePhase, onGlobalClicked, + }: PhaseManagementBarProps, +) { + const theme = useTheme(); + + const nextPhase = useCallback( + () => onUpdatePhaseIndexSelected( + getIndexInRange(phaseIndexSelected === undefined ? 0 : phaseIndexSelected + 1, profile.phases), + ), + [profile, phaseIndexSelected, onUpdatePhaseIndexSelected], + ); + + const previousPhase = useCallback( + () => onUpdatePhaseIndexSelected( + getIndexInRange(phaseIndexSelected === undefined ? 0 : phaseIndexSelected - 1, profile.phases), + ), + [profile, phaseIndexSelected, onUpdatePhaseIndexSelected], + ); + + return ( + + {profile.phases.length > 1 && ()} + + onGlobalClicked()}> + {phaseIndexSelected !== undefined && ( + <> + onDeletePhase(phaseIndexSelected)}> + onCreatePhase(phaseIndexSelected + 1)}> + + )} + {((profile.phases.length === 0)) && ( + onCreatePhase(profile.phases.length)}> + )} + + {profile.phases.length > 1 && ()} + + ); +} + +interface ProfileEditTitleProps extends ProfileEditProps { + onUpdated: (profile:Profile) => void, +} + +function ProfileEditTitle( + { + profile, + onUpdated, + onDone = undefined, + onCancel = undefined, + }: ProfileEditTitleProps, +) { + const theme = useTheme(); + const updateName = useCallback((name:string) => onUpdated({ ...profile, name }), [profile, onUpdated]); + + return ( + + + updateName(e.target.value)} + sx={{ + '& .MuiInputBase-root': { + fontSize: '1.5rem', + }, + }} + > + + + + {onDone && onDone(profile)}>} + {onCancel && } + + + ); +} + +interface PhaseEditingSectionProps { + title: string + phase: Phase; + onClose: () => void; + onUpdatePhase: (phase: Phase) => void; +} + +function PhaseEditingSection({ + title, phase, onClose, onUpdatePhase, +}: PhaseEditingSectionProps) { + const theme = useTheme(); + return ( + + + {title} + + + + + ); +} + +interface GlobalsSectionProps { + profile: Profile + onClose: () => void; + onUpdated: (profile: Profile) => void; +} + +function GlobalsSection({ profile, onUpdated, onClose }: GlobalsSectionProps) { + const theme = useTheme(); + return ( + + + Global profile settings + + + + + ); +} diff --git a/webserver/web-interface/src/components/profile/edit/ProfileEditDialog.tsx b/webserver/web-interface/src/components/profile/edit/ProfileEditDialog.tsx new file mode 100644 index 00000000..7502b47e --- /dev/null +++ b/webserver/web-interface/src/components/profile/edit/ProfileEditDialog.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { Dialog, Slide } from '@mui/material'; +import { Profile } from '../../../models/profile'; +import { ProfileEdit } from './ProfileEdit'; + +interface ProfileEditDialogProps { + open?: boolean; + profile: Profile; + onClose: () => void; + onDone: (profile: Profile) => void; +} + +export default function ProfileEditDialog({ + open = false, + profile, + onClose, + onDone, +}: ProfileEditDialogProps) { + return ( + + + + ); +} diff --git a/webserver/web-interface/src/components/profile/edit/ProfileGlobalsEdit.tsx b/webserver/web-interface/src/components/profile/edit/ProfileGlobalsEdit.tsx new file mode 100644 index 00000000..ee6e285a --- /dev/null +++ b/webserver/web-interface/src/components/profile/edit/ProfileGlobalsEdit.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { Typography } from '@mui/material'; +import Grid from '@mui/material/Unstable_Grid2/Grid2'; +import { Profile, GlobalStopConditions } from '../../../models/profile'; +import { SettingsNumberInput } from '../../inputs/settings_inputs'; +import { constrain } from '../../../models/utils'; + +export interface ProfileGlobalsEditProps { + profile: Profile; + onChange: (profile: Profile) => void; +} + +export function ProfileGlobalsEdit({ profile, onChange }: ProfileGlobalsEditProps) { + const handleStopConditionChange = (value: number, key: keyof GlobalStopConditions) => onChange( + { ...profile, globalStopConditions: { ...profile.globalStopConditions, [key]: value } }, + ); + const handleWaterTempChange = (value: number) => onChange( + { ...profile, waterTemperature: value }, + ); + + return ( + + + + handleWaterTempChange(constrain(v, 0, 105))} + /> + + + + STOP ON + + handleStopConditionChange(constrain(v, 0, 300), 'weight')} + /> + + + handleStopConditionChange(constrain(v, 0, 300), 'waterPumped')} + /> + + + handleStopConditionChange(constrain(v, 0, 1000) * 1000, 'time')} + /> + + + + ); +} diff --git a/webserver/web-interface/src/components/settings/tabs_settings.tsx b/webserver/web-interface/src/components/settings/tabs_settings.tsx new file mode 100644 index 00000000..7c1502c5 --- /dev/null +++ b/webserver/web-interface/src/components/settings/tabs_settings.tsx @@ -0,0 +1,403 @@ +import BluetoothIcon from '@mui/icons-material/Bluetooth'; +import CoffeeMakerIcon from '@mui/icons-material/CoffeeMaker'; +import TemperatureIcon from '@mui/icons-material/DeviceThermostat'; +import EqualizerIcon from '@mui/icons-material/Equalizer'; +import FlareIcon from '@mui/icons-material/Flare'; +import LogoDevIcon from '@mui/icons-material/LogoDev'; +import ScaleIcon from '@mui/icons-material/Scale'; +import { + Box, + Button, + SxProps, + Tab, + Tabs, + Theme, + Typography, + debounce, + useMediaQuery, + useTheme, +} from '@mui/material'; +import React, { useCallback, useEffect, useState } from 'react'; +import { + BoilerSettings, GaggiaSettings, LedSettings, ScalesSettings, SystemSettings, areGaggiaSettingsEqual, +} from '../../models/models'; +import { constrain } from '../../models/utils'; +import useBleScalesStore from '../../state/BleScalesDatastor'; +import { + LedColorPickerInput, + SettingsInputActions, + SettingsInputInlineLabel, + SettingsInputWrapper, + SettingsNumberInput, + SettingsToggleInput, +} from '../inputs/settings_inputs'; +import LogContainer from '../log/LogContainer'; +import ThemeModeToggle from '../theme/ThemeModeToggle'; +import ScalesCalibration from '../../pages/DialogPages/ScalesCalibrationDialog'; +import PzCalibration from '../../pages/DialogPages/PumpZeroCalibrationDialog'; + +interface TabPanelProps { + children?: React.ReactNode; + index: number; + value: number; + sx?: SxProps; +} + +function TabPanel({ + children = undefined, value, index, ...other +}: TabPanelProps) { + return ( + + ); +} + +function a11yProps(index: number) { + return { + id: `vertical-tab-${index}`, + 'aria-controls': `vertical-tabpanel-${index}`, + }; +} + +export interface TabbedSettingsProps { + settings: GaggiaSettings; + onChange: (settings:GaggiaSettings) => void; +} + +export default function TabbedSettings({ settings, onChange }: TabbedSettingsProps) { + const [selectedTab, setSelectedTab] = React.useState(0); + const [settingsInternal, setSettingsInternal] = useState(settings); + + const handleChange = (event: React.SyntheticEvent, newValue: number) => { + setSelectedTab(newValue); + }; + + const theme = useTheme(); + const smallScreen = useMediaQuery(theme.breakpoints.only('xs')); + const tabOrientation = smallScreen ? 'horizontal' : 'vertical'; + const tabPanelStyling = { + width: '100%', + mt: 2, + ml: { xs: 0, sm: 2 }, + }; + + // eslint-disable-next-line react-hooks/exhaustive-deps + const updateSettingsDebounced = useCallback( + debounce((newSettings: GaggiaSettings) => { + if (areGaggiaSettingsEqual(newSettings, settings)) return; + onChange(newSettings); + }, 1000), + [onChange, settings], + ); + + useEffect(() => { + updateSettingsDebounced(settingsInternal); + }, [settingsInternal, updateSettingsDebounced]); + + useEffect(() => setSettingsInternal(settings), [settings]); + + return ( + + + } label="Boiler" {...a11yProps(0)} /> + } label="System" {...a11yProps(1)} /> + } label="Led" {...a11yProps(2)} /> + } label="Scales" {...a11yProps(3)} /> + } label="SysLog" {...a11yProps(4)} /> + + + setSettingsInternal({ ...settingsInternal, boiler })} + /> + + + setSettingsInternal({ ...settingsInternal, system })} + /> + + + setSettingsInternal({ ...settingsInternal, led }))} + /> + + + setSettingsInternal({ ...settingsInternal, scales })} + /> + + + + + + ); +} + +interface BoilerSettingsPanelProps { + boiler: BoilerSettings; + onChange: (settings: BoilerSettings) => void; +} + +function BoilerSettingsPanel({ boiler, onChange }: BoilerSettingsPanelProps) { + return ( + + onChange({ ...boiler, steamSetPoint: constrain(value, 0, 165) })} + /> + onChange({ ...boiler, offsetTemp: constrain(value, 0, 20) })} + /> + onChange({ ...boiler, hpwr: value })} + /> + onChange({ ...boiler, mainDivider: value })} + /> + onChange({ ...boiler, brewDivider: value })} + /> + + ); +} + +interface SystemSettingsPanelProps { + system: SystemSettings; + onChange: (settings: SystemSettings) => void; +} + +function SystemSettingsPanel({ system, onChange }: SystemSettingsPanelProps) { + return ( + + onChange({ ...system, lcdSleep: value })} + maxDecimals={0} + /> + onChange({ ...system, pumpFlowAtZero: value })} + maxDecimals={3} + buttonIncrements={0.001} + /> + + Dark/Light toggle + + + onChange({ ...system, warmupState: value })} + /> + + ); +} + +interface LedSettingsPanelProps { + led: LedSettings; + onChange: (settings: LedSettings) => void; +} + +function LedSettingsPanel({ led, onChange }: LedSettingsPanelProps) { + return ( + + onChange({ ...led, state })} + /> + onChange({ ...led, disco })} + /> + onChange({ ...led, color })} + /> + + ); +} + +interface ScalesettingsPanelProps { + scales: ScalesSettings; + onChange: (settings: ScalesSettings) => void; +} + +function ScalesSettingsPanel({ scales, onChange }: ScalesettingsPanelProps) { + const theme = useTheme(); + const connectedScales = useBleScalesStore((state) => state.bleScales); + const [dialogOpen, setDialogOpen] = useState(false); + const [pzDialogOpen, pzSetDialogOpen] = useState(false); + const handleOpenDialog = () => { setDialogOpen(true); }; + const handleOpenPzDialog = () => { pzSetDialogOpen(true); }; + + return ( + <> + + + + + Predictive scales + + + pzSetDialogOpen(false)} /> + + onChange({ ...scales, forcePredictive: value })} + /> + + + + + + + Hardware scales + + + setDialogOpen(false)} + scalesF1={scales.hwScalesF1} + scalesF2={scales.hwScalesF2} + onScalesF1Change={(value) => onChange({ ...scales, hwScalesF1: value })} + onScalesF2Change={(value) => onChange({ ...scales, hwScalesF2: value })} + /> + + + onChange({ ...scales, hwScalesEnabled: value })} + /> + {scales.hwScalesEnabled && ( + <> + onChange({ ...scales, hwScalesF1: value })} + maxDecimals={0} + /> + onChange({ ...scales, hwScalesF2: value })} + maxDecimals={0} + /> + + )} + + + + + Bluetooth scales + + onChange({ ...scales, btScalesEnabled: value })} + /> + {scales.btScalesEnabled && ( + <> + onChange({ ...scales, btScalesAutoConnect: value })} + /> + + Device + {/* */} + {connectedScales.address.length > 0 + ? {connectedScales.name} + : Not connected} + {/* */} + + + )} + + + ); +} diff --git a/webserver/web-interface/src/components/shot/ShotHistory.tsx b/webserver/web-interface/src/components/shot/ShotHistory.tsx new file mode 100644 index 00000000..975a8722 --- /dev/null +++ b/webserver/web-interface/src/components/shot/ShotHistory.tsx @@ -0,0 +1,120 @@ +import Brightness5Icon from '@mui/icons-material/Brightness5'; +import Brightness7Icon from '@mui/icons-material/Brightness7'; +import NightsStayIcon from '@mui/icons-material/NightsStay'; +import { + Avatar, + ButtonBase, + List, ListItem, ListItemAvatar, + ListItemText, + Typography, + alpha, +} from '@mui/material'; +import React, { useCallback, useEffect, useState } from 'react'; +import formatTime from '../../models/time_format'; +import useShotDataStore from '../../state/ShotDataStore'; +import ShotDialog from '../../pages/ShotDialog'; +import { Shot } from '../../models/models'; + +enum TimeOfDay { + Morning = 'Morning', + Afternoon = 'Afternoon', + Evening = 'Evening', +} + +const getTimeOfDay = (date: Date): TimeOfDay => { + const hours = date.getHours(); + if (hours > 5 && hours < 12) { + return TimeOfDay.Morning; + } if (hours < 18) { + return TimeOfDay.Afternoon; + } + return TimeOfDay.Evening; +}; + +const getIcon = (timeOfDay: TimeOfDay) => { + switch (timeOfDay) { + case TimeOfDay.Morning: + return ; + case TimeOfDay.Afternoon: + return ; + case TimeOfDay.Evening: + return ; + default: + return ; + } +}; + +const getColorForTimeOfDay = (timeOfDay: TimeOfDay): string => { + switch (timeOfDay) { + case TimeOfDay.Morning: + return '#ffea00'; + case TimeOfDay.Afternoon: + return '#ffa733'; + case TimeOfDay.Evening: + return '#3d5afe'; + default: + return '#444'; + } +}; + +export default function ShotHistory() { + const shotHistory = useShotDataStore((state) => state.shotHistory); + const [viewingShot, setViewingShot] = useState(undefined); + const [sortedShotHistory, setSortedShotHistory] = useState([]); + + const handleShotSelected = useCallback((shot: Shot) => { + setViewingShot(shot); + }, [setViewingShot]); + + const handleSetOpen = useCallback((state: boolean) => { + if (!state) { + setViewingShot(undefined); + } + }, [setViewingShot]); + + useEffect(() => { + const shallowCopy = [...shotHistory]; // avoid mutating state + shallowCopy.sort((s1, s2) => s2.time - s1.time); + setSortedShotHistory(shallowCopy); + }, [setSortedShotHistory, shotHistory]); + + return ( + <> + {shotHistory.length === 0 && Pull some shots to see them here.} + {shotHistory.length > 0 && ( + + {sortedShotHistory.map((shot) => { + const shotDate = new Date(shot.time); + const timeOfDay = getTimeOfDay(shotDate); + const icon = getIcon(timeOfDay); + const color = getColorForTimeOfDay(timeOfDay); + const duration = formatTime({ time: shot.datapoints[shot.datapoints.length - 1].timeInShot }); + const formattedTimeInDay = shotDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + const formattedDay = shotDate.toLocaleDateString([], { day: 'numeric', month: 'short' }); + // const theme = useTheme(); + + return ( + + handleShotSelected(shot)}> + + + {icon} + + + + + + ); + })} + + )} + {viewingShot && ( + + )} + + ); +} diff --git a/webserver/web-interface/src/components/table/table.jsx b/webserver/web-interface/src/components/table/table.tsx similarity index 100% rename from webserver/web-interface/src/components/table/table.jsx rename to webserver/web-interface/src/components/table/table.tsx diff --git a/webserver/web-interface/src/components/table/table_data.js b/webserver/web-interface/src/components/table/table_data.ts similarity index 100% rename from webserver/web-interface/src/components/table/table_data.js rename to webserver/web-interface/src/components/table/table_data.ts diff --git a/webserver/web-interface/src/components/theme/AppTheme.js b/webserver/web-interface/src/components/theme/AppTheme.ts similarity index 63% rename from webserver/web-interface/src/components/theme/AppTheme.js rename to webserver/web-interface/src/components/theme/AppTheme.ts index b87ed2b6..161fe331 100644 --- a/webserver/web-interface/src/components/theme/AppTheme.js +++ b/webserver/web-interface/src/components/theme/AppTheme.ts @@ -1,4 +1,4 @@ -import { createTheme } from '@mui/material'; +import { PaletteMode, alpha, createTheme } from '@mui/material'; const lightTheme = createTheme({ palette: { @@ -13,7 +13,7 @@ const lightTheme = createTheme({ main: '#272727', }, background: { - default: '#F5F5F5', + default: '#E0E0E0', paper: '#FFF', }, temperature: { @@ -26,11 +26,18 @@ const lightTheme = createTheme({ main: '#1d7835', }, pressure: { - main: '#6296C5', + main: '#3c91de', }, weight: { main: '#844B48', }, + water: { + main: '#178bca', + }, + text: { + primary: alpha('#000000', 0.8), + secondary: alpha('#000000', 0.6), + }, }, }); @@ -53,18 +60,26 @@ const darkTheme = createTheme({ main: '#1d7835', }, pressure: { - main: '#6296C5', + main: '#3c91de', }, weight: { main: '#a75c58', }, background: { - default: '#111', - paper: '#111', + default: '#222', + paper: '#232323', + }, + water: { + main: '#178bca', }, + text: { + primary: alpha('#FFF', 0.8), + secondary: alpha('#FFF', 0.6), + }, + }, }); -export default function getAppTheme(mode) { +export default function getAppTheme(mode: PaletteMode) { return (mode === 'light') ? lightTheme : darkTheme; } diff --git a/webserver/web-interface/src/components/theme/ThemeModeToggle.jsx b/webserver/web-interface/src/components/theme/ThemeModeToggle.tsx similarity index 77% rename from webserver/web-interface/src/components/theme/ThemeModeToggle.jsx rename to webserver/web-interface/src/components/theme/ThemeModeToggle.tsx index 3bbf85b6..a4bdd2e3 100644 --- a/webserver/web-interface/src/components/theme/ThemeModeToggle.jsx +++ b/webserver/web-interface/src/components/theme/ThemeModeToggle.tsx @@ -1,10 +1,15 @@ import React, { createContext, useContext, useState } from 'react'; import { DarkModeToggle } from '@anatoliygatt/dark-mode-toggle'; -import { useTheme } from '@mui/material'; +import { PaletteMode, useTheme } from '@mui/material'; -export const ThemeModeContext = createContext({ +export interface ThemeModeContextType { + themeMode: PaletteMode; + changeThemeMode: (newMode: PaletteMode) => void; + } + +export const ThemeModeContext = createContext({ themeMode: 'dark', - changeThemeMode: () => {}, + changeThemeMode: (newMode: PaletteMode) => false, }); export default function ThemeModeToggle() { diff --git a/webserver/web-interface/src/components/theme/ThemeWrapper.jsx b/webserver/web-interface/src/components/theme/ThemeWrapper.tsx similarity index 69% rename from webserver/web-interface/src/components/theme/ThemeWrapper.jsx rename to webserver/web-interface/src/components/theme/ThemeWrapper.tsx index 00b2d20c..22936e1b 100644 --- a/webserver/web-interface/src/components/theme/ThemeWrapper.jsx +++ b/webserver/web-interface/src/components/theme/ThemeWrapper.tsx @@ -1,18 +1,19 @@ -import { CssBaseline, ThemeProvider, useMediaQuery } from '@mui/material'; -import React, { useMemo, useState } from 'react'; -import PropTypes from 'prop-types'; +import { + CssBaseline, PaletteMode, ThemeProvider, useMediaQuery, +} from '@mui/material'; +import React, { ReactNode, useMemo, useState } from 'react'; import getAppTheme from './AppTheme'; import { ThemeModeContext } from './ThemeModeToggle'; const SAVED_THEME_MODE_KEY = 'savedTheme'; -export default function ThemeWrapper({ children }) { +export default function ThemeWrapper({ children = undefined }: { children: ReactNode}) { const themeModeBrowserPreference = useMediaQuery('(prefers-color-scheme: dark)') ? 'dark' : 'light'; - const savedThemeMode = localStorage.getItem(SAVED_THEME_MODE_KEY); + const savedThemeMode = localStorage.getItem(SAVED_THEME_MODE_KEY) as (PaletteMode | null); const [themeMode, setThemeMode] = useState(savedThemeMode || themeModeBrowserPreference); - const changeThemeMode = (newMode) => { + const changeThemeMode = (newMode: PaletteMode) => { localStorage.setItem(SAVED_THEME_MODE_KEY, newMode); setThemeMode(newMode); }; @@ -29,11 +30,3 @@ export default function ThemeWrapper({ children }) { ); } - -ThemeWrapper.propTypes = { - children: PropTypes.node, -}; - -ThemeWrapper.defaultProps = { - children: undefined, -}; diff --git a/webserver/web-interface/src/components/theme/theme.d.ts b/webserver/web-interface/src/components/theme/theme.d.ts new file mode 100644 index 00000000..c6564b32 --- /dev/null +++ b/webserver/web-interface/src/components/theme/theme.d.ts @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable @typescript-eslint/no-empty-interface */ +import { + PaletteColor, SimplePaletteColorOptions, +} from '@mui/material/styles/createPalette'; + +/** add more custom colors here */ +type CustomPalette = { + appbar?: PaletteColor; + temperature: PaletteColor; + flow: PaletteColor; + weightFlow: PaletteColor; + pressure: PaletteColor; + weight: PaletteColor; + water: PaletteColor; +} + +type CustomPaletteOptions = { + appbar?: SimplePaletteColorOptions; + temperature: SimplePaletteColorOptions; + flow: SimplePaletteColorOptions; + weightFlow: SimplePaletteColorOptions; + pressure: SimplePaletteColorOptions; + weight: SimplePaletteColorOptions; + water: SimplePaletteColorOptions; +} + +declare module '@mui/material/styles/createPalette' { + interface Palette extends CustomPalette {} + + interface PaletteOptions extends CustomPaletteOptions {} +} diff --git a/webserver/web-interface/src/components/wifi/NetworkPropTypes.js b/webserver/web-interface/src/components/wifi/NetworkPropTypes.js deleted file mode 100644 index 452ce89c..00000000 --- a/webserver/web-interface/src/components/wifi/NetworkPropTypes.js +++ /dev/null @@ -1,15 +0,0 @@ -import PropTypes from 'prop-types'; - -export const WifiStatusPropType = PropTypes.shape({ - status: PropTypes.oneOf(['connected', 'disconnected']).isRequired, - ssid: PropTypes.string.isRequired, - ip: PropTypes.string.isRequired, -}); - -export const Network = PropTypes.shape({ - ssid: PropTypes.string.isRequired, - rssi: PropTypes.number.isRequired, - secured: PropTypes.bool.isRequired, -}); - -export const NetworksList = PropTypes.arrayOf(Network); diff --git a/webserver/web-interface/src/components/wifi/NetworkTypes.ts b/webserver/web-interface/src/components/wifi/NetworkTypes.ts new file mode 100644 index 00000000..b960cb15 --- /dev/null +++ b/webserver/web-interface/src/components/wifi/NetworkTypes.ts @@ -0,0 +1,11 @@ +export type WifiStatus = { + status: 'connected' | 'disconnected'; + ssid: string, + ip: string, +} + +export type Network = { + ssid: string; + rssi: number; + secured: boolean; +} diff --git a/webserver/web-interface/src/components/wifi/WifiSettingsCard.jsx b/webserver/web-interface/src/components/wifi/WifiSettingsCard.tsx similarity index 81% rename from webserver/web-interface/src/components/wifi/WifiSettingsCard.jsx rename to webserver/web-interface/src/components/wifi/WifiSettingsCard.tsx index 0576a044..efb7238e 100644 --- a/webserver/web-interface/src/components/wifi/WifiSettingsCard.jsx +++ b/webserver/web-interface/src/components/wifi/WifiSettingsCard.tsx @@ -1,17 +1,19 @@ import { Button, - Card, CardActions, CardContent, Typography, + Card, CardActions, CardContent, Typography, useTheme, } from '@mui/material'; import React, { useEffect, useState } from 'react'; import { disconnectFromWifi, getWifiStatus } from '../client/WifiClient'; import Loader from '../loader/Loader'; -import WifiStatus from './WifiStatus'; +import WifiStatusDisplay from './WifiStatus'; import AvailableNetworksDrawer from './available-networks/AvailableNetworksDrawer'; +import { WifiStatus } from './NetworkTypes'; export default function WifiSettingsCard() { - const [wifiStatus, setWifiStatus] = useState({}); + const [wifiStatus, setWifiStatus] = useState(undefined); const [wifiStatusLoading, setWifiStatusLoading] = useState(true); const [wifiDrawerOpen, setWiFiDrawerOpen] = useState(false); + const theme = useTheme(); function isConnected() { return wifiStatus && wifiStatus.status === 'connected'; @@ -23,7 +25,7 @@ export default function WifiSettingsCard() { const status = await getWifiStatus(); setWifiStatus(status); } catch (e) { - setWifiStatus(null); + setWifiStatus(undefined); } finally { setWifiStatusLoading(false); } @@ -42,10 +44,10 @@ export default function WifiSettingsCard() {
- + WiFi Status - {wifiStatusLoading ? : } + {wifiStatusLoading ? : } {isConnected() && } diff --git a/webserver/web-interface/src/components/wifi/WifiStatus.jsx b/webserver/web-interface/src/components/wifi/WifiStatus.tsx similarity index 69% rename from webserver/web-interface/src/components/wifi/WifiStatus.jsx rename to webserver/web-interface/src/components/wifi/WifiStatus.tsx index 12bbba50..20e964ac 100644 --- a/webserver/web-interface/src/components/wifi/WifiStatus.jsx +++ b/webserver/web-interface/src/components/wifi/WifiStatus.tsx @@ -2,9 +2,13 @@ import React from 'react'; import SignalWifi3BarIcon from '@mui/icons-material/SignalWifi3Bar'; import SignalWifiOffIcon from '@mui/icons-material/SignalWifiOff'; import { Stack, Typography } from '@mui/material'; -import { WifiStatusPropType } from './NetworkPropTypes'; +import { WifiStatus } from './NetworkTypes'; -export default function WifiStatus({ status }) { +type WifiStatusProps = { + status?: WifiStatus +} + +export default function WifiStatusDisplay({ status = undefined }: WifiStatusProps) { function isConnected() { return status && status.status === 'connected'; } @@ -13,7 +17,7 @@ export default function WifiStatus({ status }) { ? ( - {`${status.ssid} (${status.ip})`} + {`${status?.ssid} (${status?.ip})`} ) : ( @@ -23,11 +27,3 @@ export default function WifiStatus({ status }) { ); } - -WifiStatus.propTypes = { - status: WifiStatusPropType, -}; - -WifiStatus.defaultProps = { - status: null, -}; diff --git a/webserver/web-interface/src/components/wifi/available-networks/AvailableNetwork.jsx b/webserver/web-interface/src/components/wifi/available-networks/AvailableNetwork.tsx similarity index 85% rename from webserver/web-interface/src/components/wifi/available-networks/AvailableNetwork.jsx rename to webserver/web-interface/src/components/wifi/available-networks/AvailableNetwork.tsx index 54794bac..78324f15 100644 --- a/webserver/web-interface/src/components/wifi/available-networks/AvailableNetwork.jsx +++ b/webserver/web-interface/src/components/wifi/available-networks/AvailableNetwork.tsx @@ -3,17 +3,23 @@ import SignalWifi3BarIcon from '@mui/icons-material/SignalWifi3Bar'; import { Accordion, AccordionSummary, Alert, Button, TextField, Typography, AccordionDetails, Stack, } from '@mui/material'; -import PropTypes from 'prop-types'; -import { Network } from '../NetworkPropTypes'; +import { Network } from '../NetworkTypes'; import { connectToWifi } from '../../client/WifiClient'; import Loader from '../../loader/Loader'; +type AvailableNetworkProps = { + network: Network; + onClick: () => void; + onConnected: (network: Network) => void; + expanded: boolean; +} + export default function AvailableNetwork({ network, onClick, - expanded, + expanded = false, onConnected, -}) { +}: AvailableNetworkProps) { const [password, setPassword] = useState(''); const [connecting, setConnecting] = useState(false); const [connectionError, setConnectionError] = useState(false); @@ -70,16 +76,3 @@ export default function AvailableNetwork({ ); } - -AvailableNetwork.propTypes = { - network: Network.isRequired, - onClick: PropTypes.func, - onConnected: PropTypes.func, - expanded: PropTypes.bool, -}; - -AvailableNetwork.defaultProps = { - onClick: () => {}, - onConnected: () => {}, - expanded: false, -}; diff --git a/webserver/web-interface/src/components/wifi/available-networks/AvailableNetworks.jsx b/webserver/web-interface/src/components/wifi/available-networks/AvailableNetworks.tsx similarity index 76% rename from webserver/web-interface/src/components/wifi/available-networks/AvailableNetworks.jsx rename to webserver/web-interface/src/components/wifi/available-networks/AvailableNetworks.tsx index 7bab180b..ed980bce 100644 --- a/webserver/web-interface/src/components/wifi/available-networks/AvailableNetworks.jsx +++ b/webserver/web-interface/src/components/wifi/available-networks/AvailableNetworks.tsx @@ -1,15 +1,19 @@ import React, { useEffect, useState } from 'react'; -import PropTypes from 'prop-types'; import { Alert, Box } from '@mui/material'; import { getAvailableNetworks } from '../../client/WifiClient'; import Loader from '../../loader/Loader'; import AvailableNetwork from './AvailableNetwork'; +import { Network } from '../NetworkTypes'; -export default function AvailableNetworks({ onConnected }) { - const [networks, setNetworks] = useState([]); +type AvailableNetworksProps = { + onConnected: () => void; +} + +export default function AvailableNetworks({ onConnected }: AvailableNetworksProps) { + const [networks, setNetworks] = useState([]); const [loading, setLoading] = useState(false); const [networksError, setNetworksError] = useState(false); - const [expandedNetworkId, setExpandedNetworkId] = useState(null); + const [expandedNetworkId, setExpandedNetworkId] = useState(null); useEffect(() => { const loadNetworks = async () => { @@ -17,7 +21,6 @@ export default function AvailableNetworks({ onConnected }) { try { setLoading(true); setNetworksError(false); - console.log('loading networks'); const networksResponse = await getAvailableNetworks(); setNetworks(networksResponse); } catch (e) { @@ -27,6 +30,8 @@ export default function AvailableNetworks({ onConnected }) { } }; loadNetworks(); + // only load networks once per render of this component + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); if (networksError) { @@ -54,11 +59,3 @@ export default function AvailableNetworks({ onConnected }) {
); } - -AvailableNetworks.propTypes = { - onConnected: PropTypes.func, -}; - -AvailableNetworks.defaultProps = { - onConnected: () => {}, -}; diff --git a/webserver/web-interface/src/components/wifi/available-networks/AvailableNetworksDrawer.jsx b/webserver/web-interface/src/components/wifi/available-networks/AvailableNetworksDrawer.tsx similarity index 75% rename from webserver/web-interface/src/components/wifi/available-networks/AvailableNetworksDrawer.jsx rename to webserver/web-interface/src/components/wifi/available-networks/AvailableNetworksDrawer.tsx index 35772112..ab1322dd 100644 --- a/webserver/web-interface/src/components/wifi/available-networks/AvailableNetworksDrawer.jsx +++ b/webserver/web-interface/src/components/wifi/available-networks/AvailableNetworksDrawer.tsx @@ -4,12 +4,17 @@ import { Box, Button, Drawer, Stack, Typography, useTheme, } from '@mui/material'; -import PropTypes from 'prop-types'; import { refrehNetworks } from '../../client/WifiClient'; import Loader from '../../loader/Loader'; import AvailableNetworks from './AvailableNetworks'; -export default function AvailableNetworksDrawer({ open, onOpenChanged, onConnected }) { +type AvailableNetworksDrawerProps = { + open: boolean; + onOpenChanged: (isOpen: boolean) => void; + onConnected: () => void; +} + +export default function AvailableNetworksDrawer({ open, onOpenChanged, onConnected }: AvailableNetworksDrawerProps) { const theme = useTheme(); const [networksRefreshing, setNetworksRefreshing] = useState(false); const [wifiDrawerRefreshKey, setWifiDrawerRefreshKey] = useState(0); @@ -24,8 +29,8 @@ export default function AvailableNetworksDrawer({ open, onOpenChanged, onConnect } } - const toggleDrawer = (isOpen) => (event) => { - if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) { + const toggleDrawer = (isOpen: boolean) => (event: { type?: string; key?: string; }) => { + if (event?.type === 'keydown' && (event?.key === 'Tab' || event.key === 'Shift')) { return; } onOpenChanged(isOpen); @@ -42,7 +47,7 @@ export default function AvailableNetworksDrawer({ open, onOpenChanged, onConnect Available networks - + {networksRefreshing && } @@ -50,13 +55,3 @@ export default function AvailableNetworksDrawer({ open, onOpenChanged, onConnect ) : ; } - -AvailableNetworksDrawer.propTypes = { - open: PropTypes.bool.isRequired, - onOpenChanged: PropTypes.func.isRequired, - onConnected: PropTypes.func, -}; - -AvailableNetworksDrawer.defaultProps = { - onConnected: () => {}, -}; diff --git a/webserver/web-interface/src/global.d.ts b/webserver/web-interface/src/global.d.ts new file mode 100644 index 00000000..3c7629d4 --- /dev/null +++ b/webserver/web-interface/src/global.d.ts @@ -0,0 +1 @@ +declare module 'react-liquid-gauge'; diff --git a/webserver/web-interface/src/models/api.js b/webserver/web-interface/src/models/api.js deleted file mode 100644 index 6d58b943..00000000 --- a/webserver/web-interface/src/models/api.js +++ /dev/null @@ -1,38 +0,0 @@ -export const MSG_TYPE_SHOT_DATA = 'shot_data_update'; -export const MSG_TYPE_SENSOR_DATA = 'sensor_data_update'; -export const MSG_TYPE_LOG = 'log_record'; - -export function filterSocketMessage(message, ...types) { - if (!message || !message.data) { - return false; - } - return types.some((type) => message.data.indexOf(type) >= 0); -} - -export function filterJsonMessage(message, ...types) { - if (!message || !message.action) { - return false; - } - return types.some((type) => message.action === type); -} -export const defaultShotSnapshot = { - timeInShot: 0, - temperature: 0, - pressure: 0, - pumpFlow: 0, - shotWeight: 0, - targetTemperature: 0, - targetPumpFlow: 0, - targetPressure: 0, -}; - -export const apiHost = (process.env.NODE_ENV === 'development') - ? '192.168.4.1' - : window.location.host; - -export function formatTimeInShot(timeInShot) { - const milliseconds = timeInShot % 1000; - const seconds = Math.floor(timeInShot / 1000) % 60; - const minutes = Math.floor(timeInShot / 60000); - return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${milliseconds.toString().padStart(3, '0')}`; -} diff --git a/webserver/web-interface/src/models/models.ts b/webserver/web-interface/src/models/models.ts new file mode 100644 index 00000000..9a899a24 --- /dev/null +++ b/webserver/web-interface/src/models/models.ts @@ -0,0 +1,192 @@ +export enum OperationMode { + BREW_AUTO = 'BREW_AUTO', + BREW_MANUAL = 'BREW_MANUAL', + FLUSH = 'FLUSH', + FLUSH_AUTO = 'FLUSH_AUTO', + DESCALE = 'DESCALE', + STEAM = 'STEAM', +} + +export function areOperationModesEqual(mode1: OperationMode, mode2: OperationMode): boolean { + return mode1 === mode2; +} + +export interface SystemState { + startupInitFinished: boolean, + operationMode: OperationMode, + tofReady: boolean, + isSteamForgottenON: boolean, + scalesPresent: boolean, + timeAlive: number, + descaleProgress: number, + tarePending: boolean, +} + +export interface SensorState { + brewActive: boolean; + steamActive: boolean; + hotWaterSwitchState: boolean; + temperature: number; + waterTemperature: number; + pressure: number; + pumpFlow: number; + weightFlow: number; + weight: number; + waterLevel: number; +} + +export interface ShotSnapshot { + timeInShot: number; + pressure: number; + pumpFlow: number; + weightFlow: number; + temperature: number; + shotWeight: number; + waterPumped: number; + targetTemperature: number; + targetPumpFlow: number; + targetPressure: number; +} + +export interface LogMessage { + source: string, + log: string, +} + +export interface Shot { + time: number, + datapoints: ShotSnapshot[], +} + +export interface BoilerSettings { + steamSetPoint: number; + offsetTemp: number; + hpwr: number; + mainDivider: number; + brewDivider: number; +} + +export function areBoilerSettingsEqual(settings1: BoilerSettings, settings2: BoilerSettings): boolean { + return settings1.steamSetPoint === settings2.steamSetPoint + && settings1.offsetTemp === settings2.offsetTemp + && settings1.hpwr === settings2.hpwr + && settings1.mainDivider === settings2.mainDivider + && settings1.brewDivider === settings2.brewDivider; +} + +export interface SystemSettings { + pumpFlowAtZero: number; + scalesF1: number; + scalesF2: number; + lcdSleep: number; + warmupState: boolean; +} + +export function areSystemSettingsEqual(settings1: SystemSettings, settings2: SystemSettings): boolean { + return settings1.pumpFlowAtZero === settings2.pumpFlowAtZero + && settings1.scalesF1 === settings2.scalesF1 + && settings1.scalesF2 === settings2.scalesF2 + && settings1.lcdSleep === settings2.lcdSleep + && settings1.warmupState === settings2.warmupState; +} + +export interface BrewSettings { + basketPrefill: boolean; + homeOnShotFinish: boolean; + brewDeltaState: boolean; +} + +export function areBrewSettingsEqual(settings1: BrewSettings, settings2: BrewSettings): boolean { + return settings1.basketPrefill === settings2.basketPrefill + && settings1.homeOnShotFinish === settings2.homeOnShotFinish + && settings1.brewDeltaState === settings2.brewDeltaState; +} + +export interface LedColor { + R: number; + G: number; + B: number; +} + +export function areLedColorsEqual(color1: LedColor, color2: LedColor): boolean { + return color1.R === color2.R + && color1.G === color2.G + && color1.B === color2.B; +} + +export interface LedSettings { + state: boolean; + disco: boolean; + color: LedColor +} + +export function areLedSettingsEqual(settings1: LedSettings, settings2: LedSettings): boolean { + return settings1.state === settings2.state + && settings1.disco === settings2.disco + && areLedColorsEqual(settings1.color, settings2.color); +} + +export interface ScalesSettings { + forcePredictive: boolean; + hwScalesEnabled: boolean; + hwScalesF1: number; + hwScalesF2: number; + btScalesEnabled: boolean; + btScalesAutoConnect: boolean; +} + +export function areScalesSettingsEqual(settings1: ScalesSettings, settings2: ScalesSettings): boolean { + return settings1.forcePredictive === settings2.forcePredictive + && settings1.hwScalesEnabled === settings2.hwScalesEnabled + && settings1.hwScalesF1 === settings2.hwScalesF1 + && settings1.hwScalesF2 === settings2.hwScalesF2 + && settings1.btScalesEnabled === settings2.btScalesEnabled + && settings1.btScalesAutoConnect === settings2.btScalesAutoConnect; +} + +export interface GaggiaSettings { + boiler: BoilerSettings; + system: SystemSettings; + brew: BrewSettings; + led: LedSettings; + scales: ScalesSettings; +} + +export function areGaggiaSettingsEqual(settings1: GaggiaSettings, settings2: GaggiaSettings): boolean { + return areBoilerSettingsEqual(settings1.boiler, settings2.boiler) + && areSystemSettingsEqual(settings1.system, settings2.system) + && areBrewSettingsEqual(settings1.brew, settings2.brew) + && areLedSettingsEqual(settings1.led, settings2.led) + && areScalesSettingsEqual(settings1.scales, settings2.scales); +} + +export enum NotificationType { + INFO = 'INFO', + SUCCESS = 'SUCCESS', + ERROR = 'ERROR', + WARN = 'WARN', +} + +export interface Notification { + type: NotificationType, + message: string, +} + +export enum DescalingState { + IDLE='IDLE', + PHASE1='PHASE1', + PHASE2='PHASE2', + PHASE3='PHASE3', + FINISHED='FINISHED', +} + +export interface DescalingProgress { + state: DescalingState; + time: number; // (time in millis) + progress: number; // 0-100 +} + +export interface BleScales { + name: string; + address: string; +} diff --git a/webserver/web-interface/src/models/profile.js b/webserver/web-interface/src/models/profile.js deleted file mode 100644 index a669d770..00000000 --- a/webserver/web-interface/src/models/profile.js +++ /dev/null @@ -1,145 +0,0 @@ -/* eslint-disable max-classes-per-file */ - -function notDefined(value) { - return (value === null || value === undefined); -} - -export const PhaseTypes = Object.freeze({ - FLOW: Symbol('FLOW'), - PRESSURE: Symbol('PRESSURE'), -}); - -export function parsePhaseType(phaseTypeObj) { - if (notDefined(phaseTypeObj)) { - return undefined; - } - const phaseType = PhaseTypes[phaseTypeObj]; - - if (!phaseType) { - throw Error(`${phaseTypeObj} not one of PhaseTypes{${Object.keys(PhaseTypes).join(',')}}`); - } - return phaseType; -} - -export const CurveStyles = Object.freeze({ - EASE_IN: Symbol('EASE_IN'), - EASE_OUT: Symbol('EASE_OUT'), - EASE_IN_OUT: Symbol('EASE_IN_OUT'), - LINEAR: Symbol('LINEAR'), - INSTANT: Symbol('INSTANT'), -}); - -export function parseCurveStyle(curveStyleObj) { - if (notDefined(curveStyleObj)) { - return undefined; - } - const curveStyle = CurveStyles[curveStyleObj]; - - if (!curveStyle) { - throw Error(`${curveStyle} not one of CurveStyles{${Object.keys(CurveStyles).join(',')}}`); - } - return curveStyle; -} - -export class Transition { - constructor(start, end, curve, time) { - if (start === null || start === undefined) { - throw Error('start is required for Transition'); - } - this.start = start; - this.end = end; - this.curve = curve; - this.time = time; - } - - static parse(obj) { - return new Transition(obj.start, obj.end, parseCurveStyle(obj.curve), obj.time); - } -} - -export class PhaseStopConditions { - constructor( - time, - pressureAbove, - pressureBelow, - flowAbove, - flowBelow, - weight, - waterPumpedInPhase, - ) { - this.time = time; - this.pressureAbove = pressureAbove; - this.pressureBelow = pressureBelow; - this.flowAbove = flowAbove; - this.flowBelow = flowBelow; - this.weight = weight; - this.waterPumpedInPhase = waterPumpedInPhase; - } - - static parse(obj) { - return new PhaseStopConditions( - obj.time, - obj.pressureAbove, - obj.pressureBelow, - obj.flowAbove, - obj.flowBelow, - obj.weight, - obj.waterPumpedInPhase, - ); - } -} - -export class Phase { - constructor(type, target, restriction, stopConditions) { - if (notDefined(type) || notDefined(target)) { - throw Error('type and target are requred for Phase'); - } - this.type = type; - this.target = target; - this.restriction = restriction; - this.stopConditions = stopConditions; - } - - static parse(obj) { - return new Phase( - parsePhaseType(obj.type), - Transition.parse(obj.target), - obj.restriction, - obj.stopConditions ? PhaseStopConditions.parse(obj.stopConditions) : undefined, - ); - } -} - -export class GlobalStopConditions { - constructor( - time, - weight, - waterPumped, - ) { - this.time = time; - this.weight = weight; - this.waterPumped = waterPumped; - } - - static parse(obj) { - return new GlobalStopConditions( - obj.time, - obj.weight, - obj.waterPumped, - ); - } -} - -export class Profile { - constructor(phases, globalStopConditions) { - this.phases = phases; - this.globalStopConditions = globalStopConditions; - } - - static parse(obj) { - return new Profile( - Array.isArray(obj.phases) ? obj.phases.map((phase) => Phase.parse(phase)) : [], - obj.globalStopConditions ? GlobalStopConditions.parse(obj.globalStopConditions) : undefined, - ); - } -} diff --git a/webserver/web-interface/src/models/profile.ts b/webserver/web-interface/src/models/profile.ts new file mode 100644 index 00000000..906d9880 --- /dev/null +++ b/webserver/web-interface/src/models/profile.ts @@ -0,0 +1,65 @@ +export enum PhaseType { + FLOW = 'FLOW', + PRESSURE = 'PRESSURE', +} + +export enum CurveStyle { + EASE_IN= 'EASE_IN', + EASE_OUT= 'EASE_OUT', + EASE_IN_OUT= 'EASE_IN_OUT', + LINEAR= 'LINEAR', + INSTANT= 'INSTANT', +} + +export interface Transition { + start?: number; + end: number; + curve: CurveStyle + time?: number; +} + +export interface PhaseStopConditions { + time?: number; + pressureAbove?: number; + pressureBelow?: number; + flowAbove?: number; + flowBelow?: number; + weight?: number; + waterPumpedInPhase?: number; + } + +export interface Phase { + name?: string; + type: PhaseType; + target: Transition; + restriction?: number; + stopConditions: PhaseStopConditions; + skip: boolean + waterTemperature?: number; +} + +export interface GlobalStopConditions { + time?: number, + weight?: number, + waterPumped?: number, +} + +export interface BrewRecipe { + coffeeIn?: number; + coffeeOut?: number; + ratio?: number; +} + +export interface Profile { + id?: number; + name: string; + phases: Array; + globalStopConditions?: GlobalStopConditions ; + waterTemperature: number; + recipe?: BrewRecipe; +} + +export interface ProfileSummary { + id: number; + name: string; +} diff --git a/webserver/web-interface/src/models/propTypes.js b/webserver/web-interface/src/models/propTypes.js deleted file mode 100644 index adee7b01..00000000 --- a/webserver/web-interface/src/models/propTypes.js +++ /dev/null @@ -1,5 +0,0 @@ -import PropTypes from 'prop-types'; -import { Phase, Profile } from './profile'; - -export const ProfilePropType = PropTypes.instanceOf(Profile); -export const PhasePropType = PropTypes.instanceOf(Phase); diff --git a/webserver/web-interface/src/models/time_format.ts b/webserver/web-interface/src/models/time_format.ts new file mode 100644 index 00000000..d1a25424 --- /dev/null +++ b/webserver/web-interface/src/models/time_format.ts @@ -0,0 +1,37 @@ +export default function formatTime({ + time, + includeMillis = false, + truncateLeadingZeroUnits = true, +}: { + time: number; + includeMillis?: boolean; + truncateLeadingZeroUnits?: boolean; + }) { + const hours = Math.floor(time / 3600000); + const minutes = Math.floor((time - hours * 3600000) / 60000); + const seconds = Math.floor((time - hours * 3600000 - minutes * 60000) / 1000); + const milliseconds = time % 1000; + + // Format hours, minutes, and seconds as 2 digits + let hourString = (`0${hours}`).slice(-2); + const minuteString = (`0${minutes}`).slice(-2); + const secondString = (`0${seconds}`).slice(-2); + + // Format milliseconds as 3 digits + const millisecondString = (`00${milliseconds}`).slice(-3); + + // If truncateLeadingZeroUnits, remove the zero hours/minutes/seconds + if (truncateLeadingZeroUnits) { + if (hours === 0) { + hourString = ''; + } + } + + // Construct the final string + const formattedTime = `${hourString ? `${hourString}:` : ''}` + + `${minuteString ? `${minuteString}:` : ''}` + + `${secondString || '00'}` + + `${includeMillis ? `:${millisecondString}` : ''}`; + + return formattedTime; +} diff --git a/webserver/web-interface/src/models/utils.ts b/webserver/web-interface/src/models/utils.ts new file mode 100644 index 00000000..aadae990 --- /dev/null +++ b/webserver/web-interface/src/models/utils.ts @@ -0,0 +1,45 @@ +export function constrain(value: number, min: number, max: number) { + return Math.min(max, Math.max(min, value)); +} + +export function sanitizeNumberString(input: string, maxDecimals?: number) { + // Determine decimal separator for current locale + const locale = navigator.language; + const decimalSeparator = (1.1).toLocaleString(locale).substring(1, 2); + let result = ''; + + // Allow negative sign only at the start of the string + const isNegative = input.startsWith('-'); + result = result.replaceAll('-', ''); + + // Remove all non-digits, non-decimal separators and non-decimal separators + result = input.replace(new RegExp(`[^0-9\\${decimalSeparator}]`, 'g'), ''); + + // Replace all decimal separators except the first with nothing + const decimalIndex = result.indexOf(decimalSeparator); + result = result.replace(new RegExp(`\\${decimalSeparator}`, 'g'), (match, index) => (index > decimalIndex ? '' : match)); + + // Limit the number of decimal digits + const decimalPart = result.split(decimalSeparator)[1]; + if (maxDecimals && decimalPart && decimalPart.length > maxDecimals) { + result = result.slice(0, decimalIndex + maxDecimals + 1); + } + + // Remove trailing decimalSeparator edgecase + if (decimalIndex >= 0 && maxDecimals === 0) { + result = result.slice(0, decimalIndex); + } + + return isNegative ? `-${result}` : result; +} + +export function same(v1: number, v2:number) { + return Math.abs(v1 - v2) < 0.0001; +} + +export function getIndexInRange(desiredIndex: number | undefined, array: unknown[]): number | undefined { + if (array.length === 0 || desiredIndex === undefined) { + return undefined; + } + return Math.max(0, Math.min(array.length - 1, desiredIndex)); +} diff --git a/webserver/web-interface/src/pages/DialogPages/DescalingDialog.tsx b/webserver/web-interface/src/pages/DialogPages/DescalingDialog.tsx new file mode 100644 index 00000000..7212f392 --- /dev/null +++ b/webserver/web-interface/src/pages/DialogPages/DescalingDialog.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { + Box, + Button, + Dialog, DialogActions, DialogContent, DialogTitle, LinearProgress, Typography, +} from '@mui/material'; +import { DescalingProgress, DescalingState } from '../../models/models'; +import formatTime from '../../models/time_format'; + +interface DescalingDialogProps { + data: DescalingProgress; + open: boolean; + onClose: () => void; +} + +const descriptions = { + [DescalingState.IDLE]: 'Waiting. Activate Brew button to begin.', + [DescalingState.PHASE1]: 'Slowly penetrating that scale.', + [DescalingState.PHASE2]: 'Softening that scale.', + [DescalingState.PHASE3]: 'Killing that scale big time.', + [DescalingState.FINISHED]: 'Finished! Scale exterminated.', +}; + +function DescalingDialog({ data, open, onClose }:DescalingDialogProps) { + const { state, time, progress } = data; + + return ( + + Descaling Progress + + + Currently: + {' '} + {descriptions[state]} + + + Time: + {' '} + {formatTime({ time })} + + + + + {progress} + % + + + + + + + + ); +} + +export default DescalingDialog; diff --git a/webserver/web-interface/src/pages/DialogPages/PumpZeroCalibrationDialog.tsx b/webserver/web-interface/src/pages/DialogPages/PumpZeroCalibrationDialog.tsx new file mode 100644 index 00000000..50609a54 --- /dev/null +++ b/webserver/web-interface/src/pages/DialogPages/PumpZeroCalibrationDialog.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { + Box, + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Typography, +} from '@mui/material'; + +interface CalibrationDialogProps { + open: boolean; + onClose: () => void; +} + +function PzCalibrationDialog({ + open, + onClose, +}: CalibrationDialogProps) { + return ( + + + Pump Zero Calibration + + + + + Pump Zero calibration is manual for the time being. +
+ For calibration instructions check + {' '} + Discord + . +
+
+ Pump Zero is an important parameter to tune correctly, as it's directly responsible for + any and all flow-related calculations, as well as for accurately calculating any predictive scales output. +
+
+
+
+
+ + + + + +
+ ); +} + +export default PzCalibrationDialog; diff --git a/webserver/web-interface/src/pages/DialogPages/ScalesCalibrationDialog.tsx b/webserver/web-interface/src/pages/DialogPages/ScalesCalibrationDialog.tsx new file mode 100644 index 00000000..8d66104c --- /dev/null +++ b/webserver/web-interface/src/pages/DialogPages/ScalesCalibrationDialog.tsx @@ -0,0 +1,121 @@ +import React, { useCallback } from 'react'; +import { + Box, + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + debounce, +} from '@mui/material'; +import { SettingsNumberInput, SettingsNumber } from '../../components/inputs/settings_inputs'; +import useSensorStateStore from '../../state/SensorStateStore'; +import { updateTarePending } from '../../components/client/SystemStateClient'; +import useSettingsStore from '../../state/SettingsStore'; +import useSystemStateStore from '../../state/SystemStateStore'; +import useNotificationStore from '../../state/NotificationDataStore'; +import { NotificationType } from '../../models/models'; + +interface CalibrationDialogProps { + open: boolean; + onClose: () => void; + scalesF1: number; + scalesF2: number; + onScalesF1Change: (newValue: number) => void; + onScalesF2Change: (newValue: number) => void; +} + +function ScalesCalibrationDialog({ + open, + onClose, + scalesF1, + scalesF2, + onScalesF1Change, + onScalesF2Change, +}: CalibrationDialogProps) { + // const [fakeVal, setValue] = useState(0); + const currentWeight = useSensorStateStore((state) => state.sensorState.weight); + const { persistSettings } = useSettingsStore(); + const updateLocalSystemState = useSystemStateStore((state) => state.updateLocalSystemState); + const { updateLatestNotification } = useNotificationStore(); + + function setValue() { + return currentWeight; + } + + async function handlePersistSettings() { + try { + await persistSettings(); + updateLatestNotification({ message: 'Successfully persisted settings', type: NotificationType.SUCCESS }); + onClose(); + } catch (e) { + updateLatestNotification({ message: 'Failed to persisted settings', type: NotificationType.ERROR }); + } + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + const handleTare = useCallback(debounce(async () => { + try { + const systemState = await updateTarePending(true); + updateLocalSystemState(systemState); + } catch (e) { + updateLatestNotification({ message: 'Failed to tare', type: NotificationType.ERROR }); + } + }, 250), []); + + return ( + + + Scales Calibration + + + + onScalesF1Change(newValue)} + /> + onScalesF2Change(newValue)} + /> + + + + + + + + + + + + + + ); +} + +export default ScalesCalibrationDialog; diff --git a/webserver/web-interface/src/pages/Home.tsx b/webserver/web-interface/src/pages/Home.tsx new file mode 100644 index 00000000..9bd719d5 --- /dev/null +++ b/webserver/web-interface/src/pages/Home.tsx @@ -0,0 +1,511 @@ +import LocalCarWashIcon from '@mui/icons-material/LocalCarWash'; +import ScaleIcon from '@mui/icons-material/Scale'; +import ShowerIcon from '@mui/icons-material/Shower'; +import { + Box, + Button, + Container, + Unstable_Grid2 as Grid, + Paper, + Skeleton, + Tab, + Tabs, + TextField, + Theme, + Typography, + darken, + debounce, + lighten, + useMediaQuery, + useTheme, +} from '@mui/material'; +import React, { + useCallback, useEffect, useState, +} from 'react'; +import { selectActiveProfile } from '../components/client/ProfileClient'; +import { updateOperationMode, updateTarePending } from '../components/client/SystemStateClient'; +import { SettingsNumberIncrementButtons } from '../components/inputs/settings_inputs'; +import AspectRatioBox from '../components/layout/AspectRatioBox'; +import AvailableProfileSelector from '../components/profile/AvailableProfileSelector'; +import { ProfileReview } from '../components/profile/ProfilePreview'; +import ShotHistory from '../components/shot/ShotHistory'; +import { + DescalingState, + GaggiaSettings, NotificationType, OperationMode, +} from '../models/models'; +import { Profile } from '../models/profile'; +import useProfileStore from '../state/ProfileStore'; +import useSensorStateStore from '../state/SensorStateStore'; +import useSettingsStore from '../state/SettingsStore'; +import useSystemStateStore from '../state/SystemStateStore'; +import useNotificationStore from '../state/NotificationDataStore'; +import { SwitchLedButton, SwitchLedState } from '../components/inputs/SwitchLedButton'; +import PressureGauge from '../components/gauges/PressureGauge'; +import WaterLevelGauge from '../components/gauges/WaterLevelGauge'; +import TemperatureGauge from '../components/gauges/TemperatureGauge'; +import DescalingDialog from './DialogPages/DescalingDialog'; +import useDescalingProgressStore from '../state/DescalingProgressDataStore'; + +const colorScaling = (theme: Theme) => (theme.palette.mode === 'light' ? lighten : darken); + +const MemoMiddleSection = React.memo(MiddleSection); +const MemoLeftSection = React.memo(LeftSection); +const MemoRightSection = React.memo(RightSection); + +function Home() { + const theme = useTheme(); + const isBiggerScreen = useMediaQuery(theme.breakpoints.up('sm')); + const updateLatestNotification = useNotificationStore((state) => state.updateLatestNotification); + const { settings, updateLocalSettings, updateSettingsAndSync } = useSettingsStore(); + const { + activeProfile, + updateActiveProfileAndSync, persistActiveProfile, updateLocalActiveProfile, + fetchAvailableProfiles, fetchActiveProfile, + } = useProfileStore(); + + // eslint-disable-next-line react-hooks/exhaustive-deps + const handleProfileUpdate = useCallback(debounce(async (newProfile: Profile) => { + try { + await updateActiveProfileAndSync(newProfile); + updateLatestNotification({ message: 'Updated active profile.', type: NotificationType.SUCCESS }); + } catch (e) { + updateLatestNotification({ message: 'Failed to update active profile.', type: NotificationType.ERROR }); + } + }, 1000), [updateActiveProfileAndSync]); + + // eslint-disable-next-line react-hooks/exhaustive-deps + const handlePersistActiveProfile = useCallback(debounce(async () => { + try { + await persistActiveProfile(); + fetchAvailableProfiles(); // Fetch profiles to update list names + + updateLatestNotification({ message: 'Persisted active profile.', type: NotificationType.SUCCESS }); + } catch (e) { + updateLatestNotification({ message: 'Failed to persist active profile.', type: NotificationType.ERROR }); + } + }, 1000), [persistActiveProfile]); + + // eslint-disable-next-line react-hooks/exhaustive-deps + const updateSettingsDebounced = useCallback( + debounce((newSettings: GaggiaSettings) => { + updateSettingsAndSync(newSettings); + }, 1000), + [updateSettingsAndSync], + ); + + const handleBrewTempUpdate = useCallback((value: number) => { + if (!activeProfile) return; + if (value > 120 || value < 0) return; + updateLocalActiveProfile({ ...activeProfile, waterTemperature: value }); + handleProfileUpdate({ ...activeProfile, waterTemperature: value }); + }, [activeProfile, handleProfileUpdate, updateLocalActiveProfile]); + + const handleSteamTempUpdate = useCallback((value: number) => { + if (!settings) return; + if (value > 169 || value < 120) return; + const newSettings = { ...settings, boiler: { ...settings.boiler, steamSetPoint: value } }; + updateLocalSettings(newSettings); + updateSettingsDebounced(newSettings); + }, [settings, updateLocalSettings, updateSettingsDebounced]); + + const handleNewProfileSelected = useCallback((id: number) => { + if (activeProfile?.id !== id) { + selectActiveProfile(id); + fetchActiveProfile(); + } + }, [activeProfile, fetchActiveProfile]); + + return ( + + {/* */} + + + {/* Left size Gauges */} + {isBiggerScreen && ( + + + + )} + + {/* Center part - profiles */} + + + + + {/* Right part - Temperature Gauge and Scale */} + + + + + + + ); +} + +export default Home; + +function LeftSection() { + const theme = useTheme(); + return ( + <> + + + + + + + + ); +} + +interface MiddleSectionProps { + activeProfile: Profile | null, + handlePersistActiveProfile: () => void, + handleProfileUpdate: (profile: Profile) => void, + handleNewProfileSelected: (id: number) => void, +} + +function MiddleSection({ + activeProfile, handlePersistActiveProfile, handleProfileUpdate, handleNewProfileSelected, +}: MiddleSectionProps) { + const theme = useTheme(); + + return ( + + + + {activeProfile && ( + + + + )} + {!activeProfile && } + + + + {ProfileAndHistoryTabs(activeProfile, handleNewProfileSelected)} + + + + + ); +} + +interface RightSectionProps{ + handleBrewTempUpdate: (value: number) => void; + handleSteamTempUpdate: (value: number) => void; +} + +const MemoOpModeButtons = React.memo(OpModeButtons); +const MemoTargetTempInput = React.memo(TargetTempInput); +const MemoScalesInput = React.memo(ScalesInput); +const MemoTemperatureGauge = React.memo(TemperatureGauge); + +function RightSection({ + handleBrewTempUpdate, + handleSteamTempUpdate, +}: RightSectionProps) { + const theme = useTheme(); + const isBiggerScreen = useMediaQuery(theme.breakpoints.up('sm')); + const steamActive = useSensorStateStore((state) => state.sensorState.steamActive); + const steamSetPoint = useSettingsStore((state) => state.settings?.boiler.steamSetPoint || 0); + const brewTemperature = useProfileStore((state) => state.activeProfile?.waterTemperature || 0); + const targetTemp = steamActive ? steamSetPoint : brewTemperature; + const tempUpdateHandler = steamActive ? handleSteamTempUpdate : handleBrewTempUpdate; + + return ( + <> + + + + + + + + + + + + + + + + + + {/* Gauges for small screens */} + {!isBiggerScreen && ( + + + + + + + + + )} + + + ); +} + +function getBrewBtnState(operationMode: OperationMode) { + if (operationMode === OperationMode.FLUSH) return SwitchLedState.ON; + if (operationMode === OperationMode.FLUSH_AUTO) return SwitchLedState.AUTO; + return SwitchLedState.OFF; +} + +function OpModeButtons() { + const operationMode = useSystemStateStore((state) => state.systemState.operationMode); + const updateLatestNotification = useNotificationStore((state) => state.updateLatestNotification); + const updateLocalSystemState = useSystemStateStore((state) => state.updateLocalSystemState); + + // eslint-disable-next-line react-hooks/exhaustive-deps + const handleOpmodeChange = useCallback(debounce(async (newMode: OperationMode) => { + try { + const systemState = await updateOperationMode(newMode); + updateLocalSystemState(systemState); + } catch (e) { + updateLatestNotification({ message: 'Failed to change operation mode', type: NotificationType.ERROR }); + } + }, 250), []); + + const handleFlushStateChange = useCallback((newState: SwitchLedState) => { + if (newState === SwitchLedState.OFF) handleOpmodeChange(OperationMode.BREW_AUTO); + else if (newState === SwitchLedState.ON) handleOpmodeChange(OperationMode.FLUSH); + else handleOpmodeChange(OperationMode.FLUSH_AUTO); + }, [handleOpmodeChange]); + + const handleDescaleStateChange = useCallback((newState: SwitchLedState) => { + handleOpmodeChange(newState === SwitchLedState.ON ? OperationMode.DESCALE : OperationMode.BREW_AUTO); + }, [handleOpmodeChange]); + + return ( + + } + supportsAuto + label="FLUSH" + /> + } + label="DESCALE" + /> + + ); +} + +function TargetTempInput( + { targetTemp, handleTempUpdate }: { targetTemp: number, handleTempUpdate: (value: number) => void}, +) { + const theme = useTheme(); + return ( + + + TARGET TEMP + + + handleTempUpdate(parseInt(e.target.value, 10))} + InputProps={{ + sx: { + '& input': { + textAlign: 'center', + paddingLeft: 0, + paddingRight: 0, + }, + }, + }} + /> + + + + + + ); +} + +function ScalesInput() { + const theme = useTheme(); + const currentWeight = useSensorStateStore((state) => state.sensorState.weight); + const isBiggerScreen = useMediaQuery(theme.breakpoints.up('sm')); + const updateLocalSystemState = useSystemStateStore((state) => state.updateLocalSystemState); + const updateLatestNotification = useNotificationStore((state) => state.updateLatestNotification); + + // eslint-disable-next-line react-hooks/exhaustive-deps + const handleTare = useCallback(debounce(async () => { + try { + const systemState = await updateTarePending(true); + updateLocalSystemState(systemState); + } catch (e) { + updateLatestNotification({ message: 'Failed to tare', type: NotificationType.ERROR }); + } + }, 250), []); + + return ( + + + SCALE + + + + + + + + + + + ); +} + +function ProfileAndHistoryTabs( + activeProfile: Profile | null, + handleNewProfileSelected: (id: number) => void, +) { + const theme = useTheme(); + const [tabValue, setTabValue] = useState(0); + + return ( + <> + setTabValue(newValue)}> + + + + + + + + + + + + + + + ); +} + +interface TabPanelProps { + children?: React.ReactNode; + index: number; + value: number; +} + +function TabPanel({ + children = 'tabpanel', + value, + index, + ...other +}: TabPanelProps) { + return ( + + ); +} + +function a11yProps(index: number) { + return { + id: `full-width-tab-${index}`, + 'aria-controls': `full-width-tabpanel-${index}`, + }; +} + +function DiscalingDialogWrapper() { + const descalingProgress = useDescalingProgressStore((state) => state.descalingProgress); + const operationMode = useSystemStateStore((state) => state.systemState.operationMode); + const [open, setOpen] = useState(false); + + useEffect(() => { + setOpen(operationMode === OperationMode.DESCALE && descalingProgress.state !== DescalingState.IDLE); + }, [descalingProgress, operationMode]); + + return ( + setOpen(false)} + data={descalingProgress} + /> + ); +} diff --git a/webserver/web-interface/src/pages/Profiles.tsx b/webserver/web-interface/src/pages/Profiles.tsx new file mode 100644 index 00000000..91b02062 --- /dev/null +++ b/webserver/web-interface/src/pages/Profiles.tsx @@ -0,0 +1,438 @@ +/* eslint-disable no-console */ +import Copy from '@mui/icons-material/CopyAll'; +import Delete from '@mui/icons-material/Delete'; +import Edit from '@mui/icons-material/Edit'; +import Add from '@mui/icons-material/Add'; +import DownloadIcon from '@mui/icons-material/Download'; +import UploadIcon from '@mui/icons-material/Upload'; +import { + Box, + Button, + Container, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + Paper, + Skeleton, + Typography, + useMediaQuery, + useTheme, +} from '@mui/material'; +import Grid from '@mui/material/Unstable_Grid2'; +import React, { useCallback, useState, useRef } from 'react'; +import { + createProfile, deleteProfileById, getProfileById, updateProfile, +} from '../components/client/ProfileClient'; +import AvailableProfileSelector from '../components/profile/AvailableProfileSelector'; +import { ProfileReview } from '../components/profile/ProfilePreview'; +import { Profile } from '../models/profile'; +import useProfileStore from '../state/ProfileStore'; +import ProfileEditDialog from '../components/profile/edit/ProfileEditDialog'; +import useNotificationStore from '../state/NotificationDataStore'; +import { NotificationType } from '../models/models'; + +export default function Profiles() { + const theme = useTheme(); + const smallScreen = useMediaQuery(theme.breakpoints.only('xs')); + const { activeProfile, fetchAvailableProfiles } = useProfileStore(); + + const [selectedProfileId, setSelectedProfileId] = useState(activeProfile?.id); + + const [selectedProfile, setSelectedProfile] = useState(activeProfile || undefined); + + const [showEdit, setShowEdit] = useState(false); + + const handleNewProfileSelected = useCallback(async (id: number) => { + try { + setSelectedProfileId(id); + setSelectedProfile(undefined); + setSelectedProfile(await getProfileById(id)); + } catch { + setSelectedProfileId(undefined); + setSelectedProfile(undefined); + } + }, []); + + const startNewProfile = (): Profile => ({ + name: 'New Profile', + phases: [], + waterTemperature: 93, + }); + + const handleNew = useCallback(async () => { + const profileOpts = { + ...startNewProfile, + name: 'New Profile', + phases: [], + waterTemperature: 93, + }; + const profile = await createProfile(profileOpts); + await fetchAvailableProfiles(); + setSelectedProfile(profile); + setSelectedProfileId(profile.id); + setShowEdit(true); + }, [fetchAvailableProfiles]); + + const handleDelete = useCallback(async () => { + if (!selectedProfileId) return; + + await deleteProfileById(selectedProfileId); + setSelectedProfileId(undefined); + setSelectedProfile(undefined); + await fetchAvailableProfiles(); + }, [fetchAvailableProfiles, selectedProfileId]); + + const handleDuplicate = useCallback(async () => { + if (!selectedProfile) return; + + const duplicate = { ...selectedProfile, name: `${selectedProfile.name} (copy)` }; + const profile = await createProfile(duplicate); + await fetchAvailableProfiles(); + setSelectedProfile(profile); + setSelectedProfileId(profile.id); + }, [selectedProfile, fetchAvailableProfiles]); + + const handleEdit = useCallback(async () => { + if (!selectedProfile) return; + setShowEdit(true); + }, [selectedProfile]); + + const handlePersistUpdatedProfile = useCallback(async (profile: Profile) => { + if (!profile.id) return; + await updateProfile(profile); + fetchAvailableProfiles(); + setSelectedProfile(profile); + setShowEdit(false); + }, [fetchAvailableProfiles]); + + const handleExportJson = () => { + if (selectedProfile) { + downloadProfileJson(selectedProfile); + } + }; + + const handleImportJson = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + const reader = new FileReader(); + reader.onload = async (e) => { + try { + const jsonContent = e.target?.result; + const importedProfile = JSON.parse(jsonContent as string); + + // Create a new profile based on the imported data + // Adding (imported) to the profile name + // POSSIBLE: Ask for a new name in the future? + const newProfile = { + name: `${importedProfile.name} (imported)`, + phases: [...importedProfile.phases], + waterTemperature: importedProfile.waterTemperature, + }; + + // Create the new profile + const createdProfile = await createProfile(newProfile); + + await fetchAvailableProfiles(); + setSelectedProfileId(createdProfile.id); + setSelectedProfile(createdProfile); + updateLatestNotification({ message: 'Profile Imported!', type: NotificationType.SUCCESS }); + } catch (error) { + console.error('Error importing JSON:', error); + updateLatestNotification({ message: 'Cannot import profile!', type: NotificationType.ERROR }); + } + }; + reader.readAsText(file); + } + }; + + const inputRef = useRef(null); + const { updateLatestNotification } = useNotificationStore(); + + return ( + <> + + `${theme.palette.primary.main} ${theme.palette.background.paper}`, // Customize scrollbar color + '&::-webkit-scrollbar': { + width: '10px', // Customize scrollbar width for Webkit browsers (Chrome, Safari, etc.) + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: () => theme.palette.primary.main, // Customize scrollbar thumb color + borderRadius: '10px', // Customize scrollbar thumb border radius + }, + '&::-webkit-scrollbar-track': { + backgroundColor: () => theme.palette.background.paper, // Customize scrollbar track color + borderRadius: '10px', // Customize scrollbar track border radius + }, + }} + > + Manage profiles + + {smallScreen && ( + + {selectedProfile && ( + <> + + inputRef.current?.click()} + /> + + + )} + {!selectedProfile && } + + )} + `${theme.palette.primary.main} ${theme.palette.background.paper}`, // Customize scrollbar color + '&::-webkit-scrollbar': { + width: '10px', // Customize scrollbar width for Webkit browsers (Chrome, Safari, etc.) + }, + '&::-webkit-scrollbar-thumb': { + backgroundColor: () => theme.palette.primary.main, // Customize scrollbar thumb color + borderRadius: '10px', // Customize scrollbar thumb border radius + }, + '&::-webkit-scrollbar-track': { + backgroundColor: () => theme.palette.background.paper, // Customize scrollbar track color + borderRadius: '10px', // Customize scrollbar track border radius + }, + }} + > + + + {!smallScreen && ( + + {selectedProfile && ( + + + inputRef.current?.click()} + /> + + + )} + {!selectedProfile && } + + )} + + {selectedProfile && ( + setShowEdit(false)} + onDone={handlePersistUpdatedProfile} + /> + )} + + + {/* + + + + + + Load Profile + + + + + console.log(evt)} /> + + + + + + + + + + + */} + + ); +} + +export interface ActionBarProp { + onDeleteConfirmed: () => void; + onEdit: () => void; + onDuplicate: () => void; + onAdd: () => void; + onExportJson: () => void; + onImportJson: () => void; +} + +function ActionBar({ + onDeleteConfirmed, onEdit, onDuplicate, onAdd, onExportJson, onImportJson, +}: ActionBarProp) { + return ( + + + + + + + + + ); +} + +function downloadProfileJson(profile: Profile) { + const json = profileToJson(profile); + const blob = new Blob([json], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = url; + a.download = `${profile.name}.json`; + a.click(); + + URL.revokeObjectURL(url); +} + +function profileToJson(profile: Profile) { + return JSON.stringify(profile, null, 2); +} + +export function DeleteProfileButton({ onConfirm }: { onConfirm: () => void}) { + const [open, setOpen] = useState(false); + + const handleClose = () => { + setOpen(false); + }; + + const handleConfirm = () => { + setOpen(false); + onConfirm(); + }; + + return ( +
+ + + Are you sure you want to delete the profile? + + + Deleting a profile is irreversible, and all data associated with the profile will be permanently deleted. + + + + + + + +
+ ); +} diff --git a/webserver/web-interface/src/pages/Settings.tsx b/webserver/web-interface/src/pages/Settings.tsx new file mode 100644 index 00000000..5773500b --- /dev/null +++ b/webserver/web-interface/src/pages/Settings.tsx @@ -0,0 +1,115 @@ +import React from 'react'; +import { + Card, + Container, + useTheme, + Typography, + CardContent, + CardActions, + Button, + Radio, + RadioGroup, + FormControlLabel, + Box, +} from '@mui/material'; +import IconButton from '@mui/material/IconButton'; +import AttachFileOutlinedIcon from '@mui/icons-material/AttachFileOutlined'; +import Grid from '@mui/material/Unstable_Grid2'; +import SaveIcon from '@mui/icons-material/Save'; +import WifiSettingsCard from '../components/wifi/WifiSettingsCard'; +import ProgressBar from '../components/inputs/ProgressBar'; +import TabbedSettings from '../components/settings/tabs_settings'; +import useSettingsStore from '../state/SettingsStore'; +import { GaggiaSettings, NotificationType } from '../models/models'; +import useNotificationStore from '../state/NotificationDataStore'; + +export default function Settings() { + const theme = useTheme(); + const { settings, updateSettingsAndSync, persistSettings } = useSettingsStore(); + const { updateLatestNotification } = useNotificationStore(); + + async function handlePersistSettings() { + try { + await persistSettings(); + updateLatestNotification({ message: 'Successfully persisted settings', type: NotificationType.SUCCESS }); + } catch (e) { + updateLatestNotification({ message: 'Failed to persisted settings', type: NotificationType.ERROR }); + } + } + + async function handleUpdateRemoteSettings(newSettings: GaggiaSettings) { + try { + await updateSettingsAndSync(newSettings); + updateLatestNotification({ message: 'Updated running settings.', type: NotificationType.SUCCESS }); + } catch (e) { + updateLatestNotification({ message: 'Failed to update settings', type: NotificationType.ERROR }); + } + } + + return ( +
+ + + {settings && ( + + + + + + + Machine State Management + + + + + + + + + )} + + + + + + + + OTA Update + + + + + + + + + + + + + } label="STM32-Blackpill" /> + } label="ESP32-Flash" /> + } label="ESP32-Filesystem" /> + + + + + + + + +
+ ); +} diff --git a/webserver/web-interface/src/pages/ShotDialog.tsx b/webserver/web-interface/src/pages/ShotDialog.tsx new file mode 100644 index 00000000..8b19b1fe --- /dev/null +++ b/webserver/web-interface/src/pages/ShotDialog.tsx @@ -0,0 +1,132 @@ +import React, { useEffect, useState } from 'react'; +import { + AppBar, Box, Dialog, IconButton, SxProps, Theme, Toolbar, useTheme, + +} from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; +import Grid from '@mui/material/Unstable_Grid2'; +import ShotChart from '../components/chart/ShotChart'; +import { + PressureStatBox, PumpFlowStatBox, TemperatureStatBox, TimeStatBox, WeightFlowStatBox, WeightStatBox, +} from '../components/chart/StatBox'; +import useShotDataStore from '../state/ShotDataStore'; +import { Shot, ShotSnapshot } from '../models/models'; +import ShotSpeedDial from '../components/inputs/ShotSpeedDial'; + +interface ShotDialogProps { + open?: boolean; + setOpen: (value: boolean) => void; + historyShot?: Shot; +} + +export default function ShotDialog({ open = false, setOpen, historyShot = undefined }: ShotDialogProps) { + const { + latestShotDatapoint, currentShot, shotRunning, addShotToHistory, removeShotFromHistory, + } = useShotDataStore(); + const theme = useTheme(); + const fullHeightGraph = `calc(100vh - 64px - ${theme.spacing(1)})`; + const lessHeightGraph = `calc(75vh - 64px - ${theme.spacing(1)})`; + const [activeDatapoint, setActiveDatapoint] = useState(undefined); + + useEffect(() => { + if (!historyShot || historyShot.datapoints.length === 0) return; + setActiveDatapoint(historyShot.datapoints[historyShot.datapoints.length - 1]); + }, [historyShot, setActiveDatapoint]); + + useEffect(() => { + if (historyShot) return; + setActiveDatapoint(latestShotDatapoint); + }, [latestShotDatapoint, historyShot, setActiveDatapoint]); + + return ( + setOpen(false)} + PaperProps={{ elevation: 0 }} + > + + + {historyShot ? 'Reviewing shot from history' : 'Shot in progress'} + setOpen(false)} + aria-label="close" + > + + + + + + + + {historyShot && } + {!historyShot && } + {!shotRunning && ( + + + + )} + + + + + + + + ); +} + +function StatBoxes({ activeDatapoint = undefined }: { activeDatapoint?: ShotSnapshot}) { + const boxSx:SxProps = { height: { xs: '100%', sm: 'auto' } }; + + return ( + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/webserver/web-interface/src/pages/home/Home.jsx b/webserver/web-interface/src/pages/home/Home.jsx deleted file mode 100644 index d66e4025..00000000 --- a/webserver/web-interface/src/pages/home/Home.jsx +++ /dev/null @@ -1,123 +0,0 @@ -import React, { useState, useEffect, useRef } from 'react'; -import useWebSocket from 'react-use-websocket'; -import { - Box, Container, useTheme, Fab, TextField, Grid, Button, Skeleton, Stack, -} from '@mui/material'; -import AddIcon from '@mui/icons-material/Add'; -import RemoveIcon from '@mui/icons-material/Remove'; -import ScaleIcon from '@mui/icons-material/Scale'; -import { - apiHost, - filterJsonMessage, filterSocketMessage, MSG_TYPE_SENSOR_DATA, -} from '../../models/api'; -import GaugeChart from '../../components/chart/GaugeChart'; -import GaugeLiquid from '../../components/chart/GaugeLiquid'; - -function Home() { - const { lastJsonMessage } = useWebSocket(`ws://${apiHost}/ws`, { - share: true, - retryOnError: true, - shouldReconnect: () => true, - reconnectAttempts: 1000, - filter: (message) => filterSocketMessage(message, MSG_TYPE_SENSOR_DATA), - }); - const theme = useTheme(); - - const [scalesPresent, setScalesPresent] = useState(false); - - const [lastSensorData, setLastSensorData] = useState({ - temperature: 0, pressure: 0, pumpFlow: 0, weight: 0, scalesPresent: false, waterLvl: 0, - }); - - useEffect(() => { - setScalesPresent(lastSensorData.scalesPresent); - }, [lastSensorData]); - - useEffect(() => { - if (lastJsonMessage === null) { - return; - } - if (filterJsonMessage(lastJsonMessage, MSG_TYPE_SENSOR_DATA)) { - setLastSensorData(lastJsonMessage.data); - } - }, [lastJsonMessage]); - - function boxedComponent(component) { - return ( - - {component} - - ); - } - - const boxRef = useRef(null); - const [boxSize, setBoxSize] = useState({ width: 0, height: 0 }); - - useEffect(() => { - if (boxRef.current) { - const { width, height } = boxRef.current.getBoundingClientRect(); - setBoxSize({ width, height }); - } - }, []); - - return ( - - - - - {boxedComponent()} - {boxedComponent()} - {boxedComponent()} - - - - {/* */} - - - {/* For variant="text", adjust the height via font-size */} - - - - - - - - - - {boxedComponent()} - - - - - - - - - - - - - - - - - - - - ); -} - -export default Home; diff --git a/webserver/web-interface/src/pages/home/Profiles.jsx b/webserver/web-interface/src/pages/home/Profiles.jsx deleted file mode 100644 index caa8b463..00000000 --- a/webserver/web-interface/src/pages/home/Profiles.jsx +++ /dev/null @@ -1,195 +0,0 @@ -import React, { useState } from 'react'; -import { - Card, Container, useTheme, Typography, CardContent, CardActions, Paper, TextareaAutosize, Alert, -} from '@mui/material'; -import IconButton from '@mui/material/IconButton'; -import QrCodeIcon from '@mui/icons-material/QrCode'; -import UploadFileIcon from '@mui/icons-material/UploadFile'; -import AddIcon from '@mui/icons-material/Add'; -import RemoveIcon from '@mui/icons-material/Remove'; -import AutoGraphIcon from '@mui/icons-material/AutoGraph'; -import DeleteIcon from '@mui/icons-material/Delete'; -import TextField from '@mui/material/TextField'; -import Select from '@mui/material/Select'; -// import InputAdornment from '@mui/material/InputAdornment'; -import Grid from '@mui/material/Grid'; -import ProfileChart from '../../components/chart/ProfileChart'; -import { Profile } from '../../models/profile'; - -export default function Profiles() { - const theme = useTheme(); - - const [elements, setElements] = useState([ - { id: 1, type: 'select', value: '' }, - { id: 2, type: 'select', value: '' }, - { id: 3, type: 'text', value: '' }, - { id: 4, type: 'text', value: '' }, - { id: 5, type: 'text', value: '' }, - { id: 6, type: 'text', value: '' }, - ]); - const [nextId, setNextId] = useState(7); - - const handleAddRow = () => { - const newElements = [ - ...elements, - { id: nextId, type: 'select', value: '' }, - { id: nextId + 1, type: 'select', value: '' }, - { id: nextId + 2, type: 'text', value: '' }, - { id: nextId + 3, type: 'text', value: '' }, - { id: nextId + 4, type: 'text', value: '' }, - { id: nextId + 5, type: 'text', value: '' }, - ]; - setElements(newElements); - setNextId(nextId + 6); - }; - - const [error, setError] = useState(null); - const [profile, setProfile] = useState(new Profile([])); - - const updateProfile = (value) => { - try { - setProfile(Profile.parse(JSON.parse(value))); - setError(undefined); - } catch (er) { - setError(er.message); - } - }; - - const handleRemoveRow = () => { - const newElements = [...elements]; - for (let i = 0; i < 6; i++) { - newElements.pop(); - } - setElements(newElements); - }; - - const handleRemoveAll = () => { - setElements([ - { id: 1, type: 'select', value: '' }, - { id: 2, type: 'select', value: '' }, - { id: 3, type: 'text', value: '' }, - { id: 4, type: 'text', value: '' }, - { id: 5, type: 'text', value: '' }, - { id: 6, type: 'text', value: '' }, - ]); - setNextId(7); - }; - - const handleSelectChange = (event, id) => { - const updatedElements = elements.map((element) => { - if (element.id === id) { - return { ...element, value: event.target.value }; - } - return element; - }); - setElements(updatedElements); - }; - - return ( -
- - - - - - - Load Profile - - - - - - - - - - - - - - - - - - - - - - - Build Profile - - - - - - - - - - - - -
- - {elements.map((element) => { - if (element.type === 'select') { - return ( - - - - ); - } - if (element.type === 'text') { - return ( - - - - ); - } - return null; - })} - -
- {/* {inputList} */} -
-
-
-
-
-
- - - - Profile syntax playground - - - - - {error || 'Nice syntax!'} - - - - updateProfile(evt.target.value)} - style={{ width: '100%', backgroundColor: theme.palette.background.paper, color: theme.palette.text.secondary }} - > - - - - - - - - -
- ); -} diff --git a/webserver/web-interface/src/pages/home/Settings.jsx b/webserver/web-interface/src/pages/home/Settings.jsx deleted file mode 100644 index 80bb94aa..00000000 --- a/webserver/web-interface/src/pages/home/Settings.jsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import { - Card, Container, useTheme, Typography, CardContent, CardActions, Button, Radio, RadioGroup, FormControlLabel, Box -} from '@mui/material'; -import IconButton from '@mui/material/IconButton'; -import AttachFileOutlinedIcon from '@mui/icons-material/AttachFileOutlined'; -import Grid from '@mui/material/Grid'; -import WifiSettingsCard from '../../components/wifi/WifiSettingsCard'; -import ProgressBar from '../../components/inputs/ProgressBar'; -import LogContainer from '../../components/log/LogContainer'; - -export default function Settings() { - const theme = useTheme(); - - function boxedComponent(component) { - return ( - - {component} - - ); - } - - return ( -
- - - - - - - - - - OTA Update - - - - - - - - - - - - - } label="STM32-Blackpill" /> - } label="ESP32-Flash" /> - } label="ESP32-Filesystem" /> - - - - - - - - - - - -
- ); -} diff --git a/webserver/web-interface/src/pages/home/ShotDialog.jsx b/webserver/web-interface/src/pages/home/ShotDialog.jsx deleted file mode 100644 index 95be998d..00000000 --- a/webserver/web-interface/src/pages/home/ShotDialog.jsx +++ /dev/null @@ -1,121 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { - AppBar, Box, Dialog, IconButton, Stack, Toolbar, -} from '@mui/material'; -import PropTypes from 'prop-types'; -import useWebSocket from 'react-use-websocket'; -import CloseIcon from '@mui/icons-material/Close'; -import Grid from '@mui/material/Grid'; -import { - apiHost, filterSocketMessage, MSG_TYPE_SHOT_DATA, -} from '../../models/api'; -import ShotChart from '../../components/chart/ShotChart'; -import { - PressureStatBox, PumpFlowStatBox, TemperatureStatBox, TimeStatBox, WeightFlowStatBox, WeightStatBox, -} from '../../components/chart/StatBox'; - -export default function ShotDialog({ open, setOpen }) { - const { lastJsonMessage } = useWebSocket(`ws://${apiHost}/ws`, { - share: true, - retryOnError: true, - shouldReconnect: () => true, - reconnectAttempts: 1000, - filter: (message) => filterSocketMessage(message, MSG_TYPE_SHOT_DATA), - }); - - const [latestShotSnapshot, setLatestShotSnapshot] = useState(null); - - useEffect(() => { - if (lastJsonMessage === null) { - return; - } - setLatestShotSnapshot(lastJsonMessage.data); - }, [lastJsonMessage]); - - return ( - setOpen(false)} - PaperProps={{ elevation: 0 }} - > - - - - setOpen(false)} - aria-label="close" - > - - - - - - - - - - - - - {latestShotSnapshot && ( - - - - - - - - - - - - - - - - - - - - - )} - - - - - ); -} - -ShotDialog.propTypes = { - open: PropTypes.bool, - setOpen: PropTypes.func.isRequired, -}; - -ShotDialog.defaultProps = { - open: false, -}; diff --git a/webserver/web-interface/src/state/BleScalesDatastor.ts b/webserver/web-interface/src/state/BleScalesDatastor.ts new file mode 100644 index 00000000..93a0cffa --- /dev/null +++ b/webserver/web-interface/src/state/BleScalesDatastor.ts @@ -0,0 +1,29 @@ +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { BleScales } from '../models/models'; +import { getConnectedBleScales } from '../components/client/BleScalesClient'; + +interface BleScalesStore { + bleScales: BleScales, + updateBleScales: (newScales: BleScales) => void, +} + +const useBleScalesStore = create()( + devtools( + (set) => ({ + bleScales: { + name: '', + address: '', + }, + updateBleScales: (newScales: BleScales) => set(() => ({ bleScales: newScales })), + }), + ), +); + +// Fetching settings the first time if they aren't already loaded +const { bleScales, updateBleScales } = useBleScalesStore.getState(); +if (bleScales.name.length === 0) { + getConnectedBleScales().then(updateBleScales); +} + +export default useBleScalesStore; diff --git a/webserver/web-interface/src/state/DescalingProgressDataStore.ts b/webserver/web-interface/src/state/DescalingProgressDataStore.ts new file mode 100644 index 00000000..5492654c --- /dev/null +++ b/webserver/web-interface/src/state/DescalingProgressDataStore.ts @@ -0,0 +1,23 @@ +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { DescalingProgress, DescalingState } from '../models/models'; + +interface DescalingProgressStore { + descalingProgress: DescalingProgress, + updateLocalDescalingProgress: (newState: DescalingProgress) => void, +} + +const useDescalingProgressStore = create()( + devtools( + (set) => ({ + descalingProgress: { + state: DescalingState.IDLE, + progress: 0, + time: 0, + }, + updateLocalDescalingProgress: (descalingProgress: DescalingProgress) => set(() => ({ descalingProgress })), + }), + ), +); + +export default useDescalingProgressStore; diff --git a/webserver/web-interface/src/state/LogStore.ts b/webserver/web-interface/src/state/LogStore.ts new file mode 100644 index 00000000..56e1b44c --- /dev/null +++ b/webserver/web-interface/src/state/LogStore.ts @@ -0,0 +1,28 @@ +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { LogMessage } from '../models/models'; + +const MAX_LOG_MESSAGES = 200; + +interface LogMessageStore { + logs: Array, + addMessage: (message: LogMessage) => void, +} + +const useLogMessageStore = create()( + devtools( + (set) => ({ + logs: [], + addMessage: (message: LogMessage) => set((state) => { + let { logs } = state; + if (logs.length >= MAX_LOG_MESSAGES) { + logs = logs.slice(0, Math.max(0, state.logs.length - MAX_LOG_MESSAGES + 1)); + } + logs.push(message); + return { logs }; + }), + }), + ), +); + +export default useLogMessageStore; diff --git a/webserver/web-interface/src/state/NotificationDataStore.ts b/webserver/web-interface/src/state/NotificationDataStore.ts new file mode 100644 index 00000000..bf4cdbf0 --- /dev/null +++ b/webserver/web-interface/src/state/NotificationDataStore.ts @@ -0,0 +1,19 @@ +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { Notification } from '../models/models'; + +interface NotificationStore { + latestNotification?: Notification, + updateLatestNotification: (notification: Notification) => void, +} + +const useNotificationStore = create()( + devtools( + (set) => ({ + latestNotification: undefined, + updateLatestNotification: (notification: Notification) => set(() => ({ latestNotification: notification })), + }), + ), +); + +export default useNotificationStore; diff --git a/webserver/web-interface/src/state/ProfileStore.ts b/webserver/web-interface/src/state/ProfileStore.ts new file mode 100644 index 00000000..d3246a5e --- /dev/null +++ b/webserver/web-interface/src/state/ProfileStore.ts @@ -0,0 +1,65 @@ +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { Profile, ProfileSummary } from '../models/profile'; +import { + getActiveProfile, getAvailableProfiles, persistActiveProfileChanges, selectActiveProfile, updateActiveProfile, +} from '../components/client/ProfileClient'; + +interface ProfileStore { + activeProfileLoading: boolean, + availableProfilesLoading: boolean, + activeProfile: Profile | null, + availableProfiles: ProfileSummary[], + updateLocalActiveProfile: (profile: Profile) => void, + updateActiveProfileAndSync: (profile: Profile) => Promise, + fetchActiveProfile: () => Promise, + fetchAvailableProfiles: () => Promise, + persistActiveProfile: () => Promise, + selectNewProfile: (id: number) => Promise, +} + +const useProfileStore = create()( + devtools( + (set) => ({ + activeProfileLoading: false, + availableProfilesLoading: false, + activeProfile: null, + availableProfiles: [], + updateLocalActiveProfile: (profile: Profile) => { + set({ activeProfile: profile }); + }, + fetchActiveProfile: async () => { + set({ activeProfileLoading: true }); + set({ activeProfile: await getActiveProfile(), activeProfileLoading: false }); + }, + fetchAvailableProfiles: async () => { + set({ availableProfilesLoading: true }); + set({ availableProfiles: await getAvailableProfiles(), availableProfilesLoading: false }); + }, + updateActiveProfileAndSync: async (profile: Profile) => { + await updateActiveProfile(profile); + set({ activeProfile: profile }); + }, + persistActiveProfile: async () => { persistActiveProfileChanges(); }, + selectNewProfile: async (id: number) => { + set({ activeProfileLoading: true }); + await selectActiveProfile(id); + set({ activeProfile: await getActiveProfile(), activeProfileLoading: false }); + }, + }), + ), +); + +// Fetching activeProfile the first time if they aren't already loaded +const { activeProfileLoading, activeProfile, fetchActiveProfile } = useProfileStore.getState(); +if (!activeProfile && !activeProfileLoading) { + fetchActiveProfile(); +} + +// Fetching availableProfiles the first time if they aren't already loaded +const { availableProfilesLoading, availableProfiles, fetchAvailableProfiles } = useProfileStore.getState(); +if (availableProfiles.length === 0 && !availableProfilesLoading) { + fetchAvailableProfiles(); +} + +export default useProfileStore; diff --git a/webserver/web-interface/src/state/SensorStateStore.ts b/webserver/web-interface/src/state/SensorStateStore.ts new file mode 100644 index 00000000..43b5c5bf --- /dev/null +++ b/webserver/web-interface/src/state/SensorStateStore.ts @@ -0,0 +1,30 @@ +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { SensorState } from '../models/models'; + +interface SensorStateStore { + sensorState: SensorState, + updateLocalSensorState: (newState: SensorState) => void, +} + +const useSensorStateStore = create()( + devtools( + (set) => ({ + sensorState: { + brewActive: false, + steamActive: false, + hotWaterSwitchState: false, + temperature: 0, + waterTemperature: 0, + pressure: 0, + pumpFlow: 0, + weightFlow: 0, + weight: 0, + waterLevel: 0, + }, + updateLocalSensorState: (newState: SensorState) => set(() => ({ sensorState: newState })), + }), + ), +); + +export default useSensorStateStore; diff --git a/webserver/web-interface/src/state/SettingsStore.ts b/webserver/web-interface/src/state/SettingsStore.ts new file mode 100644 index 00000000..2a5f5440 --- /dev/null +++ b/webserver/web-interface/src/state/SettingsStore.ts @@ -0,0 +1,45 @@ +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { GaggiaSettings } from '../models/models'; +import { + getSettings as apiGetSettings, + updateSettings as apiUpdateSettings, + persistSettings as apiPersistSettings, +} from '../components/client/SettingsClient'; + +interface SettingsStore { + loading: boolean; + settings?: GaggiaSettings | undefined, + updateLocalSettings: (settings: GaggiaSettings) => void; + updateSettingsAndSync: (settings: GaggiaSettings) => Promise; + fetchSettings: () => Promise; + persistSettings: () => Promise; +} + +const useSettingsStore = create()( + devtools((set, get) => ({ + loading: false, + settings: undefined, + updateLocalSettings: (settings: GaggiaSettings) => set({ settings }), + updateSettingsAndSync: async (settings: GaggiaSettings) => { + set({ settings }); + await apiUpdateSettings(settings); + }, + fetchSettings: async () => { + if (get().loading) return; + set({ loading: true }); + set({ settings: await apiGetSettings(), loading: false }); + }, + persistSettings: async () => { + await apiPersistSettings(); + }, + })), +); + +// Fetching settings the first time if they aren't already loaded +const { loading, settings, fetchSettings } = useSettingsStore.getState(); +if (!settings && !loading) { + fetchSettings(); +} + +export default useSettingsStore; diff --git a/webserver/web-interface/src/state/ShotDataStore.ts b/webserver/web-interface/src/state/ShotDataStore.ts new file mode 100644 index 00000000..a8eac8bd --- /dev/null +++ b/webserver/web-interface/src/state/ShotDataStore.ts @@ -0,0 +1,132 @@ +import { create } from 'zustand'; +import { devtools, persist } from 'zustand/middleware'; +import { Shot, ShotSnapshot } from '../models/models'; + +interface ShotDataStore { + latestShotDatapoint: ShotSnapshot, + currentShot: Shot, + shotRunning: boolean, + shotHistory: Shot[], + startNewShot: () => void, + addShotDatapoint: (shotDatapoint: ShotSnapshot) => void, + addShotToHistory: (shot: Shot) => void, + removeShotFromHistory: (shot: Shot) => void, + setShotRunning: (value: boolean) => void, +} + +// This constant defines a buffer of time to prevent handling of out-of-order +// socket messages. +const OUT_OF_ORDER_BUFFER_TOLERANCE = 500; // msec +const MAX_SHOT_HISTORY_LENGTH = 50; +const MIN_SHOT_DURATION_TO_SAVE_IN_HISTORY = 14000; // msec + +const EMPTY_SNAPSHOT:ShotSnapshot = { + timeInShot: 0, + pressure: 0, + pumpFlow: 0, + weightFlow: 0, + temperature: 0, + shotWeight: 0, + waterPumped: 0, + targetTemperature: 0, + targetPumpFlow: 0, + targetPressure: 0, +}; + +function isDatapointOutOfOrder( + previousShotDatapoint: ShotSnapshot, + newShotDatapoint: ShotSnapshot, + tolerance = OUT_OF_ORDER_BUFFER_TOLERANCE, +) { + return newShotDatapoint.timeInShot > previousShotDatapoint.timeInShot - tolerance + && newShotDatapoint.timeInShot < previousShotDatapoint.timeInShot; +} + +export function isShotLongEnoughToBeStored(shot: Shot): boolean { + const lastDatapoint = shot.datapoints[shot.datapoints.length - 1]; + return lastDatapoint && lastDatapoint.timeInShot > MIN_SHOT_DURATION_TO_SAVE_IN_HISTORY; +} + +export function isNewShotStarted(currentShot: Shot, newShotDatapoint: ShotSnapshot):boolean { + if (currentShot.datapoints.length === 0) return true; + + const latestShotDatapoint = currentShot.datapoints[currentShot.datapoints.length - 1]; + return latestShotDatapoint && latestShotDatapoint.timeInShot > newShotDatapoint.timeInShot; +} + +const useShotDataStore = create()( + devtools( + persist( + (set, get) => ({ + latestShotDatapoint: EMPTY_SNAPSHOT, + shotRunning: false, + currentShot: { time: Date.now(), datapoints: [] }, + shotHistory: [], + + startNewShot: () => set(() => ({ currentShot: { time: Date.now(), datapoints: [] } })), + + addShotToHistory: (shot:Shot) => set((state) => { + if (!isShotLongEnoughToBeStored(shot)) { + return {}; + } + if (state.shotHistory.find((s) => s.time === shot.time)) { + return {}; + } + + const updatedShotHistory = [...state.shotHistory, shot]; + while (updatedShotHistory.length > MAX_SHOT_HISTORY_LENGTH) { + updatedShotHistory.shift(); // This will remove the first element from the array + } + return { shotHistory: updatedShotHistory }; + }), + + removeShotFromHistory: (shot: Shot) => set((state) => { + const shotIndex = state.shotHistory.indexOf(shot); + if (shotIndex !== -1) { + const updatedShotHistory = [...state.shotHistory]; + updatedShotHistory.splice(shotIndex, 1); + return { shotHistory: updatedShotHistory }; + } + return {}; + }), + + addShotDatapoint: (shotDatapoint: ShotSnapshot) => { + // This is here to protect the store from datapoints that are potentially delivered out of order + // It was observed that some WS messages are delivered out of order + if (isDatapointOutOfOrder(get().latestShotDatapoint, shotDatapoint)) { + return; + } + + get().setShotRunning(true); + + if (isNewShotStarted(get().currentShot, shotDatapoint)) { + get().addShotToHistory(get().currentShot); + get().startNewShot(); + } + const { currentShot } = get(); + const shot = { ...(currentShot) }; + shot.datapoints.push(shotDatapoint); + set(() => ({ currentShot: shot, latestShotDatapoint: shotDatapoint })); + }, + + setShotRunning: (value: boolean) => { + const previousValue = get().shotRunning; + + if (previousValue === value) return; + set(() => ({ shotRunning: value })); + + // Shot just stopped. Add to history + if (previousValue && !value) { + get().addShotToHistory(get().currentShot); + } + }, + }), + { + name: 'shot-storage', + partialize: (state) => ({ shotHistory: state.shotHistory }), + }, + ), + ), +); + +export default useShotDataStore; diff --git a/webserver/web-interface/src/state/SystemStateStore.ts b/webserver/web-interface/src/state/SystemStateStore.ts new file mode 100644 index 00000000..7823ce99 --- /dev/null +++ b/webserver/web-interface/src/state/SystemStateStore.ts @@ -0,0 +1,28 @@ +import { create } from 'zustand'; +import { devtools } from 'zustand/middleware'; +import { OperationMode, SystemState } from '../models/models'; + +interface SystemStateStore { + systemState: SystemState, + updateLocalSystemState: (newState: SystemState) => void, +} + +const useSystemStateStore = create()( + devtools( + (set) => ({ + systemState: { + startupInitFinished: false, + operationMode: OperationMode.BREW_AUTO, + tofReady: false, + isSteamForgottenON: false, + scalesPresent: false, + timeAlive: 0, + descaleProgress: 0, + tarePending: false, + }, + updateLocalSystemState: (newState: SystemState) => set(() => ({ systemState: newState })), + }), + ), +); + +export default useSystemStateStore; diff --git a/webserver/web-interface/tsconfig.json b/webserver/web-interface/tsconfig.json index 65a91a02..5a78efbd 100644 --- a/webserver/web-interface/tsconfig.json +++ b/webserver/web-interface/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "ESNext", "lib": ["dom", "dom.iterable", "esnext"], - "types": ["vite/client", "vite-plugin-svgr/client"], + "types": ["vite/client", "vite-plugin-svgr/client", "node"], "allowJs": true, "skipLibCheck": false, "esModuleInterop": false, diff --git a/webserver/web-interface/vite.config.ts b/webserver/web-interface/vite.config.ts index 9015671d..ae3c8413 100644 --- a/webserver/web-interface/vite.config.ts +++ b/webserver/web-interface/vite.config.ts @@ -1,26 +1,98 @@ +/* eslint-disable no-console */ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import viteTsconfigPaths from 'vite-tsconfig-paths'; import svgrPlugin from 'vite-plugin-svgr'; - -const target = 'http://192.168.2.6'; +import viteCompression from 'vite-plugin-compression'; +import { VitePWA } from 'vite-plugin-pwa'; +// This function gets the IP that the Development server will point to from `local.config.ts`. +// To change your local IP create a file named `local.config.ts` in the same directory as vite.config.ts +// And contents: +// ------------------------------------------------- +// const localConfig = { targetIp: '192.168.0.21' }; +// export default localConfig; +// ------------------------------------------------- +async function getDevelopmentIp() { + const defaultTargetIp = '192.168.4.1'; + try { + const localConfig = await import('./local.config'); + console.info(`Development server proxying to ${localConfig.default.targetIp}`); + return localConfig.default.targetIp; + } catch (e) { + console.info(`Did not find local_config.ts file. IP will default to ${defaultTargetIp}`); + return defaultTargetIp; + } +} // https://vitejs.dev/config/ -export default defineConfig({ +export default defineConfig(async () => ({ server: { proxy: { '/api': { - target, + target: `http://${await getDevelopmentIp()}`, changeOrigin: true, secure: false, + }, + '/ws': { + target: `ws://${await getDevelopmentIp()}`, ws: true, + changeOrigin: true, + secure: false, }, }, + host: '0.0.0.0', port: 3000, }, - plugins: [react(), viteTsconfigPaths(), svgrPlugin()], + plugins: [ + react(), + viteTsconfigPaths(), + svgrPlugin(), + viteCompression(), + + VitePWA({ + // registerType: 'autoUpdate', + // workbox: { + // clientsClaim: true, + // skipWaiting: true, + // }, + manifest: { + short_name: 'Gaggiuino', + name: 'Gaggiuino web portal', + protocol_handlers: [ + { + protocol: 'web+http', + url: '/', + }, + ], + icons: [ + { + src: 'favicon.png', + sizes: '64x64 32x32 24x24 16x16', + type: 'image/png', + }, + { + src: 'logo.png', + sizes: 'any', + type: 'image/png', + }, + { + src: 'splash.png', + sizes: 'any', + type: 'image/png', + }, + ], + start_url: '/', + display: 'standalone', + theme_color: '#000000', + background_color: '#ffffff', + }, + manifestFilename: 'manifest.json', + injectRegister: null, // Disable SW registration for now + // registerType: 'autoUpdate', + }), + ], build: { outDir: '../data', emptyOutDir: true, }, -}); +}));