Bump stylistic-eslint (#3520)

updates plugin and adjust docs and config for smooth cleaning :-D

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
Veeck
2024-08-12 22:52:43 +02:00
committed by GitHub
parent 780e4e2e06
commit 976c8ae00a
32 changed files with 700 additions and 565 deletions

View File

@@ -49,6 +49,7 @@
"@stylistic/function-paren-newline": ["error", "consistent"],
"@stylistic/implicit-arrow-linebreak": ["error", "beside"],
"@stylistic/max-statements-per-line": ["error", { "max": 2 }],
"@stylistic/multiline-comment-style": "off",
"@stylistic/multiline-ternary": ["error", "always-multiline"],
"@stylistic/newline-per-chained-call": ["error", { "ignoreChainWithDepth": 4 }],
"@stylistic/no-extra-parens": "off",

View File

@@ -19,7 +19,7 @@ _This release is scheduled to be released on 2024-10-01._
### Updated
- [weather] Updated `apiVersion` default from 2.5 to 3.0 (#3424)
- [core] Updated dependencies
- [core] Updated dependencies including stylistic-eslint
- [core] Allow custom module positions by setting `allowCustomModulePositions` in `config.js` (fixes #3504, related to https://github.com/MagicMirrorOrg/MagicMirror/pull/3445)
### Fixed

View File

@@ -1,6 +1,7 @@
/* global Class, xyz */
/* Simple JavaScript Inheritance
/*
* Simple JavaScript Inheritance
* By John Resig https://johnresig.com/
*
* Inspired by base2 and Prototype
@@ -22,8 +23,10 @@
Class.extend = function (prop) {
let _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
/*
* Instantiate a base class (but only create the instance,
* don't run the init constructor)
*/
initializing = true;
const prototype = new this();
initializing = false;
@@ -42,12 +45,16 @@
return function () {
const tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
/*
* Add a new ._super() method that is the same method
* but on the super-class
*/
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
/*
* The method only need to be bound temporarily, so we
* remove it when we're done executing
*/
const ret = fn.apply(this, arguments);
this._super = tmp;

View File

@@ -8,9 +8,12 @@ const Log = require("./logger");
let config = process.env.config ? JSON.parse(process.env.config) : {};
// Module to control application life.
const app = electron.app;
// Per default electron is started with --disable-gpu flag, if you want the gpu enabled,
// you must set the env var ELECTRON_ENABLE_GPU=1 on startup.
// See https://www.electronjs.org/docs/latest/tutorial/offscreen-rendering for more info.
/*
* Per default electron is started with --disable-gpu flag, if you want the gpu enabled,
* you must set the env var ELECTRON_ENABLE_GPU=1 on startup.
* See https://www.electronjs.org/docs/latest/tutorial/offscreen-rendering for more info.
*/
if (process.env.ELECTRON_ENABLE_GPU !== "1") {
app.disableHardwareAcceleration();
}
@@ -18,16 +21,21 @@ if (process.env.ELECTRON_ENABLE_GPU !== "1") {
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
/*
* Keep a global reference of the window object, if you don't, the window will
* be closed automatically when the JavaScript object is garbage collected.
*/
let mainWindow;
/**
*
*/
function createWindow () {
// see https://www.electronjs.org/docs/latest/api/screen
// Create a window that fills the screen's available work area.
/*
* see https://www.electronjs.org/docs/latest/api/screen
* Create a window that fills the screen's available work area.
*/
let electronSize = (800, 600);
try {
electronSize = electron.screen.getPrimaryDisplay().workAreaSize;
@@ -52,8 +60,10 @@ function createWindow () {
backgroundColor: "#000000"
};
// DEPRECATED: "kioskmode" backwards compatibility, to be removed
// settings these options directly instead provides cleaner interface
/*
* DEPRECATED: "kioskmode" backwards compatibility, to be removed
* settings these options directly instead provides cleaner interface
*/
if (config.kioskmode) {
electronOptionsDefaults.kiosk = true;
} else {
@@ -69,8 +79,10 @@ function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow(electronOptions);
// and load the index.html of the app.
// If config.address is not defined or is an empty string (listening on all interfaces), connect to localhost
/*
* and load the index.html of the app.
* If config.address is not defined or is an empty string (listening on all interfaces), connect to localhost
*/
let prefix;
if ((config["tls"] !== null && config["tls"]) || config.useHttps) {
@@ -149,14 +161,18 @@ app.on("window-all-closed", function () {
});
app.on("activate", function () {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
/*
* On OS X it's common to re-create a window in the app when the
* dock icon is clicked and there are no other windows open.
*/
if (mainWindow === null) {
createWindow();
}
});
/* This method will be called when SIGINT is received and will call
/*
* This method will be called when SIGINT is received and will call
* each node_helper's stop function if it exists. Added to fix #1056
*
* Note: this is only used if running Electron. Otherwise
@@ -187,8 +203,10 @@ if (process.env.clientonly) {
});
}
// Start the core application if server is run on localhost
// This starts all node helpers and starts the webserver.
/*
* 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;

View File

@@ -1,13 +1,16 @@
/* global Class, cloneObject, Loader, MMSocket, nunjucks, Translator */
/* Module Blueprint.
/*
* Module Blueprint.
* @typedef {Object} Module
*/
const Module = Class.extend({
/*********************************************************
/**
********************************************************
* All methods (and properties) below can be subclassed. *
*********************************************************/
********************************************************
*/
// Set the minimum MagicMirror² module version for this module.
requiresVersion: "2.0.0",
@@ -18,13 +21,17 @@ const Module = Class.extend({
// Timer reference used for showHide animation callbacks.
showHideTimer: null,
// Array to store lockStrings. These strings are used to lock
// visibility when hiding and showing module.
/*
* Array to store lockStrings. These strings are used to lock
* visibility when hiding and showing module.
*/
lockStrings: [],
// Storage of the nunjucks Environment,
// This should not be referenced directly.
// Use the nunjucksEnvironment() to get it.
/*
* Storage of the nunjucks Environment,
* This should not be referenced directly.
* Use the nunjucksEnvironment() to get it.
*/
_nunjucksEnvironment: null,
/**
@@ -189,9 +196,11 @@ const Module = Class.extend({
Log.log(`${this.name} is resumed.`);
},
/*********************************************
/**
********************************************
* The methods below don't need subclassing. *
*********************************************/
********************************************
*/
/**
* Set the module data.

View File

@@ -49,7 +49,8 @@ const NodeHelper = Class.extend({
this.path = path;
},
/* sendSocketNotification(notification, payload)
/*
* sendSocketNotification(notification, payload)
* Send a socket notification to the node helper.
*
* argument notification string - The identifier of the notification.
@@ -59,7 +60,8 @@ const NodeHelper = Class.extend({
this.io.of(this.name).emit(notification, payload);
},
/* setExpressApp(app)
/*
* setExpressApp(app)
* Sets the express app object for this module.
* This allows you to host files from the created webserver.
*
@@ -71,7 +73,8 @@ const NodeHelper = Class.extend({
app.use(`/${this.name}`, express.static(`${this.path}/public`));
},
/* setSocketIO(io)
/*
* setSocketIO(io)
* Sets the socket io object for this module.
* Binds message receiver.
*

View File

@@ -82,9 +82,12 @@ Module.register("calendar", {
// Define required translations.
getTranslations () {
// The translations for the default modules are defined in the core translation files.
// Therefore we can just return false. Otherwise we should have returned a dictionary.
// If you're trying to build your own module including translations, check out the documentation.
/*
* The translations for the default modules are defined in the core translation files.
* Therefore we can just return false. Otherwise we should have returned a dictionary.
* If you're trying to build your own module including translations, check out the documentation.
*/
return false;
},
@@ -148,8 +151,10 @@ Module.register("calendar", {
};
}
// tell helper to start a fetcher for this calendar
// fetcher till cycle
/*
* tell helper to start a fetcher for this calendar
* fetcher till cycle
*/
this.addCalendar(calendar.url, calendar.auth, calendarConfig);
});
@@ -627,7 +632,8 @@ Module.register("calendar", {
event.tomorrow = !event.today && event.startDate >= today + ONE_DAY && event.startDate < today + 2 * ONE_DAY;
event.dayAfterTomorrow = !event.tomorrow && event.startDate >= today + ONE_DAY * 2 && event.startDate < today + 3 * ONE_DAY;
/* if sliceMultiDayEvents is set to true, multiday events (events exceeding at least one midnight) are sliced into days,
/*
* if sliceMultiDayEvents is set to true, multiday events (events exceeding at least one midnight) are sliced into days,
* otherwise, esp. in dateheaders mode it is not clear how long these events are.
*/
const maxCount = Math.ceil((event.endDate - 1 - moment(event.startDate, "x").endOf("day").format("x")) / ONE_DAY) + 1;
@@ -677,16 +683,21 @@ Module.register("calendar", {
return events;
}
// Limit the number of days displayed
// If limitDays is set > 0, limit display to that number of days
/*
* Limit the number of days displayed
* If limitDays is set > 0, limit display to that number of days
*/
if (this.config.limitDays > 0) {
let newEvents = [];
let lastDate = today.clone().subtract(1, "days").format("YYYYMMDD");
let days = 0;
for (const ev of events) {
let eventDate = moment(ev.startDate, "x").format("YYYYMMDD");
// if date of event is later than lastdate
// check if we already are showing max unique days
/*
* if date of event is later than lastdate
* check if we already are showing max unique days
*/
if (eventDate > lastDate) {
// if the only entry in the first day is a full day event that day is not counted as unique
if (!this.config.limitDaysNeverSkip && newEvents.length === 1 && days === 1 && newEvents[0].fullDayEvent) {

View File

@@ -1,4 +1,5 @@
/* CalendarFetcher Tester
/*
* CalendarFetcher Tester
* use this script with `node debug.js` to test the fetcher without the need
* of starting the MagicMirror² core. Adjust the values below to your desire.
*/

View File

@@ -1,4 +1,5 @@
/* Default Modules List
/*
* Default Modules List
* Modules listed below can be loaded without the 'default/' prefix. Omitting the default folder name.
*/
const defaultModules = ["alert", "calendar", "clock", "compliments", "helloworld", "newsfeed", "updatenotification", "weather"];

View File

@@ -4,7 +4,8 @@ const fs = require("node:fs");
const Log = require("logger");
/* class Updater
/*
* class Updater
* Allow to self updating 3rd party modules from command defined in config
*
* [constructor] read value in config:
@@ -84,13 +85,15 @@ class Updater {
return updater;
}
// module updater with his proper command
// return object as result
//{
// error: <boolean>, // if error detected
// updated: <boolean>, // if updated successfully
// needRestart: <boolean> // if magicmirror restart required
//};
/*
* module updater with his proper command
* return object as result
* {
* error: <boolean>, // if error detected
* updated: <boolean>, // if updated successfully
* needRestart: <boolean> // if magicmirror restart required
* };
*/
updateProcess (module) {
let Result = {
error: false,

View File

@@ -1,6 +1,7 @@
/* global WeatherProvider, WeatherObject, WeatherUtils */
/* This class is a provider for Environment Canada MSC Datamart
/*
* This class is a provider for Environment Canada MSC Datamart
* Note that this is only for Canadian locations and does not require an API key (access is anonymous)
*
* EC Documentation at following links:
@@ -25,9 +26,7 @@
*
* License to use Environment Canada (EC) data is detailed here:
* https://eccc-msc.github.io/open-data/licence/readme_en/
*
*/
WeatherProvider.register("envcanada", {
// Set the name of the provider for debugging and alerting purposes (eg. provide eye-catcher)
providerName: "Environment Canada",
@@ -39,10 +38,10 @@ WeatherProvider.register("envcanada", {
provCode: "ON"
},
//
// Set config values (equates to weather module config values). Also set values pertaining to caching of
// Today's temperature forecast (for use in the Forecast functions below)
//
/*
* Set config values (equates to weather module config values). Also set values pertaining to caching of
* Today's temperature forecast (for use in the Forecast functions below)
*/
setConfig (config) {
this.config = config;
@@ -52,17 +51,17 @@ WeatherProvider.register("envcanada", {
this.cacheCurrentTemp = 999;
},
//
// Called when the weather provider is started
//
/*
* Called when the weather provider is started
*/
start () {
Log.info(`Weather provider: ${this.providerName} started.`);
this.setFetchedLocation(this.config.location);
},
//
// Override the fetchCurrentWeather method to query EC and construct a Current weather object
//
/*
* Override the fetchCurrentWeather method to query EC and construct a Current weather object
*/
fetchCurrentWeather () {
this.fetchData(this.getUrl(), "xml")
.then((data) => {
@@ -80,9 +79,9 @@ WeatherProvider.register("envcanada", {
.finally(() => this.updateAvailable());
},
//
// Override the fetchWeatherForecast method to query EC and construct Forecast weather objects
//
/*
* Override the fetchWeatherForecast method to query EC and construct Forecast weather objects
*/
fetchWeatherForecast () {
this.fetchData(this.getUrl(), "xml")
.then((data) => {
@@ -100,9 +99,9 @@ WeatherProvider.register("envcanada", {
.finally(() => this.updateAvailable());
},
//
// Override the fetchWeatherHourly method to query EC and construct Forecast weather objects
//
/*
* Override the fetchWeatherHourly method to query EC and construct Forecast weather objects
*/
fetchWeatherHourly () {
this.fetchData(this.getUrl(), "xml")
.then((data) => {
@@ -120,36 +119,30 @@ WeatherProvider.register("envcanada", {
.finally(() => this.updateAvailable());
},
//////////////////////////////////////////////////////////////////////////////////
//
// Environment Canada methods - not part of the standard Provider methods
//
//////////////////////////////////////////////////////////////////////////////////
//
// Build the EC URL based on the Site Code and Province Code specified in the config params. Note that the
// URL defaults to the English version simply because there is no language dependency in the data
// being accessed. This is only pertinent when using the EC data elements that contain a textual forecast.
//
/*
* Build the EC URL based on the Site Code and Province Code specified in the config params. Note that the
* URL defaults to the English version simply because there is no language dependency in the data
* being accessed. This is only pertinent when using the EC data elements that contain a textual forecast.
*/
getUrl () {
return `https://dd.weather.gc.ca/citypage_weather/xml/${this.config.provCode}/${this.config.siteCode}_e.xml`;
},
//
// Generate a WeatherObject based on current EC weather conditions
//
/*
* Generate a WeatherObject based on current EC weather conditions
*/
generateWeatherObjectFromCurrentWeather (ECdoc) {
const currentWeather = new WeatherObject();
// There are instances where EC will update weather data and current temperature will not be
// provided. While this is a defect in the EC systems, we need to accommodate to avoid a current temp
// of NaN being displayed. Therefore... whenever we get a valid current temp from EC, we will cache
// the value. Whenever EC data is missing current temp, we will provide the cached value
// instead. This is reasonable since the cached value will typically be accurate within the previous
// hour. The only time this does not work as expected is when MM is restarted and the first query to
// EC finds no current temp. In this scenario, MM will end up displaying a current temp of null;
/*
* There are instances where EC will update weather data and current temperature will not be
* provided. While this is a defect in the EC systems, we need to accommodate to avoid a current temp
* of NaN being displayed. Therefore... whenever we get a valid current temp from EC, we will cache
* the value. Whenever EC data is missing current temp, we will provide the cached value
* instead. This is reasonable since the cached value will typically be accurate within the previous
* hour. The only time this does not work as expected is when MM is restarted and the first query to
* EC finds no current temp. In this scenario, MM will end up displaying a current temp of null;
*/
if (ECdoc.querySelector("siteData currentConditions temperature").textContent) {
currentWeather.temperature = ECdoc.querySelector("siteData currentConditions temperature").textContent;
this.cacheCurrentTemp = currentWeather.temperature;
@@ -163,19 +156,19 @@ WeatherProvider.register("envcanada", {
currentWeather.humidity = ECdoc.querySelector("siteData currentConditions relativeHumidity").textContent;
// Ensure showPrecipitationAmount is forced to false. EC does not really provide POP for current day
// and this feature for the weather module (current only) is sort of broken in that it wants
// to say POP but will display precip as an accumulated amount vs. a percentage.
/*
* Ensure showPrecipitationAmount is forced to false. EC does not really provide POP for current day
* and this feature for the weather module (current only) is sort of broken in that it wants
* to say POP but will display precip as an accumulated amount vs. a percentage.
*/
this.config.showPrecipitationAmount = false;
//
// If the module config wants to showFeelsLike... default to the current temperature.
// Check for EC wind chill and humidex values and overwrite the feelsLikeTemp value.
// This assumes that the EC current conditions will never contain both a wind chill
// and humidex temperature.
//
/*
* If the module config wants to showFeelsLike... default to the current temperature.
* Check for EC wind chill and humidex values and overwrite the feelsLikeTemp value.
* This assumes that the EC current conditions will never contain both a wind chill
* and humidex temperature.
*/
if (this.config.showFeelsLike) {
currentWeather.feelsLikeTemp = currentWeather.temperature;
@@ -188,16 +181,10 @@ WeatherProvider.register("envcanada", {
}
}
//
// Need to map EC weather icon to MM weatherType values
//
currentWeather.weatherType = this.convertWeatherType(ECdoc.querySelector("siteData currentConditions iconCode").textContent);
//
// Capture the sunrise and sunset values from EC data
//
const sunList = ECdoc.querySelectorAll("siteData riseSet dateTime");
currentWeather.sunrise = moment(sunList[1].querySelector("timeStamp").textContent, "YYYYMMDDhhmmss");
@@ -206,13 +193,11 @@ WeatherProvider.register("envcanada", {
return currentWeather;
},
//
// Generate an array of WeatherObjects based on EC weather forecast
//
/*
* Generate an array of WeatherObjects based on EC weather forecast
*/
generateWeatherObjectsFromForecast (ECdoc) {
// Declare an array to hold each day's forecast object
const days = [];
const weather = new WeatherObject();
@@ -226,37 +211,33 @@ WeatherProvider.register("envcanada", {
weather.precipitationAmount = null;
//
// The EC forecast is held in a 12-element array - Elements 0 to 11 - with each day encompassing
// 2 elements. the first element for a day details the Today (daytime) forecast while the second
// element details the Tonight (nightime) forecast. Element 0 is always for the current day.
//
// However... the forecast is somewhat 'rolling'.
//
// If the EC forecast is queried in the morning, then Element 0 will contain Current
// Today and Element 1 will contain Current Tonight. From there, the next 5 days of forecast will be
// contained in Elements 2/3, 4/5, 6/7, 8/9, and 10/11. This module will create a 6-day forecast using
// all of these Elements.
//
// But, if the EC forecast is queried in late afternoon, the Current Today forecast will be rolled
// off and Element 0 will contain Current Tonight. From there, the next 5 days will be contained in
// Elements 1/2, 3/4, 5/6, 7/8, and 9/10. As well, Elelement 11 will contain a forecast for a 6th day,
// but only for the Today portion (not Tonight). This module will create a 6-day forecast using
// Elements 0 to 11, and will ignore the additional Todat forecast in Element 11.
//
// We need to determine if Element 0 is showing the forecast for Current Today or Current Tonight.
// This is required to understand how Min and Max temperature will be determined, and to understand
// where the next day's (aka Tomorrow's) forecast is located in the forecast array.
//
/*
* The EC forecast is held in a 12-element array - Elements 0 to 11 - with each day encompassing
* 2 elements. the first element for a day details the Today (daytime) forecast while the second
* element details the Tonight (nightime) forecast. Element 0 is always for the current day.
*
* However... the forecast is somewhat 'rolling'.
*
* If the EC forecast is queried in the morning, then Element 0 will contain Current
* Today and Element 1 will contain Current Tonight. From there, the next 5 days of forecast will be
* contained in Elements 2/3, 4/5, 6/7, 8/9, and 10/11. This module will create a 6-day forecast using
* all of these Elements.
*
* But, if the EC forecast is queried in late afternoon, the Current Today forecast will be rolled
* off and Element 0 will contain Current Tonight. From there, the next 5 days will be contained in
* Elements 1/2, 3/4, 5/6, 7/8, and 9/10. As well, Elelement 11 will contain a forecast for a 6th day,
* but only for the Today portion (not Tonight). This module will create a 6-day forecast using
* Elements 0 to 11, and will ignore the additional Todat forecast in Element 11.
*
* We need to determine if Element 0 is showing the forecast for Current Today or Current Tonight.
* This is required to understand how Min and Max temperature will be determined, and to understand
* where the next day's (aka Tomorrow's) forecast is located in the forecast array.
*/
let nextDay = 0;
let lastDay = 0;
const currentTemp = ECdoc.querySelector("siteData currentConditions temperature").textContent;
//
// If the first Element is Current Today, look at Current Today and Current Tonight for the current day.
//
if (foreGroup[0].querySelector("period[textForecastName='Today']")) {
this.todaytempCacheMin = 0;
this.todaytempCacheMax = 0;
@@ -266,167 +247,144 @@ WeatherProvider.register("envcanada", {
this.setPrecipitation(weather, foreGroup, 0);
//
// Set the Element number that will reflect where the next day's forecast is located. Also set
// the Element number where the end of the forecast will be. This is important because of the
// rolling nature of the EC forecast. In the current scenario (Today and Tonight are present
// in elements 0 and 11, we know that we will have 6 full days of forecasts and we will use
// them. We will set lastDay such that we iterate through all 12 elements of the forecast.
//
/*
* Set the Element number that will reflect where the next day's forecast is located. Also set
* the Element number where the end of the forecast will be. This is important because of the
* rolling nature of the EC forecast. In the current scenario (Today and Tonight are present
* in elements 0 and 11, we know that we will have 6 full days of forecasts and we will use
* them. We will set lastDay such that we iterate through all 12 elements of the forecast.
*/
nextDay = 2;
lastDay = 12;
}
//
// If the first Element is Current Tonight, look at Tonight only for the current day.
//
if (foreGroup[0].querySelector("period[textForecastName='Tonight']")) {
this.setMinMaxTemps(weather, foreGroup, 0, false, currentTemp);
this.setPrecipitation(weather, foreGroup, 0);
//
// Set the Element number that will reflect where the next day's forecast is located. Also set
// the Element number where the end of the forecast will be. This is important because of the
// rolling nature of the EC forecast. In the current scenario (only Current Tonight is present
// in Element 0, we know that we will have 6 full days of forecasts PLUS a half-day and
// forecast in the final element. Because we will only use full day forecasts, we set the
// lastDay number to ensure we ignore that final half-day (in forecast Element 11).
//
/*
* Set the Element number that will reflect where the next day's forecast is located. Also set
* the Element number where the end of the forecast will be. This is important because of the
* rolling nature of the EC forecast. In the current scenario (only Current Tonight is present
* in Element 0, we know that we will have 6 full days of forecasts PLUS a half-day and
* forecast in the final element. Because we will only use full day forecasts, we set the
* lastDay number to ensure we ignore that final half-day (in forecast Element 11).
*/
nextDay = 1;
lastDay = 11;
}
//
// Need to map EC weather icon to MM weatherType values. Always pick the first Element's icon to
// reflect either Today or Tonight depending on what the forecast is showing in Element 0.
//
/*
* Need to map EC weather icon to MM weatherType values. Always pick the first Element's icon to
* reflect either Today or Tonight depending on what the forecast is showing in Element 0.
*/
weather.weatherType = this.convertWeatherType(foreGroup[0].querySelector("abbreviatedForecast iconCode").textContent);
// Push the weather object into the forecast array.
days.push(weather);
//
// Now do the rest of the forecast starting at nextDay. We will process each day using 2 EC
// forecast Elements. This will address the fact that the EC forecast always includes Today and
// Tonight for each day. This is why we iterate through the forecast by a a count of 2, with each
// iteration looking at the current Element and the next Element.
//
/*
* Now do the rest of the forecast starting at nextDay. We will process each day using 2 EC
* forecast Elements. This will address the fact that the EC forecast always includes Today and
* Tonight for each day. This is why we iterate through the forecast by a a count of 2, with each
* iteration looking at the current Element and the next Element.
*/
let lastDate = moment(baseDate, "YYYYMMDDhhmmss");
for (let stepDay = nextDay; stepDay < lastDay; stepDay += 2) {
let weather = new WeatherObject();
// Add 1 to the date to reflect the current forecast day we are building
lastDate = lastDate.add(1, "day");
weather.date = moment(lastDate);
// Capture the temperatures for the current Element and the next Element in order to set
// the Min and Max temperatures for the forecast
/*
* Capture the temperatures for the current Element and the next Element in order to set
* the Min and Max temperatures for the forecast
*/
this.setMinMaxTemps(weather, foreGroup, stepDay, true, currentTemp);
weather.precipitationAmount = null;
this.setPrecipitation(weather, foreGroup, stepDay);
//
// Need to map EC weather icon to MM weatherType values. Always pick the first Element icon.
//
weather.weatherType = this.convertWeatherType(foreGroup[stepDay].querySelector("abbreviatedForecast iconCode").textContent);
// Push the weather object into the forecast array.
days.push(weather);
}
return days;
},
//
// Generate an array of WeatherObjects based on EC hourly weather forecast
//
/*
* Generate an array of WeatherObjects based on EC hourly weather forecast
*/
generateWeatherObjectsFromHourly (ECdoc) {
// Declare an array to hold each hour's forecast object
const hours = [];
// Get local timezone UTC offset so that each hourly time can be calculated properly
const baseHours = ECdoc.querySelectorAll("siteData hourlyForecastGroup dateTime");
const hourOffset = baseHours[1].getAttribute("UTCOffset");
//
// The EC hourly forecast is held in a 24-element array - Elements 0 to 23 - with Element 0 holding
// the forecast for the next 'on the hour' timeslot. This means the array is a rolling 24 hours.
//
/*
* The EC hourly forecast is held in a 24-element array - Elements 0 to 23 - with Element 0 holding
* the forecast for the next 'on the hour' timeslot. This means the array is a rolling 24 hours.
*/
const hourGroup = ECdoc.querySelectorAll("siteData hourlyForecastGroup hourlyForecast");
for (let stepHour = 0; stepHour < 24; stepHour += 1) {
const weather = new WeatherObject();
// Determine local time by applying UTC offset to the forecast timestamp
const foreTime = moment(hourGroup[stepHour].getAttribute("dateTimeUTC"), "YYYYMMDDhhmmss");
const currTime = foreTime.add(hourOffset, "hours");
weather.date = moment(currTime);
// Capture the temperature
weather.temperature = hourGroup[stepHour].querySelector("temperature").textContent;
// Capture Likelihood of Precipitation (LOP) and unit-of-measure values
const precipLOP = hourGroup[stepHour].querySelector("lop").textContent * 1.0;
if (precipLOP > 0) {
weather.precipitationProbability = precipLOP;
}
//
// Need to map EC weather icon to MM weatherType values. Always pick the first Element icon.
//
weather.weatherType = this.convertWeatherType(hourGroup[stepHour].querySelector("iconCode").textContent);
// Push the weather object into the forecast array.
hours.push(weather);
}
return hours;
},
//
// Determine Min and Max temp based on a supplied Forecast Element index and a boolen that denotes if
// the next Forecast element should be considered - i.e. look at Today *and* Tonight vs.Tonight-only
//
/*
* Determine Min and Max temp based on a supplied Forecast Element index and a boolen that denotes if
* the next Forecast element should be considered - i.e. look at Today *and* Tonight vs.Tonight-only
*/
setMinMaxTemps (weather, foreGroup, today, fullDay, currentTemp) {
const todayTemp = foreGroup[today].querySelector("temperatures temperature").textContent;
const todayClass = foreGroup[today].querySelector("temperatures temperature").getAttribute("class");
//
// The following logic is largely aimed at accommodating the Current day's forecast whereby we
// can have either Current Today+Current Tonight or only Current Tonight.
//
// If fullDay is false, then we only have Tonight for the current day's forecast - meaning we have
// lost a min or max temp value for the day. Therefore, we will see if we were able to cache the the
// Today forecast for the current day. If we have, we will use them. If we do not have the cached values,
// it means that MM or the Computer has been restarted since the time EC rolled off Today from the
// forecast. In this scenario, we will simply default to the Current Conditions temperature and then
// check the Tonight temperature.
//
/*
* The following logic is largely aimed at accommodating the Current day's forecast whereby we
* can have either Current Today+Current Tonight or only Current Tonight.
*
* If fullDay is false, then we only have Tonight for the current day's forecast - meaning we have
* lost a min or max temp value for the day. Therefore, we will see if we were able to cache the the
* Today forecast for the current day. If we have, we will use them. If we do not have the cached values,
* it means that MM or the Computer has been restarted since the time EC rolled off Today from the
* forecast. In this scenario, we will simply default to the Current Conditions temperature and then
* check the Tonight temperature.x
*/
if (fullDay === false) {
if (this.todayCached === true) {
weather.minTemperature = this.todayTempCacheMin;
@@ -437,13 +395,12 @@ WeatherProvider.register("envcanada", {
}
}
//
// We will check to see if the current Element's temperature is Low or High and set weather values
// accordingly. We will also check the condition where fullDay is true *and* we are looking at forecast
// element 0. This is a special case where we will cache temperature values so that we have them later
// in the current day when the Current Today element rolls off and we have Current Tonight only.
//
/*
* We will check to see if the current Element's temperature is Low or High and set weather values
* accordingly. We will also check the condition where fullDay is true *and* we are looking at forecast
* element 0. This is a special case where we will cache temperature values so that we have them later
* in the current day when the Current Today element rolls off and we have Current Tonight only.
*/
if (todayClass === "low") {
weather.minTemperature = todayTemp;
if (today === 0 && fullDay === true) {
@@ -473,25 +430,24 @@ WeatherProvider.register("envcanada", {
}
},
//
// Check for a Precipitation forecast. EC can provide a forecast in 2 ways: either an accumulation figure
// or a POP percentage. If there is a POP, then that is what the module will show. If there is an accumulation,
// then it will be displayed ONLY if no POP is present.
//
// POP Logic: By default, we want to show the POP for 'daytime' since we are presuming that is what
// people are more interested in seeing. While EC provides a separate POP for daytime and nightime portions
// of each day, the weather module does not really allow for that view of a daily forecast. There we will
// ignore any nightime portion. There is an exception however! For the Current day, the EC data will only show
// the nightime forecast after a certain point in the afternoon. As such, we will be showing the nightime POP
// (if one exists) in that specific scenario.
//
// Accumulation Logic: Similar to POP, we want to show accumulation for 'daytime' since we presume that is what
// people are interested in seeing. While EC provides a separate accumulation for daytime and nightime portions
// of each day, the weather module does not really allow for that view of a daily forecast. There we will
// ignore any nightime portion. There is an exception however! For the Current day, the EC data will only show
// the nightime forecast after a certain point in that specific scenario.
//
/*
* Check for a Precipitation forecast. EC can provide a forecast in 2 ways: either an accumulation figure
* or a POP percentage. If there is a POP, then that is what the module will show. If there is an accumulation,
* then it will be displayed ONLY if no POP is present.
*
* POP Logic: By default, we want to show the POP for 'daytime' since we are presuming that is what
* people are more interested in seeing. While EC provides a separate POP for daytime and nightime portions
* of each day, the weather module does not really allow for that view of a daily forecast. There we will
* ignore any nightime portion. There is an exception however! For the Current day, the EC data will only show
* the nightime forecast after a certain point in the afternoon. As such, we will be showing the nightime POP
* (if one exists) in that specific scenario.
*
* Accumulation Logic: Similar to POP, we want to show accumulation for 'daytime' since we presume that is what
* people are interested in seeing. While EC provides a separate accumulation for daytime and nightime portions
* of each day, the weather module does not really allow for that view of a daily forecast. There we will
* ignore any nightime portion. There is an exception however! For the Current day, the EC data will only show
* the nightime forecast after a certain point in that specific scenario.
*/
setPrecipitation (weather, foreGroup, today) {
if (foreGroup[today].querySelector("precipitation accumulation")) {
weather.precipitationAmount = foreGroup[today].querySelector("precipitation accumulation amount").textContent * 1.0;
@@ -505,9 +461,9 @@ WeatherProvider.register("envcanada", {
}
},
//
// Convert the icons to a more usable name.
//
/*
* Convert the icons to a more usable name.
*/
convertWeatherType (weatherType) {
const weatherTypes = {
"00": "day-sunny",

View File

@@ -1,6 +1,7 @@
/* global WeatherProvider, WeatherObject */
/* This class is a provider for Open-Meteo,
/*
* This class is a provider for Open-Meteo,
* see https://open-meteo.com/
*/
@@ -9,8 +10,11 @@ const GEOCODE_BASE = "https://api.bigdatacloud.net/data/reverse-geocode-client";
const OPEN_METEO_BASE = "https://api.open-meteo.com/v1";
WeatherProvider.register("openmeteo", {
// Set the name of the provider.
// Not strictly required, but helps for debugging.
/*
* Set the name of the provider.
* Not strictly required, but helps for debugging.
*/
providerName: "Open-Meteo",
// Set the default config properties that is specific to this provider

View File

@@ -1,12 +1,16 @@
/* global WeatherProvider, WeatherObject */
/* This class is a provider for Openweathermap,
/*
* This class is a provider for Openweathermap,
* see https://openweathermap.org/
*/
WeatherProvider.register("openweathermap", {
// Set the name of the provider.
// This isn't strictly necessary, since it will fallback to the provider identifier
// But for debugging (and future alerts) it would be nice to have the real name.
/*
* Set the name of the provider.
* This isn't strictly necessary, since it will fallback to the provider identifier
* But for debugging (and future alerts) it would be nice to have the real name.
*/
providerName: "OpenWeatherMap",
// Set the default config properties that is specific to this provider
@@ -67,8 +71,11 @@ WeatherProvider.register("openweathermap", {
this.fetchData(this.getUrl())
.then((data) => {
if (!data) {
// Did not receive usable new data.
// Maybe this needs a better check?
/*
* Did not receive usable new data.
* Maybe this needs a better check?
*/
return;
}
@@ -206,8 +213,10 @@ WeatherProvider.register("openweathermap", {
weather.weatherType = this.convertWeatherType(forecast.weather[0].icon);
}
// the same day as before
// add values from forecast to corresponding variables
/*
* the same day as before
* add values from forecast to corresponding variables
*/
minTemp.push(forecast.main.temp_min);
maxTemp.push(forecast.main.temp_max);
@@ -220,8 +229,10 @@ WeatherProvider.register("openweathermap", {
}
}
// last day
// calculate minimum/maximum temperature, specify rain amount
/*
* last day
* calculate minimum/maximum temperature, specify rain amount
*/
weather.minTemperature = Math.min.apply(null, minTemp);
weather.maxTemperature = Math.max.apply(null, maxTemp);
weather.rain = rain;
@@ -250,14 +261,18 @@ WeatherProvider.register("openweathermap", {
weather.rain = 0;
weather.snow = 0;
// forecast.rain not available if amount is zero
// The API always returns in millimeters
/*
* forecast.rain not available if amount is zero
* The API always returns in millimeters
*/
if (forecast.hasOwnProperty("rain") && !isNaN(forecast.rain)) {
weather.rain = forecast.rain;
}
// forecast.snow not available if amount is zero
// The API always returns in millimeters
/*
* forecast.snow not available if amount is zero
* The API always returns in millimeters
*/
if (forecast.hasOwnProperty("snow") && !isNaN(forecast.snow)) {
weather.snow = forecast.snow;
}
@@ -402,7 +417,8 @@ WeatherProvider.register("openweathermap", {
return weatherTypes.hasOwnProperty(weatherType) ? weatherTypes[weatherType] : null;
},
/* getParams(compliments)
/*
* getParams(compliments)
* Generates an url with api parameters based on the config.
*
* return String - URL params.

View File

@@ -1,11 +1,15 @@
/* global WeatherProvider, WeatherObject */
/* This class is a provider for Pirate Weather, it is a replacement for Dark Sky (same api),
/*
* This class is a provider for Pirate Weather, it is a replacement for Dark Sky (same api),
* see http://pirateweather.net/en/latest/
*/
WeatherProvider.register("pirateweather", {
// Set the name of the provider.
// Not strictly required, but helps for debugging.
/*
* Set the name of the provider.
* Not strictly required, but helps for debugging.
*/
providerName: "pirateweather",
// Set the default config properties that is specific to this provider

View File

@@ -1,6 +1,7 @@
/* global WeatherProvider, WeatherObject */
/* This class is a provider for SMHI (Sweden only).
/*
* This class is a provider for SMHI (Sweden only).
* Metric system is the only supported unit,
* see https://www.smhi.se/
*/
@@ -138,9 +139,11 @@ WeatherProvider.register("smhi", {
currentWeather.weatherType = this.convertWeatherType(this.paramValue(weatherData, "Wsymb2"), currentWeather.isDayTime());
currentWeather.feelsLikeTemp = this.calculateApparentTemperature(weatherData);
// Determine the precipitation amount and category and update the
// weatherObject with it, the valuetype to use can be configured or uses
// median as default.
/*
* Determine the precipitation amount and category and update the
* weatherObject with it, the valuetype to use can be configured or uses
* median as default.
*/
let precipitationValue = this.paramValue(weatherData, this.config.precipitationValue);
switch (this.paramValue(weatherData, "pcat")) {
// 0 = No precipitation

View File

@@ -1,12 +1,16 @@
/* global WeatherProvider, WeatherObject, WeatherUtils */
/* This class is a provider for UK Met Office Datapoint,
/*
* This class is a provider for UK Met Office Datapoint,
* see https://www.metoffice.gov.uk/
*/
WeatherProvider.register("ukmetoffice", {
// Set the name of the provider.
// This isn't strictly necessary, since it will fallback to the provider identifier
// But for debugging (and future alerts) it would be nice to have the real name.
/*
* Set the name of the provider.
* This isn't strictly necessary, since it will fallback to the provider identifier
* But for debugging (and future alerts) it would be nice to have the real name.
*/
providerName: "UK Met Office",
// Set the default config properties that is specific to this provider
@@ -21,8 +25,11 @@ WeatherProvider.register("ukmetoffice", {
this.fetchData(this.getUrl("3hourly"))
.then((data) => {
if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location || !data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length === 0) {
// Did not receive usable new data.
// Maybe this needs a better check?
/*
* Did not receive usable new data.
* Maybe this needs a better check?
*/
return;
}
@@ -42,8 +49,11 @@ WeatherProvider.register("ukmetoffice", {
this.fetchData(this.getUrl("daily"))
.then((data) => {
if (!data || !data.SiteRep || !data.SiteRep.DV || !data.SiteRep.DV.Location || !data.SiteRep.DV.Location.Period || data.SiteRep.DV.Location.Period.length === 0) {
// Did not receive usable new data.
// Maybe this needs a better check?
/*
* Did not receive usable new data.
* Maybe this needs a better check?
*/
return;
}
@@ -86,8 +96,11 @@ WeatherProvider.register("ukmetoffice", {
if (periodDate.isSameOrAfter(moment.utc().startOf("day"))) {
// check this is the period we want, after today the diff will be -ve
if (moment().diff(periodDate, "minutes") > 0) {
// loop round the reports looking for the one we are in
// $ value specifies the time in minutes-of-the-day: 0, 180, 360,...1260
/*
* loop round the reports looking for the one we are in
* $ value specifies the time in minutes-of-the-day: 0, 180, 360,...1260
*/
for (const rep of period.Rep) {
const p = rep.$;
if (timeInMins >= p && timeInMins - 180 < p) {
@@ -117,8 +130,10 @@ WeatherProvider.register("ukmetoffice", {
generateWeatherObjectsFromForecast (forecasts) {
const days = [];
// loop round the (5) periods getting the data
// for each period array, Day is [0], Night is [1]
/*
* loop round the (5) periods getting the data
* for each period array, Day is [0], Night is [1]
*/
for (const period of forecasts.SiteRep.DV.Location.Period) {
const weather = new WeatherObject();

View File

@@ -1,6 +1,7 @@
/* global WeatherProvider, WeatherObject */
/* This class is a provider for UK Met Office Data Hub (the replacement for their Data Point services).
/*
* This class is a provider for UK Met Office Data Hub (the replacement for their Data Point services).
* For more information on Data Hub, see https://www.metoffice.gov.uk/services/data/datapoint/notifications/weather-datahub
* Data available:
* Hourly data for next 2 days ("hourly") - https://www.metoffice.gov.uk/binaries/content/assets/metofficegovuk/pdf/data/global-spot-data-hourly.pdf
@@ -54,9 +55,11 @@ WeatherProvider.register("ukmetofficedatahub", {
return this.config.apiBase + (this.config.apiBase.endsWith("/") ? "" : "/") + forecastType + queryStrings;
},
// Build the list of headers for the request
// For DataHub requests, the API key/secret are sent in the headers rather than as query strings.
// Headers defined according to Data Hub API (https://datahub.metoffice.gov.uk/docs/f/category/site-specific/type/site-specific/api-documentation#get-/point/hourly)
/*
* Build the list of headers for the request
* For DataHub requests, the API key/secret are sent in the headers rather than as query strings.
* Headers defined according to Data Hub API (https://datahub.metoffice.gov.uk/docs/f/category/site-specific/type/site-specific/api-documentation#get-/point/hourly)
*/
getHeaders () {
return {
accept: "application/json",
@@ -78,8 +81,11 @@ WeatherProvider.register("ukmetofficedatahub", {
.then((data) => {
// Check data is usable
if (!data || !data.features || !data.features[0].properties || !data.features[0].properties.timeSeries || data.features[0].properties.timeSeries.length === 0) {
// Did not receive usable new data.
// Maybe this needs a better check?
/*
* Did not receive usable new data.
* Maybe this needs a better check?
*/
Log.error("Possibly bad current/hourly data?");
Log.error(data);
return;
@@ -127,15 +133,19 @@ WeatherProvider.register("ukmetofficedatahub", {
currentWeather.precipitationProbability = forecastDataHours[hour].probOfPrecipitation;
currentWeather.feelsLikeTemp = forecastDataHours[hour].feelsLikeTemperature;
// Pass on full details, so they can be used in custom templates
// Note the units of the supplied data when using this (see top of file)
/*
* Pass on full details, so they can be used in custom templates
* Note the units of the supplied data when using this (see top of file)
*/
currentWeather.rawData = forecastDataHours[hour];
}
}
// Determine the sunrise/sunset times - (still) not supplied in UK Met Office data
// Passes {longitude, latitude} to SunCalc, could pass height to, but
// SunCalc.getTimes doesn't take that into account
/*
* Determine the sunrise/sunset times - (still) not supplied in UK Met Office data
* Passes {longitude, latitude} to SunCalc, could pass height to, but
* SunCalc.getTimes doesn't take that into account
*/
currentWeather.updateSunTime(this.config.lat, this.config.lon);
return currentWeather;
@@ -147,8 +157,11 @@ WeatherProvider.register("ukmetofficedatahub", {
.then((data) => {
// Check data is usable
if (!data || !data.features || !data.features[0].properties || !data.features[0].properties.timeSeries || data.features[0].properties.timeSeries.length === 0) {
// Did not receive usable new data.
// Maybe this needs a better check?
/*
* Did not receive usable new data.
* Maybe this needs a better check?
*/
Log.error("Possibly bad forecast data?");
Log.error(data);
return;
@@ -203,8 +216,10 @@ WeatherProvider.register("ukmetofficedatahub", {
forecastWeather.snow = forecastDataDays[day].dayProbabilityOfSnow;
forecastWeather.feelsLikeTemp = forecastDataDays[day].dayMaxFeelsLikeTemp;
// Pass on full details, so they can be used in custom templates
// Note the units of the supplied data when using this (see top of file)
/*
* Pass on full details, so they can be used in custom templates
* Note the units of the supplied data when using this (see top of file)
*/
forecastWeather.rawData = forecastDataDays[day];
dailyForecasts.push(forecastWeather);
@@ -219,9 +234,11 @@ WeatherProvider.register("ukmetofficedatahub", {
this.fetchedLocationName = name;
},
// Match the Met Office "significant weather code" to a weathericons.css icon
// Use: https://metoffice.apiconnect.ibmcloud.com/metoffice/production/node/264
// and: https://erikflowers.github.io/weather-icons/
/*
* Match the Met Office "significant weather code" to a weathericons.css icon
* Use: https://metoffice.apiconnect.ibmcloud.com/metoffice/production/node/264
* and: https://erikflowers.github.io/weather-icons/
*/
convertWeatherType (weatherType) {
const weatherTypes = {
0: "night-clear",

View File

@@ -1,11 +1,15 @@
/* global WeatherProvider, WeatherObject */
/* This class is a provider for Weatherbit,
/*
* This class is a provider for Weatherbit,
* see https://www.weatherbit.io/
*/
WeatherProvider.register("weatherbit", {
// Set the name of the provider.
// Not strictly required, but helps for debugging.
/*
* Set the name of the provider.
* Not strictly required, but helps for debugging.
*/
providerName: "Weatherbit",
// Set the default config properties that is specific to this provider

View File

@@ -1,11 +1,15 @@
/* global WeatherProvider, WeatherObject, WeatherUtils */
/* This class is a provider for Weatherflow.
/*
* This class is a provider for Weatherflow.
* Note that the Weatherflow API does not provide snowfall.
*/
WeatherProvider.register("weatherflow", {
// Set the name of the provider.
// Not strictly required, but helps for debugging
/*
* Set the name of the provider.
* Not strictly required, but helps for debugging
*/
providerName: "WeatherFlow",
// Set the default config properties that is specific to this provider

View File

@@ -1,6 +1,7 @@
/* global WeatherProvider, WeatherObject, WeatherUtils */
/* Provider: weather.gov
/*
* Provider: weather.gov
* https://weather-gov.github.io/api/general-faqs
*
* This class is a provider for weather.gov.
@@ -9,9 +10,12 @@
*/
WeatherProvider.register("weathergov", {
// Set the name of the provider.
// This isn't strictly necessary, since it will fallback to the provider identifier
// But for debugging (and future alerts) it would be nice to have the real name.
/*
* Set the name of the provider.
* This isn't strictly necessary, since it will fallback to the provider identifier
* But for debugging (and future alerts) it would be nice to have the real name.
*/
providerName: "Weather.gov",
// Set the default config properties that is specific to this provider
@@ -98,8 +102,11 @@ WeatherProvider.register("weathergov", {
this.fetchData(this.forecastHourlyURL)
.then((data) => {
if (!data) {
// Did not receive usable new data.
// Maybe this needs a better check?
/*
* Did not receive usable new data.
* Maybe this needs a better check?
*/
return;
}
const hourly = this.generateWeatherObjectsFromHourly(data.properties.periods);
@@ -282,14 +289,18 @@ WeatherProvider.register("weathergov", {
weather.weatherType = this.convertWeatherType(forecast.shortForecast, forecast.isDaytime);
}
// the same day as before
// add values from forecast to corresponding variables
/*
* the same day as before
* add values from forecast to corresponding variables
*/
minTemp.push(forecast.temperature);
maxTemp.push(forecast.temperature);
}
// last day
// calculate minimum/maximum temperature
/*
* last day
* calculate minimum/maximum temperature
*/
weather.minTemperature = Math.min.apply(null, minTemp);
weather.maxTemperature = Math.max.apply(null, maxTemp);
@@ -302,8 +313,11 @@ WeatherProvider.register("weathergov", {
* Convert the icons to a more usable name.
*/
convertWeatherType (weatherType, isDaytime) {
//https://w1.weather.gov/xml/current_obs/weather.php
// There are way too many types to create, so lets just look for certain strings
/*
* https://w1.weather.gov/xml/current_obs/weather.php
* There are way too many types to create, so lets just look for certain strings
*/
if (weatherType.includes("Cloudy") || weatherType.includes("Partly")) {
if (isDaytime) {

View File

@@ -1,6 +1,7 @@
/* global WeatherProvider, WeatherObject */
/* This class is a provider for Yr.no, a norwegian weather service.
/*
* This class is a provider for Yr.no, a norwegian weather service.
* Terms of service: https://developer.yr.no/doc/TermsOfService/
*/
WeatherProvider.register("yr", {
@@ -67,8 +68,11 @@ WeatherProvider.register("yr", {
getWeatherData () {
return new Promise((resolve, reject) => {
// If a user has several Yr-modules, for instance one current and one forecast, the API calls must be synchronized across classes.
// This is to avoid multiple similar calls to the API.
/*
* If a user has several Yr-modules, for instance one current and one forecast, the API calls must be synchronized across classes.
* This is to avoid multiple similar calls to the API.
*/
let shouldWait = localStorage.getItem("yrIsFetchingWeatherData");
if (shouldWait) {
const checkForGo = setInterval(function () {
@@ -201,8 +205,11 @@ WeatherProvider.register("yr", {
},
getStellarData () {
// If a user has several Yr-modules, for instance one current and one forecast, the API calls must be synchronized across classes.
// This is to avoid multiple similar calls to the API.
/*
* If a user has several Yr-modules, for instance one current and one forecast, the API calls must be synchronized across classes.
* This is to avoid multiple similar calls to the API.
*/
return new Promise((resolve, reject) => {
let shouldWait = localStorage.getItem("yrIsFetchingStellarData");
if (shouldWait) {

430
package-lock.json generated
View File

@@ -27,21 +27,21 @@
"pm2": "^5.4.2",
"socket.io": "^4.7.5",
"suncalc": "^1.9.0",
"systeminformation": "^5.23.2"
"systeminformation": "^5.23.4"
},
"devDependencies": {
"@stylistic/eslint-plugin": "^1.8.1",
"@stylistic/eslint-plugin": "^2.6.2",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jest": "^28.6.0",
"eslint-plugin-jsdoc": "^48.10.2",
"eslint-plugin-jest": "^28.8.0",
"eslint-plugin-jsdoc": "^50.0.0",
"eslint-plugin-package-json": "^0.15.2",
"eslint-plugin-unicorn": "^55.0.0",
"express-basic-auth": "^1.2.1",
"husky": "^9.1.4",
"jest": "^29.7.0",
"jsdom": "^24.1.1",
"lint-staged": "^15.2.7",
"playwright": "^1.45.3",
"lint-staged": "^15.2.8",
"playwright": "^1.46.0",
"prettier": "^3.3.3",
"sinon": "^18.0.0",
"stylelint": "^16.8.1",
@@ -1850,91 +1850,114 @@
"license": "MIT"
},
"node_modules/@stylistic/eslint-plugin": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-1.8.1.tgz",
"integrity": "sha512-64My6I7uCcmSQ//427Pfg2vjSf9SDzfsGIWohNFgISMLYdC5BzJqDo647iDDJzSxINh3WTC0Ql46ifiKuOoTyA==",
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.6.2.tgz",
"integrity": "sha512-Ic5oFNM/25iuagob6LiIBkSI/A2y45TsyKtDtODXHRZDy52WfPfeexI6r+OH5+aWN9QGob2Bw+4JRM9/4areWw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@stylistic/eslint-plugin-js": "1.8.1",
"@stylistic/eslint-plugin-jsx": "1.8.1",
"@stylistic/eslint-plugin-plus": "1.8.1",
"@stylistic/eslint-plugin-ts": "1.8.1",
"@types/eslint": "^8.56.10"
"@stylistic/eslint-plugin-js": "2.6.2",
"@stylistic/eslint-plugin-jsx": "2.6.2",
"@stylistic/eslint-plugin-plus": "2.6.2",
"@stylistic/eslint-plugin-ts": "2.6.2",
"@types/eslint": "^9.6.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"peerDependencies": {
"eslint": ">=8.40.0"
}
},
"node_modules/@stylistic/eslint-plugin-js": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.8.1.tgz",
"integrity": "sha512-c5c2C8Mos5tTQd+NWpqwEu7VT6SSRooAguFPMj1cp2RkTYl1ynKoXo8MWy3k4rkbzoeYHrqC2UlUzsroAN7wtQ==",
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-2.6.2.tgz",
"integrity": "sha512-wCr/kVctAPayMU3pcOI1MKR7MoKIh6VKZU89lPklAqtJoxT+Em6RueiiARbpznUYG5eg3LymiU+aMD+aIZXdqA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/eslint": "^8.56.10",
"acorn": "^8.11.3",
"escape-string-regexp": "^4.0.0",
"eslint-visitor-keys": "^3.4.3",
"espree": "^9.6.1"
"@types/eslint": "^9.6.0",
"acorn": "^8.12.1",
"eslint-visitor-keys": "^4.0.0",
"espree": "^10.1.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"peerDependencies": {
"eslint": ">=8.40.0"
}
},
"node_modules/@stylistic/eslint-plugin-jsx": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-1.8.1.tgz",
"integrity": "sha512-k1Eb6rcjMP+mmjvj+vd9y5KUdWn1OBkkPLHXhsrHt5lCDFZxJEs0aVQzE5lpYrtVZVkpc5esTtss/cPJux0lfA==",
"node_modules/@stylistic/eslint-plugin-js/node_modules/eslint-visitor-keys": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz",
"integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==",
"dev": true,
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@stylistic/eslint-plugin-js/node_modules/espree": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz",
"integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@stylistic/eslint-plugin-js": "^1.8.1",
"@types/eslint": "^8.56.10",
"acorn": "^8.12.0",
"acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^4.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://opencollective.com/eslint"
}
},
"node_modules/@stylistic/eslint-plugin-jsx": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-2.6.2.tgz",
"integrity": "sha512-dSXK/fSPA938J1fBi10QmhzLKtZ/2TuyVNHQMk8jUhWfKJDleAogaSqcWNAbN8fwcoe9UWmt/3StiIf2oYC1aQ==",
"dev": true,
"dependencies": {
"@stylistic/eslint-plugin-js": "^2.6.2",
"@types/eslint": "^9.6.0",
"estraverse": "^5.3.0",
"picomatch": "^4.0.2"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"peerDependencies": {
"eslint": ">=8.40.0"
}
},
"node_modules/@stylistic/eslint-plugin-plus": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-1.8.1.tgz",
"integrity": "sha512-4+40H3lHYTN8OWz+US8CamVkO+2hxNLp9+CAjorI7top/lHqemhpJvKA1LD9Uh+WMY9DYWiWpL2+SZ2wAXY9fQ==",
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-2.6.2.tgz",
"integrity": "sha512-cANcPASfRvq3VTbbQCrSIXq+2AI0IW68PNYaZoXXS0ENlp7HDB8dmrsJnOgWCcoEvdCB8z/eWcG/eq/v5Qcl+Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/eslint": "^8.56.10",
"@typescript-eslint/utils": "^6.21.0"
"@types/eslint": "^9.6.0",
"@typescript-eslint/utils": "^8.0.0"
},
"peerDependencies": {
"eslint": "*"
}
},
"node_modules/@stylistic/eslint-plugin-ts": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-1.8.1.tgz",
"integrity": "sha512-/q1m+ZuO1JHfiSF16EATFzv7XSJkc5W6DocfvH5o9oB6WWYFMF77fVoBWnKT3wGptPOc2hkRupRKhmeFROdfWA==",
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-2.6.2.tgz",
"integrity": "sha512-6OEN3VtUNxjgOvWPavnC10MByr1H4zsgwNND3rQXr5lDFv93MLUnTsH+/SH15OkuqdyJgrQILI6b9lYecb1vIg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@stylistic/eslint-plugin-js": "1.8.1",
"@types/eslint": "^8.56.10",
"@typescript-eslint/utils": "^6.21.0"
"@stylistic/eslint-plugin-js": "2.6.2",
"@types/eslint": "^9.6.0",
"@typescript-eslint/utils": "^8.0.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"peerDependencies": {
"eslint": ">=8.40.0"
@@ -2033,11 +2056,10 @@
}
},
"node_modules/@types/eslint": {
"version": "8.56.10",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz",
"integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==",
"version": "9.6.0",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz",
"integrity": "sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "*",
"@types/json-schema": "*"
@@ -2047,8 +2069,7 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true,
"license": "MIT"
"dev": true
},
"node_modules/@types/glob": {
"version": "7.2.0",
@@ -2162,13 +2183,6 @@
"@types/node": "*"
}
},
"node_modules/@types/semver": {
"version": "7.5.8",
"resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
"integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/stack-utils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
@@ -2204,17 +2218,16 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz",
"integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==",
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.1.tgz",
"integrity": "sha512-NpixInP5dm7uukMiRyiHjRKkom5RIFA4dfiHvalanD2cF0CLUuQqxfg8PtEUo9yqJI2bBhF+pcSafqnG3UBnRQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "6.21.0",
"@typescript-eslint/visitor-keys": "6.21.0"
"@typescript-eslint/types": "8.0.1",
"@typescript-eslint/visitor-keys": "8.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -2222,13 +2235,12 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz",
"integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==",
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.1.tgz",
"integrity": "sha512-PpqTVT3yCA/bIgJ12czBuE3iBlM3g4inRSC5J0QOdQFAn07TYrYEQBBKgXH1lQpglup+Zy6c1fxuwTk4MTNKIw==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -2236,23 +2248,22 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz",
"integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==",
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.1.tgz",
"integrity": "sha512-8V9hriRvZQXPWU3bbiUV4Epo7EvgM6RTs+sUmxp5G//dBGy402S7Fx0W0QkB2fb4obCF8SInoUzvTYtc3bkb5w==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@typescript-eslint/types": "6.21.0",
"@typescript-eslint/visitor-keys": "6.21.0",
"@typescript-eslint/types": "8.0.1",
"@typescript-eslint/visitor-keys": "8.0.1",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"minimatch": "9.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
"minimatch": "^9.0.4",
"semver": "^7.6.0",
"ts-api-utils": "^1.3.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -2265,43 +2276,38 @@
}
},
"node_modules/@typescript-eslint/utils": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz",
"integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==",
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.1.tgz",
"integrity": "sha512-CBFR0G0sCt0+fzfnKaciu9IBsKvEKYwN9UZ+eeogK1fYHg4Qxk1yf/wLQkLXlq8wbU2dFlgAesxt8Gi76E8RTA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "6.21.0",
"@typescript-eslint/types": "6.21.0",
"@typescript-eslint/typescript-estree": "6.21.0",
"semver": "^7.5.4"
"@typescript-eslint/scope-manager": "8.0.1",
"@typescript-eslint/types": "8.0.1",
"@typescript-eslint/typescript-estree": "8.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0"
"eslint": "^8.57.0 || ^9.0.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz",
"integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==",
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.1.tgz",
"integrity": "sha512-W5E+o0UfUcK5EgchLZsyVWqARmsM7v54/qEq6PY3YI5arkgmCzHiuk0zKSJJbm71V0xdRna4BGomkCTXz2/LkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/types": "6.21.0",
"eslint-visitor-keys": "^3.4.1"
"@typescript-eslint/types": "8.0.1",
"eslint-visitor-keys": "^3.4.3"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
@@ -2328,10 +2334,9 @@
}
},
"node_modules/acorn": {
"version": "8.12.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz",
"integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==",
"license": "MIT",
"version": "8.12.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz",
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==",
"bin": {
"acorn": "bin/acorn"
},
@@ -2967,7 +2972,6 @@
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
@@ -3279,16 +3283,15 @@
}
},
"node_modules/cli-cursor": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
"integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
"integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
"dev": true,
"license": "MIT",
"dependencies": {
"restore-cursor": "^4.0.0"
"restore-cursor": "^5.0.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -3323,7 +3326,6 @@
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
"integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
"dev": true,
"license": "MIT",
"dependencies": {
"slice-ansi": "^5.0.0",
"string-width": "^7.0.0"
@@ -3460,8 +3462,7 @@
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"dev": true,
"license": "MIT"
"dev": true
},
"node_modules/combined-stream": {
"version": "1.0.8",
@@ -4145,8 +4146,7 @@
"version": "10.3.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz",
"integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==",
"dev": true,
"license": "MIT"
"dev": true
},
"node_modules/encodeurl": {
"version": "1.0.2",
@@ -4240,6 +4240,18 @@
"node": ">=6"
}
},
"node_modules/environment": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
"integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
"dev": true,
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/envsub": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/envsub/-/envsub-4.1.0.tgz",
@@ -4674,19 +4686,18 @@
}
},
"node_modules/eslint-plugin-jest": {
"version": "28.6.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.6.0.tgz",
"integrity": "sha512-YG28E1/MIKwnz+e2H7VwYPzHUYU4aMa19w0yGcwXnnmJH6EfgHahTJ2un3IyraUxNfnz/KUhJAFXNNwWPo12tg==",
"version": "28.8.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.8.0.tgz",
"integrity": "sha512-Tubj1hooFxCl52G4qQu0edzV/+EZzPUeN8p2NnW5uu4fbDs+Yo7+qDVDc4/oG3FbCqEBmu/OC3LSsyiU22oghw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@typescript-eslint/utils": "^6.0.0 || ^7.0.0"
"@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0"
},
"engines": {
"node": "^16.10.0 || ^18.12.0 || >=20.0.0"
},
"peerDependencies": {
"@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0",
"eslint": "^7.0.0 || ^8.0.0 || ^9.0.0",
"jest": "*"
},
@@ -4700,9 +4711,9 @@
}
},
"node_modules/eslint-plugin-jsdoc": {
"version": "48.10.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-48.10.2.tgz",
"integrity": "sha512-xTkf/MmEeVrTbezc6kDqCJmK9RcseIKo8X4oyoDCMvV4LY8dqrQi8kmfRrv9n0gNBkCclevaOh2Lkmu6Fs8SLg==",
"version": "50.0.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.0.0.tgz",
"integrity": "sha512-czyJ5F7/qY2LIhUD5Bl6q1CCZ8mjvfEA9HQN5nvIp/Pb8VLIlUNd+DMZdA2OKN74QQMS3pobC06hFqAOJyOv5Q==",
"dev": true,
"dependencies": {
"@es-joy/jsdoccomment": "~0.46.0",
@@ -4982,8 +4993,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
"dev": true,
"license": "MIT"
"dev": true
},
"node_modules/execa": {
"version": "5.1.1",
@@ -5534,7 +5544,6 @@
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz",
"integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
@@ -6479,7 +6488,6 @@
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
"integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
@@ -7692,22 +7700,21 @@
"license": "MIT"
},
"node_modules/lint-staged": {
"version": "15.2.7",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.7.tgz",
"integrity": "sha512-+FdVbbCZ+yoh7E/RosSdqKJyUM2OEjTciH0TFNkawKgvFp1zbGlEC39RADg+xKBG1R4mhoH2j85myBQZ5wR+lw==",
"version": "15.2.8",
"resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.8.tgz",
"integrity": "sha512-PUWFf2zQzsd9EFU+kM1d7UP+AZDbKFKuj+9JNVTBkhUFhbg4MAt6WfyMMwBfM4lYqd4D2Jwac5iuTu9rVj4zCQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "~5.3.0",
"commander": "~12.1.0",
"debug": "~4.3.4",
"debug": "~4.3.6",
"execa": "~8.0.1",
"lilconfig": "~3.1.1",
"listr2": "~8.2.1",
"lilconfig": "~3.1.2",
"listr2": "~8.2.4",
"micromatch": "~4.0.7",
"pidtree": "~0.6.0",
"string-argv": "~0.3.2",
"yaml": "~2.4.2"
"yaml": "~2.5.0"
},
"bin": {
"lint-staged": "bin/lint-staged.js"
@@ -7887,16 +7894,15 @@
}
},
"node_modules/listr2": {
"version": "8.2.3",
"resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.3.tgz",
"integrity": "sha512-Lllokma2mtoniUOS94CcOErHWAug5iu7HOmDrvWgpw8jyQH2fomgB+7lZS4HWZxytUuQwkGOwe49FvwVaA85Xw==",
"version": "8.2.4",
"resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz",
"integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==",
"dev": true,
"license": "MIT",
"dependencies": {
"cli-truncate": "^4.0.0",
"colorette": "^2.0.20",
"eventemitter3": "^5.0.1",
"log-update": "^6.0.0",
"log-update": "^6.1.0",
"rfdc": "^1.4.1",
"wrap-ansi": "^9.0.0"
},
@@ -7946,15 +7952,14 @@
"license": "MIT"
},
"node_modules/log-update": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz",
"integrity": "sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==",
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
"integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-escapes": "^6.2.0",
"cli-cursor": "^4.0.0",
"slice-ansi": "^7.0.0",
"ansi-escapes": "^7.0.0",
"cli-cursor": "^5.0.0",
"slice-ansi": "^7.1.0",
"strip-ansi": "^7.1.0",
"wrap-ansi": "^9.0.0"
},
@@ -7966,13 +7971,15 @@
}
},
"node_modules/log-update/node_modules/ansi-escapes": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz",
"integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==",
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
"integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
"dev": true,
"license": "MIT",
"dependencies": {
"environment": "^1.0.0"
},
"engines": {
"node": ">=14.16"
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -7983,7 +7990,6 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
@@ -7996,7 +8002,6 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
@@ -8009,7 +8014,6 @@
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
"integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
"dev": true,
"license": "MIT",
"dependencies": {
"get-east-asian-width": "^1.0.0"
},
@@ -8025,7 +8029,6 @@
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
"integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^6.2.1",
"is-fullwidth-code-point": "^5.0.0"
@@ -8042,7 +8045,6 @@
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.0.1"
},
@@ -8254,6 +8256,18 @@
"node": ">=6"
}
},
"node_modules/mimic-function": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
"integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
"dev": true,
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mimic-response": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
@@ -8275,11 +8289,10 @@
}
},
"node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
},
@@ -9017,7 +9030,6 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
@@ -9130,12 +9142,12 @@
}
},
"node_modules/playwright": {
"version": "1.45.3",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz",
"integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==",
"version": "1.46.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.0.tgz",
"integrity": "sha512-XYJ5WvfefWONh1uPAUAi0H2xXV5S3vrtcnXe6uAOgdGi3aSpqOSXX08IAjXW34xitfuOJsvXU5anXZxPSEQiJw==",
"dev": true,
"dependencies": {
"playwright-core": "1.45.3"
"playwright-core": "1.46.0"
},
"bin": {
"playwright": "cli.js"
@@ -9148,9 +9160,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.45.3",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz",
"integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==",
"version": "1.46.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.46.0.tgz",
"integrity": "sha512-9Y/d5UIwuJk8t3+lhmMSAJyNP1BUC/DqP3cQJDQQL/oWqAiuPTLgy7Q5dzglmTLwcBRdetzgNM/gni7ckfTr6A==",
"dev": true,
"bin": {
"playwright-core": "cli.js"
@@ -10051,22 +10063,48 @@
}
},
"node_modules/restore-cursor": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
"integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
"integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
"dev": true,
"license": "MIT",
"dependencies": {
"onetime": "^5.1.0",
"signal-exit": "^3.0.2"
"onetime": "^7.0.0",
"signal-exit": "^4.1.0"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/restore-cursor/node_modules/onetime": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
"integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
"dev": true,
"dependencies": {
"mimic-function": "^5.0.0"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/restore-cursor/node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
"dev": true,
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -10081,8 +10119,7 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"dev": true,
"license": "MIT"
"dev": true
},
"node_modules/rimraf": {
"version": "3.0.2",
@@ -10537,7 +10574,6 @@
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
"integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^6.0.0",
"is-fullwidth-code-point": "^4.0.0"
@@ -10554,7 +10590,6 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
@@ -10826,11 +10861,10 @@
}
},
"node_modules/string-width": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz",
"integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==",
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^10.3.0",
"get-east-asian-width": "^1.0.0",
@@ -10848,7 +10882,6 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
@@ -10861,7 +10894,6 @@
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.0.1"
},
@@ -11369,9 +11401,9 @@
"dev": true
},
"node_modules/systeminformation": {
"version": "5.23.2",
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.23.2.tgz",
"integrity": "sha512-FoipTSwzZR68ZAjXZ8DRH2DFEErMAOi9JvRMsn6i/hTp6Hd4W4nM1W6a+kUyMrp/pd1SIuUzUZvvkQ21yE6Dig==",
"version": "5.23.4",
"resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.23.4.tgz",
"integrity": "sha512-mD2R9xnOzKOOmIVtxekosf/ghOE/DGLqAPmsEgQMWJK0pMKxBtX19riz1Ss0tN4omcfS2FQ2RDJ4lkxgADxIPw==",
"os": [
"darwin",
"linux",
@@ -11587,7 +11619,6 @@
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
"integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=16"
},
@@ -12152,7 +12183,6 @@
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
"integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^6.2.1",
"string-width": "^7.0.0",
@@ -12170,7 +12200,6 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
@@ -12183,7 +12212,6 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
},
@@ -12196,7 +12224,6 @@
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.0.1"
},
@@ -12283,11 +12310,10 @@
"license": "ISC"
},
"node_modules/yaml": {
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz",
"integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==",
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz",
"integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==",
"dev": true,
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
},

View File

@@ -71,21 +71,21 @@
"pm2": "^5.4.2",
"socket.io": "^4.7.5",
"suncalc": "^1.9.0",
"systeminformation": "^5.23.2"
"systeminformation": "^5.23.4"
},
"devDependencies": {
"@stylistic/eslint-plugin": "^1.8.1",
"@stylistic/eslint-plugin": "^2.6.2",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jest": "^28.6.0",
"eslint-plugin-jsdoc": "^48.10.2",
"eslint-plugin-jest": "^28.8.0",
"eslint-plugin-jsdoc": "^50.0.0",
"eslint-plugin-package-json": "^0.15.2",
"eslint-plugin-unicorn": "^55.0.0",
"express-basic-auth": "^1.2.1",
"husky": "^9.1.4",
"jest": "^29.7.0",
"jsdom": "^24.1.1",
"lint-staged": "^15.2.7",
"playwright": "^1.45.3",
"lint-staged": "^15.2.8",
"playwright": "^1.46.0",
"prettier": "^3.3.3",
"sinon": "^18.0.0",
"stylelint": "^16.8.1",

View File

@@ -1,4 +1,5 @@
/* MagicMirror² Test calendar exdate
/*
* MagicMirror² Test calendar exdate
*
* By jkriegshauser
* MIT Licensed.

View File

@@ -1,4 +1,5 @@
/* MagicMirror² Test calendar exdate
/*
* MagicMirror² Test calendar exdate
*
* By jkriegshauser
* MIT Licensed.

View File

@@ -1,4 +1,5 @@
/* MagicMirror² Test calendar exdate
/*
* MagicMirror² Test calendar exdate
*
* By jkriegshauser
* MIT Licensed.

View File

@@ -1,4 +1,5 @@
/* MagicMirror² Test calendar exdate
/*
* MagicMirror² Test calendar exdate
*
* By jkriegshauser
* MIT Licensed.

View File

@@ -1,4 +1,5 @@
/* MagicMirror² Test calendar exdate
/*
* MagicMirror² Test calendar exdate
*
* By jkriegshauser
* MIT Licensed.

View File

@@ -1,4 +1,5 @@
/* MagicMirror² Test calendar exdate
/*
* MagicMirror² Test calendar exdate
*
* By jkriegshauser
* MIT Licensed.

View File

@@ -1,4 +1,5 @@
/* MagicMirror² Test config for fullday calendar entries over multiple days
/*
* MagicMirror² Test config for fullday calendar entries over multiple days
*
* By Paranoid93 https://github.com/Paranoid93/
* MIT Licensed.

View File

@@ -1,4 +1,5 @@
/* MagicMirror² Test config for fullday calendar entries over multiple days
/*
* MagicMirror² Test config for fullday calendar entries over multiple days
*
* By Paranoid93 https://github.com/Paranoid93/
* MIT Licensed.

View File

@@ -44,9 +44,10 @@ describe("Calendar module", () => {
});
});
/****************************/
// RRULE TESTS:
// Add any tests that check rrule functionality here.
/*
* RRULE TESTS:
* Add any tests that check rrule functionality here.
*/
describe("rrule", () => {
it("Issue #3393 recurrence dates past rrule until date", async () => {
await helpers.startApplication("tests/configs/modules/calendar/rrule_until.js", "07 Mar 2024 10:38:00 GMT-07:00", ["js/electron.js"], "America/Los_Angeles");
@@ -60,15 +61,16 @@ describe("Calendar module", () => {
});
});
/****************************/
// LOS ANGELES TESTS:
// In 2023, DST (GMT-7) was until 5 Nov, after which is standard (STD) (GMT-8) time.
// Test takes place on Thu 19 Oct, recurring event on a Wednesday. maximumNumberOfDays=28, so there should be
// 4 events (25 Oct, 1 Nov, (switch to STD), 8 Nov, Nov 15), but 1 Nov and 8 Nov are excluded.
// There are three separate tests:
// * before midnight GMT (3pm local time)
// * at midnight GMT in STD time (4pm local time)
// * at midnight GMT in DST time (5pm local time)
/*
* LOS ANGELES TESTS:
* In 2023, DST (GMT-7) was until 5 Nov, after which is standard (STD) (GMT-8) time.
* Test takes place on Thu 19 Oct, recurring event on a Wednesday. maximumNumberOfDays=28, so there should be
* 4 events (25 Oct, 1 Nov, (switch to STD), 8 Nov, Nov 15), but 1 Nov and 8 Nov are excluded.
* There are three separate tests:
* * before midnight GMT (3pm local time)
* * at midnight GMT in STD time (4pm local time)
* * at midnight GMT in DST time (5pm local time)
*/
describe("Exdate: LA crossover DST before midnight GMT", () => {
it("LA crossover DST before midnight GMT should have 2 events", async () => {
await helpers.startApplication("tests/configs/modules/calendar/exdate_la_before_midnight.js", "19 Oct 2023 12:30:00 GMT-07:00", ["js/electron.js"], "America/Los_Angeles");
@@ -107,15 +109,16 @@ describe("Calendar module", () => {
});
});
/****************************/
// SYDNEY TESTS:
// In 2023, standard time (STD) (GMT+10) was until 1 Oct, after which is DST (GMT+11).
// Test takes place on Thu 14 Sep, recurring event on a Wednesday. maximumNumberOfDays=28, so there should be
// 4 events (20 Sep, 27 Sep, (switch to DST), 4 Oct, 11 Oct), but 27 Sep and 4 Oct are excluded.
// There are three separate tests:
// * before midnight GMT (9am local time)
// * at midnight GMT in STD time (10am local time)
// * at midnight GMT in DST time (11am local time)
/*
* SYDNEY TESTS:
* In 2023, standard time (STD) (GMT+10) was until 1 Oct, after which is DST (GMT+11).
* Test takes place on Thu 14 Sep, recurring event on a Wednesday. maximumNumberOfDays=28, so there should be
* 4 events (20 Sep, 27 Sep, (switch to DST), 4 Oct, 11 Oct), but 27 Sep and 4 Oct are excluded.
* There are three separate tests:
* * before midnight GMT (9am local time)
* * at midnight GMT in STD time (10am local time)
* * at midnight GMT in DST time (11am local time)
*/
describe("Exdate: SYD crossover DST before midnight GMT", () => {
it("LA crossover DST before midnight GMT should have 2 events", async () => {
await helpers.startApplication("tests/configs/modules/calendar/exdate_syd_before_midnight.js", "14 Sep 2023 12:30:00 GMT+10:00", ["js/electron.js"], "Australia/Sydney");