diff --git a/.eslintrc.json b/.eslintrc.json index c1bf1cb51b..bc12bf8288 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -24,6 +24,7 @@ "rules": { "eqeqeq": "error", "import/order": "error", + "no-param-reassign": "error", "no-prototype-builtins": "off", "no-throw-literal": "error", "no-unused-vars": "off", diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..e65801a77e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,56 @@ +# .gitattributes snippet to force users to use same line endings for project. +# +# Handle line endings automatically for files detected as text +# and leave all files detected as binary untouched. +* text=auto + +# +# The above will handle all files NOT found below +# https://help.github.com/articles/dealing-with-line-endings/ +# https://github.com/Danimoth/gitattributes/blob/master/Web.gitattributes + + + +# These files are text and should be normalized (Convert crlf => lf) +*.php text +*.css text +*.scss text +*.js text +*.json text +*.htm text +*.html text +*.xml text +*.txt text +*.ini text +*.inc text +*.pl text +*.rb text +*.py text +*.scm text +*.sql text +.htaccess text +*.sh text +Dockerfile* text +*.yml text +*.yaml text +*.md text +*.markdown text + +# These files are binary and should be left untouched +# (binary is a macro for -text -diff) +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.mov binary +*.mp4 binary +*.mp3 binary +*.flv binary +*.fla binary +*.swf binary +*.gz binary +*.zip binary +*.7z binary +*.ttf binary +*.pyc binary \ No newline at end of file diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index ce7cccb418..6337c8dc1d 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -43,7 +43,7 @@ When submitting a new issue, please supply the following information: **Platform**: Place your platform here... give us your web browser/Electron version _and_ your hardware (Raspberry Pi 2/3/4, Windows, Mac, Linux, System V UNIX). -**Node Version**: Make sure it's version 14 or later (recommended is 16). +**Node Version**: Make sure it's version 16 or later (recommended is 18). **MagicMirror² Version**: Please let us know which version of MagicMirror² you are running. It can be found in the `package.json` file. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md index 087f41f13a..047a22685c 100644 --- a/.github/ISSUE_TEMPLATE/custom.md +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -31,7 +31,7 @@ When submitting a new issue, please supply the following information: **Platform**: Place your platform here... give us your web browser/Electron version _and_ your hardware (Raspberry Pi 2/3/4, Windows, Mac, Linux, System V UNIX). -**Node Version**: Make sure it's version 14 or later (recommended is 16). +**Node Version**: Make sure it's version 16 or later (recommended is 18). **MagicMirror² Version**: Please let us know which version of MagicMirror² you are running. It can be found in the `package.json` file. diff --git a/.github/workflows/automated-tests.yaml b/.github/workflows/automated-tests.yaml index 28957b000d..d2b1b77787 100644 --- a/.github/workflows/automated-tests.yaml +++ b/.github/workflows/automated-tests.yaml @@ -18,7 +18,7 @@ jobs: timeout-minutes: 30 strategy: matrix: - node-version: [14.x, 16.x, 18.x] + node-version: [16.x, 18.x, 20.x] steps: - name: "Checkout code" uses: actions/checkout@v3 @@ -27,11 +27,13 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: "npm" - - name: "Install dependencies and run tests" + - name: "Install dependencies" + run: | + npm run install-mm:dev + - name: "Run tests" run: | Xvfb :99 -screen 0 1024x768x16 & export DISPLAY=:99 - npm run install-mm:dev touch css/custom.css npm run test:prettier npm run test:js diff --git a/.github/workflows/codecov-test-suites.yaml b/.github/workflows/codecov-test-suites.yaml index 8cdcccbb7e..7d99843c19 100644 --- a/.github/workflows/codecov-test-suites.yaml +++ b/.github/workflows/codecov-test-suites.yaml @@ -19,11 +19,13 @@ jobs: steps: - name: "Checkout code" uses: actions/checkout@v3 - - name: "Install dependencies and run coverage" + - name: "Install dependencies" + run: | + npm ci + - name: "Run coverage" run: | Xvfb :99 -screen 0 1024x768x16 & export DISPLAY=:99 - npm ci touch css/custom.css npm run test:coverage - name: "Upload coverage results to codecov" diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b565fab08..c719848bfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,48 @@ This project adheres to [Semantic Versioning](https://semver.org/). ❤️ **Donate:** Enjoying MagicMirror²? [Please consider a donation!](https://magicmirror.builders/donate) With your help we can continue to improve the MagicMirror². +## [2.24.0] - 2023-07-01 + +Thanks to: @angeldeejay, @bugsounet, @buxxi, @CarJem, @dariom, @DaveChild, @dWoolridge, @eddiehung, @grenagit, @Hirschberger, @ismarslomic, @JakeBinney, @KristjanESPERANTO, @MagMar94, @naveensrinivasan, @nfogal, @oscarb, @OWL4C, @psieg, @rajniszp, @retroflex, @SkySails and @tomzt + +Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not all) of the work on this release as project collaborators. This version would not be there without their effort. Thank you guys! You are awesome! + +### Added + +- Added UV Index to hourly and current Weather, with support for Openmeteo +- Added tests for serveronly +- Set Timezone `Europe/Berlin` in unit tests (needed for new formatTime tests) +- Added no-param-reassign eslint rule and fix warnings +- updatenotification: Added `sendUpdatesNotifications` feature. Broadcast update with `UPDATES` notification to other modules +- updatenotification: allow force scanning with `SCAN_UPDATES` notification from other modules +- Added per-calendar fetchInterval + +### Removed + +- Removed unneeded (and unwanted) '.' after the year in calendar repeatingCountTitle (#2896, second attempt ...) + +### Updated + +- Added support for precipitation probability with openmeteo weather-provider +- Update electron to v25.2 and other dependencies +- Use node v20 in github workflow (replacing v14) +- Refactor formatTime into common util function for default modules +- Refactor some calendar methods into own class and added tests for them +- Split install and run commands in github actions +- Changed `fetchInterval` of calendar in `config.js.sample` to 7 days so we not to request example calendar too frequently +- Changed default calendar fetchInterval to one hour +- Changed calendar url in sample config + +### Fixed + +- Fix envcanada hourly forecast time (#3080) +- Fix electron not running under windows after async changes (#3083) +- Fix style issues after eslint-plugin-jsdoc update +- Fix don't filter out ongoing full day events (#3095) +- Fix date not shown when clock in analog mode (#3100) +- Fix envcanada today percentage-of-precipitation (#3106) +- Fix updatenotification where no branch is checked out but e.g. a version tag (#3130) + ## [2.23.0] - 2023-04-04 Thanks to: @angeldeejay, @buxxi, @CarJem, @dariom, @DaveChild, @dWoolridge, @grenagit, @Hirschberger, @KristjanESPERANTO, @MagMar94, @naveensrinivasan, @nfogal, @psieg, @rajniszp, @retroflex, @SkySails and @tomzt. @@ -16,17 +58,18 @@ Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not al - Added increments for hourly forecasts in weather module (#2996) - Added tests for hourly weather forecast - Added possibility to ignore MagicMirror repo in updatenotification module -- Added Pirate Weather as new weather provider (#3005) +- Added Pirate Weather as new weather-provider (#3005) - Added possibility to use your own templates in Alert module - Added error message if `.js` file is missing in module folder to get a hint in the logs (#2403) - Added possibility to use environment variables in `config.js` (#1756) - Added option `pastDaysCount` to default calendar module to control of how many days past events should be displayed - Added thai language to alert module - Added option `sendNotifications` in clock module (#3056) +- Added tests for some weather utils ### Removed -- Removed darksky weather provider +- Removed darksky weather-provider - Removed unneeded (and unwanted) '.' after the year in calendar repeatingCountTitle (#2896) ### Updated @@ -43,6 +86,7 @@ Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not al - Update Eslint config, add new rule and handle issue - Convert lots of callbacks to async/await - Revise require imports (#3071 and #3072) +- Use `config.js-old` instead of file with timestamp suffix when backing up config with a `config.template` in use (#3104) ### Fixed @@ -73,12 +117,12 @@ Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not al - Added new calendar options for colored entries and improved styling (#3033) - Added test for remoteFile option in compliments module -- Added hourlyWeather functionality to Weather.gov weather provider +- Added hourlyWeather functionality to Weather.gov weather-provider - Added css class names "today" and "tomorrow" for default calendar - Added Collaboration.md - Added new github action for dependency review (#2862) - Added a WeatherProvider for Open-Meteo -- Added Yr as a weather provider +- Added Yr as a weather-provider - Added config options "ignoreXOriginHeader" and "ignoreContentSecurityPolicy" - Added thai language - Added workflow rule to make sure PRs are based against develop @@ -96,8 +140,8 @@ Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not al - Updated da translation - Rework weather module - Make sure smhi provider api only gets a maximum of 6 digits coordinates (#2955) - - Use fetch instead of XMLHttpRequest in weatherprovider (#2935) - - Reworked how weatherproviders handle units (#2849) + - Use fetch instead of XMLHttpRequest in weather-provider (#2935) + - Reworked how weather-providers handle units (#2849) - Use unix() method for parsing times, fix suntimes on the way (#2950) - Refactor conversion functions into utils class (#2958) - The `cors`-method in `server.js` now supports sending and receiving HTTP headers @@ -107,7 +151,7 @@ Special thanks to @khassel, @rejas and @sdetweil for taking over most (if not al ### Fixed -- Correctly show apparent temperature in SMHI weather provider +- Correctly show apparent temperature in SMHI weather-provider - Ensure updatenotification module isn't shown when local is _ahead_ of remote - Handle node_helper errors during startup (#2944) - Possibility to change FontAwesome class in calendar, so icons like `fab fa-facebook-square` works. @@ -126,7 +170,7 @@ Special thanks to: @BKeyport, @buxxi, @davide125, @khassel, @kolbyjack, @krukle, - Added possibility to fetch calendars through socket notifications. - New scripts `install-mm` (and `install-mm:dev`) for simplifying mm installation (now: `npm run install-mm`) and adding params `--no-audit --no-fund --no-update-notifier` for less noise. - New `showTimeToday` option in calendar module shows time for current-day events even if `timeFormat` is `"relative"`. -- Added hourly forecasts, apparent temperature & custom location name to SMHI weather provider. +- Added hourly forecasts, apparent temperature & custom location name to SMHI weather-provider. - Added new electron tests for calendar and moved some compliments tests from `e2e` to `electron` because of date mocking, removed mock stuff from compliments module. ### Removed @@ -182,7 +226,7 @@ Special thanks to the following contributors: @10bias, @CFenner, @JHWelch, @k1rd - Added test for new weather forecast `absoluteDates` property. - The modules get a class hidden added/removed if they get hidden/shown which will also toggle pointer-events. - Added new config option `showTitleAsUrl` to newsfeed module. If set, the displayed title is a link to the article which is useful when running in a browser and you want to read this article. -- Added internal cors proxy to get weather providers working without public proxies (fixes #2714). The new url `http(s)://address:port/cors?url=https://whatever-to-proxy` can be used in other modules too. +- Added internal cors proxy to get weather-providers working without public proxies (fixes #2714). The new url `http(s)://address:port/cors?url=https://whatever-to-proxy` can be used in other modules too. - Added a WeatherProvider for Weatherflow. - Added new env var `ELECTRON_DISABLE_GPU` which disable gpu under electron if set (fixes #2831). - Added missing Czech translations. @@ -276,7 +320,7 @@ Special thanks to the following contributors: @apiontek, @eouia, @jupadin, @khas - Updated jsdocs and print warnings during testing too. - Updated weathergov provider to try fetching not just current, but also foreacst, when API URLs available. - Refactored clock layout. -- Refactored methods from weatherproviders into weatherobject (isDaytime, updateSunTime). +- Refactored methods from weather-providers into weatherobject (isDaytime, updateSunTime). - Use of `logger.js` in jest tests. - Run prettier over all relevant files. - Move tests needing electron in new category `electron`, use `server only` mode in `e2e` tests. @@ -374,7 +418,7 @@ Special thanks to the following contributors: @EdgardosReis, @MystaraTheGreat, @ - Code cleanup for FEELS like and added {DEGREE} placeholder for FEELSLIKE for each language. - Converted newsfeed module to use templates. - Updated documentation and help screen about invalid config files. -- Moving weather provider specific code and configuration into each provider and making hourly part of the interface. +- Moving weather-provider specific code and configuration into each provider and making hourly part of the interface. - Bump electron to v11 and enable contextIsolation. - Don't update the DOM when a module is not displayed. - Cleaned up jsdoc and tests. @@ -456,7 +500,7 @@ Special thanks to the following contributors: @Alvinger, @AndyPoms, @ashishtank, - Rename Greek translation to correct ISO 639-1 alpha-2 code (gr > el). (#2155) - Add a space after icons of sunrise and sunset. (#2169) - Fix calendar when no DTEND record found in event, startDate overlay when endDate set. (#2177) -- Fix windspeed conversion error in ukmetoffice weather provider. (#2189) +- Fix windspeed conversion error in ukmetoffice weather-provider. (#2189) - Fix console.debug not having timestamps. (#2199) - Fix calendar full day event east of UTC start time. (#2200) - Fix non-fullday recurring rule processing. (#2216) @@ -685,7 +729,7 @@ Special thanks to @sdetweil for all his great contributions! - Use Feels Like temp from feed if present - Optionally display probability of precipitation (PoP) in current weather (UK Met Office data) - Automatically try to fix eslint errors by passing `--fix` option to it -- Added sunrise and sunset times to weathergov weather provider [#1705](https://github.com/MichMich/MagicMirror/issues/1705) +- Added sunrise and sunset times to weathergov weather-provider [#1705](https://github.com/MichMich/MagicMirror/issues/1705) - Added "useLocationAsHeader" to display "location" in `config.js` as header when location name is not returned - Added to `newsfeed.js`: in order to design the news article better with css, three more class-names were introduced: newsfeed-desc, newsfeed-desc, newsfeed-desc @@ -696,7 +740,7 @@ Special thanks to @sdetweil for all his great contributions! - Updated `ical.js` to solve various calendar issues. - Updated weather city list url [#1676](https://github.com/MichMich/MagicMirror/issues/1676) - Only update clock once per minute when seconds aren't shown -- Updated weatherprovider documentation. +- Updated weather-provider documentation. ### Fixed @@ -778,7 +822,7 @@ Fixed `package.json` version number. - Added fade, fadePoint and maxNumberOfDays properties to the forecast mode [#1516](https://github.com/MichMich/MagicMirror/issues/1516) - Fixed Loading string and decimalSymbol string replace [#1538](https://github.com/MichMich/MagicMirror/issues/1538) - Show Snow amounts in new weather module [#1545](https://github.com/MichMich/MagicMirror/issues/1545) -- Added weather.gov as a new weather provider for US locations +- Added weather.gov as a new weather-provider for US locations ## [2.6.0] - 2019-01-01 diff --git a/clientonly/index.js b/clientonly/index.js index 1a0a797009..479d14bb86 100644 --- a/clientonly/index.js +++ b/clientonly/index.js @@ -11,7 +11,6 @@ /** * Get command line parameters * Assumes that a cmdline parameter is defined with `--key [value]` - * * @param {string} key key to look for at the command line * @param {string} defaultValue value if no key is given at the command line * @returns {string} the value of the parameter @@ -33,7 +32,6 @@ /** * Gets the config from the specified server url - * * @param {string} url location where the server is running. * @returns {Promise} the config */ @@ -63,7 +61,6 @@ /** * Print a message to the console in case of errors - * * @param {string} message error message to print * @param {number} code error code for the exit call */ diff --git a/config/config.js.sample b/config/config.js.sample index 799153b60c..e54be6fa28 100644 --- a/config/config.js.sample +++ b/config/config.js.sample @@ -55,8 +55,9 @@ let config = { config: { calendars: [ { + fetchInterval: 7 * 24 * 60 * 60 * 1000, symbol: "calendar-check", - url: "webcal://www.calendarlabs.com/ical-calendar/ics/76/US_Holidays.ics" + url: "https://ics.calendarlabs.com/76/mm3137/US_Holidays.ics" } ] } diff --git a/jest.config.js b/jest.config.js index 8a2403c289..fde44c9639 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,23 +6,24 @@ module.exports = async () => { projects: [ { displayName: "unit", + globalSetup: "/tests/unit/helpers/global-setup.js", moduleNameMapper: { logger: "/js/logger.js" }, testMatch: ["**/tests/unit/**/*.[jt]s?(x)"], - testPathIgnorePatterns: ["/tests/unit/mocks"] + testPathIgnorePatterns: ["/tests/unit/mocks", "/tests/unit/helpers"] }, { displayName: "electron", testMatch: ["**/tests/electron/**/*.[jt]s?(x)"], - testPathIgnorePatterns: ["/tests/electron/helpers/"] + testPathIgnorePatterns: ["/tests/electron/helpers"] }, { displayName: "e2e", setupFilesAfterEnv: ["/tests/e2e/helpers/mock-console.js"], testMatch: ["**/tests/e2e/**/*.[jt]s?(x)"], modulePaths: ["/js/"], - testPathIgnorePatterns: ["/tests/e2e/helpers/", "/tests/e2e/mocks"] + testPathIgnorePatterns: ["/tests/e2e/helpers", "/tests/e2e/mocks"] } ], collectCoverageFrom: ["./clientonly/**/*.js", "./js/**/*.js", "./modules/default/**/*.js", "./serveronly/**/*.js"], diff --git a/js/app.js b/js/app.js index 22127b666b..184940189c 100644 --- a/js/app.js +++ b/js/app.js @@ -44,7 +44,6 @@ process.on("uncaughtException", function (err) { /** * The core app. - * * @class */ function App() { @@ -53,7 +52,6 @@ function App() { /** * Loads the config file. Combines it with the defaults and returns the config - * * @async * @returns {Promise} the loaded config or the defaults if something goes wrong */ @@ -78,7 +76,7 @@ function App() { // save current config.js try { if (fs.existsSync(configFilename)) { - fs.copyFileSync(configFilename, `${configFilename}_${Date.now()}`); + fs.copyFileSync(configFilename, `${configFilename}-old`); } } catch (err) { Log.warn(`Could not copy ${configFilename}: ${err.message}`); @@ -135,7 +133,6 @@ function App() { /** * Checks the config for deprecated options and throws a warning in the logs * if it encounters one option from the deprecated.js list - * * @param {object} userConfig The user config */ function checkDeprecatedOptions(userConfig) { @@ -150,7 +147,6 @@ function App() { /** * Loads a specific module. - * * @param {string} module The name of the module (including subpath). */ function loadModule(module) { @@ -204,36 +200,21 @@ function App() { /** * Loads all modules. - * - * @param {string[]} modules All modules to be loaded + * @param {Module[]} modules All modules to be loaded + * @returns {Promise} A promise that is resolved when all modules been loaded */ async function loadModules(modules) { - return new Promise((resolve) => { - Log.log("Loading module helpers ..."); - - /** - * - */ - function loadNextModule() { - if (modules.length > 0) { - const nextModule = modules[0]; - loadModule(nextModule); - modules = modules.slice(1); - loadNextModule(); - } else { - // All modules are loaded - Log.log("All module helpers loaded."); - resolve(); - } - } + Log.log("Loading module helpers ..."); - loadNextModule(); - }); + for (let module of modules) { + await loadModule(module); + } + + Log.log("All module helpers loaded."); } /** * Compare two semantic version numbers and return the difference. - * * @param {string} a Version number a. * @param {string} b Version number b. * @returns {number} A positive number if a is larger than b, a negative @@ -259,7 +240,6 @@ function App() { * Start the core app. * * It loads the config, then it loads all modules. - * * @async * @returns {Promise} the config used */ @@ -274,6 +254,7 @@ function App() { modules.push(module.module); } } + await loadModules(modules); httpServer = new Server(config); @@ -312,7 +293,6 @@ function App() { * exists. * * Added to fix #1056 - * * @returns {Promise} A promise that is resolved when all node_helpers and * the http server has been closed */ diff --git a/js/check_config.js b/js/check_config.js index 24368734e8..22a109185c 100644 --- a/js/check_config.js +++ b/js/check_config.js @@ -18,7 +18,6 @@ const Utils = require(`${rootPath}/js/utils.js`); /** * Returns a string with path of configuration file. * Check if set by environment variable MM_CONFIG_FILE - * * @returns {string} path and filename of the config file */ function getConfigFile() { diff --git a/js/class.js b/js/class.js index 339e24b2a6..4e76935942 100644 --- a/js/class.js +++ b/js/class.js @@ -82,7 +82,6 @@ /** * Define the clone method for later use. Helper Method. - * * @param {object} obj Object to be cloned * @returns {object} the cloned object */ diff --git a/js/electron.js b/js/electron.js index 8a474bb32e..8a7c28148f 100644 --- a/js/electron.js +++ b/js/electron.js @@ -126,13 +126,6 @@ function createWindow() { }); } -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -app.on("ready", function () { - Log.log("Launching application."); - createWindow(); -}); - // Quit when all windows are closed. app.on("window-all-closed", function () { if (process.env.JEST_WORKER_ID !== undefined) { @@ -178,5 +171,11 @@ app.on("certificate-error", (event, webContents, url, error, certificate, callba // Start the core application if server is run on localhost // This starts all node helpers and starts the webserver. if (["localhost", "127.0.0.1", "::1", "::ffff:127.0.0.1", undefined].includes(config.address)) { - core.start().then((c) => (config = c)); + core.start().then((c) => { + config = c; + app.whenReady().then(() => { + Log.log("Launching application."); + createWindow(); + }); + }); } diff --git a/js/fetch.js b/js/fetch.js index 3ae874c813..b549d9ed62 100644 --- a/js/fetch.js +++ b/js/fetch.js @@ -4,7 +4,6 @@ * * Attention: After some discussion we always return the third party * implementation until the node implementation is stable and more tested - * * @see https://github.com/MichMich/MagicMirror/pull/2952 * @see https://github.com/MichMich/MagicMirror/issues/2649 * @param {string} url to be fetched diff --git a/js/loader.js b/js/loader.js index 930d57c86b..517999bb86 100644 --- a/js/loader.js +++ b/js/loader.js @@ -52,7 +52,6 @@ const Loader = (function () { /** * Retrieve list of all modules. - * * @returns {object[]} module data as configured in config */ const getAllModules = function () { @@ -61,7 +60,6 @@ const Loader = (function () { /** * Generate array with module information including module paths. - * * @returns {object[]} Module information. */ const getModuleData = function () { @@ -103,7 +101,6 @@ const Loader = (function () { /** * Load modules via ajax request and create module objects. - * * @param {object} module Information about the module we want to load. * @returns {Promise} resolved when module is loaded */ @@ -131,7 +128,6 @@ const Loader = (function () { /** * Bootstrap modules by setting the module data and loading the scripts & styles. - * * @param {object} module Information about the module we want to load. * @param {Module} mObj Modules instance. */ @@ -153,7 +149,6 @@ const Loader = (function () { /** * Load a script or stylesheet by adding it to the dom. - * * @param {string} fileName Path of the file we want to load. * @returns {Promise} resolved when the file is loaded */ @@ -229,7 +224,6 @@ const Loader = (function () { /** * Load a file (script or stylesheet). * Prevent double loading and search for files in the vendor folder. - * * @param {string} fileName Path of the file we want to load. * @param {Module} module The module that calls the loadFile function. * @returns {Promise} resolved when the file is loaded diff --git a/js/main.js b/js/main.js index 6e3ec64caa..026410c777 100644 --- a/js/main.js +++ b/js/main.js @@ -68,7 +68,6 @@ const MM = (function () { /** * Select the wrapper dom object for a specific position. - * * @param {string} position The name of the position. * @returns {HTMLElement | void} the wrapper element */ @@ -85,7 +84,6 @@ const MM = (function () { /** * Send a notification to all modules. - * * @param {string} notification The identifier of the notification. * @param {*} payload The payload of the notification. * @param {Module} sender The module that sent the notification. @@ -102,7 +100,6 @@ const MM = (function () { /** * Update the dom for a specific module. - * * @param {Module} module The module that needs an update. * @param {number} [speed] The (optional) number of microseconds for the animation. * @returns {Promise} Resolved when the dom is fully updated. @@ -129,7 +126,6 @@ const MM = (function () { /** * Update the dom with the specified content - * * @param {Module} module The module that needs an update. * @param {number} [speed] The (optional) number of microseconds for the animation. * @param {string} newHeader The new header that is generated. @@ -167,7 +163,6 @@ const MM = (function () { /** * Check if the content has changed. - * * @param {Module} module The module to check. * @param {string} newHeader The new header that is generated. * @param {HTMLElement} newContent The new content that is generated. @@ -198,7 +193,6 @@ const MM = (function () { /** * Update the content of a module on screen. - * * @param {Module} module The module to check. * @param {string} newHeader The new header that is generated. * @param {HTMLElement} newContent The new content that is generated. @@ -224,15 +218,12 @@ const MM = (function () { /** * Hide the module. - * * @param {Module} module The module to hide. * @param {number} speed The speed of the hide animation. * @param {Function} callback Called when the animation is done. * @param {object} [options] Optional settings for the hide method. */ - const hideModule = function (module, speed, callback, options) { - options = options || {}; - + const hideModule = function (module, speed, callback, options = {}) { // set lockString if set in options. if (options.lockString) { // Log.log("Has lockstring: " + options.lockString); @@ -271,15 +262,12 @@ const MM = (function () { /** * Show the module. - * * @param {Module} module The module to show. * @param {number} speed The speed of the show animation. * @param {Function} callback Called when the animation is done. * @param {object} [options] Optional settings for the show method. */ - const showModule = function (module, speed, callback, options) { - options = options || {}; - + const showModule = function (module, speed, callback, options = {}) { // remove lockString if set in options. if (options.lockString) { const index = module.lockStrings.indexOf(options.lockString); @@ -380,13 +368,11 @@ const MM = (function () { /** * Adds special selectors on a collection of modules. - * * @param {Module[]} modules Array of modules. */ const setSelectionMethodsForModules = function (modules) { /** * Filter modules with the specified classes. - * * @param {string|string[]} className one or multiple classnames (array or space divided). * @returns {Module[]} Filtered collection of modules. */ @@ -396,7 +382,6 @@ const MM = (function () { /** * Filter modules without the specified classes. - * * @param {string|string[]} className one or multiple classnames (array or space divided). * @returns {Module[]} Filtered collection of modules. */ @@ -406,7 +391,6 @@ const MM = (function () { /** * Filters a collection of modules based on classname(s). - * * @param {string|string[]} className one or multiple classnames (array or space divided). * @param {boolean} include if the filter should include or exclude the modules with the specific classes. * @returns {Module[]} Filtered collection of modules. @@ -435,7 +419,6 @@ const MM = (function () { /** * Removes a module instance from the collection. - * * @param {object} module The module instance to remove from the collection. * @returns {Module[]} Filtered collection of modules. */ @@ -450,7 +433,6 @@ const MM = (function () { /** * Walks thru a collection of modules and executes the callback with the module as an argument. - * * @param {Function} callback The function to execute with the module as an argument. */ const enumerate = function (callback) { @@ -491,7 +473,6 @@ const MM = (function () { /** * Gets called when all modules are started. - * * @param {Module[]} moduleObjects All module instances. */ modulesStarted: function (moduleObjects) { @@ -506,7 +487,6 @@ const MM = (function () { /** * Send a notification to all modules. - * * @param {string} notification The identifier of the notification. * @param {*} payload The payload of the notification. * @param {Module} sender The module that sent the notification. @@ -533,7 +513,6 @@ const MM = (function () { /** * Update the dom for a specific module. - * * @param {Module} module The module that needs an update. * @param {number} [speed] The number of microseconds for the animation. */ @@ -554,7 +533,6 @@ const MM = (function () { /** * Returns a collection of all modules currently active. - * * @returns {Module[]} A collection of all modules currently active. */ getModules: function () { @@ -564,7 +542,6 @@ const MM = (function () { /** * Hide the module. - * * @param {Module} module The module to hide. * @param {number} speed The speed of the hide animation. * @param {Function} callback Called when the animation is done. @@ -577,7 +554,6 @@ const MM = (function () { /** * Show the module. - * * @param {Module} module The module to show. * @param {number} speed The speed of the show animation. * @param {Function} callback Called when the animation is done. diff --git a/js/module.js b/js/module.js index 7f6b3804e2..110ccc5595 100644 --- a/js/module.js +++ b/js/module.js @@ -46,7 +46,6 @@ const Module = Class.extend({ /** * Returns a list of scripts the module requires to be loaded. - * * @returns {string[]} An array with filenames. */ getScripts: function () { @@ -55,7 +54,6 @@ const Module = Class.extend({ /** * Returns a list of stylesheets the module requires to be loaded. - * * @returns {string[]} An array with filenames. */ getStyles: function () { @@ -66,7 +64,6 @@ const Module = Class.extend({ * Returns a map of translation files the module requires to be loaded. * * return Map - - * * @returns {*} A map with langKeys and filenames. */ getTranslations: function () { @@ -77,7 +74,6 @@ const Module = Class.extend({ * Generates the dom which needs to be displayed. This method is called by the MagicMirror² core. * This method can to be subclassed if the module wants to display info on the mirror. * Alternatively, the getTemplate method could be subclassed. - * * @returns {HTMLElement|Promise} The dom or a promise with the dom to display. */ getDom: function () { @@ -111,7 +107,6 @@ const Module = Class.extend({ * Generates the header string which needs to be displayed if a user has a header configured for this module. * This method is called by the MagicMirror² core, but only if the user has configured a default header for the module. * This method needs to be subclassed if the module wants to display modified headers on the mirror. - * * @returns {string} The header to display above the header. */ getHeader: function () { @@ -123,7 +118,6 @@ const Module = Class.extend({ * This method needs to be subclassed if the module wants to use a template. * It can either return a template sting, or a template filename. * If the string ends with '.html' it's considered a file from within the module's folder. - * * @returns {string} The template string of filename. */ getTemplate: function () { @@ -133,7 +127,6 @@ const Module = Class.extend({ /** * Returns the data to be used in the template. * This method needs to be subclassed if the module wants to use a custom data. - * * @returns {object} The data for the template */ getTemplateData: function () { @@ -142,7 +135,6 @@ const Module = Class.extend({ /** * Called by the MagicMirror² core when a notification arrives. - * * @param {string} notification The identifier of the notification. * @param {*} payload The payload of the notification. * @param {Module} sender The module that sent the notification. @@ -158,7 +150,6 @@ const Module = Class.extend({ /** * Returns the nunjucks environment for the current module. * The environment is checked in the _nunjucksEnvironment instance variable. - * * @returns {object} The Nunjucks Environment */ nunjucksEnvironment: function () { @@ -180,7 +171,6 @@ const Module = Class.extend({ /** * Called when a socket notification arrives. - * * @param {string} notification The identifier of the notification. * @param {*} payload The payload of the notification. */ @@ -208,7 +198,6 @@ const Module = Class.extend({ /** * Set the module data. - * * @param {object} data The module data */ setData: function (data) { @@ -222,7 +211,6 @@ const Module = Class.extend({ /** * Set the module config and combine it with the module defaults. - * * @param {object} config The combined module config. * @param {boolean} deep Merge module config in deep. */ @@ -233,7 +221,6 @@ const Module = Class.extend({ /** * Returns a socket object. If it doesn't exist, it's created. * It also registers the notification callback. - * * @returns {MMSocket} a socket object */ socket: function () { @@ -250,7 +237,6 @@ const Module = Class.extend({ /** * Retrieve the path to a module file. - * * @param {string} file Filename * @returns {string} the file path */ @@ -260,7 +246,6 @@ const Module = Class.extend({ /** * Load all required stylesheets by requesting the MM object to load the files. - * * @returns {Promise} */ loadStyles: function () { @@ -269,7 +254,6 @@ const Module = Class.extend({ /** * Load all required scripts by requesting the MM object to load the files. - * * @returns {Promise} */ loadScripts: function () { @@ -278,7 +262,6 @@ const Module = Class.extend({ /** * Helper method to load all dependencies. - * * @param {string} funcName Function name to call to get scripts or styles. * @returns {Promise} */ @@ -301,6 +284,7 @@ const Module = Class.extend({ /** * Load all translations. + * @returns {Promise} */ loadTranslations: async function () { const translations = this.getTranslations() || {}; @@ -329,7 +313,6 @@ const Module = Class.extend({ /** * Request the translation for a given key with optional variables and default value. - * * @param {string} key The key of the string to translate * @param {string|object} [defaultValueOrVariables] The default value or variables for translating. * @param {string} [defaultValue] The default value with variables. @@ -344,7 +327,6 @@ const Module = Class.extend({ /** * Request an (animated) update of the module. - * * @param {number} [speed] The speed of the animation. */ updateDom: function (speed) { @@ -353,7 +335,6 @@ const Module = Class.extend({ /** * Send a notification to all modules. - * * @param {string} notification The identifier of the notification. * @param {*} payload The payload of the notification. */ @@ -363,7 +344,6 @@ const Module = Class.extend({ /** * Send a socket notification to the node helper. - * * @param {string} notification The identifier of the notification. * @param {*} payload The payload of the notification. */ @@ -373,55 +353,55 @@ const Module = Class.extend({ /** * Hide this module. - * * @param {number} speed The speed of the hide animation. * @param {Function} callback Called when the animation is done. * @param {object} [options] Optional settings for the hide method. */ - hide: function (speed, callback, options) { + hide: function (speed, callback, options = {}) { + let usedCallback = callback || function () {}; + let usedOptions = options; + if (typeof callback === "object") { - options = callback; - callback = function () {}; + Log.error("Parameter mismatch in module.hide: callback is not an optional parameter!"); + usedOptions = callback; + usedCallback = function () {}; } - callback = callback || function () {}; - options = options || {}; - MM.hideModule( this, speed, () => { this.suspend(); - callback(); + usedCallback(); }, - options + usedOptions ); }, /** * Show this module. - * * @param {number} speed The speed of the show animation. * @param {Function} callback Called when the animation is done. * @param {object} [options] Optional settings for the show method. */ show: function (speed, callback, options) { + let usedCallback = callback || function () {}; + let usedOptions = options; + if (typeof callback === "object") { - options = callback; - callback = function () {}; + Log.error("Parameter mismatch in module.show: callback is not an optional parameter!"); + usedOptions = callback; + usedCallback = function () {}; } - callback = callback || function () {}; - options = options || {}; - MM.showModule( this, speed, () => { this.resume(); - callback(); + usedCallback(); }, - options + usedOptions ); } }); @@ -445,7 +425,6 @@ const Module = Class.extend({ * ------- * * Todo: idea of Mich determinate what do you want to merge or not - * * @param {object} result the initial object * @returns {object} the merged config */ @@ -507,7 +486,6 @@ window.Module = Module; /** * Compare two semantic version numbers and return the difference. - * * @param {string} a Version number a. * @param {string} b Version number b. * @returns {number} A positive number if a is larger than b, a negative diff --git a/js/node_helper.js b/js/node_helper.js index 03c6dca796..c555eb72c0 100644 --- a/js/node_helper.js +++ b/js/node_helper.js @@ -32,7 +32,6 @@ const NodeHelper = Class.extend({ /** * This method is called when a socket notification arrives. - * * @param {string} notification The identifier of the notification. * @param {*} payload The payload of the notification. */ @@ -42,7 +41,6 @@ const NodeHelper = Class.extend({ /** * Set the module name. - * * @param {string} name Module name. */ setName(name) { @@ -51,7 +49,6 @@ const NodeHelper = Class.extend({ /** * Set the module path. - * * @param {string} path Module path. */ setPath(path) { @@ -123,7 +120,6 @@ NodeHelper.checkFetchStatus = function (response) { /** * Look at the specified error and return an appropriate error type, that * can be translated to a detailed error message - * * @param {Error} error the error from fetching something * @returns {string} the string of the detailed error message in the translations */ diff --git a/js/server.js b/js/server.js index 1454621703..771870f244 100644 --- a/js/server.js +++ b/js/server.js @@ -19,7 +19,6 @@ const { cors, getConfig, getHtml, getVersion } = require("./server_functions"); /** * Server - * * @param {object} config The MM config * @class */ @@ -31,7 +30,6 @@ function Server(config) { /** * Opens the server for incoming connections - * * @returns {Promise} A promise that is resolved when the server listens to connections */ this.open = function () { @@ -106,7 +104,6 @@ function Server(config) { /** * Closes the server and destroys all lingering connections to it. - * * @returns {Promise} A promise that resolves when server has successfully shut down */ this.close = function () { diff --git a/js/server_functions.js b/js/server_functions.js index 6ecc6cae29..8e9d9aa91d 100644 --- a/js/server_functions.js +++ b/js/server_functions.js @@ -5,7 +5,6 @@ const fetch = require("./fetch"); /** * Gets the config. - * * @param {Request} req - the request * @param {Response} res - the result */ @@ -19,7 +18,6 @@ function getConfig(req, res) { * Example input request url: /cors?sendheaders=header1:value1,header2:value2&expectedheaders=header1,header2&url=http://www.test.com/path?param1=value1 * * Only the url-param of the input request url is required. It must be the last parameter. - * * @param {Request} req - the request * @param {Response} res - the result */ @@ -57,7 +55,6 @@ async function cors(req, res) { /** * Gets headers and values to attach to the web request. - * * @param {string} url - The url containing the headers and values to send. * @returns {object} An object specifying name and value of the headers. */ @@ -79,7 +76,6 @@ function getHeadersToSend(url) { /** * Gets the headers expected from the response. - * * @param {string} url - The url containing the expected headers from the response. * @returns {string[]} headers - The name of the expected headers. */ @@ -97,7 +93,6 @@ function geExpectedRecievedHeaders(url) { /** * Gets the HTML to display the magic mirror. - * * @param {Request} req - the request * @param {Response} res - the result */ @@ -116,7 +111,6 @@ function getHtml(req, res) { /** * Gets the MagicMirror version. - * * @param {Request} req - the request * @param {Response} res - the result */ diff --git a/js/socketclient.js b/js/socketclient.js index fce49d301d..a8d2e4b592 100644 --- a/js/socketclient.js +++ b/js/socketclient.js @@ -44,10 +44,7 @@ const MMSocket = function (moduleName) { notificationCallback = callback; }; - this.sendNotification = (notification, payload) => { - if (typeof payload === "undefined") { - payload = {}; - } + this.sendNotification = (notification, payload = {}) => { this.socket.emit(notification, payload); }; }; diff --git a/js/translator.js b/js/translator.js index dd004d5414..2ebb0580fe 100644 --- a/js/translator.js +++ b/js/translator.js @@ -9,13 +9,12 @@ const Translator = (function () { /** * Load a JSON file via XHR. - * * @param {string} file Path of the file we want to load. * @returns {Promise} the translations in the specified file */ async function loadJSON(file) { const xhr = new XMLHttpRequest(); - return new Promise(function (resolve, reject) { + return new Promise(function (resolve) { xhr.overrideMimeType("application/json"); xhr.open("GET", file, true); xhr.onreadystatechange = function () { @@ -43,21 +42,17 @@ const Translator = (function () { /** * Load a translation for a given key for a given module. - * * @param {Module} module The module to load the translation for. * @param {string} key The key of the text to translate. * @param {object} variables The variables to use within the translation template (optional) * @returns {string} the translated key */ - translate: function (module, key, variables) { - variables = variables || {}; // Empty object by default - + translate: function (module, key, variables = {}) { /** * Combines template and variables like: * template: "Please wait for {timeToWait} before continuing with {work}." * variables: {timeToWait: "2 hours", work: "painting"} * to: "Please wait for 2 hours before continuing with painting." - * * @param {string} template Text with placeholder * @param {object} variables Variables for the placeholder * @returns {string} the template filled with the variables @@ -66,10 +61,11 @@ const Translator = (function () { if (Object.prototype.toString.call(template) !== "[object String]") { return template; } + let templateToUse = template; if (variables.fallback && !template.match(new RegExp("{.+}"))) { - template = variables.fallback; + templateToUse = variables.fallback; } - return template.replace(new RegExp("{([^}]+)}", "g"), function (_unused, varName) { + return templateToUse.replace(new RegExp("{([^}]+)}", "g"), function (_unused, varName) { return varName in variables ? variables[varName] : `{${varName}}`; }); } @@ -99,7 +95,6 @@ const Translator = (function () { /** * Load a translation file (json) and remember the data. - * * @param {Module} module The module to load the translation file for. * @param {string} file Path of the file we want to load. * @param {boolean} isFallback Flag to indicate fallback translations. @@ -118,7 +113,6 @@ const Translator = (function () { /** * Load the core translations. - * * @param {string} lang The language identifier of the core language. */ loadCoreTranslations: async function (lang) { diff --git a/modules/default/alert/notificationFx.js b/modules/default/alert/notificationFx.js index 8f8a84ccaa..5a7426e981 100644 --- a/modules/default/alert/notificationFx.js +++ b/modules/default/alert/notificationFx.js @@ -9,13 +9,11 @@ * * Copyright 2014, Codrops * https://tympanus.net/codrops/ - * * @param {object} window The window object */ (function (window) { /** * Extend one object with another one - * * @param {object} a The object to extend * @param {object} b The object which extends the other, overwrites existing keys * @returns {object} The merged object @@ -31,7 +29,6 @@ /** * NotificationFx constructor - * * @param {object} options The configuration options * @class */ @@ -124,7 +121,6 @@ /** * Dismiss the notification - * * @param {boolean} [close] call the onClose callback at the end */ NotificationFx.prototype.dismiss = function (close = true) { diff --git a/modules/default/calendar/calendar.js b/modules/default/calendar/calendar.js index 2ef9ca9c89..3343d347ad 100644 --- a/modules/default/calendar/calendar.js +++ b/modules/default/calendar/calendar.js @@ -1,4 +1,4 @@ -/* global cloneObject */ +/* global CalendarUtils, cloneObject */ /* MagicMirror² * Module: Calendar @@ -21,11 +21,11 @@ Module.register("calendar", { defaultRepeatingCountTitle: "", maxTitleLength: 25, maxLocationTitleLength: 25, - wrapEvents: false, // wrap events to multiple lines breaking at maxTitleLength + wrapEvents: false, // Wrap events to multiple lines breaking at maxTitleLength wrapLocationEvents: false, maxTitleLines: 3, maxEventTitleLines: 3, - fetchInterval: 5 * 60 * 1000, // Update every 5 minutes. + fetchInterval: 60 * 60 * 1000, // Update every hour animationSpeed: 2000, fade: true, urgency: 7, @@ -79,7 +79,7 @@ Module.register("calendar", { // Define required scripts. getScripts: function () { - return ["moment.js"]; + return ["calendarutils.js", "moment.js"]; }, // Define required translations. @@ -108,7 +108,7 @@ Module.register("calendar", { } // Set locale. - moment.updateLocale(config.language, this.getLocaleSpecification(config.timeFormat)); + moment.updateLocale(config.language, CalendarUtils.getLocaleSpecification(config.timeFormat)); // clear data holder before start this.calendarData = {}; @@ -313,7 +313,7 @@ Module.register("calendar", { const thisYear = new Date(parseInt(event.startDate)).getFullYear(), yearDiff = thisYear - event.firstYear; - repeatingCountTitle = `, ${yearDiff}. ${repeatingCountTitle}`; + repeatingCountTitle = `, ${yearDiff} ${repeatingCountTitle}`; } } @@ -337,7 +337,8 @@ Module.register("calendar", { } } - titleWrapper.innerHTML = this.titleTransform(event.title, this.config.titleReplace, this.config.wrapEvents, this.config.maxTitleLength, this.config.maxTitleLines) + repeatingCountTitle; + const transformedTitle = CalendarUtils.titleTransform(event.title, this.config.titleReplace); + titleWrapper.innerHTML = CalendarUtils.shorten(transformedTitle, this.config.maxTitleLength, this.config.wrapEvents, this.config.maxTitleLines) + repeatingCountTitle; const titleClass = this.titleClassForUrl(event.url); @@ -362,7 +363,7 @@ Module.register("calendar", { // Add endDate to dataheaders if showEnd is enabled if (this.config.showEnd) { - timeWrapper.innerHTML += ` - ${this.capFirst(moment(event.endDate, "x").format("LT"))}`; + timeWrapper.innerHTML += ` - ${CalendarUtils.capFirst(moment(event.endDate, "x").format("LT"))}`; } eventWrapper.appendChild(timeWrapper); @@ -378,20 +379,20 @@ Module.register("calendar", { if (this.config.timeFormat === "absolute") { // Use dateFormat - timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.dateFormat)); + timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").format(this.config.dateFormat)); // Add end time if showEnd if (this.config.showEnd) { timeWrapper.innerHTML += "-"; - timeWrapper.innerHTML += this.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat)); + timeWrapper.innerHTML += CalendarUtils.capFirst(moment(event.endDate, "x").format(this.config.dateEndFormat)); } // For full day events we use the fullDayEventDateFormat if (event.fullDayEvent) { //subtract one second so that fullDayEvents end at 23:59:59, and not at 0:00:00 one the next day event.endDate -= ONE_SECOND; - timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat)); + timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").format(this.config.fullDayEventDateFormat)); } else if (this.config.getRelative > 0 && event.startDate < now) { // Ongoing and getRelative is set - timeWrapper.innerHTML = this.capFirst( + timeWrapper.innerHTML = CalendarUtils.capFirst( this.translate("RUNNING", { fallback: `${this.translate("RUNNING")} {timeUntilEnd}`, timeUntilEnd: moment(event.endDate, "x").fromNow(true) @@ -399,19 +400,19 @@ Module.register("calendar", { ); } else if (this.config.urgency > 0 && event.startDate - now < this.config.urgency * ONE_DAY) { // Within urgency days - timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow()); + timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").fromNow()); } if (event.fullDayEvent && this.config.nextDaysRelative) { // Full days events within the next two days if (event.today) { - timeWrapper.innerHTML = this.capFirst(this.translate("TODAY")); + timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("TODAY")); } else if (event.yesterday) { - timeWrapper.innerHTML = this.capFirst(this.translate("YESTERDAY")); + timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("YESTERDAY")); } else if (event.startDate - now < ONE_DAY && event.startDate - now > 0) { - timeWrapper.innerHTML = this.capFirst(this.translate("TOMORROW")); + timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("TOMORROW")); } else if (event.startDate - now < 2 * ONE_DAY && event.startDate - now > 0) { if (this.translate("DAYAFTERTOMORROW") !== "DAYAFTERTOMORROW") { - timeWrapper.innerHTML = this.capFirst(this.translate("DAYAFTERTOMORROW")); + timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("DAYAFTERTOMORROW")); } } } @@ -420,9 +421,9 @@ Module.register("calendar", { if (event.startDate >= now || (event.fullDayEvent && event.today)) { // Use relative time if (!this.config.hideTime && !event.fullDayEvent) { - timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").calendar(null, { sameElse: this.config.dateFormat })); + timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").calendar(null, { sameElse: this.config.dateFormat })); } else { - timeWrapper.innerHTML = this.capFirst( + timeWrapper.innerHTML = CalendarUtils.capFirst( moment(event.startDate, "x").calendar(null, { sameDay: this.config.showTimeToday ? "LT" : `[${this.translate("TODAY")}]`, nextDay: `[${this.translate("TOMORROW")}]`, @@ -434,27 +435,27 @@ Module.register("calendar", { if (event.fullDayEvent) { // Full days events within the next two days if (event.today) { - timeWrapper.innerHTML = this.capFirst(this.translate("TODAY")); + timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("TODAY")); } else if (event.dayBeforeYesterday) { if (this.translate("DAYBEFOREYESTERDAY") !== "DAYBEFOREYESTERDAY") { - timeWrapper.innerHTML = this.capFirst(this.translate("DAYBEFOREYESTERDAY")); + timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("DAYBEFOREYESTERDAY")); } } else if (event.yesterday) { - timeWrapper.innerHTML = this.capFirst(this.translate("YESTERDAY")); + timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("YESTERDAY")); } else if (event.startDate - now < ONE_DAY && event.startDate - now > 0) { - timeWrapper.innerHTML = this.capFirst(this.translate("TOMORROW")); + timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("TOMORROW")); } else if (event.startDate - now < 2 * ONE_DAY && event.startDate - now > 0) { if (this.translate("DAYAFTERTOMORROW") !== "DAYAFTERTOMORROW") { - timeWrapper.innerHTML = this.capFirst(this.translate("DAYAFTERTOMORROW")); + timeWrapper.innerHTML = CalendarUtils.capFirst(this.translate("DAYAFTERTOMORROW")); } } } else if (event.startDate - now < this.config.getRelative * ONE_HOUR) { // If event is within getRelative hours, display 'in xxx' time format or moment.fromNow() - timeWrapper.innerHTML = this.capFirst(moment(event.startDate, "x").fromNow()); + timeWrapper.innerHTML = CalendarUtils.capFirst(moment(event.startDate, "x").fromNow()); } } else { // Ongoing event - timeWrapper.innerHTML = this.capFirst( + timeWrapper.innerHTML = CalendarUtils.capFirst( this.translate("RUNNING", { fallback: `${this.translate("RUNNING")} {timeUntilEnd}`, timeUntilEnd: moment(event.endDate, "x").fromNow(true) @@ -503,7 +504,9 @@ Module.register("calendar", { const descCell = document.createElement("td"); descCell.className = "location"; descCell.colSpan = "2"; - descCell.innerHTML = this.titleTransform(event.location, this.config.locationTitleReplace, this.config.wrapLocationEvents, this.config.maxLocationTitleLength, this.config.maxEventTitleLines); + + const transformedTitle = CalendarUtils.titleTransform(event.location, this.config.locationTitleReplace); + descCell.innerHTML = CalendarUtils.shorten(transformedTitle, this.config.maxLocationTitleLength, this.config.wrapLocationEvents, this.config.maxEventTitleLines); locationRow.appendChild(descCell); wrapper.appendChild(locationRow); @@ -519,31 +522,8 @@ Module.register("calendar", { return wrapper; }, - /** - * This function accepts a number (either 12 or 24) and returns a moment.js LocaleSpecification with the - * corresponding timeformat to be used in the calendar display. If no number is given (or otherwise invalid input) - * it will a localeSpecification object with the system locale time format. - * - * @param {number} timeFormat Specifies either 12 or 24 hour time format - * @returns {moment.LocaleSpecification} formatted time - */ - getLocaleSpecification: function (timeFormat) { - switch (timeFormat) { - case 12: { - return { longDateFormat: { LT: "h:mm A" } }; - } - case 24: { - return { longDateFormat: { LT: "HH:mm" } }; - } - default: { - return { longDateFormat: { LT: moment.localeData().longDateFormat("LT") } }; - } - } - }, - /** * Checks if this config contains the calendar url. - * * @param {string} url The calendar url * @returns {boolean} True if the calendar config contains the url, False otherwise */ @@ -559,7 +539,6 @@ Module.register("calendar", { /** * Creates the sorted list of all events. - * * @param {boolean} limitNumberOfEntries Whether to filter returned events for display. * @returns {object[]} Array with events. */ @@ -692,7 +671,6 @@ Module.register("calendar", { /** * Requests node helper to add calendar url. - * * @param {string} url The calendar url to add * @param {object} auth The authentication method and credentials * @param {object} calendarConfig The config of the specific calendar @@ -705,7 +683,7 @@ Module.register("calendar", { maximumEntries: calendarConfig.maximumEntries || this.config.maximumEntries, maximumNumberOfDays: calendarConfig.maximumNumberOfDays || this.config.maximumNumberOfDays, pastDaysCount: calendarConfig.pastDaysCount || this.config.pastDaysCount, - fetchInterval: this.config.fetchInterval, + fetchInterval: calendarConfig.fetchInterval || this.config.fetchInterval, symbolClass: calendarConfig.symbolClass, titleClass: calendarConfig.titleClass, timeClass: calendarConfig.timeClass, @@ -717,7 +695,6 @@ Module.register("calendar", { /** * Retrieves the symbols for a specific event. - * * @param {object} event Event to look for. * @returns {string[]} The symbols */ @@ -758,7 +735,6 @@ Module.register("calendar", { /** * Retrieves the symbolClass for a specific calendar url. - * * @param {string} url The calendar url * @returns {string} The class to be used for the symbols of the calendar */ @@ -768,7 +744,6 @@ Module.register("calendar", { /** * Retrieves the titleClass for a specific calendar url. - * * @param {string} url The calendar url * @returns {string} The class to be used for the title of the calendar */ @@ -778,7 +753,6 @@ Module.register("calendar", { /** * Retrieves the timeClass for a specific calendar url. - * * @param {string} url The calendar url * @returns {string} The class to be used for the time of the calendar */ @@ -788,7 +762,6 @@ Module.register("calendar", { /** * Retrieves the calendar name for a specific calendar url. - * * @param {string} url The calendar url * @returns {string} The name of the calendar */ @@ -798,7 +771,6 @@ Module.register("calendar", { /** * Retrieves the color for a specific calendar url. - * * @param {string} url The calendar url * @param {boolean} isBg Determines if we fetch the bgColor or not * @returns {string} The color @@ -809,7 +781,6 @@ Module.register("calendar", { /** * Retrieves the count title for a specific calendar url. - * * @param {string} url The calendar url * @returns {string} The title */ @@ -819,7 +790,6 @@ Module.register("calendar", { /** * Retrieves the maximum entry count for a specific calendar url. - * * @param {string} url The calendar url * @returns {number} The maximum entry count */ @@ -829,7 +799,6 @@ Module.register("calendar", { /** * Retrieves the maximum count of past days which events of should be displayed for a specific calendar url. - * * @param {string} url The calendar url * @returns {number} The maximum past days count */ @@ -839,7 +808,6 @@ Module.register("calendar", { /** * Helper method to retrieve the property for a specific calendar url. - * * @param {string} url The calendar url * @param {string} property The property to look for * @param {string} defaultValue The value if the property is not found @@ -870,98 +838,6 @@ Module.register("calendar", { return !!this.getCalendarProperty(url, property, undefined); }, - /** - * Shortens a string if it's longer than maxLength and add a ellipsis to the end - * - * @param {string} string Text string to shorten - * @param {number} maxLength The max length of the string - * @param {boolean} wrapEvents Wrap the text after the line has reached maxLength - * @param {number} maxTitleLines The max number of vertical lines before cutting event title - * @returns {string} The shortened string - */ - shorten: function (string, maxLength, wrapEvents, maxTitleLines) { - if (typeof string !== "string") { - return ""; - } - - if (wrapEvents === true) { - const words = string.split(" "); - let temp = ""; - let currentLine = ""; - let line = 0; - - for (let i = 0; i < words.length; i++) { - const word = words[i]; - if (currentLine.length + word.length < (typeof maxLength === "number" ? maxLength : 25) - 1) { - // max - 1 to account for a space - currentLine += `${word} `; - } else { - line++; - if (line > maxTitleLines - 1) { - if (i < words.length) { - currentLine += "…"; - } - break; - } - - if (currentLine.length > 0) { - temp += `${currentLine}
${word} `; - } else { - temp += `${word}
`; - } - currentLine = ""; - } - } - - return (temp + currentLine).trim(); - } else { - if (maxLength && typeof maxLength === "number" && string.length > maxLength) { - return `${string.trim().slice(0, maxLength)}…`; - } else { - return string.trim(); - } - } - }, - - /** - * Capitalize the first letter of a string - * - * @param {string} string The string to capitalize - * @returns {string} The capitalized string - */ - capFirst: function (string) { - return string.charAt(0).toUpperCase() + string.slice(1); - }, - - /** - * Transforms the title of an event for usage. - * Replaces parts of the text as defined in config.titleReplace. - * Shortens title based on config.maxTitleLength and config.wrapEvents - * - * @param {string} title The title to transform. - * @param {object} titleReplace Pairs of strings to be replaced in the title - * @param {boolean} wrapEvents Wrap the text after the line has reached maxLength - * @param {number} maxTitleLength The max length of the string - * @param {number} maxTitleLines The max number of vertical lines before cutting event title - * @returns {string} The transformed title. - */ - titleTransform: function (title, titleReplace, wrapEvents, maxTitleLength, maxTitleLines) { - for (let needle in titleReplace) { - const replacement = titleReplace[needle]; - - const regParts = needle.match(/^\/(.+)\/([gim]*)$/); - if (regParts) { - // the parsed pattern is a regexp. - needle = new RegExp(regParts[1], regParts[2]); - } - - title = title.replace(needle, replacement); - } - - title = this.shorten(title, maxTitleLength, wrapEvents, maxTitleLines); - return title; - }, - /** * Broadcasts the events to all other modules for reuse. * The all events available in one array, sorted on startdate. diff --git a/modules/default/calendar/calendarfetcher.js b/modules/default/calendar/calendarfetcher.js index 00688ee2bb..c7b62960d8 100644 --- a/modules/default/calendar/calendarfetcher.js +++ b/modules/default/calendar/calendarfetcher.js @@ -11,7 +11,7 @@ const ical = require("node-ical"); const fetch = require("fetch"); const Log = require("logger"); const NodeHelper = require("node_helper"); -const CalendarUtils = require("./calendarutils"); +const CalendarFetcherUtils = require("./calendarfetcherutils"); /** * @@ -72,7 +72,7 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn try { data = ical.parseICS(responseData); Log.debug(`parsed data=${JSON.stringify(data)}`); - events = CalendarUtils.filterEvents(data, { + events = CalendarFetcherUtils.filterEvents(data, { excludedEvents, includePastEvents, maximumEntries, @@ -121,7 +121,6 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn /** * Sets the on success callback - * * @param {Function} callback The on success callback. */ this.onReceive = function (callback) { @@ -130,7 +129,6 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn /** * Sets the on error callback - * * @param {Function} callback The on error callback. */ this.onError = function (callback) { @@ -139,7 +137,6 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn /** * Returns the url of this fetcher. - * * @returns {string} The url of this fetcher. */ this.url = function () { @@ -148,7 +145,6 @@ const CalendarFetcher = function (url, reloadInterval, excludedEvents, maximumEn /** * Returns current available events for this fetcher. - * * @returns {object[]} The current available events for this fetcher. */ this.events = function () { diff --git a/modules/default/calendar/calendarfetcherutils.js b/modules/default/calendar/calendarfetcherutils.js new file mode 100644 index 0000000000..d425f0a478 --- /dev/null +++ b/modules/default/calendar/calendarfetcherutils.js @@ -0,0 +1,605 @@ +/* MagicMirror² + * Calendar Fetcher Util Methods + * + * By Michael Teeuw https://michaelteeuw.nl + * MIT Licensed. + */ + +/** + * @external Moment + */ +const path = require("path"); +const moment = require("moment"); +const zoneTable = require(path.join(__dirname, "windowsZones.json")); +const Log = require("../../../js/logger"); + +const CalendarFetcherUtils = { + /** + * Calculate the time correction, either dst/std or full day in cases where + * utc time is day before plus offset + * @param {object} event the event which needs adjustment + * @param {Date} date the date on which this event happens + * @returns {number} the necessary adjustment in hours + */ + calculateTimezoneAdjustment: function (event, date) { + let adjustHours = 0; + // if a timezone was specified + if (!event.start.tz) { + Log.debug(" if no tz, guess based on now"); + event.start.tz = moment.tz.guess(); + } + Log.debug(`initial tz=${event.start.tz}`); + + // if there is a start date specified + if (event.start.tz) { + // if this is a windows timezone + if (event.start.tz.includes(" ")) { + // use the lookup table to get theIANA name as moment and date don't know MS timezones + let tz = CalendarFetcherUtils.getIanaTZFromMS(event.start.tz); + Log.debug(`corrected TZ=${tz}`); + // watch out for unregistered windows timezone names + // if we had a successful lookup + if (tz) { + // change the timezone to the IANA name + event.start.tz = tz; + // Log.debug("corrected timezone="+event.start.tz) + } + } + Log.debug(`corrected tz=${event.start.tz}`); + let current_offset = 0; // offset from TZ string or calculated + let mm = 0; // date with tz or offset + let start_offset = 0; // utc offset of created with tz + // if there is still an offset, lookup failed, use it + if (event.start.tz.startsWith("(")) { + const regex = /[+|-]\d*:\d*/; + const start_offsetString = event.start.tz.match(regex).toString().split(":"); + let start_offset = parseInt(start_offsetString[0]); + start_offset *= event.start.tz[1] === "-" ? -1 : 1; + adjustHours = start_offset; + Log.debug(`defined offset=${start_offset} hours`); + current_offset = start_offset; + event.start.tz = ""; + Log.debug(`ical offset=${current_offset} date=${date}`); + mm = moment(date); + let x = parseInt(moment(new Date()).utcOffset()); + Log.debug(`net mins=${current_offset * 60 - x}`); + + mm = mm.add(x - current_offset * 60, "minutes"); + adjustHours = (current_offset * 60 - x) / 60; + event.start = mm.toDate(); + Log.debug(`adjusted date=${event.start}`); + } else { + // get the start time in that timezone + let es = moment(event.start); + // check for start date prior to start of daylight changing date + if (es.format("YYYY") < 2007) { + es.set("year", 2013); // if so, use a closer date + } + Log.debug(`start date/time=${es.toDate()}`); + start_offset = moment.tz(es, event.start.tz).utcOffset(); + Log.debug(`start offset=${start_offset}`); + + Log.debug(`start date/time w tz =${moment.tz(moment(event.start), event.start.tz).toDate()}`); + + // get the specified date in that timezone + mm = moment.tz(moment(date), event.start.tz); + Log.debug(`event date=${mm.toDate()}`); + current_offset = mm.utcOffset(); + } + Log.debug(`event offset=${current_offset} hour=${mm.format("H")} event date=${mm.toDate()}`); + + // if the offset is greater than 0, east of london + if (current_offset !== start_offset) { + // big offset + Log.debug("offset"); + let h = parseInt(mm.format("H")); + // check if the event time is less than the offset + if (h > 0 && h < Math.abs(current_offset) / 60) { + // if so, rrule created a wrong date (utc day, oops, with utc yesterday adjusted time) + // we need to fix that + //adjustHours = 24; + // Log.debug("adjusting date") + } + //-300 > -240 + //if (Math.abs(current_offset) > Math.abs(start_offset)){ + if (current_offset > start_offset) { + adjustHours -= 1; + Log.debug("adjust down 1 hour dst change"); + //} else if (Math.abs(current_offset) < Math.abs(start_offset)) { + } else if (current_offset < start_offset) { + adjustHours += 1; + Log.debug("adjust up 1 hour dst change"); + } + } + } + Log.debug(`adjustHours=${adjustHours}`); + return adjustHours; + }, + + /** + * Filter the events from ical according to the given config + * @param {object} data the calendar data from ical + * @param {object} config The configuration object + * @returns {string[]} the filtered events + */ + filterEvents: function (data, config) { + const newEvents = []; + + // limitFunction doesn't do much limiting, see comment re: the dates + // array in rrule section below as to why we need to do the filtering + // ourselves + const limitFunction = function (date, i) { + return true; + }; + + const eventDate = function (event, time) { + return CalendarFetcherUtils.isFullDayEvent(event) ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time])); + }; + + Log.debug(`There are ${Object.entries(data).length} calendar entries.`); + Object.entries(data).forEach(([key, event]) => { + Log.debug("Processing entry..."); + const now = new Date(); + const today = moment().startOf("day").toDate(); + const future = moment().startOf("day").add(config.maximumNumberOfDays, "days").subtract(1, "seconds").toDate(); // Subtract 1 second so that events that start on the middle of the night will not repeat. + let past = today; + + if (config.includePastEvents) { + past = moment().startOf("day").subtract(config.maximumNumberOfDays, "days").toDate(); + } + + // FIXME: Ugly fix to solve the facebook birthday issue. + // Otherwise, the recurring events only show the birthday for next year. + let isFacebookBirthday = false; + if (typeof event.uid !== "undefined") { + if (event.uid.indexOf("@facebook.com") !== -1) { + isFacebookBirthday = true; + } + } + + if (event.type === "VEVENT") { + Log.debug(`Event:\n${JSON.stringify(event)}`); + let startDate = eventDate(event, "start"); + let endDate; + + if (typeof event.end !== "undefined") { + endDate = eventDate(event, "end"); + } else if (typeof event.duration !== "undefined") { + endDate = startDate.clone().add(moment.duration(event.duration)); + } else { + if (!isFacebookBirthday) { + // make copy of start date, separate storage area + endDate = moment(startDate.format("x"), "x"); + } else { + endDate = moment(startDate).add(1, "days"); + } + } + + Log.debug(`start: ${startDate.toDate()}`); + Log.debug(`end:: ${endDate.toDate()}`); + + // Calculate the duration of the event for use with recurring events. + let duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x")); + Log.debug(`duration: ${duration}`); + + // FIXME: Since the parsed json object from node-ical comes with time information + // this check could be removed (?) + if (event.start.length === 8) { + startDate = startDate.startOf("day"); + } + + const title = CalendarFetcherUtils.getTitleFromEvent(event); + Log.debug(`title: ${title}`); + + let excluded = false, + dateFilter = null; + + for (let f in config.excludedEvents) { + let filter = config.excludedEvents[f], + testTitle = title.toLowerCase(), + until = null, + useRegex = false, + regexFlags = "g"; + + if (filter instanceof Object) { + if (typeof filter.until !== "undefined") { + until = filter.until; + } + + if (typeof filter.regex !== "undefined") { + useRegex = filter.regex; + } + + // If additional advanced filtering is added in, this section + // must remain last as we overwrite the filter object with the + // filterBy string + if (filter.caseSensitive) { + filter = filter.filterBy; + testTitle = title; + } else if (useRegex) { + filter = filter.filterBy; + testTitle = title; + regexFlags += "i"; + } else { + filter = filter.filterBy.toLowerCase(); + } + } else { + filter = filter.toLowerCase(); + } + + if (CalendarFetcherUtils.titleFilterApplies(testTitle, filter, useRegex, regexFlags)) { + if (until) { + dateFilter = until; + } else { + excluded = true; + } + break; + } + } + + if (excluded) { + return; + } + + const location = event.location || false; + const geo = event.geo || false; + const description = event.description || false; + + if (typeof event.rrule !== "undefined" && event.rrule !== null && !isFacebookBirthday) { + const rule = event.rrule; + let addedEvents = 0; + + const pastMoment = moment(past); + const futureMoment = moment(future); + + // can cause problems with e.g. birthdays before 1900 + if ((rule.options && rule.origOptions && rule.origOptions.dtstart && rule.origOptions.dtstart.getFullYear() < 1900) || (rule.options && rule.options.dtstart && rule.options.dtstart.getFullYear() < 1900)) { + rule.origOptions.dtstart.setYear(1900); + rule.options.dtstart.setYear(1900); + } + + // For recurring events, get the set of start dates that fall within the range + // of dates we're looking for. + // kblankenship1989 - to fix issue #1798, converting all dates to locale time first, then converting back to UTC time + let pastLocal = 0; + let futureLocal = 0; + if (CalendarFetcherUtils.isFullDayEvent(event)) { + Log.debug("fullday"); + // if full day event, only use the date part of the ranges + pastLocal = pastMoment.toDate(); + futureLocal = futureMoment.toDate(); + + Log.debug(`pastLocal: ${pastLocal}`); + Log.debug(`futureLocal: ${futureLocal}`); + } else { + // if we want past events + if (config.includePastEvents) { + // use the calculated past time for the between from + pastLocal = pastMoment.toDate(); + } else { + // otherwise use NOW.. cause we shouldn't use any before now + pastLocal = moment().toDate(); //now + } + futureLocal = futureMoment.toDate(); // future + } + Log.debug(`Search for recurring events between: ${pastLocal} and ${futureLocal}`); + const dates = rule.between(pastLocal, futureLocal, true, limitFunction); + Log.debug(`Title: ${event.summary}, with dates: ${JSON.stringify(dates)}`); + // The "dates" array contains the set of dates within our desired date range range that are valid + // for the recurrence rule. *However*, it's possible for us to have a specific recurrence that + // had its date changed from outside the range to inside the range. For the time being, + // we'll handle this by adding *all* recurrence entries into the set of dates that we check, + // because the logic below will filter out any recurrences that don't actually belong within + // our display range. + // Would be great if there was a better way to handle this. + Log.debug(`event.recurrences: ${event.recurrences}`); + if (event.recurrences !== undefined) { + for (let r in event.recurrences) { + // Only add dates that weren't already in the range we added from the rrule so that + // we don"t double-add those events. + if (moment(new Date(r)).isBetween(pastMoment, futureMoment) !== true) { + dates.push(new Date(r)); + } + } + } + // Loop through the set of date entries to see which recurrences should be added to our event list. + for (let d in dates) { + let date = dates[d]; + // Remove the time information of each date by using its substring, using the following method: + // .toISOString().substring(0,10). + // since the date is given as ISOString with YYYY-MM-DDTHH:MM:SS.SSSZ + // (see https://momentjs.com/docs/#/displaying/as-iso-string/). + const dateKey = date.toISOString().substring(0, 10); + let curEvent = event; + let showRecurrence = true; + + // Get the offset of today where we are processing + // This will be the correction, we need to apply. + let nowOffset = new Date().getTimezoneOffset(); + // For full day events, the time might be off from RRULE/Luxon problem + // Get time zone offset of the rule calculated event + let dateoffset = date.getTimezoneOffset(); + + // Reduce the time by the following offset. + Log.debug(` recurring date is ${date} offset is ${dateoffset}`); + + let dh = moment(date).format("HH"); + Log.debug(` recurring date is ${date} offset is ${dateoffset / 60} Hour is ${dh}`); + + if (CalendarFetcherUtils.isFullDayEvent(event)) { + Log.debug("Fullday"); + // If the offset is negative (east of GMT), where the problem is + if (dateoffset < 0) { + if (dh < Math.abs(dateoffset / 60)) { + // if the rrule byweekday WAS explicitly set , correct it + // reduce the time by the offset + if (curEvent.rrule.origOptions.byweekday !== undefined) { + // Apply the correction to the date/time to get it UTC relative + date = new Date(date.getTime() - Math.abs(24 * 60) * 60000); + } + // the duration was calculated way back at the top before we could correct the start time.. + // fix it for this event entry + //duration = 24 * 60 * 60 * 1000; + Log.debug(`new recurring date1 fulldate is ${date}`); + } + } else { + // if the timezones are the same, correct date if needed + //if (event.start.tz === moment.tz.guess()) { + // if the date hour is less than the offset + if (24 - dh <= Math.abs(dateoffset / 60)) { + // if the rrule byweekday WAS explicitly set , correct it + if (curEvent.rrule.origOptions.byweekday !== undefined) { + // apply the correction to the date/time back to right day + date = new Date(date.getTime() + Math.abs(24 * 60) * 60000); + } + // the duration was calculated way back at the top before we could correct the start time.. + // fix it for this event entry + //duration = 24 * 60 * 60 * 1000; + Log.debug(`new recurring date2 fulldate is ${date}`); + } + //} + } + } else { + // not full day, but luxon can still screw up the date on the rule processing + // we need to correct the date to get back to the right event for + if (dateoffset < 0) { + // if the date hour is less than the offset + if (dh <= Math.abs(dateoffset / 60)) { + // if the rrule byweekday WAS explicitly set , correct it + if (curEvent.rrule.origOptions.byweekday !== undefined) { + // Reduce the time by t: + // Apply the correction to the date/time to get it UTC relative + date = new Date(date.getTime() - Math.abs(24 * 60) * 60000); + } + // the duration was calculated way back at the top before we could correct the start time.. + // fix it for this event entry + //duration = 24 * 60 * 60 * 1000; + Log.debug(`new recurring date1 is ${date}`); + } + } else { + // if the timezones are the same, correct date if needed + //if (event.start.tz === moment.tz.guess()) { + // if the date hour is less than the offset + if (24 - dh <= Math.abs(dateoffset / 60)) { + // if the rrule byweekday WAS explicitly set , correct it + if (curEvent.rrule.origOptions.byweekday !== undefined) { + // apply the correction to the date/time back to right day + date = new Date(date.getTime() + Math.abs(24 * 60) * 60000); + } + // the duration was calculated way back at the top before we could correct the start time.. + // fix it for this event entry + //duration = 24 * 60 * 60 * 1000; + Log.debug(`new recurring date2 is ${date}`); + } + //} + } + } + startDate = moment(date); + Log.debug(`Corrected startDate: ${startDate.toDate()}`); + + let adjustDays = CalendarFetcherUtils.calculateTimezoneAdjustment(event, date); + + // For each date that we're checking, it's possible that there is a recurrence override for that one day. + if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) { + // We found an override, so for this recurrence, use a potentially different title, start date, and duration. + curEvent = curEvent.recurrences[dateKey]; + startDate = moment(curEvent.start); + duration = parseInt(moment(curEvent.end).format("x")) - parseInt(startDate.format("x")); + } + // If there's no recurrence override, check for an exception date. Exception dates represent exceptions to the rule. + else if (curEvent.exdate !== undefined && curEvent.exdate[dateKey] !== undefined) { + // This date is an exception date, which means we should skip it in the recurrence pattern. + showRecurrence = false; + } + Log.debug(`duration: ${duration}`); + + endDate = moment(parseInt(startDate.format("x")) + duration, "x"); + if (startDate.format("x") === endDate.format("x")) { + endDate = endDate.endOf("day"); + } + + const recurrenceTitle = CalendarFetcherUtils.getTitleFromEvent(curEvent); + + // If this recurrence ends before the start of the date range, or starts after the end of the date range, don"t add + // it to the event list. + if (endDate.isBefore(past) || startDate.isAfter(future)) { + showRecurrence = false; + } + + if (CalendarFetcherUtils.timeFilterApplies(now, endDate, dateFilter)) { + showRecurrence = false; + } + + if (showRecurrence === true) { + Log.debug(`saving event: ${description}`); + addedEvents++; + newEvents.push({ + title: recurrenceTitle, + startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"), + endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"), + fullDayEvent: CalendarFetcherUtils.isFullDayEvent(event), + recurringEvent: true, + class: event.class, + firstYear: event.start.getFullYear(), + location: location, + geo: geo, + description: description + }); + } + } + // End recurring event parsing. + } else { + // Single event. + const fullDayEvent = isFacebookBirthday ? true : CalendarFetcherUtils.isFullDayEvent(event); + // Log.debug("full day event") + + // if the start and end are the same, then make end the 'end of day' value (start is at 00:00:00) + if (fullDayEvent && startDate.format("x") === endDate.format("x")) { + endDate = endDate.endOf("day"); + } + + if (config.includePastEvents) { + // Past event is too far in the past, so skip. + if (endDate < past) { + return; + } + } else { + // It's not a fullday event, and it is in the past, so skip. + if (!fullDayEvent && endDate < new Date()) { + return; + } + + // It's a fullday event, and it is before today, So skip. + if (fullDayEvent && endDate <= today) { + return; + } + } + + // It exceeds the maximumNumberOfDays limit, so skip. + if (startDate > future) { + return; + } + + if (CalendarFetcherUtils.timeFilterApplies(now, endDate, dateFilter)) { + return; + } + + // get correction for date saving and dst change between now and then + let adjustDays = CalendarFetcherUtils.calculateTimezoneAdjustment(event, startDate.toDate()); + // Every thing is good. Add it to the list. + newEvents.push({ + title: title, + startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"), + endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"), + fullDayEvent: fullDayEvent, + class: event.class, + location: location, + geo: geo, + description: description + }); + } + } + }); + + newEvents.sort(function (a, b) { + return a.startDate - b.startDate; + }); + + return newEvents; + }, + + /** + * Lookup iana tz from windows + * @param {string} msTZName the timezone name to lookup + * @returns {string|null} the iana name or null of none is found + */ + getIanaTZFromMS: function (msTZName) { + // Get hash entry + const he = zoneTable[msTZName]; + // If found return iana name, else null + return he ? he.iana[0] : null; + }, + + /** + * Gets the title from the event. + * @param {object} event The event object to check. + * @returns {string} The title of the event, or "Event" if no title is found. + */ + getTitleFromEvent: function (event) { + let title = "Event"; + if (event.summary) { + title = typeof event.summary.val !== "undefined" ? event.summary.val : event.summary; + } else if (event.description) { + title = event.description; + } + + return title; + }, + + /** + * Checks if an event is a fullday event. + * @param {object} event The event object to check. + * @returns {boolean} True if the event is a fullday event, false otherwise + */ + isFullDayEvent: function (event) { + if (event.start.length === 8 || event.start.dateOnly || event.datetype === "date") { + return true; + } + + const start = event.start || 0; + const startDate = new Date(start); + const end = event.end || 0; + if ((end - start) % (24 * 60 * 60 * 1000) === 0 && startDate.getHours() === 0 && startDate.getMinutes() === 0) { + // Is 24 hours, and starts on the middle of the night. + return true; + } + + return false; + }, + + /** + * Determines if the user defined time filter should apply + * @param {Date} now Date object using previously created object for consistency + * @param {Moment} endDate Moment object representing the event end date + * @param {string} filter The time to subtract from the end date to determine if an event should be shown + * @returns {boolean} True if the event should be filtered out, false otherwise + */ + timeFilterApplies: function (now, endDate, filter) { + if (filter) { + const until = filter.split(" "), + value = parseInt(until[0]), + increment = until[1].slice(-1) === "s" ? until[1] : `${until[1]}s`, // Massage the data for moment js + filterUntil = moment(endDate.format()).subtract(value, increment); + + return now < filterUntil.format("x"); + } + + return false; + }, + + /** + * Determines if the user defined title filter should apply + * @param {string} title the title of the event + * @param {string} filter the string to look for, can be a regex also + * @param {boolean} useRegex true if a regex should be used, otherwise it just looks for the filter as a string + * @param {string} regexFlags flags that should be applied to the regex + * @returns {boolean} True if the title should be filtered out, false otherwise + */ + titleFilterApplies: function (title, filter, useRegex, regexFlags) { + if (useRegex) { + let regexFilter = filter; + // Assume if leading slash, there is also trailing slash + if (filter[0] === "/") { + // Strip leading and trailing slashes + regexFilter = filter.substr(1).slice(0, -1); + } + return new RegExp(regexFilter, regexFlags).test(title); + } else { + return title.includes(filter); + } + } +}; + +if (typeof module !== "undefined") { + module.exports = CalendarFetcherUtils; +} diff --git a/modules/default/calendar/calendarutils.js b/modules/default/calendar/calendarutils.js index 64a7b9f461..e953b6352c 100644 --- a/modules/default/calendar/calendarutils.js +++ b/modules/default/calendar/calendarutils.js @@ -1,610 +1,114 @@ /* MagicMirror² * Calendar Util Methods * - * By Michael Teeuw https://michaelteeuw.nl + * By Rejas * MIT Licensed. */ - -/** - * @external Moment - */ -const path = require("path"); -const moment = require("moment"); -const zoneTable = require(path.join(__dirname, "windowsZones.json")); -const Log = require("../../../js/logger"); - const CalendarUtils = { /** - * Calculate the time correction, either dst/std or full day in cases where - * utc time is day before plus offset - * - * @param {object} event the event which needs adjustement - * @param {Date} date the date on which this event happens - * @returns {number} the necessary adjustment in hours + * Capitalize the first letter of a string + * @param {string} string The string to capitalize + * @returns {string} The capitalized string */ - calculateTimezoneAdjustment: function (event, date) { - let adjustHours = 0; - // if a timezone was specified - if (!event.start.tz) { - Log.debug(" if no tz, guess based on now"); - event.start.tz = moment.tz.guess(); - } - Log.debug(`initial tz=${event.start.tz}`); + capFirst: function (string) { + return string.charAt(0).toUpperCase() + string.slice(1); + }, - // if there is a start date specified - if (event.start.tz) { - // if this is a windows timezone - if (event.start.tz.includes(" ")) { - // use the lookup table to get theIANA name as moment and date don't know MS timezones - let tz = CalendarUtils.getIanaTZFromMS(event.start.tz); - Log.debug(`corrected TZ=${tz}`); - // watch out for unregistered windows timezone names - // if we had a successful lookup - if (tz) { - // change the timezone to the IANA name - event.start.tz = tz; - // Log.debug("corrected timezone="+event.start.tz) - } + /** + * This function accepts a number (either 12 or 24) and returns a moment.js LocaleSpecification with the + * corresponding time-format to be used in the calendar display. If no number is given (or otherwise invalid input) + * it will a localeSpecification object with the system locale time format. + * @param {number} timeFormat Specifies either 12 or 24-hour time format + * @returns {moment.LocaleSpecification} formatted time + */ + getLocaleSpecification: function (timeFormat) { + switch (timeFormat) { + case 12: { + return { longDateFormat: { LT: "h:mm A" } }; } - Log.debug(`corrected tz=${event.start.tz}`); - let current_offset = 0; // offset from TZ string or calculated - let mm = 0; // date with tz or offset - let start_offset = 0; // utc offset of created with tz - // if there is still an offset, lookup failed, use it - if (event.start.tz.startsWith("(")) { - const regex = /[+|-]\d*:\d*/; - const start_offsetString = event.start.tz.match(regex).toString().split(":"); - let start_offset = parseInt(start_offsetString[0]); - start_offset *= event.start.tz[1] === "-" ? -1 : 1; - adjustHours = start_offset; - Log.debug(`defined offset=${start_offset} hours`); - current_offset = start_offset; - event.start.tz = ""; - Log.debug(`ical offset=${current_offset} date=${date}`); - mm = moment(date); - let x = parseInt(moment(new Date()).utcOffset()); - Log.debug(`net mins=${current_offset * 60 - x}`); - - mm = mm.add(x - current_offset * 60, "minutes"); - adjustHours = (current_offset * 60 - x) / 60; - event.start = mm.toDate(); - Log.debug(`adjusted date=${event.start}`); - } else { - // get the start time in that timezone - let es = moment(event.start); - // check for start date prior to start of daylight changing date - if (es.format("YYYY") < 2007) { - es.set("year", 2013); // if so, use a closer date - } - Log.debug(`start date/time=${es.toDate()}`); - start_offset = moment.tz(es, event.start.tz).utcOffset(); - Log.debug(`start offset=${start_offset}`); - - Log.debug(`start date/time w tz =${moment.tz(moment(event.start), event.start.tz).toDate()}`); - - // get the specified date in that timezone - mm = moment.tz(moment(date), event.start.tz); - Log.debug(`event date=${mm.toDate()}`); - current_offset = mm.utcOffset(); + case 24: { + return { longDateFormat: { LT: "HH:mm" } }; } - Log.debug(`event offset=${current_offset} hour=${mm.format("H")} event date=${mm.toDate()}`); - - // if the offset is greater than 0, east of london - if (current_offset !== start_offset) { - // big offset - Log.debug("offset"); - let h = parseInt(mm.format("H")); - // check if the event time is less than the offset - if (h > 0 && h < Math.abs(current_offset) / 60) { - // if so, rrule created a wrong date (utc day, oops, with utc yesterday adjusted time) - // we need to fix that - //adjustHours = 24; - // Log.debug("adjusting date") - } - //-300 > -240 - //if (Math.abs(current_offset) > Math.abs(start_offset)){ - if (current_offset > start_offset) { - adjustHours -= 1; - Log.debug("adjust down 1 hour dst change"); - //} else if (Math.abs(current_offset) < Math.abs(start_offset)) { - } else if (current_offset < start_offset) { - adjustHours += 1; - Log.debug("adjust up 1 hour dst change"); - } + default: { + return { longDateFormat: { LT: moment.localeData().longDateFormat("LT") } }; } } - Log.debug(`adjustHours=${adjustHours}`); - return adjustHours; }, /** - * Filter the events from ical according to the given config - * - * @param {object} data the calendar data from ical - * @param {object} config The configuration object - * @returns {string[]} the filtered events + * Shortens a string if it's longer than maxLength and add an ellipsis to the end + * @param {string} string Text string to shorten + * @param {number} maxLength The max length of the string + * @param {boolean} wrapEvents Wrap the text after the line has reached maxLength + * @param {number} maxTitleLines The max number of vertical lines before cutting event title + * @returns {string} The shortened string */ - filterEvents: function (data, config) { - const newEvents = []; - - // limitFunction doesn't do much limiting, see comment re: the dates - // array in rrule section below as to why we need to do the filtering - // ourselves - const limitFunction = function (date, i) { - return true; - }; - - const eventDate = function (event, time) { - return CalendarUtils.isFullDayEvent(event) ? moment(event[time], "YYYYMMDD") : moment(new Date(event[time])); - }; - - Log.debug(`There are ${Object.entries(data).length} calendar entries.`); - Object.entries(data).forEach(([key, event]) => { - Log.debug("Processing entry..."); - const now = new Date(); - const today = moment().startOf("day").toDate(); - const future = moment().startOf("day").add(config.maximumNumberOfDays, "days").subtract(1, "seconds").toDate(); // Subtract 1 second so that events that start on the middle of the night will not repeat. - let past = today; - - if (config.includePastEvents) { - past = moment().startOf("day").subtract(config.maximumNumberOfDays, "days").toDate(); - } - - // FIXME: Ugly fix to solve the facebook birthday issue. - // Otherwise, the recurring events only show the birthday for next year. - let isFacebookBirthday = false; - if (typeof event.uid !== "undefined") { - if (event.uid.indexOf("@facebook.com") !== -1) { - isFacebookBirthday = true; - } - } - - if (event.type === "VEVENT") { - Log.debug(`Event:\n${JSON.stringify(event)}`); - let startDate = eventDate(event, "start"); - let endDate; + shorten: function (string, maxLength, wrapEvents, maxTitleLines) { + if (typeof string !== "string") { + return ""; + } - if (typeof event.end !== "undefined") { - endDate = eventDate(event, "end"); - } else if (typeof event.duration !== "undefined") { - endDate = startDate.clone().add(moment.duration(event.duration)); + if (wrapEvents === true) { + const words = string.split(" "); + let temp = ""; + let currentLine = ""; + let line = 0; + + for (let i = 0; i < words.length; i++) { + const word = words[i]; + if (currentLine.length + word.length < (typeof maxLength === "number" ? maxLength : 25) - 1) { + // max - 1 to account for a space + currentLine += `${word} `; } else { - if (!isFacebookBirthday) { - // make copy of start date, separate storage area - endDate = moment(startDate.format("x"), "x"); - } else { - endDate = moment(startDate).add(1, "days"); - } - } - - Log.debug(`start: ${startDate.toDate()}`); - Log.debug(`end:: ${endDate.toDate()}`); - - // Calculate the duration of the event for use with recurring events. - let duration = parseInt(endDate.format("x")) - parseInt(startDate.format("x")); - Log.debug(`duration: ${duration}`); - - // FIXME: Since the parsed json object from node-ical comes with time information - // this check could be removed (?) - if (event.start.length === 8) { - startDate = startDate.startOf("day"); - } - - const title = CalendarUtils.getTitleFromEvent(event); - Log.debug(`title: ${title}`); - - let excluded = false, - dateFilter = null; - - for (let f in config.excludedEvents) { - let filter = config.excludedEvents[f], - testTitle = title.toLowerCase(), - until = null, - useRegex = false, - regexFlags = "g"; - - if (filter instanceof Object) { - if (typeof filter.until !== "undefined") { - until = filter.until; - } - - if (typeof filter.regex !== "undefined") { - useRegex = filter.regex; - } - - // If additional advanced filtering is added in, this section - // must remain last as we overwrite the filter object with the - // filterBy string - if (filter.caseSensitive) { - filter = filter.filterBy; - testTitle = title; - } else if (useRegex) { - filter = filter.filterBy; - testTitle = title; - regexFlags += "i"; - } else { - filter = filter.filterBy.toLowerCase(); - } - } else { - filter = filter.toLowerCase(); - } - - if (CalendarUtils.titleFilterApplies(testTitle, filter, useRegex, regexFlags)) { - if (until) { - dateFilter = until; - } else { - excluded = true; + line++; + if (line > maxTitleLines - 1) { + if (i < words.length) { + currentLine += "…"; } break; } - } - - if (excluded) { - return; - } - - const location = event.location || false; - const geo = event.geo || false; - const description = event.description || false; - - if (typeof event.rrule !== "undefined" && event.rrule !== null && !isFacebookBirthday) { - const rule = event.rrule; - let addedEvents = 0; - - const pastMoment = moment(past); - const futureMoment = moment(future); - - // can cause problems with e.g. birthdays before 1900 - if ((rule.options && rule.origOptions && rule.origOptions.dtstart && rule.origOptions.dtstart.getFullYear() < 1900) || (rule.options && rule.options.dtstart && rule.options.dtstart.getFullYear() < 1900)) { - rule.origOptions.dtstart.setYear(1900); - rule.options.dtstart.setYear(1900); - } - - // For recurring events, get the set of start dates that fall within the range - // of dates we're looking for. - // kblankenship1989 - to fix issue #1798, converting all dates to locale time first, then converting back to UTC time - let pastLocal = 0; - let futureLocal = 0; - if (CalendarUtils.isFullDayEvent(event)) { - Log.debug("fullday"); - // if full day event, only use the date part of the ranges - pastLocal = pastMoment.toDate(); - futureLocal = futureMoment.toDate(); - - Log.debug(`pastLocal: ${pastLocal}`); - Log.debug(`futureLocal: ${futureLocal}`); - } else { - // if we want past events - if (config.includePastEvents) { - // use the calculated past time for the between from - pastLocal = pastMoment.toDate(); - } else { - // otherwise use NOW.. cause we shouldn't use any before now - pastLocal = moment().toDate(); //now - } - futureLocal = futureMoment.toDate(); // future - } - Log.debug(`Search for recurring events between: ${pastLocal} and ${futureLocal}`); - const dates = rule.between(pastLocal, futureLocal, true, limitFunction); - Log.debug(`Title: ${event.summary}, with dates: ${JSON.stringify(dates)}`); - // The "dates" array contains the set of dates within our desired date range range that are valid - // for the recurrence rule. *However*, it's possible for us to have a specific recurrence that - // had its date changed from outside the range to inside the range. For the time being, - // we'll handle this by adding *all* recurrence entries into the set of dates that we check, - // because the logic below will filter out any recurrences that don't actually belong within - // our display range. - // Would be great if there was a better way to handle this. - Log.debug(`event.recurrences: ${event.recurrences}`); - if (event.recurrences !== undefined) { - for (let r in event.recurrences) { - // Only add dates that weren't already in the range we added from the rrule so that - // we don"t double-add those events. - if (moment(new Date(r)).isBetween(pastMoment, futureMoment) !== true) { - dates.push(new Date(r)); - } - } - } - // Loop through the set of date entries to see which recurrences should be added to our event list. - for (let d in dates) { - let date = dates[d]; - // Remove the time information of each date by using its substring, using the following method: - // .toISOString().substring(0,10). - // since the date is given as ISOString with YYYY-MM-DDTHH:MM:SS.SSSZ - // (see https://momentjs.com/docs/#/displaying/as-iso-string/). - const dateKey = date.toISOString().substring(0, 10); - let curEvent = event; - let showRecurrence = true; - // Get the offset of today where we are processing - // This will be the correction, we need to apply. - let nowOffset = new Date().getTimezoneOffset(); - // For full day events, the time might be off from RRULE/Luxon problem - // Get time zone offset of the rule calculated event - let dateoffset = date.getTimezoneOffset(); - - // Reduce the time by the following offset. - Log.debug(` recurring date is ${date} offset is ${dateoffset}`); - - let dh = moment(date).format("HH"); - Log.debug(` recurring date is ${date} offset is ${dateoffset / 60} Hour is ${dh}`); - - if (CalendarUtils.isFullDayEvent(event)) { - Log.debug("Fullday"); - // If the offset is negative (east of GMT), where the problem is - if (dateoffset < 0) { - if (dh < Math.abs(dateoffset / 60)) { - // if the rrule byweekday WAS explicitly set , correct it - // reduce the time by the offset - if (curEvent.rrule.origOptions.byweekday !== undefined) { - // Apply the correction to the date/time to get it UTC relative - date = new Date(date.getTime() - Math.abs(24 * 60) * 60000); - } - // the duration was calculated way back at the top before we could correct the start time.. - // fix it for this event entry - //duration = 24 * 60 * 60 * 1000; - Log.debug(`new recurring date1 fulldate is ${date}`); - } - } else { - // if the timezones are the same, correct date if needed - //if (event.start.tz === moment.tz.guess()) { - // if the date hour is less than the offset - if (24 - dh <= Math.abs(dateoffset / 60)) { - // if the rrule byweekday WAS explicitly set , correct it - if (curEvent.rrule.origOptions.byweekday !== undefined) { - // apply the correction to the date/time back to right day - date = new Date(date.getTime() + Math.abs(24 * 60) * 60000); - } - // the duration was calculated way back at the top before we could correct the start time.. - // fix it for this event entry - //duration = 24 * 60 * 60 * 1000; - Log.debug(`new recurring date2 fulldate is ${date}`); - } - //} - } - } else { - // not full day, but luxon can still screw up the date on the rule processing - // we need to correct the date to get back to the right event for - if (dateoffset < 0) { - // if the date hour is less than the offset - if (dh <= Math.abs(dateoffset / 60)) { - // if the rrule byweekday WAS explicitly set , correct it - if (curEvent.rrule.origOptions.byweekday !== undefined) { - // Reduce the time by t: - // Apply the correction to the date/time to get it UTC relative - date = new Date(date.getTime() - Math.abs(24 * 60) * 60000); - } - // the duration was calculated way back at the top before we could correct the start time.. - // fix it for this event entry - //duration = 24 * 60 * 60 * 1000; - Log.debug(`new recurring date1 is ${date}`); - } - } else { - // if the timezones are the same, correct date if needed - //if (event.start.tz === moment.tz.guess()) { - // if the date hour is less than the offset - if (24 - dh <= Math.abs(dateoffset / 60)) { - // if the rrule byweekday WAS explicitly set , correct it - if (curEvent.rrule.origOptions.byweekday !== undefined) { - // apply the correction to the date/time back to right day - date = new Date(date.getTime() + Math.abs(24 * 60) * 60000); - } - // the duration was calculated way back at the top before we could correct the start time.. - // fix it for this event entry - //duration = 24 * 60 * 60 * 1000; - Log.debug(`new recurring date2 is ${date}`); - } - //} - } - } - startDate = moment(date); - Log.debug(`Corrected startDate: ${startDate.toDate()}`); - - let adjustDays = CalendarUtils.calculateTimezoneAdjustment(event, date); - - // For each date that we're checking, it's possible that there is a recurrence override for that one day. - if (curEvent.recurrences !== undefined && curEvent.recurrences[dateKey] !== undefined) { - // We found an override, so for this recurrence, use a potentially different title, start date, and duration. - curEvent = curEvent.recurrences[dateKey]; - startDate = moment(curEvent.start); - duration = parseInt(moment(curEvent.end).format("x")) - parseInt(startDate.format("x")); - } - // If there's no recurrence override, check for an exception date. Exception dates represent exceptions to the rule. - else if (curEvent.exdate !== undefined && curEvent.exdate[dateKey] !== undefined) { - // This date is an exception date, which means we should skip it in the recurrence pattern. - showRecurrence = false; - } - Log.debug(`duration: ${duration}`); - - endDate = moment(parseInt(startDate.format("x")) + duration, "x"); - if (startDate.format("x") === endDate.format("x")) { - endDate = endDate.endOf("day"); - } - - const recurrenceTitle = CalendarUtils.getTitleFromEvent(curEvent); - - // If this recurrence ends before the start of the date range, or starts after the end of the date range, don"t add - // it to the event list. - if (endDate.isBefore(past) || startDate.isAfter(future)) { - showRecurrence = false; - } - - if (CalendarUtils.timeFilterApplies(now, endDate, dateFilter)) { - showRecurrence = false; - } - - if (showRecurrence === true) { - Log.debug(`saving event: ${description}`); - addedEvents++; - newEvents.push({ - title: recurrenceTitle, - startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"), - endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"), - fullDayEvent: CalendarUtils.isFullDayEvent(event), - recurringEvent: true, - class: event.class, - firstYear: event.start.getFullYear(), - location: location, - geo: geo, - description: description - }); - } - } - // End recurring event parsing. - } else { - // Single event. - const fullDayEvent = isFacebookBirthday ? true : CalendarUtils.isFullDayEvent(event); - // Log.debug("full day event") - - if (config.includePastEvents) { - // Past event is too far in the past, so skip. - if (endDate < past) { - return; - } + if (currentLine.length > 0) { + temp += `${currentLine}
${word} `; } else { - // It's not a fullday event, and it is in the past, so skip. - if (!fullDayEvent && endDate < new Date()) { - return; - } - - // It's a fullday event, and it is before today, So skip. - if (fullDayEvent && endDate <= today) { - return; - } - } - - // It exceeds the maximumNumberOfDays limit, so skip. - if (startDate > future) { - return; - } - - if (CalendarUtils.timeFilterApplies(now, endDate, dateFilter)) { - return; - } - - // if the start and end are the same, then make end the 'end of day' value (start is at 00:00:00) - if (fullDayEvent && startDate.format("x") === endDate.format("x")) { - endDate = endDate.endOf("day"); + temp += `${word}
`; } - // get correction for date saving and dst change between now and then - let adjustDays = CalendarUtils.calculateTimezoneAdjustment(event, startDate.toDate()); - // Every thing is good. Add it to the list. - newEvents.push({ - title: title, - startDate: (adjustDays ? (adjustDays > 0 ? startDate.add(adjustDays, "hours") : startDate.subtract(Math.abs(adjustDays), "hours")) : startDate).format("x"), - endDate: (adjustDays ? (adjustDays > 0 ? endDate.add(adjustDays, "hours") : endDate.subtract(Math.abs(adjustDays), "hours")) : endDate).format("x"), - fullDayEvent: fullDayEvent, - class: event.class, - location: location, - geo: geo, - description: description - }); + currentLine = ""; } } - }); - newEvents.sort(function (a, b) { - return a.startDate - b.startDate; - }); - - return newEvents; - }, - - /** - * Lookup iana tz from windows - * - * @param {string} msTZName the timezone name to lookup - * @returns {string|null} the iana name or null of none is found - */ - getIanaTZFromMS: function (msTZName) { - // Get hash entry - const he = zoneTable[msTZName]; - // If found return iana name, else null - return he ? he.iana[0] : null; - }, - - /** - * Gets the title from the event. - * - * @param {object} event The event object to check. - * @returns {string} The title of the event, or "Event" if no title is found. - */ - getTitleFromEvent: function (event) { - let title = "Event"; - if (event.summary) { - title = typeof event.summary.val !== "undefined" ? event.summary.val : event.summary; - } else if (event.description) { - title = event.description; - } - - return title; - }, - - /** - * Checks if an event is a fullday event. - * - * @param {object} event The event object to check. - * @returns {boolean} True if the event is a fullday event, false otherwise - */ - isFullDayEvent: function (event) { - if (event.start.length === 8 || event.start.dateOnly || event.datetype === "date") { - return true; - } - - const start = event.start || 0; - const startDate = new Date(start); - const end = event.end || 0; - if ((end - start) % (24 * 60 * 60 * 1000) === 0 && startDate.getHours() === 0 && startDate.getMinutes() === 0) { - // Is 24 hours, and starts on the middle of the night. - return true; - } - - return false; - }, - - /** - * Determines if the user defined time filter should apply - * - * @param {Date} now Date object using previously created object for consistency - * @param {Moment} endDate Moment object representing the event end date - * @param {string} filter The time to subtract from the end date to determine if an event should be shown - * @returns {boolean} True if the event should be filtered out, false otherwise - */ - timeFilterApplies: function (now, endDate, filter) { - if (filter) { - const until = filter.split(" "), - value = parseInt(until[0]), - increment = until[1].slice(-1) === "s" ? until[1] : `${until[1]}s`, // Massage the data for moment js - filterUntil = moment(endDate.format()).subtract(value, increment); - - return now < filterUntil.format("x"); + return (temp + currentLine).trim(); + } else { + if (maxLength && typeof maxLength === "number" && string.length > maxLength) { + return `${string.trim().slice(0, maxLength)}…`; + } else { + return string.trim(); + } } - - return false; }, /** - * Determines if the user defined title filter should apply - * - * @param {string} title the title of the event - * @param {string} filter the string to look for, can be a regex also - * @param {boolean} useRegex true if a regex should be used, otherwise it just looks for the filter as a string - * @param {string} regexFlags flags that should be applied to the regex - * @returns {boolean} True if the title should be filtered out, false otherwise + * Transforms the title of an event for usage. + * Replaces parts of the text as defined in config.titleReplace. + * Shortens title based on config.maxTitleLength and config.wrapEvents + * @param {string} title The title to transform. + * @param {object} titleReplace Pairs of strings to be replaced in the title + * @returns {string} The transformed title. */ - titleFilterApplies: function (title, filter, useRegex, regexFlags) { - if (useRegex) { - // Assume if leading slash, there is also trailing slash - if (filter[0] === "/") { - // Strip leading and trailing slashes - filter = filter.substr(1).slice(0, -1); + titleTransform: function (title, titleReplace) { + let transformedTitle = title; + for (let needle in titleReplace) { + const replacement = titleReplace[needle]; + + const regParts = needle.match(/^\/(.+)\/([gim]*)$/); + if (regParts) { + // the parsed pattern is a regexp. + needle = new RegExp(regParts[1], regParts[2]); } - filter = new RegExp(filter, regexFlags); - - return filter.test(title); - } else { - return title.includes(filter); + transformedTitle = transformedTitle.replace(needle, replacement); } + return transformedTitle; } }; diff --git a/modules/default/calendar/node_helper.js b/modules/default/calendar/node_helper.js index 08e6158bda..05d4d4577f 100644 --- a/modules/default/calendar/node_helper.js +++ b/modules/default/calendar/node_helper.js @@ -33,7 +33,6 @@ module.exports = NodeHelper.create({ /** * Creates a fetcher for a new url if it doesn't exist yet. * Otherwise it reuses the existing one. - * * @param {string} url The url of the calendar * @param {number} fetchInterval How often does the calendar needs to be fetched in ms * @param {string[]} excludedEvents An array of words / phrases from event titles that will be excluded from being shown. diff --git a/modules/default/clock/clock.js b/modules/default/clock/clock.js index 595057c491..c063d4dc88 100644 --- a/modules/default/clock/clock.js +++ b/modules/default/clock/clock.js @@ -1,4 +1,4 @@ -/* global SunCalc */ +/* global SunCalc, formatTime */ /* MagicMirror² * Module: Clock @@ -169,21 +169,6 @@ Module.register("clock", { digitalWrapper.appendChild(timeWrapper); } - /** - * Format the time according to the config - * - * @param {object} config The config of the module - * @param {object} time time to format - * @returns {string} The formatted time string - */ - function formatTime(config, time) { - let formatString = `${hourSymbol}:mm`; - if (config.showPeriod && config.timeFormat !== 24) { - formatString += config.showPeriodUpper ? "A" : "a"; - } - return moment(time).format(formatString); - } - /**************************************************************** * Create wrappers for Sun Times, only if specified in config */ @@ -296,9 +281,14 @@ Module.register("clock", { */ if (this.config.displayType === "analog") { // Display only an analog clock - if (this.config.analogShowDate === "top") { + if (this.config.showDate) { + // Add date to the analog clock + dateWrapper.innerHTML = now.format(this.config.dateFormat); + wrapper.appendChild(dateWrapper); + } + if (this.config.analogShowDate === "bottom") { wrapper.classList.add("clock-grid-bottom"); - } else if (this.config.analogShowDate === "bottom") { + } else if (this.config.analogShowDate === "top") { wrapper.classList.add("clock-grid-top"); } wrapper.appendChild(analogWrapper); diff --git a/modules/default/compliments/compliments.js b/modules/default/compliments/compliments.js index 555bfc4678..a905581bd0 100644 --- a/modules/default/compliments/compliments.js +++ b/modules/default/compliments/compliments.js @@ -52,7 +52,6 @@ Module.register("compliments", { /** * Generate a random index for a list of compliments. - * * @param {string[]} compliments Array with compliments. * @returns {number} a random index of given array */ @@ -78,7 +77,6 @@ Module.register("compliments", { /** * Retrieve an array of compliments for the time of the day. - * * @returns {string[]} array with compliments for the time of the day. */ complimentArray: function () { @@ -115,7 +113,6 @@ Module.register("compliments", { /** * Retrieve a file from the local filesystem - * * @returns {Promise} Resolved when the file is loaded */ loadComplimentFile: async function () { @@ -127,7 +124,6 @@ Module.register("compliments", { /** * Retrieve a random compliment. - * * @returns {string} a compliment */ getRandomCompliment: function () { diff --git a/modules/default/newsfeed/newsfeed.js b/modules/default/newsfeed/newsfeed.js index 320c5a5dc8..eee0b44a7b 100644 --- a/modules/default/newsfeed/newsfeed.js +++ b/modules/default/newsfeed/newsfeed.js @@ -179,7 +179,6 @@ Module.register("newsfeed", { /** * Generate an ordered list of items for this configured module. - * * @param {object} feeds An object with feeds returned by the node helper. */ generateFeed: function (feeds) { @@ -272,7 +271,6 @@ Module.register("newsfeed", { /** * Check if this module is configured to show this feed. - * * @param {string} feedUrl Url of the feed to check. * @returns {boolean} True if it is subscribed, false otherwise */ @@ -287,7 +285,6 @@ Module.register("newsfeed", { /** * Returns title for the specific feed url. - * * @param {string} feedUrl Url of the feed * @returns {string} The title of the feed */ diff --git a/modules/default/newsfeed/newsfeedfetcher.js b/modules/default/newsfeed/newsfeedfetcher.js index 039a3ea587..51d38f83fb 100644 --- a/modules/default/newsfeed/newsfeedfetcher.js +++ b/modules/default/newsfeed/newsfeedfetcher.js @@ -14,7 +14,6 @@ const NodeHelper = require("node_helper"); /** * Responsible for requesting an update on the set interval and broadcasting the data. - * * @param {string} url URL of the news feed. * @param {number} reloadInterval Reload interval in milliseconds. * @param {string} encoding Encoding of the feed. @@ -25,12 +24,13 @@ const NodeHelper = require("node_helper"); const NewsfeedFetcher = function (url, reloadInterval, encoding, logFeedWarnings, useCorsProxy) { let reloadTimer = null; let items = []; + let reloadIntervalMS = reloadInterval; let fetchFailedCallback = function () {}; let itemsReceivedCallback = function () {}; - if (reloadInterval < 1000) { - reloadInterval = 1000; + if (reloadIntervalMS < 1000) { + reloadIntervalMS = 1000; } /* private methods */ @@ -89,9 +89,9 @@ const NewsfeedFetcher = function (url, reloadInterval, encoding, logFeedWarnings try { // 86400000 = 24 hours is mentioned in the docs as maximum value: const ttlms = Math.min(minutes * 60 * 1000, 86400000); - if (ttlms > reloadInterval) { - reloadInterval = ttlms; - Log.info(`Newsfeed-Fetcher: reloadInterval set to ttl=${reloadInterval} for url ${url}`); + if (ttlms > reloadIntervalMS) { + reloadIntervalMS = ttlms; + Log.info(`Newsfeed-Fetcher: reloadInterval set to ttl=${reloadIntervalMS} for url ${url}`); } } catch (error) { Log.warn(`Newsfeed-Fetcher: feed ttl is no valid integer=${minutes} for url ${url}`); @@ -129,19 +129,18 @@ const NewsfeedFetcher = function (url, reloadInterval, encoding, logFeedWarnings clearTimeout(reloadTimer); reloadTimer = setTimeout(function () { fetchNews(); - }, reloadInterval); + }, reloadIntervalMS); }; /* public methods */ /** * Update the reload interval, but only if we need to increase the speed. - * * @param {number} interval Interval for the update in milliseconds. */ this.setReloadInterval = function (interval) { - if (interval > 1000 && interval < reloadInterval) { - reloadInterval = interval; + if (interval > 1000 && interval < reloadIntervalMS) { + reloadIntervalMS = interval; } }; diff --git a/modules/default/newsfeed/node_helper.js b/modules/default/newsfeed/node_helper.js index 534b702033..64ba59290a 100644 --- a/modules/default/newsfeed/node_helper.js +++ b/modules/default/newsfeed/node_helper.js @@ -26,7 +26,6 @@ module.exports = NodeHelper.create({ /** * Creates a fetcher for a new feed if it doesn't exist yet. * Otherwise it reuses the existing one. - * * @param {object} feed The feed object * @param {object} config The configuration object */ diff --git a/modules/default/updatenotification/git_helper.js b/modules/default/updatenotification/git_helper.js index b4e0299c18..3628dc49d7 100644 --- a/modules/default/updatenotification/git_helper.js +++ b/modules/default/updatenotification/git_helper.js @@ -9,6 +9,7 @@ const BASE_DIR = path.normalize(`${__dirname}/../../../`); class GitHelper { constructor() { this.gitRepos = []; + this.gitResultList = []; } getRefRegex(branch) { @@ -93,18 +94,21 @@ class GitHelper { // ## develop...origin/develop // ## master...origin/master [behind 8] // ## master...origin/master [ahead 8, behind 1] + // ## HEAD (no branch) status = status.match(/## (.*)\.\.\.([^ ]*)(?: .*behind (\d+))?/); // examples for status: // [ '## develop...origin/develop', 'develop', 'origin/develop' ] // [ '## master...origin/master [behind 8]', 'master', 'origin/master', '8' ] // [ '## master...origin/master [ahead 8, behind 1]', 'master', 'origin/master', '1' ] - gitInfo.current = status[1]; - gitInfo.tracking = status[2]; - - if (status[3]) { - // git fetch was already called before so `git status -sb` delivers already the behind number - gitInfo.behind = parseInt(status[3]); - gitInfo.isBehindInStatus = true; + if (status) { + gitInfo.current = status[1]; + gitInfo.tracking = status[2]; + + if (status[3]) { + // git fetch was already called before so `git status -sb` delivers already the behind number + gitInfo.behind = parseInt(status[3]); + gitInfo.isBehindInStatus = true; + } } return gitInfo; @@ -113,7 +117,7 @@ class GitHelper { async getRepoInfo(repo) { const gitInfo = await this.getStatusInfo(repo); - if (!gitInfo) { + if (!gitInfo || !gitInfo.current) { return; } @@ -171,21 +175,38 @@ class GitHelper { } async getRepos() { - const gitResultList = []; + this.gitResultList = []; for (const repo of this.gitRepos) { try { const gitInfo = await this.getRepoInfo(repo); if (gitInfo) { - gitResultList.push(gitInfo); + this.gitResultList.push(gitInfo); } } catch (e) { Log.error(`Failed to retrieve repo info for ${repo.module}: ${e}`); } } - return gitResultList; + return this.gitResultList; + } + + async checkUpdates() { + var updates = []; + + const allRepos = await this.gitResultList.map((module) => { + return new Promise((resolve) => { + if (module.behind > 0 && module.module !== "MagicMirror") { + Log.info(`Update found for module: ${module.module}`); + updates.push(module); + } + resolve(module); + }); + }); + await Promise.all(allRepos); + + return updates; } } diff --git a/modules/default/updatenotification/node_helper.js b/modules/default/updatenotification/node_helper.js index ae3e03751d..607e145804 100644 --- a/modules/default/updatenotification/node_helper.js +++ b/modules/default/updatenotification/node_helper.js @@ -25,15 +25,25 @@ module.exports = NodeHelper.create({ }, async socketNotificationReceived(notification, payload) { - if (notification === "CONFIG") { - this.config = payload; - } else if (notification === "MODULES") { - // if this is the 1st time thru the update check process - if (!this.updateProcessStarted) { - this.updateProcessStarted = true; - await this.configureModules(payload); - await this.performFetch(); - } + switch (notification) { + case "CONFIG": + this.config = payload; + break; + case "MODULES": + // if this is the 1st time thru the update check process + if (!this.updateProcessStarted) { + this.updateProcessStarted = true; + await this.configureModules(payload); + await this.performFetch(); + } + break; + case "SCAN_UPDATES": + // 1st time of check allows to force new scan + if (this.updateProcessStarted) { + clearTimeout(this.updateTimer); + await this.performFetch(); + } + break; } }, @@ -44,6 +54,11 @@ module.exports = NodeHelper.create({ this.sendSocketNotification("STATUS", repo); } + if (this.config.sendUpdatesNotifications) { + const updates = await this.gitHelper.checkUpdates(); + if (updates.length) this.sendSocketNotification("UPDATES", updates); + } + this.scheduleNextFetch(this.config.updateInterval); }, diff --git a/modules/default/updatenotification/updatenotification.js b/modules/default/updatenotification/updatenotification.js index 602e76f464..73327ec844 100644 --- a/modules/default/updatenotification/updatenotification.js +++ b/modules/default/updatenotification/updatenotification.js @@ -8,7 +8,8 @@ Module.register("updatenotification", { defaults: { updateInterval: 10 * 60 * 1000, // every 10 minutes refreshInterval: 24 * 60 * 60 * 1000, // one day - ignoreModules: [] + ignoreModules: [], + sendUpdatesNotifications: false }, suspended: false, @@ -33,15 +34,25 @@ Module.register("updatenotification", { }, notificationReceived(notification) { - if (notification === "DOM_OBJECTS_CREATED") { - this.sendSocketNotification("CONFIG", this.config); - this.sendSocketNotification("MODULES", Object.keys(Module.definitions)); + switch (notification) { + case "DOM_OBJECTS_CREATED": + this.sendSocketNotification("CONFIG", this.config); + this.sendSocketNotification("MODULES", Object.keys(Module.definitions)); + break; + case "SCAN_UPDATES": + this.sendSocketNotification("SCAN_UPDATES"); + break; } }, socketNotificationReceived(notification, payload) { - if (notification === "STATUS") { - this.updateUI(payload); + switch (notification) { + case "STATUS": + this.updateUI(payload); + break; + case "UPDATES": + this.sendNotification("UPDATES", payload); + break; } }, diff --git a/modules/default/utils.js b/modules/default/utils.js index 604ab0431f..e60d96ea87 100644 --- a/modules/default/utils.js +++ b/modules/default/utils.js @@ -1,6 +1,5 @@ /** * A function to make HTTP requests via the server to avoid CORS-errors. - * * @param {string} url the url to fetch from * @param {string} type what contenttype to expect in the response, can be "json" or "xml" * @param {boolean} useCorsProxy A flag to indicate @@ -10,12 +9,14 @@ */ async function performWebRequest(url, type = "json", useCorsProxy = false, requestHeaders = undefined, expectedResponseHeaders = undefined) { const request = {}; + let requestUrl; if (useCorsProxy) { - url = getCorsUrl(url, requestHeaders, expectedResponseHeaders); + requestUrl = getCorsUrl(url, requestHeaders, expectedResponseHeaders); } else { + requestUrl = url; request.headers = getHeadersToSend(requestHeaders); } - const response = await fetch(url, request); + const response = await fetch(requestUrl, request); const data = await response.text(); if (type === "xml") { @@ -33,7 +34,6 @@ async function performWebRequest(url, type = "json", useCorsProxy = false, reque /** * Gets a URL that will be used when calling the CORS-method on the server. - * * @param {string} url the url to fetch from * @param {Array.<{name: string, value:string}>} requestHeaders the HTTP headers to send * @param {Array.} expectedResponseHeaders the expected HTTP headers to receive @@ -64,7 +64,6 @@ const getCorsUrl = function (url, requestHeaders, expectedResponseHeaders) { /** * Gets the part of the CORS URL that represents the HTTP headers to send. - * * @param {Array.<{name: string, value:string}>} requestHeaders the HTTP headers to send * @returns {string} to be used as request-headers component in CORS URL. */ @@ -85,7 +84,6 @@ const getRequestHeaderString = function (requestHeaders) { /** * Gets headers and values to attach to the web request. - * * @param {Array.<{name: string, value:string}>} requestHeaders the HTTP headers to send * @returns {object} An object specifying name and value of the headers. */ @@ -102,7 +100,6 @@ const getHeadersToSend = (requestHeaders) => { /** * Gets the part of the CORS URL that represents the expected HTTP headers to receive. - * * @param {Array.} expectedResponseHeaders the expected HTTP headers to receive * @returns {string} to be used as the expected HTTP-headers component in CORS URL. */ @@ -123,7 +120,6 @@ const getExpectedResponseHeadersString = function (expectedResponseHeaders) { /** * Gets the values for the expected headers from the response. - * * @param {Array.} expectedResponseHeaders the expected HTTP headers to receive * @param {Response} response the HTTP response * @returns {string} to be used as the expected HTTP-headers component in CORS URL. @@ -141,7 +137,36 @@ const getHeadersFromResponse = (expectedResponseHeaders, response) => { return responseHeaders; }; +/** + * Format the time according to the config + * @param {object} config The config of the module + * @param {object} time time to format + * @returns {string} The formatted time string + */ +const formatTime = (config, time) => { + let date = moment(time); + + if (config.timezone) { + date = date.tz(config.timezone); + } + + if (config.timeFormat !== 24) { + if (config.showPeriod) { + if (config.showPeriodUpper) { + return date.format("h:mm A"); + } else { + return date.format("h:mm a"); + } + } else { + return date.format("h:mm"); + } + } + + return date.format("HH:mm"); +}; + if (typeof module !== "undefined") module.exports = { - performWebRequest + performWebRequest, + formatTime }; diff --git a/modules/default/weather/current.njk b/modules/default/weather/current.njk index df673afeef..09781db738 100644 --- a/modules/default/weather/current.njk +++ b/modules/default/weather/current.njk @@ -28,6 +28,12 @@ {% endif %} {% endif %} + {% if config.showUVIndex %} + +
+ {{ current.uv_index }} + + {% endif %} {% endif %}
@@ -61,12 +67,12 @@ {{ "FEELS" | translate({DEGREE: current.feelsLike() | roundValue | unit("temperature") | decimalSymbol }) }}
{% endif %} - {% if config.showPrecipitationAmount and current.precipitationAmount %} + {% if config.showPrecipitationAmount and current.precipitationAmount %} {{ "PRECIP_AMOUNT" | translate }} {{ current.precipitationAmount | unit("precip", current.precipitationUnits) }}
{% endif %} - {% if config.showPrecipitationProbability and current.precipitationProbability %} + {% if config.showPrecipitationProbability and current.precipitationProbability %} {{ "PRECIP_POP" | translate }} {{ current.precipitationProbability }}% diff --git a/modules/default/weather/forecast.njk b/modules/default/weather/forecast.njk index 0ea390f0a5..af5825e3e2 100644 --- a/modules/default/weather/forecast.njk +++ b/modules/default/weather/forecast.njk @@ -32,6 +32,12 @@ {{ f.precipitationProbability | unit("precip", "%") }} {% endif %} + {% if config.showUVIndex %} + + {{ f.uv_index }} + + + {% endif %} {% set currentStep = currentStep + 1 %} {% endfor %} diff --git a/modules/default/weather/hourly.njk b/modules/default/weather/hourly.njk index a0699fab39..51fb67d52e 100644 --- a/modules/default/weather/hourly.njk +++ b/modules/default/weather/hourly.njk @@ -10,6 +10,14 @@ {{ hour.temperature | roundValue | unit("temperature") }} + {% if config.showUVIndex %} + + {% if hour.uv_index!=0 %} + {{ hour.uv_index }} + + {% endif %} + + {% endif %} {% if config.showPrecipitationAmount %} {{ hour.precipitationAmount | unit("precip", hour.precipitationUnits) }} diff --git a/modules/default/weather/providers/envcanada.js b/modules/default/weather/providers/envcanada.js index 39ddba2a1c..4c0bf02f5b 100644 --- a/modules/default/weather/providers/envcanada.js +++ b/modules/default/weather/providers/envcanada.js @@ -384,7 +384,7 @@ WeatherProvider.register("envcanada", { const foreTime = moment(hourGroup[stepHour].getAttribute("dateTimeUTC"), "YYYYMMDDhhmmss"); const currTime = foreTime.add(hourOffset, "hours"); - weather.date = moment.unix(currTime); + weather.date = moment(currTime); // Capture the temperature @@ -505,9 +505,9 @@ WeatherProvider.register("envcanada", { } // Check Today element for POP - - if (foreGroup[today].querySelector("abbreviatedForecast pop").textContent > 0) { - weather.precipitationProbability = foreGroup[today].querySelector("abbreviatedForecast pop").textContent; + const precipPOP = foreGroup[today].querySelector("abbreviatedForecast pop").textContent * 1.0; + if (precipPOP > 0) { + weather.precipitationProbability = precipPOP; } }, diff --git a/modules/default/weather/providers/openmeteo.js b/modules/default/weather/providers/openmeteo.js index 9d95aea1f7..79d7d1fadb 100644 --- a/modules/default/weather/providers/openmeteo.js +++ b/modules/default/weather/providers/openmeteo.js @@ -76,6 +76,10 @@ WeatherProvider.register("openmeteo", { "et0_fao_evapotranspiration", // Total precipitation (rain, showers, snow) sum of the preceding hour "precipitation", + // Precipitation Probability + "precipitation_probability", + // UV index + "uv_index", // Snowfall amount of the preceding hour in centimeters. For the water equivalent in millimeter, divide by 7. E.g. 7 cm snow = 10 mm precipitation water equivalent "snowfall", // Rain from large scale weather systems of the preceding hour in millimeter @@ -130,6 +134,8 @@ WeatherProvider.register("openmeteo", { "winddirection_10m_dominant", // The sum of solar radiation on a given day in Megajoules "shortwave_radiation_sum", + //UV Index + "uv_index_max", // Daily sum of ET₀ Reference Evapotranspiration of a well watered grass field "et0_fao_evapotranspiration" ], @@ -194,7 +200,6 @@ WeatherProvider.register("openmeteo", { /** * Overrides method for setting config to check if endpoint is correct for hourly - * * @param {object} config The configuration object */ setConfig(config) { @@ -382,6 +387,8 @@ WeatherProvider.register("openmeteo", { currentWeather.rain = parseFloat(weather.hourly[h].rain); currentWeather.snow = parseFloat(weather.hourly[h].snowfall * 10); currentWeather.precipitationAmount = parseFloat(weather.hourly[h].precipitation); + currentWeather.precipitationProbability = parseFloat(weather.hourly[h].precipitation_probability); + currentWeather.uv_index = parseFloat(weather.hourly[h].uv_index); return currentWeather; }, @@ -405,6 +412,8 @@ WeatherProvider.register("openmeteo", { currentWeather.rain = parseFloat(weather.rain_sum); currentWeather.snow = parseFloat(weather.snowfall_sum * 10); currentWeather.precipitationAmount = parseFloat(weather.precipitation_sum); + currentWeather.precipitationProbability = parseFloat(weather.precipitation_probability); + currentWeather.uv_index = parseFloat(weather.uv_index_max); days.push(currentWeather); }); @@ -438,6 +447,8 @@ WeatherProvider.register("openmeteo", { currentWeather.rain = parseFloat(weather.rain); currentWeather.snow = parseFloat(weather.snowfall * 10); currentWeather.precipitationAmount = parseFloat(weather.precipitation); + currentWeather.precipitationProbability = parseFloat(weather.precipitation_probability); + currentWeather.uv_index = parseFloat(weather.uv_index); hours.push(currentWeather); }); diff --git a/modules/default/weather/providers/openweathermap.js b/modules/default/weather/providers/openweathermap.js index d1bd378c55..5d5670ddb4 100644 --- a/modules/default/weather/providers/openweathermap.js +++ b/modules/default/weather/providers/openweathermap.js @@ -90,7 +90,6 @@ WeatherProvider.register("openweathermap", { /** * Overrides method for setting config to check if endpoint is correct for hourly - * * @param {object} config The configuration object */ setConfig(config) { @@ -435,6 +434,7 @@ WeatherProvider.register("openweathermap", { } else if (this.firstEvent && this.firstEvent.location) { params += `q=${this.firstEvent.location}`; } else { + // TODO hide doesnt exist! this.hide(this.config.animationSpeed, { lockString: this.identifier }); return; } diff --git a/modules/default/weather/providers/smhi.js b/modules/default/weather/providers/smhi.js index 0115bcf5bb..0cdd85f1a9 100644 --- a/modules/default/weather/providers/smhi.js +++ b/modules/default/weather/providers/smhi.js @@ -69,7 +69,6 @@ WeatherProvider.register("smhi", { /** * Overrides method for setting config with checks for the precipitationValue being unset or invalid - * * @param {object} config The configuration object */ setConfig(config) { @@ -82,7 +81,6 @@ WeatherProvider.register("smhi", { /** * Of all the times returned find out which one is closest to the current time, should be the first if the data isn't old. - * * @param {object[]} times Array of time objects * @returns {object} The weatherdata closest to the current time */ @@ -100,7 +98,6 @@ WeatherProvider.register("smhi", { /** * Get the forecast url for the configured coordinates - * * @returns {string} the url for the specified coordinates */ getURL() { @@ -115,7 +112,6 @@ WeatherProvider.register("smhi", { /** * Calculates the apparent temperature based on known atmospheric data. - * * @param {object} weatherData Weatherdata to use for the calculation * @returns {number} The apparent temperature */ @@ -132,7 +128,6 @@ WeatherProvider.register("smhi", { * Converts the returned data into a WeatherObject with required properties set for both current weather and forecast. * The returned units is always in metric system. * Requires coordinates to determine if its daytime or nighttime to know which icon to use and also to set sunrise and sunset. - * * @param {object} weatherData Weatherdata to convert * @param {object} coordinates Coordinates of the locations of the weather * @returns {WeatherObject} The converted weatherdata at the specified location @@ -178,7 +173,6 @@ WeatherProvider.register("smhi", { /** * Takes all the data points and converts it to one WeatherObject per day. - * * @param {object[]} allWeatherData Array of weatherdata * @param {object} coordinates Coordinates of the locations of the weather * @param {string} groupBy The interval to use for grouping the data (day, hour) @@ -230,7 +224,6 @@ WeatherProvider.register("smhi", { /** * Resolve coordinates from the response data (probably preferably to use * this if it's not matching the config values exactly) - * * @param {object} data Response data from the weather service * @returns {{lon, lat}} the lat/long coordinates of the data */ @@ -241,7 +234,6 @@ WeatherProvider.register("smhi", { /** * The distance between the data points is increasing in the data the more distant the prediction is. * Find these gaps and fill them with the previous hours data to make the data returned a complete set. - * * @param {object[]} data Response data from the weather service * @returns {object[]} Given data with filled gaps */ @@ -263,7 +255,6 @@ WeatherProvider.register("smhi", { /** * Helper method to get a property from the returned data set. - * * @param {object} currentWeatherData Weatherdata to get from * @param {string} name The name of the property * @returns {*} The value of the property in the weatherdata @@ -276,7 +267,6 @@ WeatherProvider.register("smhi", { * Map the icon value from SMHI to an icon that MagicMirror² understands. * Uses different icons depending on if its daytime or nighttime. * SMHI's description of what the numeric value means is the comment after the case. - * * @param {number} input The SMHI icon value * @param {boolean} isDayTime True if the icon should be for daytime, false for nighttime * @returns {string} The icon name for the MagicMirror diff --git a/modules/default/weather/providers/ukmetoffice.js b/modules/default/weather/providers/ukmetoffice.js index 8f03cbe6af..49c7d80c24 100644 --- a/modules/default/weather/providers/ukmetoffice.js +++ b/modules/default/weather/providers/ukmetoffice.js @@ -189,7 +189,6 @@ WeatherProvider.register("ukmetoffice", { /** * Generates an url with api parameters based on the config. - * * @param {string} forecastType daily or 3hourly forecast * @returns {string} url */ diff --git a/modules/default/weather/providers/weatherbit.js b/modules/default/weather/providers/weatherbit.js index 7d0468bccb..298d23bab2 100644 --- a/modules/default/weather/providers/weatherbit.js +++ b/modules/default/weather/providers/weatherbit.js @@ -65,7 +65,6 @@ WeatherProvider.register("weatherbit", { /** * Overrides method for setting config to check if endpoint is correct for hourly - * * @param {object} config The configuration object */ setConfig(config) { diff --git a/modules/default/weather/providers/yr.js b/modules/default/weather/providers/yr.js index 09e2643df1..52de53ba12 100644 --- a/modules/default/weather/providers/yr.js +++ b/modules/default/weather/providers/yr.js @@ -110,13 +110,15 @@ WeatherProvider.register("yr", { this.getWeatherDataFromYr(weatherData?.downloadedAt) .then((weatherData) => { Log.debug("Got weather data from yr."); + let data; if (weatherData) { this.cacheWeatherData(weatherData); + data = weatherData; } else { //Undefined if unchanged - weatherData = this.getWeatherDataFromCache(); + data = this.getWeatherDataFromCache(); } - resolve(weatherData); + resolve(data); }) .catch((err) => { Log.error(err); @@ -266,14 +268,14 @@ WeatherProvider.register("yr", { this.getStellarDataFromYr(today, 2) .then((stellarData) => { if (stellarData) { - stellarData = { + const data = { today: stellarData }; - stellarData.tomorrow = Object.assign({}, stellarData.today); - stellarData.today.date = today; - stellarData.tomorrow.date = tomorrow; - this.cacheStellarData(stellarData); - resolve(stellarData); + data.tomorrow = Object.assign({}, data.today); + data.today.date = today; + data.tomorrow.date = tomorrow; + this.cacheStellarData(data); + resolve(data); } else { Log.error(`Something went wrong when fetching stellar data. Responses: ${stellarData}`); reject(stellarData); diff --git a/modules/default/weather/weather.css b/modules/default/weather/weather.css index c2b7fe5ec1..816f0a9b74 100644 --- a/modules/default/weather/weather.css +++ b/modules/default/weather/weather.css @@ -30,7 +30,8 @@ } .weather .precipitation-amount, -.weather .precipitation-prob { +.weather .precipitation-prob, +.weather .uv-index { padding-left: 20px; padding-right: 0; } diff --git a/modules/default/weather/weather.js b/modules/default/weather/weather.js index 949c11cd24..08f754c270 100644 --- a/modules/default/weather/weather.js +++ b/modules/default/weather/weather.js @@ -1,4 +1,4 @@ -/* global WeatherProvider, WeatherUtils */ +/* global WeatherProvider, WeatherUtils, formatTime */ /* MagicMirror² * Module: Weather @@ -27,6 +27,7 @@ Module.register("weather", { showPeriodUpper: false, showPrecipitationAmount: false, showPrecipitationProbability: false, + showUVIndex: false, showSun: true, showWindDirection: true, showWindDirectionAsArrow: false, @@ -211,50 +212,37 @@ Module.register("weather", { this.nunjucksEnvironment().addFilter( "formatTime", function (date) { - date = moment(date); - - if (this.config.timeFormat !== 24) { - if (this.config.showPeriod) { - if (this.config.showPeriodUpper) { - return date.format("h:mm A"); - } else { - return date.format("h:mm a"); - } - } else { - return date.format("h:mm"); - } - } - - return date.format("HH:mm"); + return formatTime(this.config, date); }.bind(this) ); this.nunjucksEnvironment().addFilter( "unit", function (value, type, valueUnit) { + let formattedValue; if (type === "temperature") { - value = `${this.roundValue(WeatherUtils.convertTemp(value, this.config.tempUnits))}°`; + formattedValue = `${this.roundValue(WeatherUtils.convertTemp(value, this.config.tempUnits))}°`; if (this.config.degreeLabel) { if (this.config.tempUnits === "metric") { - value += "C"; + formattedValue += "C"; } else if (this.config.tempUnits === "imperial") { - value += "F"; + formattedValue += "F"; } else { - value += "K"; + formattedValue += "K"; } } } else if (type === "precip") { if (value === null || isNaN(value) || value === 0 || value.toFixed(2) === "0.00") { - value = ""; + formattedValue = ""; } else { - value = WeatherUtils.convertPrecipitationUnit(value, valueUnit, this.config.units); + formattedValue = WeatherUtils.convertPrecipitationUnit(value, valueUnit, this.config.units); } } else if (type === "humidity") { - value += "%"; + formattedValue = `${value}%`; } else if (type === "wind") { - value = WeatherUtils.convertWind(value, this.config.windUnits); + formattedValue = WeatherUtils.convertWind(value, this.config.windUnits); } - return value; + return formattedValue; }.bind(this) ); diff --git a/modules/default/weather/weatherobject.js b/modules/default/weather/weatherobject.js index 565061da08..9fae88737c 100644 --- a/modules/default/weather/weatherobject.js +++ b/modules/default/weather/weatherobject.js @@ -75,7 +75,6 @@ class WeatherObject { /** * Determines if the sun sets or rises next. Uses the current time and not * the date from the weather-forecast. - * * @param {Moment} date an optional date where you want to get the next * action for. Useful only in tests, defaults to the current time. * @returns {string} "sunset" or "sunrise" @@ -93,7 +92,6 @@ class WeatherObject { /** * Checks if the weatherObject is at dayTime. - * * @returns {boolean} true if it is at dayTime */ isDayTime() { @@ -105,7 +103,6 @@ class WeatherObject { * Update the sunrise / sunset time depending on the location. This can be * used if your provider doesn't provide that data by itself. Then SunCalc * is used here to calculate them according to the location. - * * @param {number} lat latitude * @param {number} lon longitude */ @@ -121,7 +118,6 @@ class WeatherObject { * * Before being handed to other modules, mutable values must be cloned safely. * Especially 'moment' object is not immutable, so original 'date', 'sunrise', 'sunset' could be corrupted or changed by other modules. - * * @returns {object} plained object clone of original weatherObject */ simpleClone() { diff --git a/modules/default/weather/weatherprovider.js b/modules/default/weather/weatherprovider.js index e7bfe5b7d7..662f87befd 100644 --- a/modules/default/weather/weatherprovider.js +++ b/modules/default/weather/weatherprovider.js @@ -113,7 +113,6 @@ const WeatherProvider = Class.extend({ /** * A convenience function to make requests. - * * @param {string} url the url to fetch from * @param {string} type what contenttype to expect in the response, can be "json" or "xml" * @param {Array.<{name: string, value:string}>} requestHeaders the HTTP headers to send @@ -138,7 +137,6 @@ WeatherProvider.providers = []; /** * Static method to register a new weather provider. - * * @param {string} providerIdentifier The name of the weather provider * @param {object} providerDetails The details of the weather provider */ @@ -148,23 +146,22 @@ WeatherProvider.register = function (providerIdentifier, providerDetails) { /** * Static method to initialize a new weather provider. - * * @param {string} providerIdentifier The name of the weather provider * @param {object} delegate The weather module * @returns {object} The new weather provider */ WeatherProvider.initialize = function (providerIdentifier, delegate) { - providerIdentifier = providerIdentifier.toLowerCase(); + const pi = providerIdentifier.toLowerCase(); - const provider = new WeatherProvider.providers[providerIdentifier](); + const provider = new WeatherProvider.providers[pi](); const config = Object.assign({}, provider.defaults, delegate.config); provider.delegate = delegate; provider.setConfig(config); - provider.providerIdentifier = providerIdentifier; + provider.providerIdentifier = pi; if (!provider.providerName) { - provider.providerName = providerIdentifier; + provider.providerName = pi; } return provider; diff --git a/modules/default/weather/weatherutils.js b/modules/default/weather/weatherutils.js index 42b5da1ec9..47fea3355d 100644 --- a/modules/default/weather/weatherutils.js +++ b/modules/default/weather/weatherutils.js @@ -7,7 +7,6 @@ const WeatherUtils = { /** * Convert wind (from m/s) to beaufort scale - * * @param {number} speedInMS the windspeed you want to convert * @returns {number} the speed in beaufort */ @@ -25,30 +24,30 @@ const WeatherUtils = { /** * Convert a value in a given unit to a string with a converted * value and a postfix matching the output unit system. - * * @param {number} value - The value to convert. * @param {string} valueUnit - The unit the values has. Default is mm. * @param {string} outputUnit - The unit system (imperial/metric) the return value should have. * @returns {string} - A string with tha value and a unit postfix. */ convertPrecipitationUnit(value, valueUnit, outputUnit) { + if (valueUnit === "%") return `${value.toFixed(0)} ${valueUnit}`; + + let convertedValue = value; + let conversionUnit = valueUnit; if (outputUnit === "imperial") { - if (valueUnit && valueUnit.toLowerCase() === "cm") value = value * 0.3937007874; - else value = value * 0.03937007874; - valueUnit = "in"; + if (valueUnit && valueUnit.toLowerCase() === "cm") convertedValue = convertedValue * 0.3937007874; + else convertedValue = convertedValue * 0.03937007874; + conversionUnit = "in"; } else { - valueUnit = valueUnit ? valueUnit : "mm"; + conversionUnit = valueUnit ? valueUnit : "mm"; } - if (valueUnit === "%") return `${value.toFixed(0)} ${valueUnit}`; - - return `${value.toFixed(2)} ${valueUnit}`; + return `${convertedValue.toFixed(2)} ${conversionUnit}`; }, /** * Convert temp (from degrees C) into imperial or metric unit depending on * your config - * * @param {number} tempInC the temperature in celsius you want to convert * @param {string} unit can be 'imperial' or 'metric' * @returns {number} the converted temperature @@ -59,7 +58,6 @@ const WeatherUtils = { /** * Convert wind speed into another unit. - * * @param {number} windInMS the windspeed in meter/sec you want to convert * @param {string} unit can be 'beaufort', 'kmh', 'knots, 'imperial' (mph) * or 'metric' (mps) diff --git a/package-lock.json b/package-lock.json index 2a6e8a4987..b386bb5006 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,66 +1,74 @@ { "name": "magicmirror", - "version": "2.23.0", + "version": "2.24.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "magicmirror", - "version": "2.23.0", + "version": "2.24.0", "hasInstallScript": true, "license": "MIT", "dependencies": { "colors": "^1.4.0", "console-stamp": "^3.1.1", - "digest-fetch": "^2.0.1", + "digest-fetch": "^2.0.3", "envsub": "^4.1.0", - "eslint": "^8.36.0", + "eslint": "^8.43.0", "express": "^4.18.2", "express-ipfilter": "^1.3.1", "feedme": "^2.0.2", - "helmet": "^6.0.1", + "helmet": "^7.0.0", "iconv-lite": "^0.6.3", "luxon": "^1.28.1", - "module-alias": "^2.2.2", + "module-alias": "^2.2.3", "moment": "^2.29.4", - "node-fetch": "^2.6.9", - "node-ical": "^0.16.0", - "socket.io": "^4.6.1" + "node-fetch": "^2.6.12", + "node-ical": "^0.16.1", + "socket.io": "^4.7.1" }, "devDependencies": { "eslint-config-prettier": "^8.8.0", "eslint-plugin-import": "^2.27.5", - "eslint-plugin-jest": "^27.2.1", - "eslint-plugin-jsdoc": "^40.1.0", + "eslint-plugin-jest": "^27.2.2", + "eslint-plugin-jsdoc": "^46.4.2", "eslint-plugin-prettier": "^4.2.1", "express-basic-auth": "^1.2.1", "husky": "^8.0.3", "jest": "^29.5.0", - "jsdom": "^21.1.1", + "jsdom": "^22.1.0", "lodash": "^4.17.21", - "playwright": "^1.32.1", - "prettier": "^2.8.7", + "playwright": "^1.35.1", + "prettier": "^2.8.8", "pretty-quick": "^3.1.3", - "sinon": "^15.0.2", - "stylelint": "^15.3.0", - "stylelint-config-standard": "^31.0.0", + "sinon": "^15.2.0", + "stylelint": "^15.9.0", + "stylelint-config-standard": "^33.0.0", "stylelint-prettier": "^3.0.0", "suncalc": "^1.9.0" }, "engines": { - "node": ">=14" + "node": ">=16" }, "optionalDependencies": { - "electron": "^22.3.4" + "electron": "^25.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==", + "engines": { + "node": ">=0.10.0" } }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { @@ -68,42 +76,42 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "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==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz", + "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz", - "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", + "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.3", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.3", - "@babel/types": "^7.21.3", + "@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", @@ -125,12 +133,12 @@ "dev": true }, "node_modules/@babel/generator": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz", - "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", + "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", "dev": true, "dependencies": { - "@babel/types": "^7.21.3", + "@babel/types": "^7.22.5", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -139,28 +147,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "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, "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", + "@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" @@ -173,151 +167,151 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "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, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "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, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "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, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "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==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "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, "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" + "@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" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "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, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "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, "dependencies": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "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, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "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==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "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==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "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, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "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, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -397,9 +391,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", - "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", + "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, "bin": { "parser": "bin/babel-parser.js" @@ -469,12 +463,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "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.18.6" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -571,12 +565,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "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.19.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -586,33 +580,33 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "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.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@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.21.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz", - "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.3", - "@babel/types": "^7.21.3", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz", + "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==", + "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" }, @@ -630,13 +624,13 @@ } }, "node_modules/@babel/types": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", - "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -650,25 +644,31 @@ "dev": true }, "node_modules/@csstools/css-parser-algorithms": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.0.1.tgz", - "integrity": "sha512-B9/8PmOtU6nBiibJg0glnNktQDZ3rZnGn/7UmDfrm2vMtrdlXO3p7ErE95N0up80IRk9YEtB5jyj/TmQ1WH3dw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.2.0.tgz", + "integrity": "sha512-9BoQ/jSrPq4vv3b9jjLW+PNNv56KlDH5JMx5yASSNrCtvq70FCNZUjXRvbCeR9hYj9ZyhURtqpU/RFIgg6kiOw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "engines": { "node": "^14 || ^16 || >=18" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, "peerDependencies": { - "@csstools/css-tokenizer": "^2.0.0" + "@csstools/css-tokenizer": "^2.1.1" } }, "node_modules/@csstools/css-tokenizer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.0.tgz", - "integrity": "sha512-dtqFyoJBHUxGi9zPZdpCKP1xk8tq6KPHJ/NY4qWXiYo6IcSGwzk3L8x2XzZbbyOyBs9xQARoGveU2AsgLj6D2A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz", + "integrity": "sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA==", "dev": true, "engines": { "node": "^14 || ^16 || >=18" @@ -679,26 +679,32 @@ } }, "node_modules/@csstools/media-query-list-parser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.0.1.tgz", - "integrity": "sha512-X2/OuzEbjaxhzm97UJ+95GrMeT29d1Ib+Pu+paGLuRWZnWRK9sI9r3ikmKXPWGA1C4y4JEdBEFpp9jEqCvLeRA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.1.tgz", + "integrity": "sha512-pUjtFbaKbiFNjJo8pprrIaXLvQvWIlwPiFnRI4sEnc4F0NIGTOsw8kaJSR3CmZAKEvV8QYckovgAnWQC0bgLLQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "engines": { "node": "^14 || ^16 || >=18" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.0.0", - "@csstools/css-tokenizer": "^2.0.0" + "@csstools/css-parser-algorithms": "^2.2.0", + "@csstools/css-tokenizer": "^2.1.1" } }, "node_modules/@csstools/selector-specificity": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.1.1.tgz", - "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", "dev": true, "engines": { "node": "^14 || ^16 || >=18" @@ -708,7 +714,6 @@ "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.4", "postcss-selector-parser": "^6.0.10" } }, @@ -734,17 +739,17 @@ } }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.37.0.tgz", - "integrity": "sha512-hjK0wnsPCYLlF+HHB4R/RbUjOWeLW2SlarB67+Do5WsKILOkmIZvvPJFbtWSmbypxcjpoECLAMzoao0D4Bg5ZQ==", + "version": "0.39.4", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz", + "integrity": "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==", "dev": true, "dependencies": { "comment-parser": "1.3.1", - "esquery": "^1.4.0", + "esquery": "^1.5.0", "jsdoc-type-pratt-parser": "~4.0.0" }, "engines": { - "node": "^14 || ^16 || ^17 || ^18 || ^19" + "node": ">=16" } }, "node_modules/@eslint-community/eslint-utils": { @@ -770,13 +775,13 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz", - "integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.0", + "espree": "^9.5.2", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -792,17 +797,17 @@ } }, "node_modules/@eslint/js": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", - "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", + "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", @@ -1222,13 +1227,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "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, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -1253,21 +1259,27 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "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, "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1319,27 +1331,27 @@ } }, "node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "dependencies": { - "@sinonjs/commons": "^2.0.0" + "@sinonjs/commons": "^3.0.0" } }, "node_modules/@sinonjs/samsam": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", - "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", "dev": true, "dependencies": { "@sinonjs/commons": "^2.0.0", @@ -1389,9 +1401,9 @@ } }, "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==", + "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", @@ -1421,12 +1433,12 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "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.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/cacheable-request": { @@ -1494,9 +1506,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": { @@ -1527,9 +1539,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "16.18.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.18.tgz", - "integrity": "sha512-fwGw1uvQAzabxL1pyoknPlJIF2t7+K90uTqynleKRx24n3lYcxWa3+KByLhgkF8GEAK2c7hC8Ki0RkNM5H15jQ==" + "version": "18.16.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.18.tgz", + "integrity": "sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -1538,9 +1550,9 @@ "dev": true }, "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/responselike": { @@ -1553,9 +1565,9 @@ } }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, "node_modules/@types/stack-utils": { @@ -1565,9 +1577,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", - "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -1589,13 +1601,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.56.0.tgz", - "integrity": "sha512-jGYKyt+iBakD0SA5Ww8vFqGpoV2asSjwt60Gl6YcO8ksQ8s2HlUEyHBMSa38bdLopYqGf7EYQMUIGdT/Luw+sw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", + "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/visitor-keys": "5.56.0" + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1606,9 +1618,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.56.0.tgz", - "integrity": "sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", + "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1619,13 +1631,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz", - "integrity": "sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", + "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/visitor-keys": "5.56.0", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1658,9 +1670,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1679,17 +1691,17 @@ "dev": true }, "node_modules/@typescript-eslint/utils": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.56.0.tgz", - "integrity": "sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", + "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", "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.56.0", - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/typescript-estree": "5.56.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/typescript-estree": "5.60.1", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -1739,9 +1751,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -1760,12 +1772,12 @@ "dev": true }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.56.0.tgz", - "integrity": "sha512-1mFdED7u5bZpX6Xxf5N9U2c18sb+8EvU3tyOIj6LQZ5OOvnmj8BVeNNP603OFPm5KkS1a7IvCIcwrdHXaEMG/Q==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", + "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.56.0", + "@typescript-eslint/types": "5.60.1", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1795,9 +1807,9 @@ } }, "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.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", "bin": { "acorn": "bin/acorn" }, @@ -1805,16 +1817,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "dev": true, - "dependencies": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -1823,15 +1825,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -1921,6 +1914,15 @@ "node": ">= 8" } }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "engines": { + "node": ">=14" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2051,9 +2053,9 @@ } }, "node_modules/axios": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", - "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -2267,9 +2269,9 @@ } }, "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": [ { @@ -2279,13 +2281,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" @@ -2318,6 +2324,18 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "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, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -2336,9 +2354,9 @@ } }, "node_modules/cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "optional": true, "dependencies": { "clone-response": "^1.0.2", @@ -2409,9 +2427,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001468", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001468.tgz", - "integrity": "sha512-zgAo8D5kbOyUcRAgSmgyuvBkjrGk5CGYG5TYgFdpQv+ywcyEpo1LOWoG8YmoflGnh+V+UsNuKYedsoYs0hzV5A==", + "version": "1.0.30001509", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001509.tgz", + "integrity": "sha512-2uDDk+TRiTX5hMcUYT/7CSyzMZxjfGu0vAUjS2g0LSD8UoXOv0LtpH4LxGMemsiPq6LCVIUjNwVM0erkOkGCDA==", "dev": true, "funding": [ { @@ -2421,6 +2439,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -2472,9 +2494,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/clarinet": { @@ -2655,9 +2677,9 @@ } }, "node_modules/cosmiconfig": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz", - "integrity": "sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==", + "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", @@ -2946,9 +2968,9 @@ } }, "node_modules/digest-fetch": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-2.0.1.tgz", - "integrity": "sha512-OgzIWveqj8BlQ8hfJv97a9iOzWOgvI5Z3rGAnjkeNpHepHZpD/DHBDJ9mtfDclH5vkbUSGRqNEosYCH1FSO6Pg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-2.0.3.tgz", + "integrity": "sha512-HuTjHQE+wplAR+H8/YGwQjIGR1RQUCEsQcRyp3dZfuuxpSQH4OTm4BkHxyXuzxwmxUrNVzIPf9XkXi8QMJDNwQ==", "dependencies": { "base-64": "^0.1.0", "js-sha256": "^0.9.0", @@ -2997,14 +3019,14 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron": { - "version": "22.3.4", - "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.4.tgz", - "integrity": "sha512-EY/ieC3gnKYUNOQPJSCIbiMBwEnGs/j0yIAUf0pXPK4BRh2nvXTD5d9OdouAIN7bRNLLPgqoTm0uXgZPAWTVkg==", + "version": "25.2.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-25.2.0.tgz", + "integrity": "sha512-I/rhcW2sV2fyiveVSBr2N7v5ZiCtdGY0UiNCDZgk2fpSC+irQjbeh7JT2b4vWmJ2ogOXBjqesrN9XszTIG6DHg==", "hasInstallScript": true, "optional": true, "dependencies": { "@electron/get": "^2.0.0", - "@types/node": "^16.11.26", + "@types/node": "^18.11.18", "extract-zip": "^2.0.1" }, "bin": { @@ -3015,9 +3037,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.333", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.333.tgz", - "integrity": "sha512-YyE8+GKyGtPEP1/kpvqsdhD6rA/TP1DUFDN4uiU/YI52NzDxmwHkEb3qjId8hLBa5siJvG0sfC3O66501jMruQ==", + "version": "1.4.445", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.445.tgz", + "integrity": "sha512-++DB+9VK8SBJwC+X1zlMfJ1tMA3F0ipi39GdEp+x3cV2TyBihqAgad8cNMWtLDEkbH39nlDQP7PfGrDr3Dr7HA==", "dev": true }, "node_modules/emittery": { @@ -3056,9 +3078,9 @@ } }, "node_modules/engine.io": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", - "integrity": "sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.1.tgz", + "integrity": "sha512-mGqhI+D7YxS9KJMppR6Iuo37Ed3abhU8NdfgSvJSDUafQutrN+sPTncJYTyM9+tkhSmWodKtVYGPPHyXJEwEQA==", "dependencies": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", @@ -3068,7 +3090,7 @@ "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", + "engine.io-parser": "~5.1.0", "ws": "~8.11.0" }, "engines": { @@ -3076,9 +3098,9 @@ } }, "node_modules/engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.1.0.tgz", + "integrity": "sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w==", "engines": { "node": ">=10.0.0" } @@ -3112,9 +3134,9 @@ } }, "node_modules/entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", - "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, "engines": { "node": ">=0.12" @@ -3288,89 +3310,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "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/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.36.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", - "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", + "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.1", - "@eslint/js": "8.36.0", - "@humanwhocodes/config-array": "^0.11.8", + "@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", @@ -3379,9 +3328,9 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.5.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", @@ -3389,13 +3338,12 @@ "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "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-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -3450,9 +3398,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "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, "dependencies": { "debug": "^3.2.7" @@ -3526,9 +3474,9 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "27.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.2.1.tgz", - "integrity": "sha512-l067Uxx7ZT8cO9NJuf+eJHvt6bqJyz2Z29wykyEdz/OtmcELQl2MQGQLX8J94O1cSJWAwUSEvCjwjA7KEK3Hmg==", + "version": "27.2.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.2.2.tgz", + "integrity": "sha512-euzbp06F934Z7UDl5ZUaRPLAc9MKjh0rMPERrHT7UhlCEwgb25kBj37TvMgWeHZVkR5I9CayswrpoaqZU1RImw==", "dev": true, "dependencies": { "@typescript-eslint/utils": "^5.10.0" @@ -3538,7 +3486,8 @@ }, "peerDependencies": { "@typescript-eslint/eslint-plugin": "^5.0.0", - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0", + "jest": "*" }, "peerDependenciesMeta": { "@typescript-eslint/eslint-plugin": { @@ -3550,21 +3499,23 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "40.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-40.1.0.tgz", - "integrity": "sha512-ANvrhiu62VlSorARM0hup60VQsS3hNyp0Ca7cnJDj8tpJzM7tNhBVqMVYXSuLzEmqrpwx6aAh+NAN2DdAGG5fQ==", + "version": "46.4.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.2.tgz", + "integrity": "sha512-fmIgOe7irf9otkMtsPjr5P39wC5LzA6aEU/nydfUlc8JaEiS93uhPaxI+x/v5s1Ckm+IZeP3006do2n2ehZcNQ==", "dev": true, "dependencies": { - "@es-joy/jsdoccomment": "~0.37.0", + "@es-joy/jsdoccomment": "~0.39.4", + "are-docs-informative": "^0.0.2", "comment-parser": "1.3.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "esquery": "^1.5.0", - "semver": "^7.3.8", + "is-builtin-module": "^3.2.1", + "semver": "^7.5.1", "spdx-expression-parse": "^3.0.1" }, "engines": { - "node": "^14 || ^16 || ^17 || ^18 || ^19" + "node": ">=16" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" @@ -3583,9 +3534,9 @@ } }, "node_modules/eslint-plugin-jsdoc/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -3625,33 +3576,39 @@ } }, "node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "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-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "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==", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", - "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dependencies": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3882,9 +3839,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, "node_modules/fast-glob": { @@ -4195,12 +4152,13 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" }, "funding": { @@ -4306,9 +4264,9 @@ } }, "node_modules/global-agent/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "optional": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4460,10 +4418,10 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "devOptional": true }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" }, "node_modules/handlebars": { "version": "4.7.7", @@ -4569,11 +4527,11 @@ } }, "node_modules/helmet": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-6.0.1.tgz", - "integrity": "sha512-8wo+VdQhTMVBMCITYZaGTbE4lvlthelPYSvoyNvk4RECTmrVjMerp9RfUOQXZWLvCcAn1pKj7ZRxK4lI9Alrcw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.0.0.tgz", + "integrity": "sha512-MsIgYmdBh460ZZ8cJC81q4XJknjG567wzEmv46WOBblDb6TUd3z8/GhgmsM9pn8g2B80tAJ4m5/d3Bi1KrSUBQ==", "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/hosted-git-info": { @@ -4625,9 +4583,9 @@ "dev": true }, "node_modules/html-tags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", - "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", "dev": true, "engines": { "node": ">=8" @@ -4904,6 +4862,21 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -4916,9 +4889,9 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -5692,9 +5665,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5807,15 +5780,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-sha256": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", @@ -5853,25 +5817,22 @@ } }, "node_modules/jsdom": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-21.1.1.tgz", - "integrity": "sha512-Jjgdmw48RKcdAIQyUD1UdBh2ecH7VqwaXPN3ehoZN6MqgVbMn+lRm1aAT1AsdJRAJpwfa4IpwgzySn61h2qu3w==", + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz", + "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==", "dev": true, "dependencies": { "abab": "^2.0.6", - "acorn": "^8.8.2", - "acorn-globals": "^7.0.0", "cssstyle": "^3.0.0", "data-urls": "^4.0.0", "decimal.js": "^10.4.3", "domexception": "^4.0.0", - "escodegen": "^2.0.0", "form-data": "^4.0.0", "html-encoding-sniffer": "^3.0.0", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.1", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", + "nwsapi": "^2.2.4", "parse5": "^7.1.2", "rrweb-cssom": "^0.6.0", "saxes": "^6.0.0", @@ -5886,7 +5847,7 @@ "xml-name-validator": "^4.0.0" }, "engines": { - "node": ">=14" + "node": ">=16" }, "peerDependencies": { "canvas": "^2.5.0" @@ -6347,9 +6308,9 @@ } }, "node_modules/module-alias": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", - "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz", + "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==" }, "node_modules/moment": { "version": "2.29.4", @@ -6360,9 +6321,9 @@ } }, "node_modules/moment-timezone": { - "version": "0.5.41", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.41.tgz", - "integrity": "sha512-e0jGNZDOHfBXJGz8vR/sIMXvBIGJJcqFjmlg9lmE+5KX1U7/RZNMswfD8nKnNCnQdKTIj50IaRKwl1fvMLyyRg==", + "version": "0.5.43", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", + "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", "dependencies": { "moment": "^2.29.4" }, @@ -6401,10 +6362,16 @@ } }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6443,6 +6410,15 @@ "path-to-regexp": "^1.7.0" } }, + "node_modules/nise/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, "node_modules/nise/node_modules/path-to-regexp": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", @@ -6453,9 +6429,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -6491,11 +6467,11 @@ } }, "node_modules/node-ical": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.16.0.tgz", - "integrity": "sha512-LgLN6gm2D1AIaBQnbAw8nz+lH2ZT08lOSGhpzi3z+f2JDt3rSkKrWCx96URO8RmGxgx2Cojw15OBWZSpL18kag==", + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.16.1.tgz", + "integrity": "sha512-AAlJbvyRlQ5QT3LtYAvveY/gZlvHAIjHR/suQp1YVX/RySCxI/qZcyauRDKv2QSH0zNG0J8iv5T1gv6FadoETA==", "dependencies": { - "axios": "1.3.4", + "axios": "1.4.0", "moment-timezone": "^0.5.31", "rrule": "2.6.4", "uuid": "^9.0.0" @@ -6508,9 +6484,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "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 }, "node_modules/normalize-package-data": { @@ -6541,9 +6517,9 @@ } }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6595,9 +6571,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.5.tgz", + "integrity": "sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==", "dev": true }, "node_modules/object-assign": { @@ -6693,16 +6669,16 @@ } }, "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==", "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" @@ -6872,9 +6848,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" @@ -6945,37 +6921,37 @@ } }, "node_modules/playwright": { - "version": "1.32.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.32.1.tgz", - "integrity": "sha512-GnEizysWMvoqHC3I9l8+4/ZxeLwLNdJJG76xdKGxzOcIZDcw5RSk/FKrFb5CuA+zcLpjIM2p9eR9Z4CuUDkWXg==", + "version": "1.35.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.35.1.tgz", + "integrity": "sha512-NbwBeGJLu5m7VGM0+xtlmLAH9VUfWwYOhUi/lSEDyGg46r1CA9RWlvoc5yywxR9AzQb0mOCm7bWtOXV7/w43ZA==", "dev": true, "hasInstallScript": true, "dependencies": { - "playwright-core": "1.32.1" + "playwright-core": "1.35.1" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/playwright-core": { - "version": "1.32.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.1.tgz", - "integrity": "sha512-KZYUQC10mXD2Am1rGlidaalNGYk3LU1vZqqNk0gT4XPty1jOqgup8KDP8l2CUlqoNKhXM5IfGjWgW37xvGllBA==", + "version": "1.35.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.35.1.tgz", + "integrity": "sha512-pNXb6CQ7OqmGDRspEjlxE49w+4YtR6a3X6mT1hZXeJHWmsEz7SunmvZeiG/+y1yyMZdHnnn73WKYdtV1er0Xyg==", "dev": true, "bin": { - "playwright": "cli.js" + "playwright-core": "cli.js" }, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "version": "8.4.24", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", + "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", "dev": true, "funding": [ { @@ -6985,10 +6961,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -7025,9 +7005,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", - "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -7052,9 +7032,9 @@ } }, "node_modules/prettier": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", - "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -7288,9 +7268,9 @@ } }, "node_modules/pure-rand": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz", - "integrity": "sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", + "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", "dev": true, "funding": [ { @@ -7548,13 +7528,13 @@ } }, "node_modules/regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -7596,12 +7576,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -7648,9 +7628,9 @@ } }, "node_modules/resolve.exports": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.1.tgz", - "integrity": "sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", "dev": true, "engines": { "node": ">=10" @@ -7942,14 +7922,14 @@ "dev": true }, "node_modules/sinon": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.2.tgz", - "integrity": "sha512-PCVP63XZkg0/LOqQH5rEU4LILuvTFMb5tNxTHfs6VUMNnZz2XrnGSTZbAGITjzwQWbl/Bl/8hi4G3zZWjyBwHg==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", + "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^10.0.2", - "@sinonjs/samsam": "^7.0.1", + "@sinonjs/fake-timers": "^10.3.0", + "@sinonjs/samsam": "^8.0.0", "diff": "^5.1.0", "nise": "^5.1.4", "supports-color": "^7.2.0" @@ -7959,15 +7939,6 @@ "url": "https://opencollective.com/sinon" } }, - "node_modules/sinon/node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, "node_modules/sinon/node_modules/diff": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", @@ -8010,16 +7981,17 @@ } }, "node_modules/socket.io": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", - "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.1.tgz", + "integrity": "sha512-W+utHys2w//dhFjy7iQQu9sGd3eokCjGbl2r59tyLqNiJJBdIebn3GAKEXBr3osqHTObJi2die/25bCx2zsaaw==", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", + "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.4.1", + "engine.io": "~6.5.0", "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.1" + "socket.io-parser": "~4.2.4" }, "engines": { "node": ">=10.0.0" @@ -8054,9 +8026,9 @@ } }, "node_modules/socket.io-parser": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", - "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -8305,18 +8277,18 @@ "dev": true }, "node_modules/stylelint": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.3.0.tgz", - "integrity": "sha512-9UYBYk7K9rtlKcTUDZrtntE840sZM00qyYBQHHe7tjwMNUsPsGvR6Fd43IxHEAhRrDLzpy3TVaHb6CReBB3eFg==", + "version": "15.9.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.9.0.tgz", + "integrity": "sha512-sXtAZi64CllWr6A+8ymDWnlIaYwuAa7XRmGnJxLQXFNnLjd3Izm4HAD+loKVaZ7cpK6SLxhAUX1lwPJKGCn0mg==", "dev": true, "dependencies": { - "@csstools/css-parser-algorithms": "^2.0.1", - "@csstools/css-tokenizer": "^2.1.0", - "@csstools/media-query-list-parser": "^2.0.1", - "@csstools/selector-specificity": "^2.1.1", + "@csstools/css-parser-algorithms": "^2.2.0", + "@csstools/css-tokenizer": "^2.1.1", + "@csstools/media-query-list-parser": "^2.1.0", + "@csstools/selector-specificity": "^2.2.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", - "cosmiconfig": "^8.1.0", + "cosmiconfig": "^8.2.0", "css-functions-list": "^3.1.0", "css-tree": "^2.3.1", "debug": "^4.3.4", @@ -8326,7 +8298,7 @@ "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", - "html-tags": "^3.2.0", + "html-tags": "^3.3.1", "ignore": "^5.2.4", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", @@ -8337,11 +8309,11 @@ "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.21", + "postcss": "^8.4.24", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.11", + "postcss-selector-parser": "^6.0.13", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", @@ -8350,8 +8322,7 @@ "supports-hyperlinks": "^3.0.0", "svg-tags": "^1.0.0", "table": "^6.8.1", - "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^5.0.0" + "write-file-atomic": "^5.0.1" }, "bin": { "stylelint": "bin/stylelint.js" @@ -8365,24 +8336,24 @@ } }, "node_modules/stylelint-config-recommended": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-11.0.0.tgz", - "integrity": "sha512-SoGIHNI748OCZn6BxFYT83ytWoYETCINVHV3LKScVAWQQauWdvmdDqJC5YXWjpBbxg2E761Tg5aUGKLFOVhEkA==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-12.0.0.tgz", + "integrity": "sha512-x6x8QNARrGO2sG6iURkzqL+Dp+4bJorPMMRNPScdvaUK8PsynriOcMW7AFDKqkWAS5wbue/u8fUT/4ynzcmqdQ==", "dev": true, "peerDependencies": { - "stylelint": "^15.3.0" + "stylelint": "^15.5.0" } }, "node_modules/stylelint-config-standard": { - "version": "31.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-31.0.0.tgz", - "integrity": "sha512-CUGAmtROCvX0YgMY2+6P9tqSkHj5z/75XxrQ8bGxvkCa1xYdGDx4poM0pa7cXc3s74/PZLJH/okxZZouRfOSGw==", + "version": "33.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-33.0.0.tgz", + "integrity": "sha512-eyxnLWoXImUn77+ODIuW9qXBDNM+ALN68L3wT1lN2oNspZ7D9NVGlNHb2QCUn4xDug6VZLsh0tF8NyoYzkgTzg==", "dev": true, "dependencies": { - "stylelint-config-recommended": "^11.0.0" + "stylelint-config-recommended": "^12.0.0" }, "peerDependencies": { - "stylelint": "^15.3.0" + "stylelint": "^15.5.0" } }, "node_modules/stylelint-prettier": { @@ -8416,14 +8387,26 @@ "node": ">=8" } }, + "node_modules/stylelint/node_modules/signal-exit": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/stylelint/node_modules/write-file-atomic": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.0.tgz", - "integrity": "sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "dependencies": { "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "signal-exit": "^4.0.1" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -8588,9 +8571,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", @@ -8742,9 +8725,9 @@ } }, "node_modules/typescript": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", - "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, "peer": true, "bin": { @@ -8752,7 +8735,7 @@ "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/uglify-js": { @@ -8799,9 +8782,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "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, "funding": [ { @@ -8811,6 +8794,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -8818,7 +8805,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -8864,12 +8851,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "node_modules/v8-to-istanbul": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", @@ -9020,14 +9001,6 @@ "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==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -9120,9 +9093,9 @@ "dev": true }, "node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "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", @@ -9178,47 +9151,52 @@ } }, "dependencies": { + "@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==" + }, "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "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.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "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==", "dev": true, "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.5" } }, "@babel/compat-data": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz", - "integrity": "sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz", + "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==", "dev": true }, "@babel/core": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.3.tgz", - "integrity": "sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", + "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", "dev": true, "requires": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.21.2", - "@babel/helpers": "^7.21.0", - "@babel/parser": "^7.21.3", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.3", - "@babel/types": "^7.21.3", + "@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", @@ -9235,153 +9213,140 @@ } }, "@babel/generator": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.3.tgz", - "integrity": "sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==", + "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.21.3", + "@babel/types": "^7.22.5", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } } }, "@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "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.20.5", - "@babel/helper-validator-option": "^7.18.6", + "@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" } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "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.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "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.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "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.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "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==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-transforms": { - "version": "7.21.2", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", - "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", + "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.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.2", - "@babel/types": "^7.21.2" + "@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-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "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-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "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.20.2" + "@babel/types": "^7.22.5" } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "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.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "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==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "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==", "dev": true }, "@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "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/helpers": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", - "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", + "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.20.7", - "@babel/traverse": "^7.21.0", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -9445,9 +9410,9 @@ } }, "@babel/parser": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz", - "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==", + "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-syntax-async-generators": { @@ -9496,12 +9461,12 @@ } }, "@babel/plugin-syntax-jsx": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", - "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "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, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.22.5" } }, "@babel/plugin-syntax-logical-assignment-operators": { @@ -9568,39 +9533,39 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "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, "requires": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" } }, "@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "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.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" } }, "@babel/traverse": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.3.tgz", - "integrity": "sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.21.3", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.3", - "@babel/types": "^7.21.3", + "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" }, @@ -9614,13 +9579,13 @@ } }, "@babel/types": { - "version": "7.21.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.3.tgz", - "integrity": "sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" } }, @@ -9631,29 +9596,29 @@ "dev": true }, "@csstools/css-parser-algorithms": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.0.1.tgz", - "integrity": "sha512-B9/8PmOtU6nBiibJg0glnNktQDZ3rZnGn/7UmDfrm2vMtrdlXO3p7ErE95N0up80IRk9YEtB5jyj/TmQ1WH3dw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.2.0.tgz", + "integrity": "sha512-9BoQ/jSrPq4vv3b9jjLW+PNNv56KlDH5JMx5yASSNrCtvq70FCNZUjXRvbCeR9hYj9ZyhURtqpU/RFIgg6kiOw==", "dev": true, "requires": {} }, "@csstools/css-tokenizer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.0.tgz", - "integrity": "sha512-dtqFyoJBHUxGi9zPZdpCKP1xk8tq6KPHJ/NY4qWXiYo6IcSGwzk3L8x2XzZbbyOyBs9xQARoGveU2AsgLj6D2A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.1.tgz", + "integrity": "sha512-GbrTj2Z8MCTUv+52GE0RbFGM527xuXZ0Xa5g0Z+YN573uveS4G0qi6WNOMyz3yrFM/jaILTTwJ0+umx81EzqfA==", "dev": true }, "@csstools/media-query-list-parser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.0.1.tgz", - "integrity": "sha512-X2/OuzEbjaxhzm97UJ+95GrMeT29d1Ib+Pu+paGLuRWZnWRK9sI9r3ikmKXPWGA1C4y4JEdBEFpp9jEqCvLeRA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.1.tgz", + "integrity": "sha512-pUjtFbaKbiFNjJo8pprrIaXLvQvWIlwPiFnRI4sEnc4F0NIGTOsw8kaJSR3CmZAKEvV8QYckovgAnWQC0bgLLQ==", "dev": true, "requires": {} }, "@csstools/selector-specificity": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.1.1.tgz", - "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", "dev": true, "requires": {} }, @@ -9674,13 +9639,13 @@ } }, "@es-joy/jsdoccomment": { - "version": "0.37.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.37.0.tgz", - "integrity": "sha512-hjK0wnsPCYLlF+HHB4R/RbUjOWeLW2SlarB67+Do5WsKILOkmIZvvPJFbtWSmbypxcjpoECLAMzoao0D4Bg5ZQ==", + "version": "0.39.4", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz", + "integrity": "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==", "dev": true, "requires": { "comment-parser": "1.3.1", - "esquery": "^1.4.0", + "esquery": "^1.5.0", "jsdoc-type-pratt-parser": "~4.0.0" } }, @@ -9698,13 +9663,13 @@ "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==" }, "@eslint/eslintrc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz", - "integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.0", + "espree": "^9.5.2", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -9714,14 +9679,14 @@ } }, "@eslint/js": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", - "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==" + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", + "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==" }, "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "requires": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", @@ -10048,13 +10013,14 @@ } }, "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "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.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" } }, "@jridgewell/resolve-uri": { @@ -10070,19 +10036,27 @@ "dev": true }, "@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==", + "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.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "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 + } } }, "@nodelib/fs.scandir": { @@ -10121,27 +10095,27 @@ "optional": true }, "@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, "requires": { "type-detect": "4.0.8" } }, "@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "requires": { - "@sinonjs/commons": "^2.0.0" + "@sinonjs/commons": "^3.0.0" } }, "@sinonjs/samsam": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-7.0.1.tgz", - "integrity": "sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", "dev": true, "requires": { "@sinonjs/commons": "^2.0.0", @@ -10187,9 +10161,9 @@ "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==", + "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, "requires": { "@babel/parser": "^7.20.7", @@ -10219,12 +10193,12 @@ } }, "@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "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, "requires": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "@types/cacheable-request": { @@ -10292,9 +10266,9 @@ } }, "@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 }, "@types/json5": { @@ -10325,9 +10299,9 @@ "dev": true }, "@types/node": { - "version": "16.18.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.18.tgz", - "integrity": "sha512-fwGw1uvQAzabxL1pyoknPlJIF2t7+K90uTqynleKRx24n3lYcxWa3+KByLhgkF8GEAK2c7hC8Ki0RkNM5H15jQ==" + "version": "18.16.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.18.tgz", + "integrity": "sha512-/aNaQZD0+iSBAGnvvN2Cx92HqE5sZCPZtx2TsK+4nvV23fFe09jVDvpArXr2j9DnYlzuU9WuoykDDc6wqvpNcw==" }, "@types/normalize-package-data": { "version": "2.4.1", @@ -10336,9 +10310,9 @@ "dev": true }, "@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 }, "@types/responselike": { @@ -10351,9 +10325,9 @@ } }, "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, "@types/stack-utils": { @@ -10363,9 +10337,9 @@ "dev": true }, "@types/yargs": { - "version": "17.0.22", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", - "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "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": "*" @@ -10387,29 +10361,29 @@ } }, "@typescript-eslint/scope-manager": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.56.0.tgz", - "integrity": "sha512-jGYKyt+iBakD0SA5Ww8vFqGpoV2asSjwt60Gl6YcO8ksQ8s2HlUEyHBMSa38bdLopYqGf7EYQMUIGdT/Luw+sw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", + "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/visitor-keys": "5.56.0" + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1" } }, "@typescript-eslint/types": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.56.0.tgz", - "integrity": "sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", + "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz", - "integrity": "sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", + "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/visitor-keys": "5.56.0", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -10427,9 +10401,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -10444,17 +10418,17 @@ } }, "@typescript-eslint/utils": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.56.0.tgz", - "integrity": "sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", + "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", "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.56.0", - "@typescript-eslint/types": "5.56.0", - "@typescript-eslint/typescript-estree": "5.56.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/typescript-estree": "5.60.1", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -10485,9 +10459,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -10502,12 +10476,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.56.0.tgz", - "integrity": "sha512-1mFdED7u5bZpX6Xxf5N9U2c18sb+8EvU3tyOIj6LQZ5OOvnmj8BVeNNP603OFPm5KkS1a7IvCIcwrdHXaEMG/Q==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", + "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.56.0", + "@typescript-eslint/types": "5.60.1", "eslint-visitor-keys": "^3.3.0" } }, @@ -10527,19 +10501,9 @@ } }, "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==" - }, - "acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "dev": true, - "requires": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==" }, "acorn-jsx": { "version": "5.3.2", @@ -10547,12 +10511,6 @@ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "requires": {} }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, "agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -10613,6 +10571,12 @@ "picomatch": "^2.0.4" } }, + "are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -10704,9 +10668,9 @@ "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==" }, "axios": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", - "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", + "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", @@ -10887,15 +10851,15 @@ } }, "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, "requires": { - "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" } }, "bser": { @@ -10919,6 +10883,12 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "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 + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -10931,9 +10901,9 @@ "optional": true }, "cacheable-request": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz", - "integrity": "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "optional": true, "requires": { "clone-response": "^1.0.2", @@ -10985,9 +10955,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001468", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001468.tgz", - "integrity": "sha512-zgAo8D5kbOyUcRAgSmgyuvBkjrGk5CGYG5TYgFdpQv+ywcyEpo1LOWoG8YmoflGnh+V+UsNuKYedsoYs0hzV5A==", + "version": "1.0.30001509", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001509.tgz", + "integrity": "sha512-2uDDk+TRiTX5hMcUYT/7CSyzMZxjfGu0vAUjS2g0LSD8UoXOv0LtpH4LxGMemsiPq6LCVIUjNwVM0erkOkGCDA==", "dev": true }, "chalk": { @@ -11017,9 +10987,9 @@ "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==", + "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 }, "clarinet": { @@ -11155,9 +11125,9 @@ } }, "cosmiconfig": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz", - "integrity": "sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==", + "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", @@ -11354,9 +11324,9 @@ "dev": true }, "digest-fetch": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-2.0.1.tgz", - "integrity": "sha512-OgzIWveqj8BlQ8hfJv97a9iOzWOgvI5Z3rGAnjkeNpHepHZpD/DHBDJ9mtfDclH5vkbUSGRqNEosYCH1FSO6Pg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/digest-fetch/-/digest-fetch-2.0.3.tgz", + "integrity": "sha512-HuTjHQE+wplAR+H8/YGwQjIGR1RQUCEsQcRyp3dZfuuxpSQH4OTm4BkHxyXuzxwmxUrNVzIPf9XkXi8QMJDNwQ==", "requires": { "base-64": "^0.1.0", "js-sha256": "^0.9.0", @@ -11396,20 +11366,20 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "electron": { - "version": "22.3.4", - "resolved": "https://registry.npmjs.org/electron/-/electron-22.3.4.tgz", - "integrity": "sha512-EY/ieC3gnKYUNOQPJSCIbiMBwEnGs/j0yIAUf0pXPK4BRh2nvXTD5d9OdouAIN7bRNLLPgqoTm0uXgZPAWTVkg==", + "version": "25.2.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-25.2.0.tgz", + "integrity": "sha512-I/rhcW2sV2fyiveVSBr2N7v5ZiCtdGY0UiNCDZgk2fpSC+irQjbeh7JT2b4vWmJ2ogOXBjqesrN9XszTIG6DHg==", "optional": true, "requires": { "@electron/get": "^2.0.0", - "@types/node": "^16.11.26", + "@types/node": "^18.11.18", "extract-zip": "^2.0.1" } }, "electron-to-chromium": { - "version": "1.4.333", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.333.tgz", - "integrity": "sha512-YyE8+GKyGtPEP1/kpvqsdhD6rA/TP1DUFDN4uiU/YI52NzDxmwHkEb3qjId8hLBa5siJvG0sfC3O66501jMruQ==", + "version": "1.4.445", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.445.tgz", + "integrity": "sha512-++DB+9VK8SBJwC+X1zlMfJ1tMA3F0ipi39GdEp+x3cV2TyBihqAgad8cNMWtLDEkbH39nlDQP7PfGrDr3Dr7HA==", "dev": true }, "emittery": { @@ -11439,9 +11409,9 @@ } }, "engine.io": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", - "integrity": "sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.1.tgz", + "integrity": "sha512-mGqhI+D7YxS9KJMppR6Iuo37Ed3abhU8NdfgSvJSDUafQutrN+sPTncJYTyM9+tkhSmWodKtVYGPPHyXJEwEQA==", "requires": { "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", @@ -11451,7 +11421,7 @@ "cookie": "~0.4.1", "cors": "~2.8.5", "debug": "~4.3.1", - "engine.io-parser": "~5.0.3", + "engine.io-parser": "~5.1.0", "ws": "~8.11.0" }, "dependencies": { @@ -11469,14 +11439,14 @@ } }, "engine.io-parser": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", - "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.1.0.tgz", + "integrity": "sha512-enySgNiK5tyZFynt3z7iqBR+Bto9EVVVvDFuTT0ioHCGbzirZVGDGiQjZzEp8hWl6hd5FSVytJGuScX1C1C35w==" }, "entities": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", - "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true }, "env-paths": { @@ -11612,70 +11582,16 @@ "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 - }, - "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.36.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", - "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", + "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.1", - "@eslint/js": "8.36.0", - "@humanwhocodes/config-array": "^0.11.8", + "@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", @@ -11684,9 +11600,9 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.5.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", @@ -11694,13 +11610,12 @@ "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "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-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -11743,9 +11658,9 @@ } }, "eslint-module-utils": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", - "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "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" @@ -11806,26 +11721,28 @@ } }, "eslint-plugin-jest": { - "version": "27.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.2.1.tgz", - "integrity": "sha512-l067Uxx7ZT8cO9NJuf+eJHvt6bqJyz2Z29wykyEdz/OtmcELQl2MQGQLX8J94O1cSJWAwUSEvCjwjA7KEK3Hmg==", + "version": "27.2.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.2.2.tgz", + "integrity": "sha512-euzbp06F934Z7UDl5ZUaRPLAc9MKjh0rMPERrHT7UhlCEwgb25kBj37TvMgWeHZVkR5I9CayswrpoaqZU1RImw==", "dev": true, "requires": { "@typescript-eslint/utils": "^5.10.0" } }, "eslint-plugin-jsdoc": { - "version": "40.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-40.1.0.tgz", - "integrity": "sha512-ANvrhiu62VlSorARM0hup60VQsS3hNyp0Ca7cnJDj8tpJzM7tNhBVqMVYXSuLzEmqrpwx6aAh+NAN2DdAGG5fQ==", + "version": "46.4.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.2.tgz", + "integrity": "sha512-fmIgOe7irf9otkMtsPjr5P39wC5LzA6aEU/nydfUlc8JaEiS93uhPaxI+x/v5s1Ckm+IZeP3006do2n2ehZcNQ==", "dev": true, "requires": { - "@es-joy/jsdoccomment": "~0.37.0", + "@es-joy/jsdoccomment": "~0.39.4", + "are-docs-informative": "^0.0.2", "comment-parser": "1.3.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "esquery": "^1.5.0", - "semver": "^7.3.8", + "is-builtin-module": "^3.2.1", + "semver": "^7.5.1", "spdx-expression-parse": "^3.0.1" }, "dependencies": { @@ -11839,9 +11756,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -11865,27 +11782,27 @@ } }, "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "requires": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==" + "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==" }, "espree": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", - "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "requires": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" } }, "esprima": { @@ -12060,9 +11977,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, "fast-glob": { @@ -12299,12 +12216,13 @@ "dev": true }, "get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", + "has-proto": "^1.0.1", "has-symbols": "^1.0.3" } }, @@ -12377,9 +12295,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "optional": true, "requires": { "lru-cache": "^6.0.0" @@ -12493,10 +12411,10 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "devOptional": true }, - "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==" + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" }, "handlebars": { "version": "4.7.7", @@ -12561,9 +12479,9 @@ } }, "helmet": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-6.0.1.tgz", - "integrity": "sha512-8wo+VdQhTMVBMCITYZaGTbE4lvlthelPYSvoyNvk4RECTmrVjMerp9RfUOQXZWLvCcAn1pKj7ZRxK4lI9Alrcw==" + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.0.0.tgz", + "integrity": "sha512-MsIgYmdBh460ZZ8cJC81q4XJknjG567wzEmv46WOBblDb6TUd3z8/GhgmsM9pn8g2B80tAJ4m5/d3Bi1KrSUBQ==" }, "hosted-git-info": { "version": "4.1.0", @@ -12607,9 +12525,9 @@ "dev": true }, "html-tags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", - "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", "dev": true }, "http-cache-semantics": { @@ -12805,15 +12723,24 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, + "is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "requires": { + "builtin-modules": "^3.3.0" + } + }, "is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" }, "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dev": true, "requires": { "has": "^1.0.3" @@ -13379,9 +13306,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -13470,11 +13397,6 @@ } } }, - "js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==" - }, "js-sha256": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz", @@ -13506,25 +13428,22 @@ "dev": true }, "jsdom": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-21.1.1.tgz", - "integrity": "sha512-Jjgdmw48RKcdAIQyUD1UdBh2ecH7VqwaXPN3ehoZN6MqgVbMn+lRm1aAT1AsdJRAJpwfa4IpwgzySn61h2qu3w==", + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz", + "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==", "dev": true, "requires": { "abab": "^2.0.6", - "acorn": "^8.8.2", - "acorn-globals": "^7.0.0", "cssstyle": "^3.0.0", "data-urls": "^4.0.0", "decimal.js": "^10.4.3", "domexception": "^4.0.0", - "escodegen": "^2.0.0", "form-data": "^4.0.0", "html-encoding-sniffer": "^3.0.0", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.1", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", + "nwsapi": "^2.2.4", "parse5": "^7.1.2", "rrweb-cssom": "^0.6.0", "saxes": "^6.0.0", @@ -13881,9 +13800,9 @@ } }, "module-alias": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", - "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz", + "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==" }, "moment": { "version": "2.29.4", @@ -13891,9 +13810,9 @@ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" }, "moment-timezone": { - "version": "0.5.41", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.41.tgz", - "integrity": "sha512-e0jGNZDOHfBXJGz8vR/sIMXvBIGJJcqFjmlg9lmE+5KX1U7/RZNMswfD8nKnNCnQdKTIj50IaRKwl1fvMLyyRg==", + "version": "0.5.43", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", + "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", "requires": { "moment": "^2.29.4" } @@ -13923,9 +13842,9 @@ } }, "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "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": { @@ -13956,6 +13875,15 @@ "path-to-regexp": "^1.7.0" }, "dependencies": { + "@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, "path-to-regexp": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", @@ -13968,9 +13896,9 @@ } }, "node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "requires": { "whatwg-url": "^5.0.0" }, @@ -13997,11 +13925,11 @@ } }, "node-ical": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.16.0.tgz", - "integrity": "sha512-LgLN6gm2D1AIaBQnbAw8nz+lH2ZT08lOSGhpzi3z+f2JDt3rSkKrWCx96URO8RmGxgx2Cojw15OBWZSpL18kag==", + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.16.1.tgz", + "integrity": "sha512-AAlJbvyRlQ5QT3LtYAvveY/gZlvHAIjHR/suQp1YVX/RySCxI/qZcyauRDKv2QSH0zNG0J8iv5T1gv6FadoETA==", "requires": { - "axios": "1.3.4", + "axios": "1.4.0", "moment-timezone": "^0.5.31", "rrule": "2.6.4", "uuid": "^9.0.0" @@ -14014,9 +13942,9 @@ "dev": true }, "node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "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-package-data": { @@ -14041,9 +13969,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -14079,9 +14007,9 @@ } }, "nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.5.tgz", + "integrity": "sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==", "dev": true }, "object-assign": { @@ -14147,16 +14075,16 @@ } }, "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==", "requires": { + "@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" } }, "p-cancelable": { @@ -14272,9 +14200,9 @@ "dev": true }, "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 }, "pkg-dir": { @@ -14326,27 +14254,27 @@ } }, "playwright": { - "version": "1.32.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.32.1.tgz", - "integrity": "sha512-GnEizysWMvoqHC3I9l8+4/ZxeLwLNdJJG76xdKGxzOcIZDcw5RSk/FKrFb5CuA+zcLpjIM2p9eR9Z4CuUDkWXg==", + "version": "1.35.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.35.1.tgz", + "integrity": "sha512-NbwBeGJLu5m7VGM0+xtlmLAH9VUfWwYOhUi/lSEDyGg46r1CA9RWlvoc5yywxR9AzQb0mOCm7bWtOXV7/w43ZA==", "dev": true, "requires": { - "playwright-core": "1.32.1" + "playwright-core": "1.35.1" } }, "playwright-core": { - "version": "1.32.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.1.tgz", - "integrity": "sha512-KZYUQC10mXD2Am1rGlidaalNGYk3LU1vZqqNk0gT4XPty1jOqgup8KDP8l2CUlqoNKhXM5IfGjWgW37xvGllBA==", + "version": "1.35.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.35.1.tgz", + "integrity": "sha512-pNXb6CQ7OqmGDRspEjlxE49w+4YtR6a3X6mT1hZXeJHWmsEz7SunmvZeiG/+y1yyMZdHnnn73WKYdtV1er0Xyg==", "dev": true }, "postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", + "version": "8.4.24", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", + "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", "dev": true, "requires": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } @@ -14371,9 +14299,9 @@ "requires": {} }, "postcss-selector-parser": { - "version": "6.0.11", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", - "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", "dev": true, "requires": { "cssesc": "^3.0.0", @@ -14392,9 +14320,9 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, "prettier": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", - "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true }, "prettier-linter-helpers": { @@ -14563,9 +14491,9 @@ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" }, "pure-rand": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.1.tgz", - "integrity": "sha512-t+x1zEHDjBwkDGY5v5ApnZ/utcd4XYDiJsaQQoptTXgUXX95sDg1elCdJghzicm7n2mbCBJ3uYWr6M22SO19rg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", + "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", "dev": true }, "qs": { @@ -14745,13 +14673,13 @@ } }, "regexp.prototype.flags": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", - "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", "requires": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" } }, "replace-last": { @@ -14778,12 +14706,12 @@ "dev": true }, "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dev": true, "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -14817,9 +14745,9 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, "resolve.exports": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.1.tgz", - "integrity": "sha512-OEJWVeimw8mgQuj3HfkNl4KqRevH7lzeQNaWRPfx0PPse7Jk6ozcsG4FKVgtzDsC1KUF+YlTHh17NcgHOPykLw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", "dev": true }, "responselike": { @@ -15032,28 +14960,19 @@ "dev": true }, "sinon": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.0.2.tgz", - "integrity": "sha512-PCVP63XZkg0/LOqQH5rEU4LILuvTFMb5tNxTHfs6VUMNnZz2XrnGSTZbAGITjzwQWbl/Bl/8hi4G3zZWjyBwHg==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", + "integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", "dev": true, "requires": { "@sinonjs/commons": "^3.0.0", - "@sinonjs/fake-timers": "^10.0.2", - "@sinonjs/samsam": "^7.0.1", + "@sinonjs/fake-timers": "^10.3.0", + "@sinonjs/samsam": "^8.0.0", "diff": "^5.1.0", "nise": "^5.1.4", "supports-color": "^7.2.0" }, "dependencies": { - "@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, "diff": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", @@ -15086,16 +15005,17 @@ } }, "socket.io": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", - "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.1.tgz", + "integrity": "sha512-W+utHys2w//dhFjy7iQQu9sGd3eokCjGbl2r59tyLqNiJJBdIebn3GAKEXBr3osqHTObJi2die/25bCx2zsaaw==", "requires": { "accepts": "~1.3.4", "base64id": "~2.0.0", + "cors": "~2.8.5", "debug": "~4.3.2", - "engine.io": "~6.4.1", + "engine.io": "~6.5.0", "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.1" + "socket.io-parser": "~4.2.4" } }, "socket.io-adapter": { @@ -15115,9 +15035,9 @@ } }, "socket.io-parser": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", - "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", "requires": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -15311,18 +15231,18 @@ "dev": true }, "stylelint": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.3.0.tgz", - "integrity": "sha512-9UYBYk7K9rtlKcTUDZrtntE840sZM00qyYBQHHe7tjwMNUsPsGvR6Fd43IxHEAhRrDLzpy3TVaHb6CReBB3eFg==", + "version": "15.9.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.9.0.tgz", + "integrity": "sha512-sXtAZi64CllWr6A+8ymDWnlIaYwuAa7XRmGnJxLQXFNnLjd3Izm4HAD+loKVaZ7cpK6SLxhAUX1lwPJKGCn0mg==", "dev": true, "requires": { - "@csstools/css-parser-algorithms": "^2.0.1", - "@csstools/css-tokenizer": "^2.1.0", - "@csstools/media-query-list-parser": "^2.0.1", - "@csstools/selector-specificity": "^2.1.1", + "@csstools/css-parser-algorithms": "^2.2.0", + "@csstools/css-tokenizer": "^2.1.1", + "@csstools/media-query-list-parser": "^2.1.0", + "@csstools/selector-specificity": "^2.2.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", - "cosmiconfig": "^8.1.0", + "cosmiconfig": "^8.2.0", "css-functions-list": "^3.1.0", "css-tree": "^2.3.1", "debug": "^4.3.4", @@ -15332,7 +15252,7 @@ "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", - "html-tags": "^3.2.0", + "html-tags": "^3.3.1", "ignore": "^5.2.4", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", @@ -15343,11 +15263,11 @@ "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.21", + "postcss": "^8.4.24", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.11", + "postcss-selector-parser": "^6.0.13", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", @@ -15356,8 +15276,7 @@ "supports-hyperlinks": "^3.0.0", "svg-tags": "^1.0.0", "table": "^6.8.1", - "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^5.0.0" + "write-file-atomic": "^5.0.1" }, "dependencies": { "balanced-match": { @@ -15372,32 +15291,38 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, + "signal-exit": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "dev": true + }, "write-file-atomic": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.0.tgz", - "integrity": "sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "requires": { "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "signal-exit": "^4.0.1" } } } }, "stylelint-config-recommended": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-11.0.0.tgz", - "integrity": "sha512-SoGIHNI748OCZn6BxFYT83ytWoYETCINVHV3LKScVAWQQauWdvmdDqJC5YXWjpBbxg2E761Tg5aUGKLFOVhEkA==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-12.0.0.tgz", + "integrity": "sha512-x6x8QNARrGO2sG6iURkzqL+Dp+4bJorPMMRNPScdvaUK8PsynriOcMW7AFDKqkWAS5wbue/u8fUT/4ynzcmqdQ==", "dev": true, "requires": {} }, "stylelint-config-standard": { - "version": "31.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-31.0.0.tgz", - "integrity": "sha512-CUGAmtROCvX0YgMY2+6P9tqSkHj5z/75XxrQ8bGxvkCa1xYdGDx4poM0pa7cXc3s74/PZLJH/okxZZouRfOSGw==", + "version": "33.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-33.0.0.tgz", + "integrity": "sha512-eyxnLWoXImUn77+ODIuW9qXBDNM+ALN68L3wT1lN2oNspZ7D9NVGlNHb2QCUn4xDug6VZLsh0tF8NyoYzkgTzg==", "dev": true, "requires": { - "stylelint-config-recommended": "^11.0.0" + "stylelint-config-recommended": "^12.0.0" } }, "stylelint-prettier": { @@ -15536,9 +15461,9 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "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, "requires": { "psl": "^1.1.33", @@ -15652,9 +15577,9 @@ } }, "typescript": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.2.tgz", - "integrity": "sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", "dev": true, "peer": true }, @@ -15687,9 +15612,9 @@ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" }, "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "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", @@ -15730,12 +15655,6 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "v8-to-istanbul": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", @@ -15852,11 +15771,6 @@ "is-typed-array": "^1.1.10" } }, - "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==" - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -15920,9 +15834,9 @@ "dev": true }, "yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "requires": { "cliui": "^8.0.1", diff --git a/package.json b/package.json index 2f51a2da7c..c8fdd5f03a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "magicmirror", - "version": "2.23.0", + "version": "2.24.0", "description": "The open source modular smart mirror platform.", "main": "js/electron.js", "scripts": { @@ -51,43 +51,43 @@ "devDependencies": { "eslint-config-prettier": "^8.8.0", "eslint-plugin-import": "^2.27.5", - "eslint-plugin-jest": "^27.2.1", - "eslint-plugin-jsdoc": "^40.1.0", + "eslint-plugin-jest": "^27.2.2", + "eslint-plugin-jsdoc": "^46.4.2", "eslint-plugin-prettier": "^4.2.1", "express-basic-auth": "^1.2.1", "husky": "^8.0.3", "jest": "^29.5.0", - "jsdom": "^21.1.1", + "jsdom": "^22.1.0", "lodash": "^4.17.21", - "playwright": "^1.32.1", - "prettier": "^2.8.7", + "playwright": "^1.35.1", + "prettier": "^2.8.8", "pretty-quick": "^3.1.3", - "sinon": "^15.0.2", - "stylelint": "^15.3.0", - "stylelint-config-standard": "^31.0.0", + "sinon": "^15.2.0", + "stylelint": "^15.9.0", + "stylelint-config-standard": "^33.0.0", "stylelint-prettier": "^3.0.0", "suncalc": "^1.9.0" }, "optionalDependencies": { - "electron": "^22.3.4" + "electron": "^25.2.0" }, "dependencies": { "colors": "^1.4.0", "console-stamp": "^3.1.1", - "digest-fetch": "^2.0.1", + "digest-fetch": "^2.0.3", "envsub": "^4.1.0", - "eslint": "^8.36.0", + "eslint": "^8.43.0", "express": "^4.18.2", "express-ipfilter": "^1.3.1", "feedme": "^2.0.2", - "helmet": "^6.0.1", + "helmet": "^7.0.0", "iconv-lite": "^0.6.3", "luxon": "^1.28.1", - "module-alias": "^2.2.2", + "module-alias": "^2.2.3", "moment": "^2.29.4", - "node-fetch": "^2.6.9", - "node-ical": "^0.16.0", - "socket.io": "^4.6.1" + "node-fetch": "^2.6.12", + "node-ical": "^0.16.1", + "socket.io": "^4.7.1" }, "_moduleAliases": { "node_helper": "js/node_helper.js", @@ -95,6 +95,6 @@ "fetch": "js/fetch.js" }, "engines": { - "node": ">=14" + "node": ">=16" } } diff --git a/tests/configs/modules/clock/clock_analog.js b/tests/configs/modules/clock/clock_analog.js index fdca561562..d303c3f995 100644 --- a/tests/configs/modules/clock/clock_analog.js +++ b/tests/configs/modules/clock/clock_analog.js @@ -9,7 +9,8 @@ let config = { position: "middle_center", config: { displayType: "analog", - analogFace: "face-006" + analogFace: "face-006", + showDate: false } } ] diff --git a/tests/configs/modules/clock/clock_showDateAnalog.js b/tests/configs/modules/clock/clock_showDateAnalog.js new file mode 100644 index 0000000000..e5f5c49b7d --- /dev/null +++ b/tests/configs/modules/clock/clock_showDateAnalog.js @@ -0,0 +1,25 @@ +/* MagicMirror² Test config for default clock module + * + * By Johan Hammar + * MIT Licensed. + */ +let config = { + timeFormat: 12, + + modules: [ + { + module: "clock", + position: "middle_center", + config: { + showTime: true, + showDate: true, + displayType: "analog" + } + } + ] +}; + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/configs/port_variable.js.template b/tests/configs/port_variable.js.template index 3d8d9791ad..0a0d58bb9a 100644 --- a/tests/configs/port_variable.js.template +++ b/tests/configs/port_variable.js.template @@ -3,7 +3,7 @@ * By Rodrigo Ramírez Norambuena https://rodrigoramirez.com * MIT Licensed. */ -let config = require(process.cwd() + "/tests/configs/default.js").configFactory({ +let config = require(`${process.cwd()}/tests/configs/default.js`).configFactory({ port: ${MM_PORT} }); diff --git a/tests/e2e/helpers/mock-console.js b/tests/e2e/helpers/mock-console.js index 3f9909f11a..f87be96424 100644 --- a/tests/e2e/helpers/mock-console.js +++ b/tests/e2e/helpers/mock-console.js @@ -1,10 +1,17 @@ /** * Suppresses errors concerning web server already shut down. - * * @param {string} err The error message. */ const mockError = (err) => { - if (err.includes("ECONNREFUSED") || err.includes("ECONNRESET") || err.includes("socket hang up") || err.includes("exports is not defined") || err.includes("write EPIPE")) { + if ( + err.includes("ECONNREFUSED") || + err.includes("ECONNRESET") || + err.includes("socket hang up") || + err.includes("exports is not defined") || + err.includes("write EPIPE") || + err.includes("AggregateError") || + err.includes("ERR_SOCKET_CONNECTION_TIMEOUT") + ) { jest.fn(); } else { console.dir(err); diff --git a/tests/e2e/modules/clock_spec.js b/tests/e2e/modules/clock_spec.js index 1ffa74a1bf..fa8ce28f7a 100644 --- a/tests/e2e/modules/clock_spec.js +++ b/tests/e2e/modules/clock_spec.js @@ -107,4 +107,18 @@ describe("Clock module", () => { expect(elem).not.toBe(null); }); }); + + describe("with analog clock face and date enabled", () => { + beforeAll(async () => { + await helpers.startApplication("tests/configs/modules/clock/clock_showDateAnalog.js"); + await helpers.getDocument(); + }); + + it("should show the analog clock face and the date", async () => { + const elemClock = helpers.waitForElement(".clock-circle"); + await expect(elemClock).not.toBe(null); + const elemDate = helpers.waitForElement(".clock .date"); + await expect(elemDate).not.toBe(null); + }); + }); }); diff --git a/tests/e2e/modules/compliments_spec.js b/tests/e2e/modules/compliments_spec.js index 52d232e5dc..97c044f3e6 100644 --- a/tests/e2e/modules/compliments_spec.js +++ b/tests/e2e/modules/compliments_spec.js @@ -3,7 +3,6 @@ const helpers = require("../helpers/global-setup"); describe("Compliments module", () => { /** * move similar tests in function doTest - * * @param {Array} complimentsArray The array of compliments. */ const doTest = async (complimentsArray) => { @@ -25,7 +24,7 @@ describe("Compliments module", () => { await helpers.getDocument(); }); - it("Show anytime because if configure empty parts of day compliments and set anytime compliments", async () => { + it("shows anytime because if configure empty parts of day compliments and set anytime compliments", async () => { await doTest(["Anytime here"]); }); }); @@ -36,7 +35,7 @@ describe("Compliments module", () => { await helpers.getDocument(); }); - it("Show anytime compliments", async () => { + it("shows anytime compliments", async () => { await doTest(["Anytime here"]); }); }); diff --git a/tests/e2e/modules_empty_spec.js b/tests/e2e/modules_empty_spec.js index ddd08e821e..dfe6c0813c 100644 --- a/tests/e2e/modules_empty_spec.js +++ b/tests/e2e/modules_empty_spec.js @@ -9,13 +9,13 @@ describe("Check configuration without modules", () => { await helpers.stopApplication(); }); - it("Show the message MagicMirror² title", async () => { + it("shows the message MagicMirror² title", async () => { const elem = await helpers.waitForElement("#module_1_helloworld .module-content"); expect(elem).not.toBe(null); expect(elem.textContent).toContain("MagicMirror²"); }); - it("Show the url of michael's website", async () => { + it("shows the url of michael's website", async () => { const elem = await helpers.waitForElement("#module_5_helloworld .module-content"); expect(elem).not.toBe(null); expect(elem.textContent).toContain("www.michaelteeuw.nl"); diff --git a/tests/e2e/serveronly_spec.js b/tests/e2e/serveronly_spec.js new file mode 100644 index 0000000000..78ecba30f5 --- /dev/null +++ b/tests/e2e/serveronly_spec.js @@ -0,0 +1,28 @@ +const helpers = require("./helpers/global-setup"); + +const delay = (time) => { + return new Promise((resolve) => setTimeout(resolve, time)); +}; + +describe("App environment", () => { + let serverProcess; + beforeAll(async () => { + process.env.MM_CONFIG_FILE = "tests/configs/default.js"; + serverProcess = await require("child_process").spawn("npm", ["run", "server"], { env: process.env, detached: true }); + // we have to wait until the server is startet + await delay(2000); + }); + afterAll(async () => { + await process.kill(-serverProcess.pid); + }); + + it("get request from http://localhost:8080 should return 200", async () => { + const res = await helpers.fetch("http://localhost:8080"); + expect(res.status).toBe(200); + }); + + it("get request from http://localhost:8080/nothing should return 404", async () => { + const res = await helpers.fetch("http://localhost:8080/nothing"); + expect(res.status).toBe(404); + }); +}); diff --git a/tests/electron/modules/calendar_spec.js b/tests/electron/modules/calendar_spec.js index cacc550da5..b0b7c27679 100644 --- a/tests/electron/modules/calendar_spec.js +++ b/tests/electron/modules/calendar_spec.js @@ -3,7 +3,6 @@ const helpers = require("../helpers/global-setup"); describe("Calendar module", () => { /** * move similar tests in function doTest - * * @param {string} cssClass css selector */ const doTest = async (cssClass) => { diff --git a/tests/electron/modules/compliments_spec.js b/tests/electron/modules/compliments_spec.js index 3afa83de83..a6dd384e70 100644 --- a/tests/electron/modules/compliments_spec.js +++ b/tests/electron/modules/compliments_spec.js @@ -3,7 +3,6 @@ const helpers = require("../helpers/global-setup"); describe("Compliments module", () => { /** * move similar tests in function doTest - * * @param {Array} complimentsArray The array of compliments. */ const doTest = async (complimentsArray) => { @@ -36,7 +35,7 @@ describe("Compliments module", () => { describe("Feature date in compliments module", () => { describe("Set date and empty compliments for anytime, morning, evening and afternoon", () => { - it("Show happy new year compliment on new years day", async () => { + it("shows happy new year compliment on new years day", async () => { await helpers.startApplication("tests/configs/modules/compliments/compliments_date.js", "01 Jan 2022 10:00:00 GMT"); await doTest(["Happy new year!"]); }); diff --git a/tests/unit/helpers/global-setup.js b/tests/unit/helpers/global-setup.js new file mode 100644 index 0000000000..fc064b46bc --- /dev/null +++ b/tests/unit/helpers/global-setup.js @@ -0,0 +1,3 @@ +module.exports = async () => { + process.env.TZ = "Europe/Berlin"; +}; diff --git a/tests/unit/modules/default/calendar/calendar_fetcher_utils_spec.js b/tests/unit/modules/default/calendar/calendar_fetcher_utils_spec.js new file mode 100644 index 0000000000..06870de95e --- /dev/null +++ b/tests/unit/modules/default/calendar/calendar_fetcher_utils_spec.js @@ -0,0 +1,53 @@ +global.moment = require("moment-timezone"); + +const CalendarFetcherUtils = require("../../../../../modules/default/calendar/calendarfetcherutils"); + +describe("Calendar fetcher utils test", () => { + const defaultConfig = { + excludedEvents: [], + includePastEvents: false, + maximumEntries: 10, + maximumNumberOfDays: 365 + }; + + describe("filterEvents", () => { + it("should return only ongoing and upcoming non full day events", () => { + const minusOneHour = moment().subtract(1, "hours").toDate(); + const minusTwoHours = moment().subtract(2, "hours").toDate(); + const plusOneHour = moment().add(1, "hours").toDate(); + const plusTwoHours = moment().add(2, "hours").toDate(); + + const filteredEvents = CalendarFetcherUtils.filterEvents( + { + pastEvent: { type: "VEVENT", start: minusTwoHours, end: minusOneHour, summary: "pastEvent" }, + ongoingEvent: { type: "VEVENT", start: minusOneHour, end: plusOneHour, summary: "ongoingEvent" }, + upcomingEvent: { type: "VEVENT", start: plusOneHour, end: plusTwoHours, summary: "upcomingEvent" } + }, + defaultConfig + ); + + expect(filteredEvents.length).toEqual(2); + expect(filteredEvents[0].title).toBe("ongoingEvent"); + expect(filteredEvents[1].title).toBe("upcomingEvent"); + }); + + it("should return only ongoing and upcoming full day events", () => { + const yesterday = moment().subtract(1, "days").startOf("day").toDate(); + const today = moment().startOf("day").toDate(); + const tomorrow = moment().add(1, "days").startOf("day").toDate(); + + const filteredEvents = CalendarFetcherUtils.filterEvents( + { + pastEvent: { type: "VEVENT", start: yesterday, end: yesterday, summary: "pastEvent" }, + ongoingEvent: { type: "VEVENT", start: today, end: today, summary: "ongoingEvent" }, + upcomingEvent: { type: "VEVENT", start: tomorrow, end: tomorrow, summary: "upcomingEvent" } + }, + defaultConfig + ); + + expect(filteredEvents.length).toEqual(2); + expect(filteredEvents[0].title).toBe("ongoingEvent"); + expect(filteredEvents[1].title).toBe("upcomingEvent"); + }); + }); +}); diff --git a/tests/unit/functions/calendar_spec.js b/tests/unit/modules/default/calendar/calendar_utils_spec.js similarity index 52% rename from tests/unit/functions/calendar_spec.js rename to tests/unit/modules/default/calendar/calendar_utils_spec.js index 34e16dd36d..7cf896c137 100644 --- a/tests/unit/functions/calendar_spec.js +++ b/tests/unit/modules/default/calendar/calendar_utils_spec.js @@ -1,18 +1,8 @@ global.moment = require("moment"); -describe("Functions into modules/default/calendar/calendar.js", () => { - // Fake for use by calendar.js - Module = {}; - Module.definitions = {}; - Module.register = (name, moduleDefinition) => { - Module.definitions[name] = moduleDefinition; - }; - - beforeAll(() => { - // load calendar.js - require("../../../modules/default/calendar/calendar"); - }); +const CalendarUtils = require("../../../../../modules/default/calendar/calendarutils"); +describe("Calendar utils tests", () => { describe("capFirst", () => { const words = { rodrigo: "Rodrigo", @@ -24,68 +14,88 @@ describe("Functions into modules/default/calendar/calendar.js", () => { Object.keys(words).forEach((word) => { it(`for '${word}' should return '${words[word]}'`, () => { - expect(Module.definitions.calendar.capFirst(word)).toBe(words[word]); + expect(CalendarUtils.capFirst(word)).toBe(words[word]); }); }); + + it("should not capitalize other letters", () => { + expect(CalendarUtils.capFirst("event")).not.toBe("EVent"); + }); }); describe("getLocaleSpecification", () => { it("should return a valid moment.LocaleSpecification for a 12-hour format", () => { - expect(Module.definitions.calendar.getLocaleSpecification(12)).toEqual({ longDateFormat: { LT: "h:mm A" } }); + expect(CalendarUtils.getLocaleSpecification(12)).toEqual({ longDateFormat: { LT: "h:mm A" } }); }); it("should return a valid moment.LocaleSpecification for a 24-hour format", () => { - expect(Module.definitions.calendar.getLocaleSpecification(24)).toEqual({ longDateFormat: { LT: "HH:mm" } }); + expect(CalendarUtils.getLocaleSpecification(24)).toEqual({ longDateFormat: { LT: "HH:mm" } }); }); it("should return the current system locale when called without timeFormat number", () => { - expect(Module.definitions.calendar.getLocaleSpecification()).toEqual({ longDateFormat: { LT: moment.localeData().longDateFormat("LT") } }); + expect(CalendarUtils.getLocaleSpecification()).toEqual({ longDateFormat: { LT: moment.localeData().longDateFormat("LT") } }); }); it("should return a 12-hour longDateFormat when using the 'en' locale", () => { const localeBackup = moment.locale(); moment.locale("en"); - expect(Module.definitions.calendar.getLocaleSpecification()).toEqual({ longDateFormat: { LT: "h:mm A" } }); + expect(CalendarUtils.getLocaleSpecification()).toEqual({ longDateFormat: { LT: "h:mm A" } }); moment.locale(localeBackup); }); it("should return a 12-hour longDateFormat when using the 'au' locale", () => { const localeBackup = moment.locale(); moment.locale("au"); - expect(Module.definitions.calendar.getLocaleSpecification()).toEqual({ longDateFormat: { LT: "h:mm A" } }); + expect(CalendarUtils.getLocaleSpecification()).toEqual({ longDateFormat: { LT: "h:mm A" } }); moment.locale(localeBackup); }); it("should return a 12-hour longDateFormat when using the 'eg' locale", () => { const localeBackup = moment.locale(); moment.locale("eg"); - expect(Module.definitions.calendar.getLocaleSpecification()).toEqual({ longDateFormat: { LT: "h:mm A" } }); + expect(CalendarUtils.getLocaleSpecification()).toEqual({ longDateFormat: { LT: "h:mm A" } }); moment.locale(localeBackup); }); it("should return a 24-hour longDateFormat when using the 'nl' locale", () => { const localeBackup = moment.locale(); moment.locale("nl"); - expect(Module.definitions.calendar.getLocaleSpecification()).toEqual({ longDateFormat: { LT: "HH:mm" } }); + expect(CalendarUtils.getLocaleSpecification()).toEqual({ longDateFormat: { LT: "HH:mm" } }); moment.locale(localeBackup); }); it("should return a 24-hour longDateFormat when using the 'fr' locale", () => { const localeBackup = moment.locale(); moment.locale("fr"); - expect(Module.definitions.calendar.getLocaleSpecification()).toEqual({ longDateFormat: { LT: "HH:mm" } }); + expect(CalendarUtils.getLocaleSpecification()).toEqual({ longDateFormat: { LT: "HH:mm" } }); moment.locale(localeBackup); }); it("should return a 24-hour longDateFormat when using the 'uk' locale", () => { const localeBackup = moment.locale(); moment.locale("uk"); - expect(Module.definitions.calendar.getLocaleSpecification()).toEqual({ longDateFormat: { LT: "HH:mm" } }); + expect(CalendarUtils.getLocaleSpecification()).toEqual({ longDateFormat: { LT: "HH:mm" } }); moment.locale(localeBackup); }); }); describe("shorten", () => { + it("should not shorten if short enough", () => { + expect(CalendarUtils.shorten("Event 1", 10, false, 1)).toBe("Event 1"); + }); + + it("should shorten into one line", () => { + expect(CalendarUtils.shorten("Example event at 12 o clock", 10, true, 1)).toBe("Example …"); + }); + + it("should shorten into three lines", () => { + expect(CalendarUtils.shorten("Example event at 12 o clock", 10, true, 3)).toBe("Example
event at 12 o
clock"); + }); + + it("should not shorten into three lines if wrap is false", () => { + expect(CalendarUtils.shorten("Example event at 12 o clock", 10, false, 3)).toBe("Example ev…"); + }); + const strings = { " String with whitespace at the beginning that needs trimming": { length: 16, return: "String with whit…" }, "long string that needs shortening": { length: 16, return: "long string that…" }, @@ -95,34 +105,44 @@ describe("Functions into modules/default/calendar/calendar.js", () => { Object.keys(strings).forEach((string) => { it(`for '${string}' should return '${strings[string].return}'`, () => { - expect(Module.definitions.calendar.shorten(string, strings[string].length)).toBe(strings[string].return); + expect(CalendarUtils.shorten(string, strings[string].length)).toBe(strings[string].return); }); }); it("should return an empty string if shorten is called with a non-string", () => { - expect(Module.definitions.calendar.shorten(100)).toBe(""); + expect(CalendarUtils.shorten(100)).toBe(""); }); it("should not shorten the string if shorten is called with a non-number maxLength", () => { - expect(Module.definitions.calendar.shorten("This is a test string", "This is not a number")).toBe("This is a test string"); + expect(CalendarUtils.shorten("This is a test string", "This is not a number")).toBe("This is a test string"); }); it("should wrap the string instead of shorten it if shorten is called with wrapEvents = true (with maxLength defined as 20)", () => { - expect(Module.definitions.calendar.shorten("This is a wrapEvent test. Should wrap the string instead of shorten it if called with wrapEvent = true", 20, true)).toBe( + expect(CalendarUtils.shorten("This is a wrapEvent test. Should wrap the string instead of shorten it if called with wrapEvent = true", 20, true)).toBe( "This is a
wrapEvent test. Should wrap
the string instead of
shorten it if called with
wrapEvent = true" ); }); it("should wrap the string instead of shorten it if shorten is called with wrapEvents = true (without maxLength defined, default 25)", () => { - expect(Module.definitions.calendar.shorten("This is a wrapEvent test. Should wrap the string instead of shorten it if called with wrapEvent = true", undefined, true)).toBe( + expect(CalendarUtils.shorten("This is a wrapEvent test. Should wrap the string instead of shorten it if called with wrapEvent = true", undefined, true)).toBe( "This is a wrapEvent
test. Should wrap the string
instead of shorten it if called
with wrapEvent = true" ); }); it("should wrap and shorten the string in the second line if called with wrapEvents = true and maxTitleLines = 2", () => { - expect(Module.definitions.calendar.shorten("This is a wrapEvent and maxTitleLines test. Should wrap and shorten the string in the second line if called with wrapEvents = true and maxTitleLines = 2", undefined, true, 2)).toBe( + expect(CalendarUtils.shorten("This is a wrapEvent and maxTitleLines test. Should wrap and shorten the string in the second line if called with wrapEvents = true and maxTitleLines = 2", undefined, true, 2)).toBe( "This is a wrapEvent and
maxTitleLines test. Should wrap and …" ); }); }); + + describe("titleTransform and shorten combined", () => { + it("should replace the birthday and wrap nicely", () => { + const transformedTitle = CalendarUtils.titleTransform("Michael Teeuw's birthday", { + "De verjaardag van ": "", + "'s birthday": "" + }); + expect(CalendarUtils.shorten(transformedTitle, 10, true, 2)).toBe("Michael
Teeuw"); + }); + }); }); diff --git a/tests/unit/modules/default/utils_spec.js b/tests/unit/modules/default/utils_spec.js index f9c9390ae5..8ffe0876d6 100644 --- a/tests/unit/modules/default/utils_spec.js +++ b/tests/unit/modules/default/utils_spec.js @@ -1,23 +1,22 @@ -const { performWebRequest } = require("../../../../modules/default/utils"); +global.moment = require("moment-timezone"); +const { performWebRequest, formatTime } = require("../../../../modules/default/utils"); const nodeVersion = process.version.match(/^v(\d+)\.*/)[1]; -describe("Utils tests", () => { - describe("The performWebRequest-method", () => { +describe("Default modules utils tests", () => { + describe("performWebRequest", () => { if (nodeVersion > 18) { const locationHost = "localhost:8080"; const locationProtocol = "http"; let fetchResponse; let fetchMock; - let url; + let urlToCall; beforeEach(() => { fetchResponse = new Response(); global.fetch = jest.fn(() => Promise.resolve(fetchResponse)); fetchMock = global.fetch; - - url = "www.test.com"; }); describe("When using cors proxy", () => { @@ -29,24 +28,23 @@ describe("Utils tests", () => { }); test("Calls correct URL once", async () => { - const urlToCall = "http://www.test.com/path?param1=value1"; - url = urlToCall; + urlToCall = "http://www.test.com/path?param1=value1"; - await performWebRequest(url, "json", true); + await performWebRequest(urlToCall, "json", true); expect(fetchMock.mock.calls.length).toBe(1); expect(fetchMock.mock.calls[0][0]).toBe(`${locationProtocol}//${locationHost}/cors?url=${urlToCall}`); }); test("Sends correct headers", async () => { - const urlToCall = "http://www.test.com/path?param1=value1"; - url = urlToCall; + urlToCall = "http://www.test.com/path?param1=value1"; + const headers = [ { name: "header1", value: "value1" }, { name: "header2", value: "value2" } ]; - await performWebRequest(url, "json", true, headers); + await performWebRequest(urlToCall, "json", true, headers); expect(fetchMock.mock.calls.length).toBe(1); expect(fetchMock.mock.calls[0][0]).toBe(`${locationProtocol}//${locationHost}/cors?sendheaders=header1:value1,header2:value2&url=${urlToCall}`); @@ -55,24 +53,22 @@ describe("Utils tests", () => { describe("When not using cors proxy", () => { test("Calls correct URL once", async () => { - const urlToCall = "http://www.test.com/path?param1=value1"; - url = urlToCall; + urlToCall = "http://www.test.com/path?param1=value1"; - await performWebRequest(url); + await performWebRequest(urlToCall); expect(fetchMock.mock.calls.length).toBe(1); expect(fetchMock.mock.calls[0][0]).toBe(urlToCall); }); test("Sends correct headers", async () => { - const urlToCall = "http://www.test.com/path?param1=value1"; - url = urlToCall; + urlToCall = "http://www.test.com/path?param1=value1"; const headers = [ { name: "header1", value: "value1" }, { name: "header2", value: "value2" } ]; - await performWebRequest(url, "json", false, headers); + await performWebRequest(urlToCall, "json", false, headers); const expectedHeaders = { headers: { header1: "value1", header2: "value2" } }; expect(fetchMock.mock.calls.length).toBe(1); @@ -82,23 +78,27 @@ describe("Utils tests", () => { describe("When receiving json format", () => { test("Returns undefined when no data is received", async () => { - const response = await performWebRequest(url); + urlToCall = "www.test.com"; + + const response = await performWebRequest(urlToCall); expect(response).toBe(undefined); }); test("Returns object when data is received", async () => { + urlToCall = "www.test.com"; fetchResponse = new Response('{"body": "some content"}'); - const response = await performWebRequest(url); + const response = await performWebRequest(urlToCall); expect(response.body).toBe("some content"); }); test("Returns expected headers when data is received", async () => { + urlToCall = "www.test.com"; fetchResponse = new Response('{"body": "some content"}', { headers: { header1: "value1", header2: "value2" } }); - const response = await performWebRequest(url, "json", false, undefined, ["header1"]); + const response = await performWebRequest(urlToCall, "json", false, undefined, ["header1"]); expect(response.headers.length).toBe(1); expect(response.headers[0].name).toBe("header1"); @@ -109,4 +109,64 @@ describe("Utils tests", () => { test("Always ok, need one test", () => {}); } }); + + describe("formatTime", () => { + const time = new Date(); + + beforeEach(async () => { + time.setHours(13, 13); + }); + + it("should convert correctly according to the config", () => { + expect( + formatTime( + { + timeFormat: 24 + }, + time + ) + ).toBe("13:13"); + expect( + formatTime( + { + showPeriod: true, + showPeriodUpper: true, + timeFormat: 12 + }, + time + ) + ).toBe("1:13 PM"); + expect( + formatTime( + { + showPeriod: true, + showPeriodUpper: false, + timeFormat: 12 + }, + time + ) + ).toBe("1:13 pm"); + expect( + formatTime( + { + showPeriod: false, + timeFormat: 12 + }, + time + ) + ).toBe("1:13"); + }); + + it("should convert correctly into another timezone", () => { + expect( + formatTime( + { + timeFormat: 24, + timezone: "America/Toronto" + }, + time + ) + ).toBe("07:13"); + }); + }); }); diff --git a/tests/unit/functions/weather_object_spec.js b/tests/unit/modules/default/weather/weather_object_spec.js similarity index 51% rename from tests/unit/functions/weather_object_spec.js rename to tests/unit/modules/default/weather/weather_object_spec.js index 5f9444e457..2bc87d78e3 100644 --- a/tests/unit/functions/weather_object_spec.js +++ b/tests/unit/modules/default/weather/weather_object_spec.js @@ -1,5 +1,4 @@ -const WeatherObject = require("../../../modules/default/weather/weatherobject"); -const WeatherUtils = require("../../../modules/default/weather/weatherutils"); +const WeatherObject = require("../../../../../modules/default/weather/weatherobject"); global.moment = require("moment-timezone"); global.SunCalc = require("suncalc"); @@ -47,38 +46,3 @@ describe("WeatherObject", () => { moment.tz.setDefault(originalTimeZone); }); }); - -describe("WeatherUtils", () => { - it("should convert windspeed correctly from mps to beaufort", () => { - expect(Math.round(WeatherUtils.convertWind(5, "beaufort"))).toBe(3); - expect(Math.round(WeatherUtils.convertWind(300, "beaufort"))).toBe(12); - }); - - it("should convert windspeed correctly from mps to kmh", () => { - expect(Math.round(WeatherUtils.convertWind(11.75, "kmh"))).toBe(42); - }); - - it("should convert windspeed correctly from mps to knots", () => { - expect(Math.round(WeatherUtils.convertWind(10, "knots"))).toBe(19); - }); - - it("should convert windspeed correctly from mph to mps", () => { - expect(Math.round(WeatherUtils.convertWindToMetric(93.951324266285))).toBe(42); - }); - - it("should convert windspeed correctly from kmh to mps", () => { - expect(Math.round(WeatherUtils.convertWindToMs(151.2))).toBe(42); - }); - - it("should convert wind direction correctly from cardinal to value", () => { - expect(WeatherUtils.convertWindDirection("SSE")).toBe(157); - }); - - it("should return a calculated feelsLike info", () => { - expect(WeatherUtils.calculateFeelsLike(0, 20, 40)).toBe(-9.444444444444445); - }); - - it("should return a calculated feelsLike info", () => { - expect(WeatherUtils.calculateFeelsLike(30, 0, 60)).toBe(32.8320322777777); - }); -}); diff --git a/tests/unit/modules/default/weather/weather_utils_spec.js b/tests/unit/modules/default/weather/weather_utils_spec.js index 8aefd3c9fe..8eae07cf1d 100644 --- a/tests/unit/modules/default/weather/weather_utils_spec.js +++ b/tests/unit/modules/default/weather/weather_utils_spec.js @@ -1,45 +1,99 @@ const weather = require("../../../../../modules/default/weather/weatherutils"); +const WeatherUtils = require("../../../../../modules/default/weather/weatherutils"); describe("Weather utils tests", () => { - describe("convertPrecipitationUnit tests", () => { - it("Should keep value and unit if outputUnit is undefined", () => { + describe("windspeed conversion", () => { + it("should convert windspeed correctly from mps to beaufort", () => { + expect(Math.round(WeatherUtils.convertWind(5, "beaufort"))).toBe(3); + expect(Math.round(WeatherUtils.convertWind(300, "beaufort"))).toBe(12); + }); + + it("should convert windspeed correctly from mps to mps", () => { + expect(WeatherUtils.convertWind(11.75, "FOOBAR")).toBe(11.75); + }); + + it("should convert windspeed correctly from mps to kmh", () => { + expect(Math.round(WeatherUtils.convertWind(11.75, "kmh"))).toBe(42); + }); + + it("should convert windspeed correctly from mps to knots", () => { + expect(Math.round(WeatherUtils.convertWind(10, "knots"))).toBe(19); + }); + + it("should convert windspeed correctly from mph to mps", () => { + expect(Math.round(WeatherUtils.convertWindToMetric(93.951324266285))).toBe(42); + }); + + it("should convert windspeed correctly from kmh to mps", () => { + expect(Math.round(WeatherUtils.convertWindToMs(151.2))).toBe(42); + }); + }); + + describe("wind direction conversion", () => { + it("should convert wind direction correctly from cardinal to value", () => { + expect(WeatherUtils.convertWindDirection("SSE")).toBe(157); + }); + }); + + describe("feelsLike calculation", () => { + it("should return a calculated feelsLike info", () => { + expect(WeatherUtils.calculateFeelsLike(0, 20, 40)).toBe(-9.444444444444445); + }); + + it("should return a calculated feelsLike info", () => { + expect(WeatherUtils.calculateFeelsLike(30, 0, 60)).toBe(32.8320322777777); + }); + }); + + describe("precipitationUnit conversion", () => { + it("should keep value and unit if outputUnit is undefined", () => { const values = [1, 2]; const units = ["mm", "cm"]; for (let i = 0; i < values.length; i++) { - var result = weather.convertPrecipitationUnit(values[i], units[i], undefined); + const result = weather.convertPrecipitationUnit(values[i], units[i], undefined); expect(result).toBe(`${values[i].toFixed(2)} ${units[i]}`); } }); - it("Should keep value and unit if outputUnit is metric", () => { + it("should keep value and unit if outputUnit is metric", () => { const values = [1, 2]; const units = ["mm", "cm"]; for (let i = 0; i < values.length; i++) { - var result = weather.convertPrecipitationUnit(values[i], units[i], "metric"); + const result = weather.convertPrecipitationUnit(values[i], units[i], "metric"); expect(result).toBe(`${values[i].toFixed(2)} ${units[i]}`); } }); - it("Should use mm unit if input unit is undefined", () => { + it("should use mm unit if input unit is undefined", () => { const values = [1, 2]; for (let i = 0; i < values.length; i++) { - var result = weather.convertPrecipitationUnit(values[i], undefined, "metric"); + const result = weather.convertPrecipitationUnit(values[i], undefined, "metric"); expect(result).toBe(`${values[i].toFixed(2)} mm`); } }); - it("Should convert value and unit if outputUnit is imperial", () => { + it("should convert value and unit if outputUnit is imperial", () => { const values = [1, 2]; const units = ["mm", "cm"]; const expectedValues = [0.04, 0.79]; for (let i = 0; i < values.length; i++) { - var result = weather.convertPrecipitationUnit(values[i], units[i], "imperial"); + const result = weather.convertPrecipitationUnit(values[i], units[i], "imperial"); expect(result).toBe(`${expectedValues[i]} in`); } }); + + it("should round percentage values regardless of output units", () => { + const values = [0.1, 2.22, 9.999]; + const output = [undefined, "imperial", "metric"]; + const result = ["0 %", "2 %", "10 %"]; + + for (let i = 0; i < values.length; i++) { + expect(weather.convertPrecipitationUnit(values[i], "%", output[i])).toBe(result[i]); + } + }); }); }); diff --git a/th.json b/th.json deleted file mode 100644 index 26f472b7b9..0000000000 --- a/th.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "LOADING": "กำลังโหลด …", - - "TODAY": "วันนี้", - "TOMORROW": "พรุ่งนี้", - "RUNNING": "สิ้นสุดใน", - "EMPTY": "ไม่มีกิจกรรมที่กำลังจะมาถึง", - "WEEK": "สัปดาห์ที่ {weekNumber}", - - "N": "น", - "NNE": "น.ต.อ.น.", - "NE": "ต.อ.น.", - "ENE": "ต.อ.ต.อ.น.", - "E": "ต.อ.", - "ESE": "ต.อ.ต.อ.ต.", - "SE": "ต.อ.ต.", - "SSE": "ต.ต.อ.ต.", - "S": "ต.", - "SSW": "ต.ต.ต.ต.", - "SW": "ต.ต.ต.", - "WSW": "ต.ต.ต.ต.ต.", - "W": "ต.ต.", - "WNW": "ต.ต.ต.ต.น.", - "NW": "ต.ต.น.", - "NNW": "น.ต.ต.น.", - - "PRECIP_POP": "ความแม่นยำ", - "PRECIP_AMOUNT": "ปริมาณน้ำฝน", - - "MODULE_CONFIG_CHANGED": "ตัวเลือกการกำหนดค่าสำหรับโมดูล {MODULE_NAME} มีการเปลี่ยนแปลง\nโปรดตรวจสอบเอกสารประกอบ", - "MODULE_CONFIG_ERROR": "เกิดข้อผิดพลาดในโมดูล {MODULE_NAME} {ERROR}", - "MODULE_ERROR_MALFORMED_URL": "URL ผิดรูปแบบ", - "MODULE_ERROR_NO_CONNECTION": "ไม่มีการเชื่อมต่ออินเทอร์เน็ต.", - "MODULE_ERROR_UNAUTHORIZED": "การอนุญาตล้มเหลว", - "MODULE_ERROR_UNSPECIFIED": "ตรวจสอบบันทึกสำหรับรายละเอียดเพิ่มเติม", - - "NEWSFEED_NO_ITEMS": "ไม่มีข่าวในขณะนี้", - - "UPDATE_NOTIFICATION": "MagicMirror² มีการอัปเดต", - "UPDATE_NOTIFICATION_MODULE": "มีการอัปเดตสำหรับโมดูล {MODULE_NAME}", - "UPDATE_INFO_SINGLE": "การติดตั้งปัจจุบันถูกคอมมิท {COMMIT_COUNT} รายการในสาขา {BRANCH_NAME}", - "UPDATE_INFO_MULTIPLE": "การติดตั้งปัจจุบันคือ {COMMIT_COUNT} เป็นคอมมิทที่อยู่เบื้องหลังในสาขา {BRANCH_NAME}" -} diff --git a/vendor/package-lock.json b/vendor/package-lock.json index 3ee7a27516..f8d0cadee0 100644 --- a/vendor/package-lock.json +++ b/vendor/package-lock.json @@ -7,18 +7,18 @@ "name": "magicmirror-vendors", "license": "MIT", "dependencies": { - "@fortawesome/fontawesome-free": "^6.3.0", + "@fortawesome/fontawesome-free": "^6.4.0", "moment": "^2.29.4", - "moment-timezone": "^0.5.41", - "nunjucks": "^3.2.3", + "moment-timezone": "^0.5.43", + "nunjucks": "^3.2.4", "suncalc": "^1.9.0", "weathericons": "^2.1.0" } }, "node_modules/@fortawesome/fontawesome-free": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.3.0.tgz", - "integrity": "sha512-qVtd5i1Cc7cdrqnTWqTObKQHjPWAiRwjUPaXObaeNPcy7+WKxJumGBx66rfSFgK6LNpIasVKkEgW8oyf0tmPLA==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz", + "integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==", "hasInstallScript": true, "engines": { "node": ">=6" @@ -51,9 +51,9 @@ } }, "node_modules/moment-timezone": { - "version": "0.5.41", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.41.tgz", - "integrity": "sha512-e0jGNZDOHfBXJGz8vR/sIMXvBIGJJcqFjmlg9lmE+5KX1U7/RZNMswfD8nKnNCnQdKTIj50IaRKwl1fvMLyyRg==", + "version": "0.5.43", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", + "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", "dependencies": { "moment": "^2.29.4" }, @@ -62,9 +62,9 @@ } }, "node_modules/nunjucks": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", - "integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", + "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", "dependencies": { "a-sync-waterfall": "^1.0.0", "asap": "^2.0.3", @@ -98,9 +98,9 @@ }, "dependencies": { "@fortawesome/fontawesome-free": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.3.0.tgz", - "integrity": "sha512-qVtd5i1Cc7cdrqnTWqTObKQHjPWAiRwjUPaXObaeNPcy7+WKxJumGBx66rfSFgK6LNpIasVKkEgW8oyf0tmPLA==" + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.4.0.tgz", + "integrity": "sha512-0NyytTlPJwB/BF5LtRV8rrABDbe3TdTXqNB3PdZ+UUUZAEIrdOJdmABqKjt4AXwIoJNaRVVZEXxpNrqvE1GAYQ==" }, "a-sync-waterfall": { "version": "1.0.1", @@ -123,17 +123,17 @@ "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" }, "moment-timezone": { - "version": "0.5.41", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.41.tgz", - "integrity": "sha512-e0jGNZDOHfBXJGz8vR/sIMXvBIGJJcqFjmlg9lmE+5KX1U7/RZNMswfD8nKnNCnQdKTIj50IaRKwl1fvMLyyRg==", + "version": "0.5.43", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", + "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", "requires": { "moment": "^2.29.4" } }, "nunjucks": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", - "integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", + "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", "requires": { "a-sync-waterfall": "^1.0.0", "asap": "^2.0.3", diff --git a/vendor/package.json b/vendor/package.json index b83dd916c5..7e0de4115b 100644 --- a/vendor/package.json +++ b/vendor/package.json @@ -10,10 +10,10 @@ "url": "https://github.com/MichMich/MagicMirror/issues" }, "dependencies": { - "@fortawesome/fontawesome-free": "^6.3.0", + "@fortawesome/fontawesome-free": "^6.4.0", "moment": "^2.29.4", - "moment-timezone": "^0.5.41", - "nunjucks": "^3.2.3", + "moment-timezone": "^0.5.43", + "nunjucks": "^3.2.4", "suncalc": "^1.9.0", "weathericons": "^2.1.0" }