diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml new file mode 100644 index 0000000000..3dab691546 --- /dev/null +++ b/.github/workflows/codeql.yaml @@ -0,0 +1,37 @@ +name: CodeQL + +on: + push: + branches: [develop, master] + pull_request: + branches: [develop] + schedule: + - cron: "0 4 * * 1" + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [actions, javascript-typescript] + + steps: + - uses: actions/checkout@v6 + + - uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + queries: security-and-quality + + - uses: github/codeql-action/autobuild@v4 + + - uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/electron-rebuild.yaml b/.github/workflows/electron-rebuild.yaml index e9cad3346c..dd30f538c7 100644 --- a/.github/workflows/electron-rebuild.yaml +++ b/.github/workflows/electron-rebuild.yaml @@ -1,5 +1,8 @@ name: "Electron Rebuild Testing" +permissions: + contents: read + on: [pull_request] jobs: diff --git a/.github/workflows/enforce-pullrequest-rules.yaml b/.github/workflows/enforce-pullrequest-rules.yaml index aa4d51beb9..aa3973cfbc 100644 --- a/.github/workflows/enforce-pullrequest-rules.yaml +++ b/.github/workflows/enforce-pullrequest-rules.yaml @@ -3,6 +3,9 @@ name: "Enforce Pull-Request Rules" +permissions: + contents: read + on: pull_request: push: diff --git a/cspell.config.json b/cspell.config.json index 1fa1ba6873..69c2c43ca8 100644 --- a/cspell.config.json +++ b/cspell.config.json @@ -13,9 +13,12 @@ "armv", "ashishtank", "autoplay", + "avghumidity", + "avgtemp", "Autorestart", "beada", "Behaviour", + "Beschreibung", "Binney", "bluemanos", "bnitkin", @@ -111,6 +114,7 @@ "flopp", "fontawesome", "fontface", + "forecastday", "forecastweather", "fortawesome", "frameguard", @@ -186,14 +190,18 @@ "luxon", "lxsession", "magicmirror", + "mapbox", "martingron", "marvai", "mastermerge", "matchtype", "maxentries", + "maxtemp", + "maxwind", "Meteo", "michaelteeuw", "michmich", + "mintemp", "Midori", "mirontoli", "MISSINGLANG", @@ -212,7 +220,9 @@ "NEWSFEED", "newsfeedfetcher", "newsfetcher", + "newyear", "newsitems", + "nextdaysrelative", "nfogal", "njwilliams", "nonrepeating", @@ -239,8 +249,10 @@ "pmin", "Português", "PRECIP", + "precips", "Problema", "psieg", + "ptype", "pubdate", "radokristof", "rajniszp", @@ -255,12 +267,14 @@ "Rosso", "Rothfusz", "rrule", + "sameorigin", "savvadam", "sdetweil", "searchstr", "sendheaders", "serveronly", "sexualized", + "showend", "Sitecode", "skpanagiotis", "SMHI", @@ -295,8 +309,11 @@ "timeformat", "titlereplacestr", "titlesearchstr", + "TOCTOU", "todaytemp", "tomzt", + "totalprecip", + "totalsnow", "trunc", "ttlms", "ukmetoffice", @@ -317,6 +334,7 @@ "Vorberechnung", "vppencilsharpener", "Wallys", + "weatherapi", "Weatherbit", "weathercode", "WEATHERDATA", @@ -336,6 +354,7 @@ "Woolridge", "worktree", "Wsymb", + "xhvw", "xlarge", "xmark", "xrandr", diff --git a/defaultmodules/calendar/calendar.js b/defaultmodules/calendar/calendar.js index cfc625431a..e70cbb852d 100644 --- a/defaultmodules/calendar/calendar.js +++ b/defaultmodules/calendar/calendar.js @@ -167,7 +167,7 @@ Module.register("calendar", { this.selfUpdate(); }, - notificationReceived (notification, payload, sender) { + notificationReceived (notification, payload) { if (notification === "FETCH_CALENDAR") { this.sendSocketNotification(notification, { url: payload.url, id: this.identifier }); } diff --git a/defaultmodules/calendar/calendarfetcher.js b/defaultmodules/calendar/calendarfetcher.js index 557f99946c..809b924cf1 100644 --- a/defaultmodules/calendar/calendarfetcher.js +++ b/defaultmodules/calendar/calendarfetcher.js @@ -1,6 +1,5 @@ const ical = require("node-ical"); const Log = require("logger"); -const { Agent } = require("undici"); const CalendarFetcherUtils = require("./calendarfetcherutils"); const HTTPFetcher = require("#http_fetcher"); diff --git a/defaultmodules/calendar/calendarfetcherutils.js b/defaultmodules/calendar/calendarfetcherutils.js index 7234514d72..437c081e28 100644 --- a/defaultmodules/calendar/calendarfetcherutils.js +++ b/defaultmodules/calendar/calendarfetcherutils.js @@ -62,7 +62,7 @@ const CalendarFetcherUtils = { // Subtract 1 second so that events that start on the middle of the night will not repeat. .subtract(1, "seconds"); - Object.entries(data).forEach(([key, event]) => { + Object.values(data).forEach((event) => { if (event.type !== "VEVENT") { return; } diff --git a/defaultmodules/compliments/compliments.js b/defaultmodules/compliments/compliments.js index a20810a9c8..2ded7c191b 100644 --- a/defaultmodules/compliments/compliments.js +++ b/defaultmodules/compliments/compliments.js @@ -308,7 +308,7 @@ Module.register("compliments", { }, // Override notification handler. - notificationReceived (notification, payload, sender) { + notificationReceived (notification, payload) { if (notification === "CURRENTWEATHER_TYPE") { this.currentWeatherType = payload.type; } diff --git a/defaultmodules/newsfeed/newsfeed.js b/defaultmodules/newsfeed/newsfeed.js index b24906962c..cbf1aefa9e 100644 --- a/defaultmodules/newsfeed/newsfeed.js +++ b/defaultmodules/newsfeed/newsfeed.js @@ -411,7 +411,7 @@ Module.register("newsfeed", { } }, - notificationReceived (notification, payload, sender) { + notificationReceived (notification) { const before = this.activeItem; if (notification === "MODULE_DOM_CREATED" && this.config.hideLoading) { this.hide(); diff --git a/defaultmodules/newsfeed/newsfeedfetcher.js b/defaultmodules/newsfeed/newsfeedfetcher.js index 12febd6a35..1a5f421ab8 100644 --- a/defaultmodules/newsfeed/newsfeedfetcher.js +++ b/defaultmodules/newsfeed/newsfeedfetcher.js @@ -40,7 +40,7 @@ class NewsfeedFetcher { }); // Wire up HTTPFetcher events - this.httpFetcher.on("response", (response) => this.#handleResponse(response)); + this.httpFetcher.on("response", (response) => void this.#handleResponse(response)); this.httpFetcher.on("error", (errorInfo) => this.fetchFailedCallback(this, errorInfo)); } @@ -67,7 +67,7 @@ class NewsfeedFetcher { * Handles successful HTTP response * @param {Response} response - The fetch Response object */ - #handleResponse (response) { + async #handleResponse (response) { this.items = []; const parser = new FeedMe(); @@ -106,11 +106,6 @@ class NewsfeedFetcher { parser.on("end", () => this.broadcastItems()); - parser.on("error", (error) => { - Log.error(`${this.url} - Feed parsing failed: ${error.message}`); - this.fetchFailedCallback(this, this.#createParseError(`Feed parsing failed: ${error.message}`, error)); - }); - parser.on("ttl", (minutes) => { const ttlms = Math.min(minutes * 60 * 1000, 86400000); if (ttlms > this.httpFetcher.reloadInterval) { @@ -123,7 +118,7 @@ class NewsfeedFetcher { const nodeStream = response.body instanceof stream.Readable ? response.body : stream.Readable.fromWeb(response.body); - nodeStream.pipe(iconv.decodeStream(this.encoding)).pipe(parser); + await stream.promises.pipeline(nodeStream, iconv.decodeStream(this.encoding), parser); } catch (error) { Log.error(`${this.url} - Stream processing failed: ${error.message}`); this.fetchFailedCallback(this, this.#createParseError(`Stream processing failed: ${error.message}`, error)); diff --git a/defaultmodules/updatenotification/git_helper.js b/defaultmodules/updatenotification/git_helper.js index 0444c9500c..bfc51e4809 100644 --- a/defaultmodules/updatenotification/git_helper.js +++ b/defaultmodules/updatenotification/git_helper.js @@ -1,5 +1,5 @@ const util = require("node:util"); -const exec = util.promisify(require("node:child_process").exec); +const execFile = util.promisify(require("node:child_process").execFile); const fs = require("node:fs"); const path = require("node:path"); const Log = require("logger"); @@ -14,14 +14,14 @@ class GitHelper { return new RegExp(`s*([a-z,0-9]+[.][.][a-z,0-9]+) ${branch}`, "g"); } - async execShell (command) { - const { stdout = "", stderr = "" } = await exec(command); + async execGit (moduleFolder, ...args) { + const { stdout = "", stderr = "" } = await execFile("git", args, { cwd: moduleFolder }); return { stdout, stderr }; } async isGitRepo (moduleFolder) { - const { stderr } = await this.execShell(`cd ${moduleFolder} && git remote -v`); + const { stderr } = await this.execGit(moduleFolder, "remote", "-v"); if (stderr) { Log.error(`Failed to fetch git data for ${moduleFolder}: ${stderr}`); @@ -51,7 +51,7 @@ class GitHelper { // Folder has .git and has at least one git remote, watch this folder this.gitRepos.push({ module: moduleName, folder: moduleFolder }); } - } catch (err) { + } catch { // Error when directory .git doesn't exist or doesn't have any remotes // This module is not managed with git, skip } @@ -69,7 +69,7 @@ class GitHelper { if (repo.module === "MagicMirror") { // the hash is only needed for the mm repo - const { stderr, stdout } = await this.execShell(`cd ${repo.folder} && git rev-parse HEAD`); + const { stderr, stdout } = await this.execGit(repo.folder, "rev-parse", "HEAD"); if (stderr) { Log.error(`Failed to get current commit hash for ${repo.module}: ${stderr}`); @@ -78,7 +78,7 @@ class GitHelper { gitInfo.hash = stdout; } - const { stderr, stdout } = await this.execShell(`cd ${repo.folder} && git status -sb`); + const { stderr, stdout } = await this.execGit(repo.folder, "status", "-sb"); if (stderr) { Log.error(`Failed to get git status for ${repo.module}: ${stderr}`); @@ -123,7 +123,7 @@ class GitHelper { return gitInfo; } - const { stderr } = await this.execShell(`cd ${repo.folder} && git fetch -n --dry-run`); + const { stderr } = await this.execGit(repo.folder, "fetch", "-n", "--dry-run"); // example output: // From https://github.com/MagicMirrorOrg/MagicMirror @@ -140,7 +140,7 @@ class GitHelper { // get behind with refs try { - const { stdout } = await this.execShell(`cd ${repo.folder} && git rev-list --ancestry-path --count ${refDiff}`); + const { stdout } = await this.execGit(repo.folder, "rev-list", "--ancestry-path", "--count", refDiff); gitInfo.behind = parseInt(stdout); // for MagicMirror-Repo and "master" branch avoid getting notified when no tag is in refDiff @@ -148,14 +148,14 @@ class GitHelper { if (gitInfo.behind > 0 && gitInfo.module === "MagicMirror" && gitInfo.current === "master") { let tagList = ""; try { - const { stdout } = await this.execShell(`cd ${repo.folder} && git ls-remote -q --tags --refs`); + const { stdout } = await this.execGit(repo.folder, "ls-remote", "-q", "--tags", "--refs"); tagList = stdout.trim(); } catch (err) { Log.error(`Failed to get tag list for ${repo.module}: ${err}`); } // check if tag is between commits and only report behind > 0 if so try { - const { stdout } = await this.execShell(`cd ${repo.folder} && git rev-list --ancestry-path ${refDiff}`); + const { stdout } = await this.execGit(repo.folder, "rev-list", "--ancestry-path", refDiff); let cnt = 0; for (const ref of stdout.trim().split("\n")) { if (tagList.includes(ref)) cnt++; // tag found @@ -193,19 +193,15 @@ class GitHelper { return this.gitResultList; } - async checkUpdates () { - var updates = []; + checkUpdates () { + const 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); + for (const moduleInfo of this.gitResultList) { + if (moduleInfo.behind > 0 && moduleInfo.module !== "MagicMirror") { + Log.info(`Update found for module: ${moduleInfo.module}`); + updates.push(moduleInfo); + } + } return updates; } diff --git a/defaultmodules/updatenotification/update_helper.js b/defaultmodules/updatenotification/update_helper.js index 34bf289c41..7fcec31ff6 100644 --- a/defaultmodules/updatenotification/update_helper.js +++ b/defaultmodules/updatenotification/update_helper.js @@ -113,7 +113,7 @@ class Updater { Log.info(`Updating ${module.name}...`); return new Promise((resolve) => { - Exec(Command, { cwd: modulePath, timeout: this.timeout }, (error, stdout, stderr) => { + Exec(Command, { cwd: modulePath, timeout: this.timeout }, (error, stdout) => { if (error) { Log.error(`exec error: ${error}`); Result.error = true; @@ -143,7 +143,7 @@ class Updater { pm2Restart () { Log.info("[PM2] restarting MagicMirror..."); const pm2 = require("pm2"); - pm2.restart(this.PM2Id, (err, proc) => { + pm2.restart(this.PM2Id, (err) => { if (err) { Log.error("[PM2] restart Error", err); } diff --git a/defaultmodules/weather/current.njk b/defaultmodules/weather/current.njk index b75966d3b1..3e66d52706 100644 --- a/defaultmodules/weather/current.njk +++ b/defaultmodules/weather/current.njk @@ -38,7 +38,7 @@ {% if config.showUVIndex %}
- {{ current.uv_index }} + {{ current.uvIndex }} {% endif %} @@ -78,11 +78,11 @@
{% endif %} - {% if config.showPrecipitationAmount and current.precipitationAmount %} + {% if config.showPrecipitationAmount and current.precipitationAmount is defined and current.precipitationAmount is not none %} {{ "PRECIP_AMOUNT" | translate }} {{ current.precipitationAmount | unit("precip", current.precipitationUnits) }}
{% endif %} - {% if config.showPrecipitationProbability and current.precipitationProbability %} + {% if config.showPrecipitationProbability and current.precipitationProbability is defined and current.precipitationProbability is not none %} {{ "PRECIP_POP" | translate }} {{ current.precipitationProbability }}% {% endif %} diff --git a/defaultmodules/weather/forecast.njk b/defaultmodules/weather/forecast.njk index 50c6bb53d8..1b28ed2d2f 100644 --- a/defaultmodules/weather/forecast.njk +++ b/defaultmodules/weather/forecast.njk @@ -31,7 +31,7 @@ {% endif %} {% if config.showUVIndex %} - {{ f.uv_index }} + {{ f.uvIndex }} {% endif %} diff --git a/defaultmodules/weather/hourly.njk b/defaultmodules/weather/hourly.njk index 05416de7dd..6db7cd2cd8 100644 --- a/defaultmodules/weather/hourly.njk +++ b/defaultmodules/weather/hourly.njk @@ -15,8 +15,8 @@ {{ hour.temperature | roundValue | unit("temperature") }} {% if config.showUVIndex %} - {% if hour.uv_index!=0 %} - {{ hour.uv_index }} + {% if hour.uvIndex!=0 %} + {{ hour.uvIndex }} {% endif %} diff --git a/defaultmodules/weather/node_helper.js b/defaultmodules/weather/node_helper.js index f9ea08c42a..153ab1c17c 100644 --- a/defaultmodules/weather/node_helper.js +++ b/defaultmodules/weather/node_helper.js @@ -4,6 +4,7 @@ const Log = require("logger"); module.exports = NodeHelper.create({ providers: {}, + lastData: {}, start () { Log.log(`Starting node helper for: ${this.name}`); @@ -31,7 +32,16 @@ module.exports = NodeHelper.create({ Log.log(`Attempting to initialize provider ${identifier} for instance ${instanceId}`); if (this.providers[instanceId]) { - Log.log(`Weather provider ${identifier} already initialized for instance ${instanceId}`); + Log.log(`Weather provider ${identifier} already initialized for instance ${instanceId}, re-sending WEATHER_INITIALIZED`); + // Client may have restarted (e.g. page reload) - re-send so it recovers location name + this.sendSocketNotification("WEATHER_INITIALIZED", { + instanceId, + locationName: this.providers[instanceId].locationName + }); + // Push cached data immediately so reconnecting clients don't wait for next scheduled fetch + if (this.lastData[instanceId]) { + this.sendSocketNotification("WEATHER_DATA", this.lastData[instanceId]); + } return; } @@ -48,11 +58,9 @@ module.exports = NodeHelper.create({ provider.setCallbacks( (data) => { // On data received - this.sendSocketNotification("WEATHER_DATA", { - instanceId, - type: config.type, - data - }); + const payload = { instanceId, type: config.type, data }; + this.lastData[instanceId] = payload; + this.sendSocketNotification("WEATHER_DATA", payload); }, (errorInfo) => { // On error @@ -96,6 +104,7 @@ module.exports = NodeHelper.create({ Log.log(`Stopping weather provider for instance ${instanceId}`); provider.stop(); delete this.providers[instanceId]; + delete this.lastData[instanceId]; } else { Log.warn(`No provider found for instance ${instanceId}`); } diff --git a/defaultmodules/weather/providers/openmeteo.js b/defaultmodules/weather/providers/openmeteo.js index 7cc5f43995..ff02cc617e 100644 --- a/defaultmodules/weather/providers/openmeteo.js +++ b/defaultmodules/weather/providers/openmeteo.js @@ -418,46 +418,18 @@ class OpenMeteoProvider { // Add hourly data if available if (parsedData.hourly) { - let h; - const currentTime = parsedData.current_weather.time; - - // Handle both data shapes: object with arrays or array of objects (after transpose) - if (Array.isArray(parsedData.hourly)) { - // Array of objects (after transpose) - const hourlyIndex = parsedData.hourly.findIndex((hour) => hour.time.getTime() === currentTime.getTime()); - h = hourlyIndex !== -1 ? hourlyIndex : 0; - - if (hourlyIndex === -1) { - Log.debug("[openmeteo] Could not find current time in hourly data, using index 0"); - } - - const hourData = parsedData.hourly[h]; - if (hourData) { - current.humidity = hourData.relativehumidity_2m; - current.feelsLikeTemp = hourData.apparent_temperature; - current.rain = hourData.rain; - current.snow = hourData.snowfall ? hourData.snowfall * 10 : undefined; - current.precipitationAmount = hourData.precipitation; - current.precipitationProbability = hourData.precipitation_probability; - current.uvIndex = hourData.uv_index; - } - } else if (parsedData.hourly.time) { - // Object with arrays (before transpose - shouldn't happen in normal flow) - const hourlyIndex = parsedData.hourly.time.findIndex((time) => time === currentTime); - h = hourlyIndex !== -1 ? hourlyIndex : 0; - - if (hourlyIndex === -1) { - Log.debug("[openmeteo] Could not find current time in hourly data, using index 0"); - } - - current.humidity = parsedData.hourly.relativehumidity_2m?.[h]; - current.feelsLikeTemp = parsedData.hourly.apparent_temperature?.[h]; - current.rain = parsedData.hourly.rain?.[h]; - current.snow = parsedData.hourly.snowfall?.[h] ? parsedData.hourly.snowfall[h] * 10 : undefined; - current.precipitationAmount = parsedData.hourly.precipitation?.[h]; - current.precipitationProbability = parsedData.hourly.precipitation_probability?.[h]; - current.uvIndex = parsedData.hourly.uv_index?.[h]; - } + // Open-Meteo updates current_weather every 15 min, but hourly entries only + // exist at full hours — find the last entry at or before the current time. + const currentMs = parsedData.current_weather.time.getTime(); + const hourlyIndex = parsedData.hourly.findLastIndex((hour) => hour.time.getTime() <= currentMs); + const hourData = parsedData.hourly[Math.max(0, hourlyIndex)]; + current.humidity = hourData.relativehumidity_2m; + current.feelsLikeTemp = hourData.apparent_temperature; + current.rain = hourData.rain; + current.snow = hourData.snowfall != null ? hourData.snowfall * 10 : null; + current.precipitationAmount = hourData.precipitation; + current.precipitationProbability = hourData.precipitation_probability; + current.uvIndex = hourData.uv_index; } // Add daily data if available (after transpose, daily is array of objects) diff --git a/defaultmodules/weather/providers/openweathermap.js b/defaultmodules/weather/providers/openweathermap.js index 995855f17e..a60ade013e 100644 --- a/defaultmodules/weather/providers/openweathermap.js +++ b/defaultmodules/weather/providers/openweathermap.js @@ -96,28 +96,41 @@ class OpenWeatherMapProvider { #handleResponse (data) { try { - // Set location name from timezone - if (data.timezone) { - this.locationName = data.timezone; - } - let weatherData; - const onecallData = this.#generateWeatherObjectsFromOnecall(data); - - switch (this.config.type) { - case "current": - weatherData = onecallData.current; - break; - case "forecast": - case "daily": - weatherData = onecallData.days; - break; - case "hourly": - weatherData = onecallData.hours; - break; - default: - Log.error(`[openweathermap] Unknown type: ${this.config.type}`); - throw new Error(`Unknown weather type: ${this.config.type}`); + + if (this.config.weatherEndpoint === "/onecall") { + // One Call API (v3.0) + if (data.timezone) { + this.locationName = data.timezone; + } + + const onecallData = this.#generateWeatherObjectsFromOnecall(data); + + switch (this.config.type) { + case "current": + weatherData = onecallData.current; + break; + case "forecast": + case "daily": + weatherData = onecallData.days; + break; + case "hourly": + weatherData = onecallData.hours; + break; + default: + Log.error(`[openweathermap] Unknown type: ${this.config.type}`); + throw new Error(`Unknown weather type: ${this.config.type}`); + } + } else if (this.config.weatherEndpoint === "/weather") { + // Current weather endpoint (API v2.5) + weatherData = this.#generateWeatherObjectFromCurrentWeather(data); + } else if (this.config.weatherEndpoint === "/forecast") { + // 3-hourly forecast endpoint (API v2.5) + weatherData = this.config.type === "hourly" + ? this.#generateHourlyWeatherObjectsFromForecast(data) + : this.#generateDailyWeatherObjectsFromForecast(data); + } else { + throw new Error(`Unknown weather endpoint: ${this.config.weatherEndpoint}`); } if (weatherData && this.onDataCallback) { @@ -134,6 +147,123 @@ class OpenWeatherMapProvider { } } + #generateWeatherObjectFromCurrentWeather (data) { + const timezoneOffsetMinutes = (data.timezone ?? 0) / 60; + + if (data.name && data.sys?.country) { + this.locationName = `${data.name}, ${data.sys.country}`; + } else if (data.name) { + this.locationName = data.name; + } + + const weather = {}; + weather.date = weatherUtils.applyTimezoneOffset(new Date(data.dt * 1000), timezoneOffsetMinutes); + weather.temperature = data.main.temp; + weather.feelsLikeTemp = data.main.feels_like; + weather.humidity = data.main.humidity; + weather.windSpeed = data.wind.speed; + weather.windFromDirection = data.wind.deg; + weather.weatherType = weatherUtils.convertWeatherType(data.weather[0].icon); + weather.sunrise = weatherUtils.applyTimezoneOffset(new Date(data.sys.sunrise * 1000), timezoneOffsetMinutes); + weather.sunset = weatherUtils.applyTimezoneOffset(new Date(data.sys.sunset * 1000), timezoneOffsetMinutes); + + return weather; + } + + #extractThreeHourPrecipitation (forecast) { + const rain = Number.parseFloat(forecast.rain?.["3h"] ?? "") || 0; + const snow = Number.parseFloat(forecast.snow?.["3h"] ?? "") || 0; + const precipitationAmount = rain + snow; + + return { + rain, + snow, + precipitationAmount, + hasPrecipitation: precipitationAmount > 0 + }; + } + + #generateHourlyWeatherObjectsFromForecast (data) { + const timezoneOffsetSeconds = data.city?.timezone ?? 0; + const timezoneOffsetMinutes = timezoneOffsetSeconds / 60; + + if (data.city?.name && data.city?.country) { + this.locationName = `${data.city.name}, ${data.city.country}`; + } + + return data.list.map((forecast) => { + const weather = {}; + weather.date = weatherUtils.applyTimezoneOffset(new Date(forecast.dt * 1000), timezoneOffsetMinutes); + weather.temperature = forecast.main.temp; + weather.feelsLikeTemp = forecast.main.feels_like; + weather.humidity = forecast.main.humidity; + weather.windSpeed = forecast.wind.speed; + weather.windFromDirection = forecast.wind.deg; + weather.weatherType = weatherUtils.convertWeatherType(forecast.weather[0].icon); + weather.precipitationProbability = forecast.pop !== undefined ? forecast.pop * 100 : undefined; + + const precipitation = this.#extractThreeHourPrecipitation(forecast); + if (precipitation.hasPrecipitation) { + weather.rain = precipitation.rain; + weather.snow = precipitation.snow; + weather.precipitationAmount = precipitation.precipitationAmount; + } + + return weather; + }); + } + + #generateDailyWeatherObjectsFromForecast (data) { + const timezoneOffsetSeconds = data.city?.timezone ?? 0; + const timezoneOffsetMinutes = timezoneOffsetSeconds / 60; + + if (data.city?.name && data.city?.country) { + this.locationName = `${data.city.name}, ${data.city.country}`; + } + + const dayMap = new Map(); + + for (const forecast of data.list) { + // Shift dt by timezone offset so UTC fields represent local time + const localDate = new Date((forecast.dt + timezoneOffsetSeconds) * 1000); + const dateKey = `${localDate.getUTCFullYear()}-${String(localDate.getUTCMonth() + 1).padStart(2, "0")}-${String(localDate.getUTCDate()).padStart(2, "0")}`; + + if (!dayMap.has(dateKey)) { + dayMap.set(dateKey, { + date: weatherUtils.applyTimezoneOffset(new Date(forecast.dt * 1000), timezoneOffsetMinutes), + minTemps: [], + maxTemps: [], + rain: 0, + snow: 0, + weatherType: weatherUtils.convertWeatherType(forecast.weather[0].icon) + }); + } + + const day = dayMap.get(dateKey); + day.minTemps.push(forecast.main.temp_min); + day.maxTemps.push(forecast.main.temp_max); + + const hour = localDate.getUTCHours(); + if (hour >= 8 && hour <= 17) { + day.weatherType = weatherUtils.convertWeatherType(forecast.weather[0].icon); + } + + const precipitation = this.#extractThreeHourPrecipitation(forecast); + day.rain += precipitation.rain; + day.snow += precipitation.snow; + } + + return Array.from(dayMap.values()).map((day) => ({ + date: day.date, + minTemperature: Math.min(...day.minTemps), + maxTemperature: Math.max(...day.maxTemps), + weatherType: day.weatherType, + rain: day.rain, + snow: day.snow, + precipitationAmount: day.rain + day.snow + })); + } + #generateWeatherObjectsFromOnecall (data) { let precip; diff --git a/defaultmodules/weather/providers/smhi.js b/defaultmodules/weather/providers/smhi.js index 163d5bafde..26b3673fb0 100644 --- a/defaultmodules/weather/providers/smhi.js +++ b/defaultmodules/weather/providers/smhi.js @@ -5,8 +5,32 @@ const HTTPFetcher = require("#http_fetcher"); /** * Server-side weather provider for SMHI (Swedish Meteorological and Hydrological Institute) * Sweden only, metric system - * API: https://opendata.smhi.se/apidocs/metfcst/ + * + * API: SNOW1gv1 — https://opendata.smhi.se/metfcst/snow1gv1 + * Migrated from PMP3gv2 (deprecated 2026-03-31, returns HTTP 404) + * + * Version: 2.0.1 (2026-04-02) + * + * Key differences from PMP3gv2: + * - URL: snow1g/version/1 (was pmp3g/version/2) + * - Time key: "time" (was "validTime") + * - Data structure: flat object entry.data.X (was parameters[].find().values[0]) + * - Parameter names: human-readable (air_temperature, wind_speed, etc.) + * - Coordinates: flat [lon, lat] (was nested [[lon, lat]]) + * - Precipitation types: different value mapping (1=rain, not snow) */ + +/** + * Maps user-facing config precipitationValue to SNOW1gv1 parameter names. + * Maintains backward compatibility with existing MagicMirror configs. + */ +const PRECIP_VALUE_MAP = { + pmin: "precipitation_amount_min", + pmean: "precipitation_amount_mean", + pmedian: "precipitation_amount_median", + pmax: "precipitation_amount_max" +}; + class SMHIProvider { constructor (config) { this.config = { @@ -19,7 +43,7 @@ class SMHIProvider { }; // Validate precipitationValue - if (!["pmin", "pmean", "pmedian", "pmax"].includes(this.config.precipitationValue)) { + if (!Object.keys(PRECIP_VALUE_MAP).includes(this.config.precipitationValue)) { Log.warn(`[smhi] Invalid precipitationValue: ${this.config.precipitationValue}, using pmedian`); this.config.precipitationValue = "pmedian"; } @@ -152,14 +176,20 @@ class SMHIProvider { return this.#convertWeatherDataGroupedBy(filled, coordinates, "hour"); } + /** + * Find the time series entry closest to the current time. + * SNOW1gv1 uses "time" instead of PMP3gv2's "validTime". + * @param {Array} times - Array of SNOW1gv1 time series entries. + * @returns {object} The time series entry closest to the current time. + */ #getClosestToCurrentTime (times) { const now = new Date(); let minDiff = null; let closest = times[0]; for (const time of times) { - const validTime = new Date(time.validTime); - const diff = Math.abs(validTime - now); + const entryTime = new Date(time.time); + const diff = Math.abs(entryTime - now); if (minDiff === null || diff < minDiff) { minDiff = diff; @@ -170,18 +200,27 @@ class SMHIProvider { return closest; } + /** + * Convert a single SNOW1gv1 time series entry to MagicMirror weather object. + * + * SNOW1gv1 data structure: entry.data.parameter_name (flat object) + * PMP3gv2 used: entry.parameters[{name, values}] (array of objects) + * @param {object} weatherData - A single SNOW1gv1 time series entry. + * @param {object} coordinates - Object with lat and lon properties. + * @returns {object} MagicMirror-formatted weather data object. + */ #convertWeatherDataToObject (weatherData, coordinates) { - const date = new Date(weatherData.validTime); + const date = new Date(weatherData.time); const { sunrise, sunset } = getSunTimes(date, coordinates.lat, coordinates.lon); const isDay = isDayTime(date, sunrise, sunset); const current = { date: date, - humidity: this.#paramValue(weatherData, "r"), - temperature: this.#paramValue(weatherData, "t"), - windSpeed: this.#paramValue(weatherData, "ws"), - windFromDirection: this.#paramValue(weatherData, "wd"), - weatherType: this.#convertWeatherType(this.#paramValue(weatherData, "Wsymb2"), isDay), + humidity: this.#paramValue(weatherData, "relative_humidity"), + temperature: this.#paramValue(weatherData, "air_temperature"), + windSpeed: this.#paramValue(weatherData, "wind_speed"), + windFromDirection: this.#paramValue(weatherData, "wind_from_direction"), + weatherType: this.#convertWeatherType(this.#paramValue(weatherData, "symbol_code"), isDay), feelsLikeTemp: this.#calculateApparentTemperature(weatherData), sunrise: sunrise, sunset: sunset, @@ -190,28 +229,37 @@ class SMHIProvider { precipitationAmount: 0 }; - // Determine precipitation amount and category - const precipitationValue = this.#paramValue(weatherData, this.config.precipitationValue); - const pcat = this.#paramValue(weatherData, "pcat"); - + // Map user config (pmedian/pmean/pmin/pmax) to SNOW1gv1 parameter name + const precipParamName = PRECIP_VALUE_MAP[this.config.precipitationValue]; + const precipitationValue = this.#paramValue(weatherData, precipParamName); + const pcat = this.#paramValue(weatherData, "predominant_precipitation_type_at_surface"); + + // SNOW1gv1 precipitation type mapping (differs from PMP3gv2!): + // 0 = no precipitation + // 1 = rain + // 2 = sleet (snow + rain mix) + // 5 = snow / freezing rain + // 6 = freezing mixed precipitation + // 11 = drizzle / light rain switch (pcat) { - case 1: // Snow - current.snow = precipitationValue; + case 1: // Rain + case 11: // Drizzle / light rain + current.rain = precipitationValue; current.precipitationAmount = precipitationValue; break; - case 2: // Snow and rain (50/50 split) + case 2: // Sleet / mixed rain and snow current.snow = precipitationValue / 2; current.rain = precipitationValue / 2; current.precipitationAmount = precipitationValue; break; - case 3: // Rain - case 4: // Drizzle - case 5: // Freezing rain - case 6: // Freezing drizzle - current.rain = precipitationValue; + case 5: // Snow / freezing rain + case 6: // Freezing mixed precipitation + current.snow = precipitationValue; current.precipitationAmount = precipitationValue; break; - // case 0: No precipitation - defaults already set to 0 + case 0: + default: + break; } return current; @@ -285,15 +333,22 @@ class SMHIProvider { } } + /** + * Fill gaps in time series data for forecast/hourly grouping. + * SNOW1gv1 has variable time steps: 1h (0-48h), 2h (49-72h), 6h (73-132h), 12h (133h+). + * Uses "time" key instead of PMP3gv2's "validTime". + * @param {Array} data - Array of SNOW1gv1 time series entries. + * @returns {Array} Time series with hourly gaps filled using previous entry data. + */ #fillInGaps (data) { if (data.length === 0) return []; const result = []; - result.push(data[0]); // Keep first data point + result.push(data[0]); for (let i = 1; i < data.length; i++) { - const from = new Date(data[i - 1].validTime); - const to = new Date(data[i].validTime); + const from = new Date(data[i - 1].time); + const to = new Date(data[i].time); const hours = Math.floor((to - from) / (1000 * 60 * 60)); // Fill gaps with previous data point (start at j=1 since j=0 is already pushed) @@ -301,7 +356,7 @@ class SMHIProvider { const current = { ...data[i - 1] }; const newTime = new Date(from); newTime.setHours(from.getHours() + j); - current.validTime = newTime.toISOString(); + current.time = newTime.toISOString(); result.push(current); } @@ -312,13 +367,21 @@ class SMHIProvider { return result; } + /** + * Extract coordinates from SNOW1gv1 response. + * SNOW1gv1 returns flat GeoJSON Point: { coordinates: [lon, lat] } + * PMP3gv2 returned nested: { coordinates: [[lon, lat]] } + * @param {object} data - The full SNOW1gv1 API response object. + * @returns {object} Object with lat and lon properties. + */ #resolveCoordinates (data) { - // SMHI returns coordinates in [lon, lat] format - // Fall back to config if response structure is unexpected - if (data?.geometry?.coordinates?.[0] && Array.isArray(data.geometry.coordinates[0]) && data.geometry.coordinates[0].length >= 2) { + const coords = data?.geometry?.coordinates; + + if (Array.isArray(coords) && coords.length >= 2 && typeof coords[0] === "number") { + // SNOW1gv1 flat format: [lon, lat] return { - lat: data.geometry.coordinates[0][1], - lon: data.geometry.coordinates[0][0] + lat: coords[1], + lon: coords[0] }; } @@ -329,20 +392,57 @@ class SMHIProvider { }; } + /** + * Calculate apparent (feels-like) temperature using humidity and wind. + * Uses SNOW1gv1 parameter names. + * @param {object} weatherData - A single SNOW1gv1 time series entry. + * @returns {number|null} Apparent temperature in °C, or raw temperature if data is missing. + */ #calculateApparentTemperature (weatherData) { - const Ta = this.#paramValue(weatherData, "t"); - const rh = this.#paramValue(weatherData, "r"); - const ws = this.#paramValue(weatherData, "ws"); - const p = (rh / 100) * 6.105 * Math.exp((17.27 * Ta) / (237.7 + Ta)); + const Ta = this.#paramValue(weatherData, "air_temperature"); + const rh = this.#paramValue(weatherData, "relative_humidity"); + const ws = this.#paramValue(weatherData, "wind_speed"); + if (Ta === null || rh === null || ws === null) { + return Ta; // Fallback to raw temperature if data missing + } + + const p = (rh / 100) * 6.105 * Math.exp((17.27 * Ta) / (237.7 + Ta)); return Ta + 0.33 * p - 0.7 * ws - 4; } + /** + * Get parameter value from SNOW1gv1 flat data structure. + * SNOW1gv1: weatherData.data.parameter_name (direct property access) + * PMP3gv2 used: weatherData.parameters.find(p => p.name === name).values[0] + * + * Returns null if parameter missing or equals SMHI missing value (9999). + * @param {object} weatherData - A single SNOW1gv1 time series entry. + * @param {string} name - The SNOW1gv1 parameter name to look up. + * @returns {number|null} The parameter value, or null if missing. + */ #paramValue (weatherData, name) { - const param = weatherData.parameters.find((p) => p.name === name); - return param ? param.values[0] : null; + const value = weatherData.data?.[name]; + + if (value === undefined || value === null) { + return null; + } + + // SMHI uses 9999 as missing value sentinel for all parameters + if (value === 9999) { + return null; + } + + return value; } + /** + * Convert SMHI symbol_code (1-27) to MagicMirror weather icon names. + * Symbol codes are identical between PMP3gv2 and SNOW1gv1. + * @param {number} input - SMHI symbol_code value (1-27). + * @param {boolean} isDayTime - Whether the current time is during daytime. + * @returns {string|null} MagicMirror weather icon name, or null if unknown. + */ #convertWeatherType (input, isDayTime) { switch (input) { case 1: @@ -387,10 +487,16 @@ class SMHIProvider { } } + /** + * Build SNOW1gv1 forecast URL. + * Changed from: pmp3g/version/2 + * Changed to: snow1g/version/1 + * @returns {string} The full SNOW1gv1 API URL for the configured coordinates. + */ #getUrl () { const lon = this.config.lon.toFixed(6); const lat = this.config.lat.toFixed(6); - return `https://opendata-download-metfcst.smhi.se/api/category/pmp3g/version/2/geotype/point/lon/${lon}/lat/${lat}/data.json`; + return `https://opendata-download-metfcst.smhi.se/api/category/snow1g/version/1/geotype/point/lon/${lon}/lat/${lat}/data.json`; } } diff --git a/defaultmodules/weather/providers/weatherapi.js b/defaultmodules/weather/providers/weatherapi.js index 0ffb1821a2..30124b8029 100644 --- a/defaultmodules/weather/providers/weatherapi.js +++ b/defaultmodules/weather/providers/weatherapi.js @@ -351,7 +351,7 @@ class WeatherAPIProvider { weather.precipitationProbability = precipitationProbability; } - weather.uv_index = this.#toNumber(forecastDay.day?.uv); + weather.uvIndex = this.#toNumber(forecastDay.day?.uv); days.push(weather); @@ -410,7 +410,7 @@ class WeatherAPIProvider { const willSnow = this.#toNumber(hourData.will_it_snow) ?? 0; weather.precipitationProbability = (willRain + willSnow) * 50; - weather.uv_index = this.#toNumber(hourData.uv); + weather.uvIndex = this.#toNumber(hourData.uv); hours.push(weather); diff --git a/defaultmodules/weather/providers/weatherflow.js b/defaultmodules/weather/providers/weatherflow.js index 8a4d18daa8..e94af5109f 100644 --- a/defaultmodules/weather/providers/weatherflow.js +++ b/defaultmodules/weather/providers/weatherflow.js @@ -145,12 +145,15 @@ class WeatherFlowProvider { const weather = { date: new Date(), - humidity: current.relative_humidity || null, - temperature: current.air_temperature || null, - feelsLikeTemp: current.feels_like || null, + humidity: current.relative_humidity ?? null, + temperature: current.air_temperature ?? null, + feelsLikeTemp: current.feels_like ?? null, windSpeed: current.wind_avg != null ? convertKmhToMs(current.wind_avg) : null, - windFromDirection: current.wind_direction || null, + windFromDirection: current.wind_direction ?? null, weatherType: this.#convertWeatherType(current.icon), + precipitationAmount: current.precip_accum_local_day ?? null, + precipitationUnits: "mm", + precipitationProbability: current.precip_probability ?? null, uvIndex: current.uv || null, sunrise: daily.sunrise ? new Date(daily.sunrise * 1000) : null, sunset: daily.sunset ? new Date(daily.sunset * 1000) : null @@ -175,9 +178,9 @@ class WeatherFlowProvider { for (const forecast of data.forecast.daily) { const weather = { date: new Date(forecast.day_start_local * 1000), - minTemperature: forecast.air_temp_low || null, - maxTemperature: forecast.air_temp_high || null, - precipitationProbability: forecast.precip_probability || null, + minTemperature: forecast.air_temp_low ?? null, + maxTemperature: forecast.air_temp_high ?? null, + precipitationProbability: forecast.precip_probability ?? null, weatherType: this.#convertWeatherType(forecast.icon), precipitationAmount: 0.0, precipitationUnits: "mm", @@ -193,8 +196,8 @@ class WeatherFlowProvider { if (hourDate.getFullYear() === forecastDate.getFullYear() && hourDate.getMonth() === forecastDate.getMonth() && hourDate.getDate() === forecastDate.getDate()) { - weather.uvIndex = Math.max(weather.uvIndex, hour.uv || 0); - weather.precipitationAmount += hour.precip || 0; + weather.uvIndex = Math.max(weather.uvIndex, hour.uv ?? 0); + weather.precipitationAmount += hour.precip ?? 0; } else if (hourDate > forecastDate) { // Check if we've moved to the next day const diffMs = hourDate - forecastDate; @@ -224,14 +227,14 @@ class WeatherFlowProvider { for (const hour of data.forecast.hourly) { const weather = { date: new Date(hour.time * 1000), - temperature: hour.air_temperature || null, - feelsLikeTemp: hour.feels_like || null, - humidity: hour.relative_humidity || null, + temperature: hour.air_temperature ?? null, + feelsLikeTemp: hour.feels_like ?? null, + humidity: hour.relative_humidity ?? null, windSpeed: hour.wind_avg != null ? convertKmhToMs(hour.wind_avg) : null, - windFromDirection: hour.wind_direction || null, + windFromDirection: hour.wind_direction ?? null, weatherType: this.#convertWeatherType(hour.icon), - precipitationProbability: hour.precip_probability || null, - precipitationAmount: hour.precip || 0, + precipitationProbability: hour.precip_probability ?? null, + precipitationAmount: hour.precip ?? 0, precipitationUnits: "mm", uvIndex: hour.uv || null }; diff --git a/defaultmodules/weather/providers/yr.js b/defaultmodules/weather/providers/yr.js index 0de8b51964..9b44571f9e 100644 --- a/defaultmodules/weather/providers/yr.js +++ b/defaultmodules/weather/providers/yr.js @@ -314,7 +314,7 @@ class YrProvider { // Convert collected data to forecast objects const days = []; - for (const [dateStr, data] of dailyData) { + for (const data of dailyData.values()) { const stellarInfo = this.#getStellarInfoForDate(data.date); const dayData = { diff --git a/defaultmodules/weather/weather.js b/defaultmodules/weather/weather.js index cf0170a7b5..e40e41a4cd 100644 --- a/defaultmodules/weather/weather.js +++ b/defaultmodules/weather/weather.js @@ -1,11 +1,11 @@ -/* global WeatherProvider, WeatherUtils, WeatherObject, formatTime */ +/* global WeatherUtils, WeatherObject, formatTime */ Module.register("weather", { // Default module config. defaults: { weatherProvider: "openweathermap", roundTemp: false, - type: "current", // current, forecast, daily (equivalent to forecast), hourly (only with OpenWeatherMap /onecall endpoint) + type: "current", // current, forecast, daily (equivalent to forecast), hourly lang: config.language, units: config.units, tempUnits: config.units, @@ -110,8 +110,8 @@ Module.register("weather", { this.config.showHumidity = this.config.showHumidity ? "wind" : "none"; } - // All providers run server-side: generate unique instance ID and initialize via node_helper - this.instanceId = `${this.identifier}_${Date.now()}`; + // All providers run server-side: use stable identifier so reconnects don't spawn duplicate HTTPFetchers + this.instanceId = this.identifier; if (window.initWeatherTheme) window.initWeatherTheme(this); @@ -242,7 +242,23 @@ Module.register("weather", { // Add all the data to the template. getTemplateData () { - const hourlyData = this.weatherHourlyArray?.filter((e, i) => (i + 1) % this.config.hourlyForecastIncrements === this.config.hourlyForecastIncrements - 1); + const now = new Date(); + // Filter out past entries, but keep the current hour (e.g. show 0:00 at 0:10). + // This ensures consistent behavior across all providers, regardless of whether + // a provider filters past entries itself. + const startOfHour = new Date(now); + startOfHour.setMinutes(0, 0, 0); + const upcomingHourlyData = this.weatherHourlyArray + ?.filter((entry) => entry.date?.valueOf() >= startOfHour.getTime()); + const hourlySourceData = upcomingHourlyData?.length ? upcomingHourlyData : this.weatherHourlyArray; + + const increment = this.config.hourlyForecastIncrements; + const keepByConfiguredIncrement = (_entry, index) => { + // Keep the existing offset behavior of hourlyForecastIncrements. + return (index + 1) % increment === increment - 1; + }; + + const hourlyData = hourlySourceData?.filter(keepByConfiguredIncrement); return { config: this.config, diff --git a/eslint.config.mjs b/eslint.config.mjs index fcde3a6800..05232e45df 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -68,7 +68,6 @@ export default defineConfig([ "no-throw-literal": "error", "no-undefined": "off", "no-unneeded-ternary": "error", - "no-unused-vars": "off", "no-useless-return": "error", "no-warning-comments": "off", "object-shorthand": ["error", "methods"], diff --git a/js/animateCSS.js b/js/animateCSS.js index 8721553eee..eacb51d3a8 100644 --- a/js/animateCSS.js +++ b/js/animateCSS.js @@ -1,4 +1,5 @@ /* enumeration of animations in Array **/ + const AnimateCSSIn = [ // Attention seekers "bounce", @@ -155,4 +156,4 @@ function removeAnimateCSS (element, animation) { node.classList.remove("animate__animated", animationName); node.style.removeProperty("--animate-duration"); } -if (typeof window === "undefined") module.exports = { AnimateCSSIn, AnimateCSSOut }; +if (typeof window === "undefined") module.exports = { AnimateCSSIn, AnimateCSSOut, addAnimateCSS, removeAnimateCSS }; diff --git a/js/app.js b/js/app.js index 04bda1221e..0e6cba45e0 100644 --- a/js/app.js +++ b/js/app.js @@ -84,7 +84,7 @@ function App () { try { fs.accessSync(moduleFile, fs.constants.R_OK); - } catch (e) { + } catch { Log.warn(`No ${moduleFile} found for module: ${moduleName}.`); } @@ -93,7 +93,7 @@ function App () { let loadHelper = true; try { fs.accessSync(helperPath, fs.constants.R_OK); - } catch (e) { + } catch { loadHelper = false; Log.log(`No helper found for module: ${moduleName}.`); } @@ -174,7 +174,8 @@ function App () { */ this.start = async function () { const configObj = Utils.loadConfig(); - config = configObj.fullConf; + global.config = configObj.fullConf; + const config = global.config; Utils.checkConfigFile(configObj); global.defaultModulesDir = config.defaultModulesDir; @@ -188,7 +189,7 @@ function App () { try { fs.renameSync(`${global.root_path}/css/custom.css`, `${global.root_path}/${env.customCss}`); Log.warn(`WARNING! Your custom css file was moved from ${global.root_path}/css/custom.css to ${global.root_path}/${env.customCss}`); - } catch (err) { + } catch { Log.warn("WARNING! Your custom css file is currently located in the css folder. Please move it to the config folder!"); } } @@ -245,7 +246,7 @@ function App () { Log.log("Sockets connected & modules started ..."); - return config; + return global.config; }; /** diff --git a/js/defaults.js b/js/defaults.js index bd590894cb..81b5264e61 100644 --- a/js/defaults.js +++ b/js/defaults.js @@ -11,6 +11,8 @@ const defaults = { basePath: "/", electronOptions: {}, ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], + cors: "disabled", // or "allowAll" or "allowWhitelist" + corsDomainWhitelist: [], // example: ["api.mapbox.com"] language: "en", logLevel: ["INFO", "LOG", "WARN", "ERROR"], diff --git a/js/electron.js b/js/electron.js index 9b4465a1b9..ac48dd4b3f 100644 --- a/js/electron.js +++ b/js/electron.js @@ -115,7 +115,7 @@ function createWindow () { } // simulate mouse move to hide black cursor on start - mainWindow.webContents.on("dom-ready", (event) => { + mainWindow.webContents.on("dom-ready", () => { mainWindow.webContents.sendInputEvent({ type: "mouseMove", x: 0, y: 0 }); }); diff --git a/js/http_fetcher.js b/js/http_fetcher.js index f72b4a3be9..b2d7b8c083 100644 --- a/js/http_fetcher.js +++ b/js/http_fetcher.js @@ -1,5 +1,5 @@ const { EventEmitter } = require("node:events"); -const { Agent } = require("undici"); +const { fetch: undiciFetch, Agent } = require("undici"); const Log = require("logger"); const { getUserAgent } = require("#server_functions"); @@ -183,6 +183,19 @@ class HTTPFetcher extends EventEmitter { return null; } + /** + * Returns a shortened version of the URL for log messages. + * @returns {string} Shortened URL + */ + #shortenUrl () { + try { + const urlObj = new URL(this.url); + return `${urlObj.origin}${urlObj.pathname}${urlObj.search.length > 50 ? "?..." : urlObj.search}`; + } catch { + return this.url; + } + } + /** * Determines the retry delay for a non-ok response * @param {Response} response - The fetch Response object @@ -198,28 +211,35 @@ class HTTPFetcher extends EventEmitter { errorType = "AUTH_FAILURE"; delay = Math.max(this.reloadInterval * 5, THIRTY_MINUTES); message = `Authentication failed (${status}). Check your API key. Waiting ${Math.round(delay / 60000)} minutes before retry.`; - Log.error(`${this.logContext}${this.url} - ${message}`); + Log.error(`${this.logContext}${this.#shortenUrl()} - ${message}`); } else if (status === 429) { errorType = "RATE_LIMITED"; const retryAfter = response.headers.get("retry-after"); const parsed = retryAfter ? this.#parseRetryAfter(retryAfter) : null; delay = parsed !== null ? Math.max(parsed, this.reloadInterval) : Math.max(this.reloadInterval * 2, FIFTEEN_MINUTES); message = `Rate limited (429). Retrying in ${Math.round(delay / 60000)} minutes.`; - Log.warn(`${this.logContext}${this.url} - ${message}`); + Log.warn(`${this.logContext}${this.#shortenUrl()} - ${message}`); } else if (status >= 500) { errorType = "SERVER_ERROR"; this.serverErrorCount = Math.min(this.serverErrorCount + 1, this.maxRetries); - delay = this.reloadInterval * Math.pow(2, this.serverErrorCount); - message = `Server error (${status}). Retry #${this.serverErrorCount} in ${Math.round(delay / 60000)} minutes.`; - Log.error(`${this.logContext}${this.url} - ${message}`); + if (this.serverErrorCount >= this.maxRetries) { + delay = this.reloadInterval; + message = `Server error (${status}). Max retries reached, retrying at configured interval (${Math.round(delay / 1000)}s).`; + } else { + delay = HTTPFetcher.calculateBackoffDelay(this.serverErrorCount, { + maxDelay: this.reloadInterval + }); + message = `Server error (${status}). Retry #${this.serverErrorCount} in ${Math.round(delay / 1000)}s.`; + } + Log.error(`${this.logContext}${this.#shortenUrl()} - ${message}`); } else if (status >= 400) { errorType = "CLIENT_ERROR"; delay = Math.max(this.reloadInterval * 2, FIFTEEN_MINUTES); message = `Client error (${status}). Retrying in ${Math.round(delay / 60000)} minutes.`; - Log.error(`${this.logContext}${this.url} - ${message}`); + Log.error(`${this.logContext}${this.#shortenUrl()} - ${message}`); } else { message = `Unexpected HTTP status ${status}.`; - Log.error(`${this.logContext}${this.url} - ${message}`); + Log.error(`${this.logContext}${this.#shortenUrl()} - ${message}`); } return { @@ -263,12 +283,19 @@ class HTTPFetcher extends EventEmitter { const timeoutId = setTimeout(() => controller.abort(), this.timeout); try { - const response = await fetch(this.url, { - ...this.getRequestOptions(), + const requestOptions = this.getRequestOptions(); + // Use undici.fetch when a custom dispatcher is present (e.g. selfSignedCert), + // because Node's global fetch and npm undici@8 Agents are incompatible. + // For regular requests, use globalThis.fetch so MSW and other interceptors work. + const fetchFn = requestOptions.dispatcher ? undiciFetch : globalThis.fetch; + const response = await fetchFn(this.url, { + ...requestOptions, signal: controller.signal }); - if (!response.ok) { + const isSuccessfulResponse = response.ok || response.status === 304; + + if (!isSuccessfulResponse) { const { delay, errorInfo } = this.#getDelayForResponse(response); nextDelay = delay; this.emit("error", errorInfo); @@ -288,28 +315,22 @@ class HTTPFetcher extends EventEmitter { const isTimeout = error.name === "AbortError"; const message = isTimeout ? `Request timeout after ${this.timeout}ms` : `Network error: ${error.message}`; - // Apply exponential backoff for network errors this.networkErrorCount = Math.min(this.networkErrorCount + 1, this.maxRetries); - const backoffDelay = HTTPFetcher.calculateBackoffDelay(this.networkErrorCount, { - maxDelay: this.reloadInterval - }); - nextDelay = backoffDelay; - - // Truncate URL for cleaner logs - let shortUrl = this.url; - try { - const urlObj = new URL(this.url); - shortUrl = `${urlObj.origin}${urlObj.pathname}${urlObj.search.length > 50 ? "?..." : urlObj.search}`; - } catch (urlError) { - // If URL parsing fails, use original URL - } + const exhausted = this.networkErrorCount >= this.maxRetries; - // Gradual log-level escalation: WARN for first 2 attempts, ERROR after - const retryMessage = `Retry #${this.networkErrorCount} in ${Math.round(nextDelay / 1000)}s.`; - if (this.networkErrorCount <= 2) { - Log.warn(`${this.logContext}${shortUrl} - ${message} ${retryMessage}`); + if (exhausted) { + nextDelay = this.reloadInterval; + Log.error(`${this.logContext}${this.#shortenUrl()} - ${message} Max retries reached, retrying at configured interval (${Math.round(nextDelay / 1000)}s).`); } else { - Log.error(`${this.logContext}${shortUrl} - ${message} ${retryMessage}`); + nextDelay = HTTPFetcher.calculateBackoffDelay(this.networkErrorCount, { + maxDelay: this.reloadInterval + }); + const retryMsg = `${this.logContext}${this.#shortenUrl()} - ${message} Retry #${this.networkErrorCount} in ${Math.round(nextDelay / 1000)}s.`; + if (this.networkErrorCount <= 2) { + Log.warn(retryMsg); + } else { + Log.error(retryMsg); + } } const errorInfo = this.#createErrorInfo( @@ -319,18 +340,6 @@ class HTTPFetcher extends EventEmitter { nextDelay, error ); - - /** - * Error event - fired when fetch fails - * @event HTTPFetcher#error - * @type {object} - * @property {string} message - Error description - * @property {number|null} statusCode - HTTP status or null for network errors - * @property {number} retryDelay - Ms until next retry - * @property {number} retryCount - Number of consecutive server errors - * @property {string} url - The URL that was fetched - * @property {Error|null} originalError - The original error - */ this.emit("error", errorInfo); } finally { clearTimeout(timeoutId); diff --git a/js/ip_access_control.js b/js/ip_access_control.js index be1f1063ef..0fef1a4e55 100644 --- a/js/ip_access_control.js +++ b/js/ip_access_control.js @@ -22,12 +22,12 @@ function isAllowed (clientIp, whitelist) { // Single IP address - let ipaddr.process normalize both const allowedAddr = ipaddr.process(entry); return addr.toString() === allowedAddr.toString(); - } catch (err) { + } catch { Log.warn(`Invalid whitelist entry: ${entry}`); return false; } }); - } catch (err) { + } catch { Log.warn(`Failed to parse client IP: ${clientIp}`); return false; } diff --git a/js/loader.js b/js/loader.js index 53283d59b6..31cf7cc0f4 100644 --- a/js/loader.js +++ b/js/loader.js @@ -1,5 +1,6 @@ /* global defaultModules, vendor */ +// eslint-disable-next-line no-unused-vars const Loader = (function () { /* Create helper variables */ diff --git a/js/logger.js b/js/logger.js index b4b552288b..28d6db5227 100644 --- a/js/logger.js +++ b/js/logger.js @@ -29,7 +29,7 @@ return styleText("gray", parentDir === "js" ? `[${baseName}]` : `[${parentDir}]`); } } - } catch (err) { /* ignore */ } + } catch { /* ignore */ } return styleText("gray", "[unknown]"); }; diff --git a/js/main.js b/js/main.js index 359f71df0e..73d6075f3b 100644 --- a/js/main.js +++ b/js/main.js @@ -1,4 +1,4 @@ -/* global Loader, defaults, addAnimateCSS, removeAnimateCSS, AnimateCSSIn, AnimateCSSOut, modulePositions, io */ +/* global Loader, addAnimateCSS, removeAnimateCSS, AnimateCSSIn, AnimateCSSOut, modulePositions, io */ const MM = (function () { let modules = []; @@ -408,7 +408,7 @@ const MM = (function () { updateWrapperStates(); // Waiting for DOM-changes done in updateWrapperStates before we can start the animation. - const dummy = moduleWrapper.parentElement.parentElement.offsetHeight; + void moduleWrapper.parentElement.parentElement.offsetHeight; moduleWrapper.style.opacity = 1; if (haveAnimateName) { @@ -475,7 +475,20 @@ const MM = (function () { const loadConfig = async function () { try { const res = await fetch(new URL("config/", `${location.origin}${config.basePath}`)); - config = JSON.parse(await res.text()); + + // The server tags functions as { __mmFunction: "" } because + // JSON.stringify can't serialise live functions. This reviver turns + // those tagged objects back into callable functions. + config = JSON.parse(await res.text(), (key, value) => { + if (value && typeof value === "object" && typeof value.__mmFunction === "string") { + try { + return new Function(`return (${value.__mmFunction})`)(); + } catch { + Log.warn(`Failed to revive function for config key "${key}".`); + } + } + return value; + }); } catch (error) { Log.error("Unable to retrieve config", error); } diff --git a/js/node_helper.js b/js/node_helper.js index 8910699930..506f1d85c9 100644 --- a/js/node_helper.js +++ b/js/node_helper.js @@ -89,7 +89,7 @@ const NodeHelper = Class.extend({ io.of(this.name).on("connection", (socket) => { // register catch all. socket.onAny((notification, payload) => { - if (config.hideConfigSecrets && payload && typeof payload === "object") { + if (config?.hideConfigSecrets && payload && typeof payload === "object") { try { const payloadStr = replaceSecretPlaceholder(JSON.stringify(payload)); this.socketNotificationReceived(notification, JSON.parse(payloadStr)); diff --git a/js/server.js b/js/server.js index 9922958ca9..f8a51e714d 100644 --- a/js/server.js +++ b/js/server.js @@ -98,7 +98,7 @@ function Server (configObj) { } let directories = ["/config", "/css", "/favicon.svg", "/defaultmodules", "/modules", "/node_modules/animate.css", "/node_modules/@fontsource", "/node_modules/@fortawesome", "/translations", "/tests/configs", "/tests/mocks"]; - for (const [key, value] of Object.entries(vendor)) { + for (const value of Object.values(vendor)) { const dirArr = value.split("/"); if (dirArr[0] === "node_modules") directories.push(`/${dirArr[0]}/${dirArr[1]}`); } @@ -111,12 +111,21 @@ function Server (configObj) { const getStartup = (req, res) => res.send(startUp); const getConfig = (req, res) => { - if (config.hideConfigSecrets) { - res.send(configObj.redactedConf); - } else { - res.send(configObj.fullConf); - } + const obj = config.hideConfigSecrets ? configObj.redactedConf : configObj.fullConf; + // Functions can't survive JSON.stringify, so we wrap them in a + // tagged object { __mmFunction: "" }. The client-side + // JSON reviver in main.js recognises this tag and reconstructs + // the live function from the source string. + const jsonString = JSON.stringify(obj, (key, value) => { + if (typeof value === "function") { + return { __mmFunction: value.toString() }; + } + return value; + }); + res.set("Content-Type", "application/json"); + res.send(jsonString); }; + app.get("/config", (req, res) => getConfig(req, res)); app.get("/cors", async (req, res) => await cors(req, res)); diff --git a/js/server_functions.js b/js/server_functions.js index 063e9cfe35..2d55bc3b44 100644 --- a/js/server_functions.js +++ b/js/server_functions.js @@ -1,5 +1,8 @@ +const dns = require("node:dns"); const fs = require("node:fs"); const path = require("node:path"); +const ipaddr = require("ipaddr.js"); +const undici = require("undici"); const Log = require("logger"); const startUp = new Date(); @@ -19,9 +22,16 @@ function getStartup (req, res) { * @returns {string} the input with real variable content */ function replaceSecretPlaceholder (input) { - return input.replaceAll(/\*\*(SECRET_[^*]+)\*\*/g, (match, group) => { - return process.env[group]; - }); + if (global.config.cors !== "allowAll") { + return input.replaceAll(/\*\*(SECRET_[^*]+)\*\*/g, (match, group) => { + return process.env[group]; + }); + } else { + if (input.includes("**SECRET_")) { + Log.error("Replacing secrets doesn't work with CORS `allowAll`, you need to set `cors` to `disabled` or `allowWhitelist` in `config.js`"); + } + return input; + } } /** @@ -35,9 +45,13 @@ function replaceSecretPlaceholder (input) { * @returns {Promise} A promise that resolves when the response is sent */ async function cors (req, res) { + if (global.config.cors === "disabled") { + Log.error("CORS is disabled, you need to enable it in `config.js` by setting `cors` to `allowAll` or `allowWhitelist`"); + return res.status(403).json({ error: "CORS proxy is disabled" }); + } + let url; try { const urlRegEx = "url=(.+?)$"; - let url; const match = new RegExp(urlRegEx, "g").exec(req.url); if (!match) { @@ -46,17 +60,61 @@ async function cors (req, res) { return res.status(400).send(url); } else { url = match[1]; - if (typeof config !== "undefined") { + if (typeof global.config !== "undefined") { if (config.hideConfigSecrets) { url = replaceSecretPlaceholder(url); } } + // Validate protocol before attempting connection (non-http/https are never allowed) + let parsed; + try { + parsed = new URL(url); + } catch { + Log.warn(`SSRF blocked (invalid URL): ${url}`); + return res.status(403).json({ error: "Forbidden: private or reserved addresses are not allowed" }); + } + if (parsed.protocol !== "http:" && parsed.protocol !== "https:") { + Log.warn(`SSRF blocked (protocol): ${url}`); + return res.status(403).json({ error: "Forbidden: private or reserved addresses are not allowed" }); + } + + // Block localhost by hostname before even creating the dispatcher (no DNS needed). + if (parsed.hostname.toLowerCase() === "localhost") { + Log.warn(`SSRF blocked (localhost): ${url}`); + return res.status(403).json({ error: "Forbidden: private or reserved addresses are not allowed" }); + } + + // Whitelist check: if enabled, only allow explicitly listed domains + if (global.config.cors === "allowWhitelist" && !global.config.corsDomainWhitelist.includes(parsed.hostname.toLowerCase())) { + Log.warn(`CORS blocked (not in whitelist): ${url}`); + return res.status(403).json({ error: "Forbidden: domain not in corsDomainWhitelist" }); + } + const headersToSend = getHeadersToSend(req.url); const expectedReceivedHeaders = geExpectedReceivedHeaders(req.url); Log.log(`cors url: ${url}`); - const response = await fetch(url, { headers: headersToSend }); + // Resolve DNS once and validate the IP. The validated IP is then pinned + // for the actual connection so fetch() cannot re-resolve to a different + // address. This prevents DNS rebinding / TOCTOU attacks (GHSA-xhvw-r95j-xm4v). + const { address, family } = await dns.promises.lookup(parsed.hostname); + if (ipaddr.process(address).range() !== "unicast") { + Log.warn(`SSRF blocked: ${url}`); + return res.status(403).json({ error: "Forbidden: private or reserved addresses are not allowed" }); + } + + // Pin the validated IP — fetch() reuses it instead of doing its own DNS lookup + const dispatcher = new undici.Agent({ + connect: { + lookup: (_h, _o, cb) => { + const addresses = [{ address: address, family: family }]; + process.nextTick(() => cb(null, addresses)); + } + } + }); + + const response = await undici.fetch(url, { dispatcher, headers: headersToSend }); if (response.ok) { for (const header of expectedReceivedHeaders) { const headerValue = response.headers.get(header); @@ -69,7 +127,6 @@ async function cors (req, res) { } } } catch (error) { - // Only log errors in non-test environments to keep test output clean if (process.env.mmTestMode !== "true") { Log.error(`Error in CORS request: ${error}`); } @@ -144,15 +201,15 @@ function getVersion (req, res) { function getUserAgent () { const defaultUserAgent = `Mozilla/5.0 (Node.js ${Number(process.version.match(/^v(\d+\.\d+)/)[1])}) MagicMirror/${global.version}`; - if (typeof config === "undefined") { + if (typeof global.config === "undefined") { return defaultUserAgent; } - switch (typeof config.userAgent) { + switch (typeof global.config.userAgent) { case "function": - return config.userAgent(); + return global.config.userAgent(); case "string": - return config.userAgent; + return global.config.userAgent; default: return defaultUserAgent; } @@ -163,7 +220,7 @@ function getUserAgent () { * @returns {object} environment variables key: values */ function getEnvVarsAsObj () { - const obj = { modulesDir: `${config.foreignModulesDir}`, defaultModulesDir: `${config.defaultModulesDir}`, customCss: `${config.customCss}` }; + const obj = { modulesDir: `${global.config.foreignModulesDir}`, defaultModulesDir: `${global.config.defaultModulesDir}`, customCss: `${global.config.customCss}` }; if (process.env.MM_MODULES_DIR) { obj.modulesDir = process.env.MM_MODULES_DIR.replace(`${global.root_path}/`, ""); } diff --git a/js/socketclient.js b/js/socketclient.js index 73fdbf08a9..0c3a7c6691 100644 --- a/js/socketclient.js +++ b/js/socketclient.js @@ -1,5 +1,6 @@ /* global io */ +// eslint-disable-next-line no-unused-vars const MMSocket = function (moduleName) { if (typeof moduleName !== "string") { throw new Error("Please set the module name for the MMSocket."); diff --git a/js/translator.js b/js/translator.js index 497c42687d..b1b52ec7de 100644 --- a/js/translator.js +++ b/js/translator.js @@ -17,7 +17,7 @@ const Translator = (function () { throw new Error(`Unexpected response status: ${response.status}`); } return await response.json(); - } catch (exception) { + } catch { Log.error(`Loading json file =${file} failed`); return null; } diff --git a/js/utils.js b/js/utils.js index f1c80ba2d0..e947bee9ba 100644 --- a/js/utils.js +++ b/js/utils.js @@ -55,7 +55,7 @@ const getModulePositions = () => { try { fs.writeFileSync(discoveredPositionsJSFilename, `const modulePositions=${JSON.stringify(modulePositions)}`); } - catch (error) { + catch { Log.error("unable to write js/positions.js with the discovered module positions\nmake the MagicMirror/js folder writeable by the user starting MagicMirror"); } } @@ -111,7 +111,7 @@ const loadConfig = () => { try { fs.accessSync(templateFile, fs.constants.F_OK); Log.warn("config.js.template files are deprecated and not used anymore. You can use variables inside config.js so copy the template file content into config.js if needed."); - } catch (error) { + } catch { // no action } diff --git a/package-lock.json b/package-lock.json index 1234f28d45..5e384ba317 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,70 +1,70 @@ { "name": "magicmirror", - "version": "2.35.0", + "version": "2.36.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "magicmirror", - "version": "2.35.0", + "version": "2.36.0", "hasInstallScript": true, "license": "MIT", "dependencies": { "@fontsource/roboto": "^5.2.10", "@fontsource/roboto-condensed": "^5.2.8", "@fortawesome/fontawesome-free": "^7.2.0", - "ajv": "^8.18.0", + "ajv": "^8.20.0", "animate.css": "^4.1.1", "croner": "^10.0.1", - "eslint": "^10.1.0", + "eslint": "^10.2.1", "express": "^5.2.1", "feedme": "^2.0.2", - "globals": "^17.4.0", + "globals": "^17.5.0", "helmet": "^8.1.0", "html-to-text": "^9.0.5", "iconv-lite": "^0.7.2", "ipaddr.js": "^2.3.0", "moment": "^2.30.1", - "moment-timezone": "^0.6.1", - "node-ical": "^0.25.6", + "moment-timezone": "^0.6.2", + "node-ical": "^0.26.0", "nunjucks": "^3.2.4", "pm2": "^6.0.14", "socket.io": "^4.8.3", "suncalc": "^1.9.0", "systeminformation": "^5.31.5", - "undici": "^7.24.6", + "undici": "^8.1.0", "weathericons": "^2.1.0" }, "devDependencies": { "@eslint/js": "^10.0.1", "@stylistic/eslint-plugin": "^5.10.0", - "@vitest/coverage-v8": "^4.1.2", - "@vitest/eslint-plugin": "^1.6.14", - "@vitest/ui": "^4.1.2", - "cspell": "^9.7.0", + "@vitest/coverage-v8": "^4.1.5", + "@vitest/eslint-plugin": "^1.6.16", + "@vitest/ui": "^4.1.5", + "cspell": "^10.0.0", "eslint-plugin-import-x": "^4.16.2", - "eslint-plugin-jsdoc": "^62.8.1", - "eslint-plugin-package-json": "^0.91.1", - "eslint-plugin-playwright": "^2.10.1", + "eslint-plugin-jsdoc": "^62.9.0", + "eslint-plugin-package-json": "^0.91.2", + "eslint-plugin-playwright": "^2.10.2", "express-basic-auth": "^1.2.1", "husky": "^9.1.7", - "jsdom": "^29.0.1", + "jsdom": "^29.1.0", "lint-staged": "^16.4.0", - "markdownlint-cli2": "^0.22.0", - "msw": "^2.12.14", - "playwright": "^1.58.2", - "prettier": "^3.8.1", + "markdownlint-cli2": "^0.22.1", + "msw": "^2.13.6", + "playwright": "^1.59.1", + "prettier": "^3.8.3", "prettier-plugin-jinja-template": "^2.1.0", - "stylelint": "^17.6.0", + "stylelint": "^17.9.1", "stylelint-config-standard": "^40.0.0", "stylelint-prettier": "^5.0.3", - "vitest": "^4.1.2" + "vitest": "^4.1.5" }, "engines": { "node": ">=22.21.1 <23 || >=24" }, "optionalDependencies": { - "electron": "^41.1.0" + "electron": "^41.3.0" } }, "node_modules/@altano/repository-tools": { @@ -75,39 +75,49 @@ "license": "ISC" }, "node_modules/@asamuzakjp/css-color": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.1.tgz", - "integrity": "sha512-iGWN8E45Ws0XWx3D44Q1t6vX2LqhCKcwfmwBYCDsFrYFS6m4q/Ks61L2veETaLv+ckDC6+dTETJoaAAb7VjLiw==", + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", "dev": true, "license": "MIT", "dependencies": { - "@csstools/css-calc": "^3.1.1", - "@csstools/css-color-parser": "^4.0.2", + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0", - "lru-cache": "^11.2.7" + "@csstools/css-tokenizer": "^4.0.0" }, "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/@asamuzakjp/dom-selector": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.4.tgz", - "integrity": "sha512-jXR6x4AcT3eIrS2fSNAwJpwirOkGcd+E7F7CP3zjdTqz9B/2huHOL8YJZBgekKwLML+u7qB/6P1LXQuMScsx0w==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", "dev": true, "license": "MIT", "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.2.1", - "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.7" + "is-potential-custom-element-name": "^1.0.1" }, "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/@asamuzakjp/nwsapi": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", @@ -272,9 +282,9 @@ } }, "node_modules/@cspell/cspell-bundled-dicts": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-9.7.0.tgz", - "integrity": "sha512-s7h1vo++Q3AsfQa3cs0u/KGwm3SYInuIlC4kjlCBWjQmb4KddiZB5O1u0+3TlA7GycHb5M4CR7MDfHUICgJf+w==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-10.0.0.tgz", + "integrity": "sha512-ci410HEkng2582oOjlRHQtlGXwh+rUC/mVcN9dObLHpKhvPgzn2S6vT56pARstxxZpcCUG/oLhn3dCqdJlVzmA==", "dev": true, "license": "MIT", "dependencies": { @@ -282,54 +292,54 @@ "@cspell/dict-al": "^1.1.1", "@cspell/dict-aws": "^4.0.17", "@cspell/dict-bash": "^4.2.2", - "@cspell/dict-companies": "^3.2.10", + "@cspell/dict-companies": "^3.2.11", "@cspell/dict-cpp": "^7.0.2", "@cspell/dict-cryptocurrencies": "^5.0.5", "@cspell/dict-csharp": "^4.0.8", - "@cspell/dict-css": "^4.0.19", + "@cspell/dict-css": "^4.1.1", "@cspell/dict-dart": "^2.3.2", "@cspell/dict-data-science": "^2.0.13", "@cspell/dict-django": "^4.1.6", "@cspell/dict-docker": "^1.1.17", - "@cspell/dict-dotnet": "^5.0.12", + "@cspell/dict-dotnet": "^5.0.13", "@cspell/dict-elixir": "^4.0.8", - "@cspell/dict-en_us": "^4.4.29", + "@cspell/dict-en_us": "^4.4.33", "@cspell/dict-en-common-misspellings": "^2.1.12", - "@cspell/dict-en-gb-mit": "^3.1.18", - "@cspell/dict-filetypes": "^3.0.15", + "@cspell/dict-en-gb-mit": "^3.1.22", + "@cspell/dict-filetypes": "^3.0.18", "@cspell/dict-flutter": "^1.1.1", - "@cspell/dict-fonts": "^4.0.5", + "@cspell/dict-fonts": "^4.0.6", "@cspell/dict-fsharp": "^1.1.1", - "@cspell/dict-fullstack": "^3.2.8", + "@cspell/dict-fullstack": "^3.2.9", "@cspell/dict-gaming-terms": "^1.1.2", "@cspell/dict-git": "^3.1.0", "@cspell/dict-golang": "^6.0.26", "@cspell/dict-google": "^1.0.9", "@cspell/dict-haskell": "^4.0.6", - "@cspell/dict-html": "^4.0.14", + "@cspell/dict-html": "^4.0.15", "@cspell/dict-html-symbol-entities": "^4.0.5", "@cspell/dict-java": "^5.0.12", "@cspell/dict-julia": "^1.1.1", "@cspell/dict-k8s": "^1.0.12", "@cspell/dict-kotlin": "^1.1.1", - "@cspell/dict-latex": "^5.0.0", + "@cspell/dict-latex": "^5.1.0", "@cspell/dict-lorem-ipsum": "^4.0.5", "@cspell/dict-lua": "^4.0.8", "@cspell/dict-makefile": "^1.0.5", - "@cspell/dict-markdown": "^2.0.14", + "@cspell/dict-markdown": "^2.0.16", "@cspell/dict-monkeyc": "^1.0.12", "@cspell/dict-node": "^5.0.9", - "@cspell/dict-npm": "^5.2.34", + "@cspell/dict-npm": "^5.2.38", "@cspell/dict-php": "^4.1.1", "@cspell/dict-powershell": "^5.0.15", - "@cspell/dict-public-licenses": "^2.0.15", - "@cspell/dict-python": "^4.2.25", + "@cspell/dict-public-licenses": "^2.0.16", + "@cspell/dict-python": "^4.2.26", "@cspell/dict-r": "^2.1.1", - "@cspell/dict-ruby": "^5.1.0", + "@cspell/dict-ruby": "^5.1.1", "@cspell/dict-rust": "^4.1.2", "@cspell/dict-scala": "^5.0.9", "@cspell/dict-shell": "^1.1.2", - "@cspell/dict-software-terms": "^5.1.21", + "@cspell/dict-software-terms": "^5.2.2", "@cspell/dict-sql": "^2.2.1", "@cspell/dict-svelte": "^1.0.7", "@cspell/dict-swift": "^2.0.6", @@ -339,86 +349,86 @@ "@cspell/dict-zig": "^1.0.0" }, "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/@cspell/cspell-json-reporter": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-9.7.0.tgz", - "integrity": "sha512-6xpGXlMtQA3hV2BCAQcPkpx9eI12I0o01i9eRqSSEDKtxuAnnrejbcCpL+5OboAjTp3/BSeNYSnhuWYLkSITWQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-10.0.0.tgz", + "integrity": "sha512-hq5dui2ngYMZKbBauX7K1tkqlu81sX/uaCO49ZJLPjeZsE1auZLtHehDLfAr/ZXoj/dLYeQMSKiaJyE+qLVPHA==", "dev": true, "license": "MIT", "dependencies": { - "@cspell/cspell-types": "9.7.0" + "@cspell/cspell-types": "10.0.0" }, "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/@cspell/cspell-performance-monitor": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-performance-monitor/-/cspell-performance-monitor-9.7.0.tgz", - "integrity": "sha512-w1PZIFXuvjnC6mQHyYAFnrsn5MzKnEcEkcK1bj4OG00bAt7WX2VUA/eNNt9c1iHozCQ+FcRYlfbGxuBmNyzSgw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-performance-monitor/-/cspell-performance-monitor-10.0.0.tgz", + "integrity": "sha512-2vMh2pLt2dg/ArYvWjMP4v9HCm0pRhONsEJyc8oHdZyOYvX7trixX894I0M39+VBf3yWtPCEgYRh1UDXNIZRig==", "dev": true, "license": "MIT", "engines": { - "node": ">=20.18" + "node": ">=22.18.0" } }, "node_modules/@cspell/cspell-pipe": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-9.7.0.tgz", - "integrity": "sha512-iiisyRpJciU9SOHNSi0ZEK0pqbEMFRatI/R4O+trVKb+W44p4MNGClLVRWPGUmsFbZKPJL3jDtz0wPlG0/JCZA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-10.0.0.tgz", + "integrity": "sha512-qcgHhQvtEX8LSwIVsWrdUgiGim52lN3jT+ghlkdp72v+nBcGKsS2frEKTmbGLug+xcqppkzs6Q6VmsFp1MGtfA==", "dev": true, "license": "MIT", "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/@cspell/cspell-resolver": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-9.7.0.tgz", - "integrity": "sha512-uiEgS238mdabDnwavo6HXt8K98jlh/jpm7NONroM9NTr9rzck2VZKD2kXEj85wDNMtRsRXNoywTjwQ8WTB6/+w==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-10.0.0.tgz", + "integrity": "sha512-8H+IUDB7SmrpcRugQ5f55qG81ZShk6nQRk+natLz41TEY98D8/LCmjHEkh/vhDPph9pVJmNUp7JcM2E1UHEa2g==", "dev": true, "license": "MIT", "dependencies": { "global-directory": "^5.0.0" }, "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/@cspell/cspell-service-bus": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-9.7.0.tgz", - "integrity": "sha512-fkqtaCkg4jY/FotmzjhIavbXuH0AgUJxZk78Ktf4XlhqOZ4wDeUWrCf220bva4mh3TWiLx/ae9lIlpl59Vx6hA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-10.0.0.tgz", + "integrity": "sha512-V7eigqg/TOoKwNK4Q18wr9KGxA8U5SFcoWVS8RyAxv4mQ+yNKHhvHEbRBifjPbQDer66afOrclb2UbqkIy2SOw==", "dev": true, "license": "MIT", "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/@cspell/cspell-types": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-9.7.0.tgz", - "integrity": "sha512-Tdfx4eH2uS+gv9V9NCr3Rz+c7RSS6ntXp3Blliud18ibRUlRxO9dTaOjG4iv4x0nAmMeedP1ORkEpeXSkh2QiQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-10.0.0.tgz", + "integrity": "sha512-IQA++Idqb8fZzkCbHq3+T+9yG9WpeaBxomOrG2KcR/Pj0CgnovzuApYKL2cc35UWLePboKinMeqEPiweFpHVug==", "dev": true, "license": "MIT", "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/@cspell/cspell-worker": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@cspell/cspell-worker/-/cspell-worker-9.7.0.tgz", - "integrity": "sha512-cjEApFF0aOAa1vTUk+e7xP8ofK7iC7hsRzj1FmvvVQz8PoLWPRaq+1bT89ypPsZQvavqm5sIgb97S60/aW4TVg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-worker/-/cspell-worker-10.0.0.tgz", + "integrity": "sha512-V5bjMldNksilnja3fu8muQmkW5/guyua1yNVOhoE2r7othSvjuDlGMl8g2bQSrWjp+UXu0dP/BEZ6JC/IfNwTA==", "dev": true, "license": "MIT", "dependencies": { - "cspell-lib": "9.7.0" + "cspell-lib": "10.0.0" }, "engines": { - "node": ">=20.18" + "node": ">=22.18.0" } }, "node_modules/@cspell/dict-ada": { @@ -847,57 +857,57 @@ "license": "MIT" }, "node_modules/@cspell/dynamic-import": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-9.7.0.tgz", - "integrity": "sha512-Ws36IYvtS/8IN3x6K9dPLvTmaArodRJmzTn2Rkf2NaTnIYWhRuFzsP3SVVO59NN3fXswAEbmz5DSbVUe8bPZHg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-10.0.0.tgz", + "integrity": "sha512-fMqu/5Ma1Q5ZCR/Par+Q4pvaTKmx5pKZzQmkwld2hNounVdk2OaIPM9MzpNn6I1mLk5J+wTnIZmfcWNAzNP9aQ==", "dev": true, "license": "MIT", "dependencies": { - "@cspell/url": "9.7.0", + "@cspell/url": "10.0.0", "import-meta-resolve": "^4.2.0" }, "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/@cspell/filetypes": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-9.7.0.tgz", - "integrity": "sha512-Ln9e/8wGOyTeL3DCCs6kwd18TSpTw3kxsANjTrzLDASrX4cNmAdvc9J5dcIuBHPaqOAnRQxuZbzUlpRh73Y24w==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-10.0.0.tgz", + "integrity": "sha512-UP57j9yrDtlCHpFxc/eGho1m8DP5olfu9KRWwd5fiqL9nMSE2rUJtPzQyvqmDwO5bVZt3B+fTVdo4gxuiqw25A==", "dev": true, "license": "MIT", "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/@cspell/rpc": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@cspell/rpc/-/rpc-9.7.0.tgz", - "integrity": "sha512-VnZ4ABgQeoS4RwofcePkDP7L6tf3Kh5D7LQKoyRM4R6XtfSsYefym6XKaRl3saGtthH5YyjgNJ0Tgdjen4wAAw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cspell/rpc/-/rpc-10.0.0.tgz", + "integrity": "sha512-QrpOZMwz2pAjvl6Hky2PauYoMpLCASn3osjn7uKUbgFV70sahyj6tmx4rRgRX7vHu2WQLZev+YsuO4EujiBDOg==", "dev": true, "license": "MIT", "engines": { - "node": ">=20.18" + "node": ">=22.18.0" } }, "node_modules/@cspell/strong-weak-map": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-9.7.0.tgz", - "integrity": "sha512-5xbvDASjklrmy88O6gmGXgYhpByCXqOj5wIgyvwZe2l83T1bE+iOfGI4pGzZJ/mN+qTn1DNKq8BPBPtDgb7Q2Q==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-10.0.0.tgz", + "integrity": "sha512-JRsato0s2IjYdsng+AGL6oAqgZVQgih5aWKdmxs21H6EdhMaoFDmRE5kXm/RT5a6OMdtnzQM9DqeToqBChWIOQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/@cspell/url": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@cspell/url/-/url-9.7.0.tgz", - "integrity": "sha512-ZaaBr0pTvNxmyUbIn+nVPXPr383VqJzfUDMWicgTjJIeo2+T2hOq2kNpgpvTIrWtZrsZnSP8oXms1+sKTjcvkw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@cspell/url/-/url-10.0.0.tgz", + "integrity": "sha512-q+0pHQ8DbqjemyaOn/mTtBRbCuKDqhnsVbZ6J9zkTsxPgMpccjy0s5oLXwomfrrxMRBH+UcbERwtUmE+SbnoIQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/@csstools/color-helpers": { @@ -921,9 +931,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", - "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.0.tgz", + "integrity": "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==", "dev": true, "funding": [ { @@ -945,9 +955,9 @@ } }, "node_modules/@csstools/css-color-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz", - "integrity": "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.0.tgz", + "integrity": "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==", "dev": true, "funding": [ { @@ -962,7 +972,7 @@ "license": "MIT", "dependencies": { "@csstools/color-helpers": "^6.0.2", - "@csstools/css-calc": "^3.1.1" + "@csstools/css-calc": "^3.2.0" }, "engines": { "node": ">=20.19.0" @@ -996,9 +1006,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.2.tgz", - "integrity": "sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.3.tgz", + "integrity": "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==", "dev": true, "funding": [ { @@ -1153,14 +1163,14 @@ } }, "node_modules/@emnapi/core": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", - "integrity": "sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.0", + "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, @@ -1173,9 +1183,9 @@ "optional": true }, "node_modules/@emnapi/runtime": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", - "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "dev": true, "license": "MIT", "optional": true, @@ -1192,9 +1202,9 @@ "optional": true }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", - "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, "license": "MIT", "optional": true, @@ -1211,32 +1221,22 @@ "optional": true }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.84.0.tgz", - "integrity": "sha512-0xew1CxOam0gV5OMjh2KjFQZsKL2bByX1+q4j3E73MpYIdyUxcZb/xQct9ccUb+ve5KGUYbCUxyPnYB7RbuP+w==", + "version": "0.86.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.86.0.tgz", + "integrity": "sha512-ukZmRQ81WiTpDWO6D/cTBM7XbrNtutHKvAVnZN/8pldAwLoJArGOvkNyxPTBGsPjsoaQBJxlH+tE2TNA/92Qgw==", "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.8", - "@typescript-eslint/types": "^8.54.0", - "comment-parser": "1.4.5", + "@typescript-eslint/types": "^8.58.0", + "comment-parser": "1.4.6", "esquery": "^1.7.0", - "jsdoc-type-pratt-parser": "~7.1.1" + "jsdoc-type-pratt-parser": "~7.2.0" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@es-joy/jsdoccomment/node_modules/comment-parser": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.5.tgz", - "integrity": "sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12.0.0" - } - }, "node_modules/@es-joy/resolve.exports": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@es-joy/resolve.exports/-/resolve.exports-1.2.0.tgz", @@ -1287,12 +1287,12 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.23.3", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.3.tgz", - "integrity": "sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==", + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^3.0.3", + "@eslint/object-schema": "^3.0.5", "debug": "^4.3.1", "minimatch": "^10.2.4" }, @@ -1301,21 +1301,21 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.3.tgz", - "integrity": "sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.1" + "@eslint/core": "^1.2.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.1.tgz", - "integrity": "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -1346,21 +1346,21 @@ } }, "node_modules/@eslint/object-schema": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.3.tgz", - "integrity": "sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", "license": "Apache-2.0", "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.6.1.tgz", - "integrity": "sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.1", + "@eslint/core": "^1.2.1", "levn": "^0.4.1" }, "engines": { @@ -1413,27 +1413,40 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1461,27 +1474,27 @@ } }, "node_modules/@inquirer/ansi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", - "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.5.tgz", + "integrity": "sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" } }, "node_modules/@inquirer/confirm": { - "version": "5.1.21", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", - "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.0.12.tgz", + "integrity": "sha512-h9FgGun3QwVYNj5TWIZZ+slii73bMoBFjPfVIGtnFuL4t8gBiNDV9PcSfIzkuxvgquJKt9nr1QzszpBzTbH8Og==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.3.2", - "@inquirer/type": "^3.0.10" + "@inquirer/core": "^11.1.9", + "@inquirer/type": "^4.0.5" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1493,23 +1506,22 @@ } }, "node_modules/@inquirer/core": { - "version": "10.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", - "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "version": "11.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.1.9.tgz", + "integrity": "sha512-BDE4fG22uYh1bGSifcj7JSx119TVYNViMhMu85usp4Fswrzh6M0DV3yld64jA98uOAa2GSQ4Bg4bZRm2d2cwSg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.2", - "@inquirer/figures": "^1.0.15", - "@inquirer/type": "^3.0.10", + "@inquirer/ansi": "^2.0.5", + "@inquirer/figures": "^2.0.5", + "@inquirer/type": "^4.0.5", "cli-width": "^4.1.0", - "mute-stream": "^2.0.0", - "signal-exit": "^4.1.0", - "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.3" + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1520,103 +1532,24 @@ } } }, - "node_modules/@inquirer/core/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@inquirer/core/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@inquirer/core/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@inquirer/figures": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", - "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.5.tgz", + "integrity": "sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" } }, "node_modules/@inquirer/type": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", - "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.5.tgz", + "integrity": "sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=18" + "node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0" }, "peerDependencies": { "@types/node": ">=18" @@ -1675,9 +1608,9 @@ "license": "MIT" }, "node_modules/@mswjs/interceptors": { - "version": "0.41.3", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.3.tgz", - "integrity": "sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA==", + "version": "0.41.6", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.6.tgz", + "integrity": "sha512-qmDvJIjcNsZ6tXWy2G9yuCgMPTTn35GMA3dPpSLm7QJVpbQzYdw0ALy1bKoivXnEM3U93/OrK+/M719b+fg84Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1692,6 +1625,13 @@ "node": ">=18" } }, + "node_modules/@mswjs/interceptors/node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -1744,9 +1684,9 @@ } }, "node_modules/@open-draft/deferred-promise": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", - "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-3.0.0.tgz", + "integrity": "sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==", "dev": true, "license": "MIT" }, @@ -1769,9 +1709,9 @@ "license": "MIT" }, "node_modules/@oxc-project/types": { - "version": "0.122.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", - "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "version": "0.127.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz", + "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==", "dev": true, "license": "MIT", "funding": { @@ -2044,9 +1984,9 @@ "license": "MIT" }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==", "cpu": [ "arm64" ], @@ -2061,9 +2001,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==", "cpu": [ "arm64" ], @@ -2078,9 +2018,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==", "cpu": [ "x64" ], @@ -2095,9 +2035,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==", "cpu": [ "x64" ], @@ -2112,9 +2052,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", - "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz", + "integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==", "cpu": [ "arm" ], @@ -2129,9 +2069,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==", "cpu": [ "arm64" ], @@ -2149,9 +2089,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz", + "integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==", "cpu": [ "arm64" ], @@ -2169,9 +2109,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==", "cpu": [ "ppc64" ], @@ -2189,9 +2129,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==", "cpu": [ "s390x" ], @@ -2209,9 +2149,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==", "cpu": [ "x64" ], @@ -2229,9 +2169,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz", + "integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==", "cpu": [ "x64" ], @@ -2249,9 +2189,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==", "cpu": [ "arm64" ], @@ -2266,9 +2206,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", - "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz", + "integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==", "cpu": [ "wasm32" ], @@ -2276,16 +2216,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" }, "engines": { - "node": ">=14.0.0" + "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", - "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "dev": true, "license": "MIT", "optional": true, @@ -2302,9 +2244,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==", "cpu": [ "arm64" ], @@ -2319,9 +2261,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==", "cpu": [ "x64" ], @@ -2336,9 +2278,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", - "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz", + "integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==", "dev": true, "license": "MIT" }, @@ -2566,9 +2508,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.12.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", - "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -2584,6 +2526,16 @@ "@types/node": "*" } }, + "node_modules/@types/set-cookie-parser": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz", + "integrity": "sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/statuses": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz", @@ -2618,14 +2570,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", - "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.1.tgz", + "integrity": "sha512-+MuHQlHiEr00Of/IQbE/MmEoi44znZHbR/Pz7Opq4HryUOlRi+/44dro9Ycy8Fyo+/024IWtw8m4JUMCGTYxDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.58.0", - "@typescript-eslint/types": "^8.58.0", + "@typescript-eslint/tsconfig-utils": "^8.59.1", + "@typescript-eslint/types": "^8.59.1", "debug": "^4.4.3" }, "engines": { @@ -2640,14 +2592,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", - "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.1.tgz", + "integrity": "sha512-LwuHQI4pDOYVKvmH2dkaJo6YZCSgouVgnS/z7yBPKBMvgtBvyLqiLy9Z6b7+m/TRcX1NFYUqZetI5Y+aT4GEfg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0" + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2658,9 +2610,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", - "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.1.tgz", + "integrity": "sha512-/0nEyPbX7gRsk0Uwfe4ALwwgxuA66d/l2mhRDNlAvaj4U3juhUtJNq0DsY8M2AYwwb9rEq2hrC3IcIcEt++iJA==", "dev": true, "license": "MIT", "engines": { @@ -2675,9 +2627,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", - "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.1.tgz", + "integrity": "sha512-ZDCjgccSdYPw5Bxh+my4Z0lJU96ZDN7jbBzvmEn0FZx3RtU1C7VWl6NbDx94bwY3V5YsgwRzJPOgeY2Q/nLG8A==", "dev": true, "license": "MIT", "engines": { @@ -2689,16 +2641,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", - "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.1.tgz", + "integrity": "sha512-OUd+vJS05sSkOip+BkZ/2NS8RMxrAAJemsC6vU3kmfLyeaJT0TftHkV9mcx2107MmsBVXXexhVu4F0TZXyMl4g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.58.0", - "@typescript-eslint/tsconfig-utils": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/visitor-keys": "8.58.0", + "@typescript-eslint/project-service": "8.59.1", + "@typescript-eslint/tsconfig-utils": "8.59.1", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/visitor-keys": "8.59.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -2717,16 +2669,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", - "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.1.tgz", + "integrity": "sha512-3pIeoXhCeYH9FSCBI8P3iNwJlGuzPlYKkTlen2O9T1DSeeg8UG8jstq6BLk+Mda0qup7mgk4z4XL4OzRaxZ8LA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.58.0", - "@typescript-eslint/types": "8.58.0", - "@typescript-eslint/typescript-estree": "8.58.0" + "@typescript-eslint/scope-manager": "8.59.1", + "@typescript-eslint/types": "8.59.1", + "@typescript-eslint/typescript-estree": "8.59.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2741,13 +2693,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", - "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "version": "8.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.1.tgz", + "integrity": "sha512-LdDNl6C5iJExcM0Yh0PwAIBb9PrSiCsWamF/JyEZawm3kFDnRoaq3LGE4bpyRao/fWeGKKyw7icx0YxrLFC5Cg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/types": "8.59.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -3065,14 +3017,14 @@ ] }, "node_modules/@vitest/coverage-v8": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.2.tgz", - "integrity": "sha512-sPK//PHO+kAkScb8XITeB1bf7fsk85Km7+rt4eeuRR3VS1/crD47cmV5wicisJmjNdfeokTZwjMk4Mj2d58Mgg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.5.tgz", + "integrity": "sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.2", + "@vitest/utils": "4.1.5", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", @@ -3086,8 +3038,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.1.2", - "vitest": "4.1.2" + "@vitest/browser": "4.1.5", + "vitest": "4.1.5" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -3096,9 +3048,9 @@ } }, "node_modules/@vitest/eslint-plugin": { - "version": "1.6.14", - "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.6.14.tgz", - "integrity": "sha512-PXZ5ysw4eHU9h8nDtBvVcGC7Z2C/T9CFdheqSw1NNXFYqViojub0V9bgdYI67iBTOcra2mwD0EYldlY9bGPf2Q==", + "version": "1.6.16", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.6.16.tgz", + "integrity": "sha512-2pBN1F1JXq6zTSaYC58CMJa7pGxXIRsLfOioeZM4cPE3pRdSh1ySTSoHPQlOTEF5WgoVzWZQxhGQ3ygT78hOVg==", "dev": true, "license": "MIT", "dependencies": { @@ -3127,16 +3079,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz", - "integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", + "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.2", - "@vitest/utils": "4.1.2", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -3145,13 +3097,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz", - "integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz", + "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.2", + "@vitest/spy": "4.1.5", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -3172,9 +3124,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.2.tgz", - "integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", + "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", "dev": true, "license": "MIT", "dependencies": { @@ -3185,13 +3137,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.2.tgz", - "integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz", + "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.2", + "@vitest/utils": "4.1.5", "pathe": "^2.0.3" }, "funding": { @@ -3199,14 +3151,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.2.tgz", - "integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz", + "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.2", - "@vitest/utils": "4.1.2", + "@vitest/pretty-format": "4.1.5", + "@vitest/utils": "4.1.5", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -3215,9 +3167,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.2.tgz", - "integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", + "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", "dev": true, "license": "MIT", "funding": { @@ -3225,13 +3177,13 @@ } }, "node_modules/@vitest/ui": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.2.tgz", - "integrity": "sha512-/irhyeAcKS2u6Zokagf9tqZJ0t8S6kMZq4ZG9BHZv7I+fkRrYfQX4w7geYeC2r6obThz39PDxvXQzZX+qXqGeg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.5.tgz", + "integrity": "sha512-3Z9HNFiV0IF1fk0JPiK+7kE1GcaIPefQQIBYur6PM5yFIq6agys3uqP/0t966e1wXfmjbRCHDe7qW236Xjwnag==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.2", + "@vitest/utils": "4.1.5", "fflate": "^0.8.2", "flatted": "^3.4.2", "pathe": "^2.0.3", @@ -3243,17 +3195,17 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "4.1.2" + "vitest": "4.1.5" } }, "node_modules/@vitest/utils": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.2.tgz", - "integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", + "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.2", + "@vitest/pretty-format": "4.1.5", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -3311,9 +3263,9 @@ } }, "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -3549,9 +3501,9 @@ } }, "node_modules/basic-ftp": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", - "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", + "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -3890,23 +3842,6 @@ "node": ">=0.3.6" } }, - "node_modules/clear-module": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/clear-module/-/clear-module-4.1.2.tgz", - "integrity": "sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^2.0.0", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -4166,9 +4101,9 @@ } }, "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", "engines": { "node": ">=18" @@ -4266,6 +4201,33 @@ "node": ">=6" } }, + "node_modules/cosmiconfig/node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cosmiconfig/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/croner": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/croner/-/croner-10.0.1.tgz", @@ -4300,31 +4262,31 @@ } }, "node_modules/cspell": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/cspell/-/cspell-9.7.0.tgz", - "integrity": "sha512-ftxOnkd+scAI7RZ1/ksgBZRr0ouC7QRKtPQhD/PbLTKwAM62sSvRhE1bFsuW3VKBn/GilWzTjkJ40WmnDqH5iQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-10.0.0.tgz", + "integrity": "sha512-R25gsSR1SLlcGyw48fwJwp0PjXrVdl7RDO/Dm5+s4DvC1uQSlyiUxsr/8ZtbyC/MPeUJFQN9B4luqLlSm0WelQ==", "dev": true, "license": "MIT", "dependencies": { - "@cspell/cspell-json-reporter": "9.7.0", - "@cspell/cspell-performance-monitor": "9.7.0", - "@cspell/cspell-pipe": "9.7.0", - "@cspell/cspell-types": "9.7.0", - "@cspell/cspell-worker": "9.7.0", - "@cspell/dynamic-import": "9.7.0", - "@cspell/url": "9.7.0", + "@cspell/cspell-json-reporter": "10.0.0", + "@cspell/cspell-performance-monitor": "10.0.0", + "@cspell/cspell-pipe": "10.0.0", + "@cspell/cspell-types": "10.0.0", + "@cspell/cspell-worker": "10.0.0", + "@cspell/dynamic-import": "10.0.0", + "@cspell/url": "10.0.0", "ansi-regex": "^6.2.2", "chalk": "^5.6.2", "chalk-template": "^1.1.2", "commander": "^14.0.3", - "cspell-config-lib": "9.7.0", - "cspell-dictionary": "9.7.0", - "cspell-gitignore": "9.7.0", - "cspell-glob": "9.7.0", - "cspell-io": "9.7.0", - "cspell-lib": "9.7.0", + "cspell-config-lib": "10.0.0", + "cspell-dictionary": "10.0.0", + "cspell-gitignore": "10.0.0", + "cspell-glob": "10.0.0", + "cspell-io": "10.0.0", + "cspell-lib": "10.0.0", "fast-json-stable-stringify": "^2.1.0", - "flatted": "^3.3.3", + "flatted": "^3.4.2", "semver": "^7.7.4", "tinyglobby": "^0.2.15" }, @@ -4333,155 +4295,154 @@ "cspell-esm": "bin.mjs" }, "engines": { - "node": ">=20.18" + "node": ">=22.18.0" }, "funding": { "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" } }, "node_modules/cspell-config-lib": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-9.7.0.tgz", - "integrity": "sha512-pguh8A3+bSJ1OOrKCiQan8bvaaY125de76OEFz7q1Pq309lIcDrkoL/W4aYbso/NjrXaIw6OjkgPMGRBI/IgGg==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-10.0.0.tgz", + "integrity": "sha512-HWK7SRnJ3N/kOThw/uzmXmQYCzBxu58Jkq2hHyte1voDl118BeNFoaNRWMpYdHbBi3kCj8gaZu8wGtm+Zmdhxw==", "dev": true, "license": "MIT", "dependencies": { - "@cspell/cspell-types": "9.7.0", - "comment-json": "^4.5.1", - "smol-toml": "^1.6.0", - "yaml": "^2.8.2" + "@cspell/cspell-types": "10.0.0", + "comment-json": "^4.6.2", + "smol-toml": "^1.6.1", + "yaml": "^2.8.3" }, "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/cspell-dictionary": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-9.7.0.tgz", - "integrity": "sha512-k/Wz0so32+0QEqQe21V9m4BNXM5ZN6lz3Ix/jLCbMxFIPl6wT711ftjOWIEMFhvUOP0TWXsbzcuE9mKtS5mTig==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-10.0.0.tgz", + "integrity": "sha512-KubSoEAJO+77KPSSWjoLCz0+MIWVNq3joGTSyxucAZrBSJD64Y1O4BHHr1aj6XHIZwXhWWNScQlrQR3OcIulng==", "dev": true, "license": "MIT", "dependencies": { - "@cspell/cspell-performance-monitor": "9.7.0", - "@cspell/cspell-pipe": "9.7.0", - "@cspell/cspell-types": "9.7.0", - "cspell-trie-lib": "9.7.0", + "@cspell/cspell-performance-monitor": "10.0.0", + "@cspell/cspell-pipe": "10.0.0", + "@cspell/cspell-types": "10.0.0", + "cspell-trie-lib": "10.0.0", "fast-equals": "^6.0.0" }, "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/cspell-gitignore": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-9.7.0.tgz", - "integrity": "sha512-MtoYuH4ah4K6RrmaF834npMcRsTKw0658mC6yvmBacUQOmwB/olqyuxF3fxtbb55HDb7cXDQ35t1XuwwGEQeZw==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-10.0.0.tgz", + "integrity": "sha512-55XLH9Y52eR7QgyV28Uaw8V9cN1YZ3PQIyrN9YBR4ndQNBKJxO9+jX1nwSspwnccCZiE/N+GGxFzRBb75JDSCw==", "dev": true, "license": "MIT", "dependencies": { - "@cspell/url": "9.7.0", - "cspell-glob": "9.7.0", - "cspell-io": "9.7.0" + "@cspell/url": "10.0.0", + "cspell-glob": "10.0.0", + "cspell-io": "10.0.0" }, "bin": { "cspell-gitignore": "bin.mjs" }, "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/cspell-glob": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-9.7.0.tgz", - "integrity": "sha512-LUeAoEsoCJ+7E3TnUmWBscpVQOmdwBejMlFn0JkXy6LQzxrybxXBKf65RSdIv1o5QtrhQIMa358xXYQG0sv/tA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-10.0.0.tgz", + "integrity": "sha512-bXS35fMcA9X7GEkfnWBfoPd/vTnxxfXW+YHt6tWxu5fejfs00qUbjWp1oLC9FxRaXWxIkfsYp2mi1k1jYl4RVw==", "dev": true, "license": "MIT", "dependencies": { - "@cspell/url": "9.7.0", - "picomatch": "^4.0.3" + "@cspell/url": "10.0.0", + "picomatch": "^4.0.4" }, "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/cspell-grammar": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-9.7.0.tgz", - "integrity": "sha512-oEYME+7MJztfVY1C06aGcJgEYyqBS/v/ETkQGPzf/c6ObSAPRcUbVtsXZgnR72Gru9aBckc70xJcD6bELdoWCA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-10.0.0.tgz", + "integrity": "sha512-49udtYzkcCYEIDJbFOb4IwiAJebOYZnYvG6o6Ep19Tq0Xwjk7i4vxUprNiFNDCWFbcbJRPE9cpwQUVwp5WFGLw==", "dev": true, "license": "MIT", "dependencies": { - "@cspell/cspell-pipe": "9.7.0", - "@cspell/cspell-types": "9.7.0" + "@cspell/cspell-pipe": "10.0.0", + "@cspell/cspell-types": "10.0.0" }, "bin": { "cspell-grammar": "bin.mjs" }, "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/cspell-io": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-9.7.0.tgz", - "integrity": "sha512-V7x0JHAUCcJPRCH8c0MQkkaKmZD2yotxVyrNEx2SZTpvnKrYscLEnUUTWnGJIIf9znzISqw116PLnYu2c+zd6Q==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-10.0.0.tgz", + "integrity": "sha512-NQCAUhx9DwKApxPuFl7EK1K1XSaQEAPld45yjjwv93xF8rJkEGkgzOwjbqafwAD20eKYv1a7oj/9EC0S5jETSw==", "dev": true, "license": "MIT", "dependencies": { - "@cspell/cspell-service-bus": "9.7.0", - "@cspell/url": "9.7.0" + "@cspell/cspell-service-bus": "10.0.0", + "@cspell/url": "10.0.0" }, "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/cspell-lib": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-9.7.0.tgz", - "integrity": "sha512-aTx/aLRpnuY1RJnYAu+A8PXfm1oIUdvAQ4W9E66bTgp1LWI+2G2++UtaPxRIgI0olxE9vcXqUnKpjOpO+5W9bQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspell/cspell-bundled-dicts": "9.7.0", - "@cspell/cspell-performance-monitor": "9.7.0", - "@cspell/cspell-pipe": "9.7.0", - "@cspell/cspell-resolver": "9.7.0", - "@cspell/cspell-types": "9.7.0", - "@cspell/dynamic-import": "9.7.0", - "@cspell/filetypes": "9.7.0", - "@cspell/rpc": "9.7.0", - "@cspell/strong-weak-map": "9.7.0", - "@cspell/url": "9.7.0", - "clear-module": "^4.1.2", - "cspell-config-lib": "9.7.0", - "cspell-dictionary": "9.7.0", - "cspell-glob": "9.7.0", - "cspell-grammar": "9.7.0", - "cspell-io": "9.7.0", - "cspell-trie-lib": "9.7.0", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-10.0.0.tgz", + "integrity": "sha512-PowW6JEjuv/F2aFEirZvBxpzHdchOnpsUJbeIcFcai0++taLTbHQObROBEBf7e0S8DnHpVD5TZkqrTME5e44wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-bundled-dicts": "10.0.0", + "@cspell/cspell-performance-monitor": "10.0.0", + "@cspell/cspell-pipe": "10.0.0", + "@cspell/cspell-resolver": "10.0.0", + "@cspell/cspell-types": "10.0.0", + "@cspell/dynamic-import": "10.0.0", + "@cspell/filetypes": "10.0.0", + "@cspell/rpc": "10.0.0", + "@cspell/strong-weak-map": "10.0.0", + "@cspell/url": "10.0.0", + "cspell-config-lib": "10.0.0", + "cspell-dictionary": "10.0.0", + "cspell-glob": "10.0.0", + "cspell-grammar": "10.0.0", + "cspell-io": "10.0.0", + "cspell-trie-lib": "10.0.0", "env-paths": "^4.0.0", "gensequence": "^8.0.8", - "import-fresh": "^3.3.1", + "import-fresh": "^4.0.0", "resolve-from": "^5.0.0", "vscode-languageserver-textdocument": "^1.0.12", "vscode-uri": "^3.1.0", "xdg-basedir": "^5.1.0" }, "engines": { - "node": ">=20" + "node": ">=22.18.0" } }, "node_modules/cspell-trie-lib": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-9.7.0.tgz", - "integrity": "sha512-a2YqmcraL3g6I/4gY7SYWEZfP73oLluUtxO7wxompk/kOG2K1FUXyQfZXaaR7HxVv10axT1+NrjhOmXpfbI6LA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-10.0.0.tgz", + "integrity": "sha512-R8qrMx10E/bm3Lecslwxn9XYo5NzSRK1rtandEX5n9UmEYHoBXjZELkg5+TOnV8VgrVaJSK57XtcGrbKp/4kSg==", "dev": true, "license": "MIT", "engines": { - "node": ">=20" + "node": ">=22.18.0" }, "peerDependencies": { - "@cspell/cspell-types": "9.7.0" + "@cspell/cspell-types": "10.0.0" } }, "node_modules/css-functions-list": { @@ -4850,9 +4811,9 @@ "license": "MIT" }, "node_modules/electron": { - "version": "41.1.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-41.1.0.tgz", - "integrity": "sha512-0XRFyxRqetmqtkkBvV++wGbHYJ7bD++f6EgJW8y9kX4pPRagwlmKDtzqXZhKiu0DIQppm3sXxzHWK9GYP91OKQ==", + "version": "41.3.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-41.3.0.tgz", + "integrity": "sha512-2Q5aeocmFdeheZGDUTrAvSR3t+n0c3d104AJWWEnt7syJU0tE4VdibMYaPtQ47QuXSoUf0/xSsfUUvu/uSXIfg==", "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4895,9 +4856,9 @@ } }, "node_modules/engine.io": { - "version": "6.6.6", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.6.tgz", - "integrity": "sha512-U2SN0w3OpjFRVlrc17E6TMDmH58Xl9rai1MblNjAdwWp07Kk+llmzX0hjDpQdrDGzwmvOtgM5yI+meYX6iZ2xA==", + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.7.tgz", + "integrity": "sha512-DgOngfDKM2EviOH3Mr9m7ks1q8roetLy/IMmYthAYzbpInMbYc/GS+fWFA3rl1gvwKVsQrVV61fo5emD1y3OJQ==", "license": "MIT", "dependencies": { "@types/cors": "^2.8.12", @@ -5070,9 +5031,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", "dev": true, "license": "MIT" }, @@ -5145,17 +5106,17 @@ } }, "node_modules/eslint": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.1.0.tgz", - "integrity": "sha512-S9jlY/ELKEUwwQnqWDO+f+m6sercqOPSqXM5Go94l7DOmxHVDgmSFGWEzeE/gwgTAr0W103BWt0QLe/7mabIvA==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.1.tgz", + "integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.3", - "@eslint/config-helpers": "^0.5.3", - "@eslint/core": "^1.1.1", - "@eslint/plugin-kit": "^0.6.1", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.5.5", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -5282,19 +5243,19 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "62.8.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.8.1.tgz", - "integrity": "sha512-e9358PdHgvcMF98foNd3L7hVCw70Lt+YcSL7JzlJebB8eT5oRJtW6bHMQKoAwJtw6q0q0w/fRIr2kwnHdFDI6A==", + "version": "62.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.9.0.tgz", + "integrity": "sha512-PY7/X4jrVgoIDncUmITlUqK546Ltmx/Pd4Hdsu4CvSjryQZJI2mEV4vrdMufyTetMiZ5taNSqvK//BTgVUlNkA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@es-joy/jsdoccomment": "~0.84.0", + "@es-joy/jsdoccomment": "~0.86.0", "@es-joy/resolve.exports": "1.2.0", "are-docs-informative": "^0.0.2", - "comment-parser": "1.4.5", + "comment-parser": "1.4.6", "debug": "^4.4.3", "escape-string-regexp": "^4.0.0", - "espree": "^11.1.0", + "espree": "^11.2.0", "esquery": "^1.7.0", "html-entities": "^2.6.0", "object-deep-merge": "^2.0.0", @@ -5310,16 +5271,6 @@ "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" } }, - "node_modules/eslint-plugin-jsdoc/node_modules/comment-parser": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.5.tgz", - "integrity": "sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12.0.0" - } - }, "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", @@ -5352,9 +5303,9 @@ } }, "node_modules/eslint-plugin-package-json": { - "version": "0.91.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-package-json/-/eslint-plugin-package-json-0.91.1.tgz", - "integrity": "sha512-rxmCAcuTvDqrtywsmVHcFxEZdJUTByetAelAZiAcjMqo0BS1090KbjTeyEOiEdpEqxwa83c+xHCjFoZX9OKEcA==", + "version": "0.91.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-package-json/-/eslint-plugin-package-json-0.91.2.tgz", + "integrity": "sha512-tuPjHVYOjqEJtErmzuYiQY6o877l9Kb7+lfLhR/mAzHuy9CBqhRreIGPsQmVM/dMJ8yQVg92Bz1vBAgugELIXw==", "dev": true, "license": "MIT", "dependencies": { @@ -5363,7 +5314,7 @@ "detect-indent": "^7.0.2", "detect-newline": "^4.0.1", "eslint-fix-utils": "~0.4.1", - "package-json-validator": "^1.3.1", + "package-json-validator": "^1.4.1", "semver": "^7.7.3", "sort-object-keys": "^2.0.0", "sort-package-json": "^3.4.0", @@ -5378,9 +5329,9 @@ } }, "node_modules/eslint-plugin-playwright": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.10.1.tgz", - "integrity": "sha512-qea3UxBOb8fTwJ77FMApZKvRye5DOluDHcev0LDJwID3RELeun0JlqzrNIXAB/SXCyB/AesCW/6sZfcT9q3Edg==", + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.10.2.tgz", + "integrity": "sha512-0N+2OWc3NZbOZ0gK8mp2TK6Qu3UWcJTQ9rqU0UM2yRJXgT758pvpY0lsOLIySfbyFrLqn3TcXjixbmcK90VnuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5425,9 +5376,9 @@ } }, "node_modules/eslint/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -5744,6 +5695,23 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "license": "MIT" }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", @@ -5760,6 +5728,16 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.0.tgz", + "integrity": "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -5915,9 +5893,9 @@ "license": "ISC" }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -6077,9 +6055,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.13.7", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", - "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", "dev": true, "license": "MIT", "dependencies": { @@ -6220,9 +6198,9 @@ } }, "node_modules/globals": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", - "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", + "version": "17.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.5.0.tgz", + "integrity": "sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==", "license": "MIT", "engines": { "node": ">=18" @@ -6249,9 +6227,9 @@ } }, "node_modules/globby": { - "version": "16.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-16.1.1.tgz", - "integrity": "sha512-dW7vl+yiAJSp6aCekaVnVJxurRv7DCOLyXqEG3RYMYUg7AuJ2jCqPkZTA8ooqC2vtnkaMcV5WfFBMuEnTu1OQg==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-16.2.0.tgz", + "integrity": "sha512-QrJia2qDf5BB/V6HYlDTs0I0lBahyjLzpGQg3KT7FnCdTonAyPy2RtY802m2k4ALx6Dp752f82WsOczEVr3l6Q==", "dev": true, "license": "MIT", "dependencies": { @@ -6389,9 +6367,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -6401,11 +6379,15 @@ } }, "node_modules/headers-polyfill": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", - "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-5.0.1.tgz", + "integrity": "sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@types/set-cookie-parser": "^2.4.10", + "set-cookie-parser": "^3.0.1" + } }, "node_modules/helmet": { "version": "8.1.0", @@ -6423,6 +6405,19 @@ "dev": true, "license": "MIT" }, + "node_modules/hosted-git-info": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/html-encoding-sniffer": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", @@ -6617,45 +6612,18 @@ } }, "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-4.0.0.tgz", + "integrity": "sha512-Fpi660c7VPDM3fPKYovStd9IP1CPOikf6v/dGxJJMmHPcwYQIMJ4W7kO1avBYEpMqkCh+Dx3Ln6H7VYqgztLjw==", "dev": true, "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, "engines": { - "node": ">=6" + "node": ">=22.15" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-fresh/node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/import-meta-resolve": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", @@ -6693,9 +6661,9 @@ } }, "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.1.tgz", + "integrity": "sha512-1FMu8/N15Ck1BL551Jf42NYIoin2unWjLQ2Fze/DXryJRl5twqtwNHlO39qERGbIOcKYWHdgRryhOC+NG4eaLw==", "license": "MIT", "engines": { "node": ">= 12" @@ -6990,9 +6958,9 @@ "license": "Apache-2.0" }, "node_modules/jsdoc-type-pratt-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.1.1.tgz", - "integrity": "sha512-/2uqY7x6bsrpi3i9LVU6J89352C0rpMk0as8trXxCtvd4kPk1ke/Eyif6wqfSLvoNJqcDG9Vk4UsXgygzCt2xA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.2.0.tgz", + "integrity": "sha512-dh140MMgjyg3JhJZY/+iEzW+NO5xR2gpbDFKHqotCmexElVntw7GjWjt511+C/Ef02RU5TKYrJo/Xlzk+OLaTw==", "dev": true, "license": "MIT", "engines": { @@ -7000,28 +6968,28 @@ } }, "node_modules/jsdom": { - "version": "29.0.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.1.tgz", - "integrity": "sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==", + "version": "29.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.0.tgz", + "integrity": "sha512-YNUc7fB9QuvSSQWfrH0xF+TyABkxUwx8sswgIDaCrw4Hol8BghdZDkITtZheRJeMtzWlnTfsM3bBBusRvpO1wg==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^5.0.1", - "@asamuzakjp/dom-selector": "^7.0.3", + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", "@bramus/specificity": "^2.4.2", - "@csstools/css-syntax-patches-for-csstree": "^1.1.1", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", "@exodus/bytes": "^1.15.0", "css-tree": "^3.2.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.7", - "parse5": "^8.0.0", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.1", - "undici": "^7.24.5", + "undici": "^7.25.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", @@ -7040,6 +7008,16 @@ } } }, + "node_modules/jsdom/node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -7133,9 +7111,9 @@ } }, "node_modules/katex": { - "version": "0.16.44", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.44.tgz", - "integrity": "sha512-EkxoDTk8ufHqHlf9QxGwcxeLkWRR3iOuYfRpfORgYfqc8s13bgb+YtRY59NK5ZpRaCwq1kqA6a5lpX8C/eLphQ==", + "version": "0.16.45", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.45.tgz", + "integrity": "sha512-pQpZbdBu7wCTmQUh7ufPmLr0pFoObnGUoL/yhtwJDgmmQpbkg/0HSVti25Fu4rmd1oCR6NGWe9vqTWuWv3GcNA==", "dev": true, "funding": [ "https://opencollective.com/katex", @@ -7548,9 +7526,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash.truncate": { @@ -7608,9 +7586,9 @@ } }, "node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -7698,13 +7676,13 @@ } }, "node_modules/markdownlint-cli2": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli2/-/markdownlint-cli2-0.22.0.tgz", - "integrity": "sha512-mOC9BY/XGtdX3M9n3AgERd79F0+S7w18yBBTNIQ453sI87etZfp1z4eajqSMV70CYjbxKe5ktKvT2HCpvcWx9w==", + "version": "0.22.1", + "resolved": "https://registry.npmjs.org/markdownlint-cli2/-/markdownlint-cli2-0.22.1.tgz", + "integrity": "sha512-X14ZbytybDCXAViDmtN4DKLt9ZTrRn+oOrxTYlg3a65jS6QcYYbAkGPh/En2L/GDNbFYJ6lKaQSUNrrbN1bPrw==", "dev": true, "license": "MIT", "dependencies": { - "globby": "16.1.1", + "globby": "16.2.0", "js-yaml": "4.1.1", "jsonc-parser": "3.3.1", "jsonpointer": "5.0.1", @@ -7712,7 +7690,7 @@ "markdownlint": "0.40.0", "markdownlint-cli2-formatter-default": "0.0.6", "micromatch": "4.0.8", - "smol-toml": "1.6.0" + "smol-toml": "1.6.1" }, "bin": { "markdownlint-cli2": "markdownlint-cli2-bin.mjs" @@ -7737,19 +7715,6 @@ "markdownlint-cli2": ">=0.0.4" } }, - "node_modules/markdownlint-cli2/node_modules/smol-toml": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz", - "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 18" - }, - "funding": { - "url": "https://github.com/sponsors/cyyynthia" - } - }, "node_modules/markdownlint/node_modules/string-width": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", @@ -8512,9 +8477,9 @@ } }, "node_modules/moment-timezone": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.6.1.tgz", - "integrity": "sha512-1B9lmAhB9D9/sHaPC1N7wLFEVUoFldxOpOO96lOD1PvJ43vCd0ozDPbu0FEL3++VvawOlDkq8YD373tJmP5JHw==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.6.2.tgz", + "integrity": "sha512-lDsQv8FoGdBUdf0+TjGsq2orxKuXdwFlQ6Zw6TX3xIcTwTfEpCLyKqvEauvCHJ8iu3KBV8+uPhlv70YsNGdUBQ==", "license": "MIT", "dependencies": { "moment": "^2.29.4" @@ -8540,29 +8505,29 @@ "license": "MIT" }, "node_modules/msw": { - "version": "2.12.14", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.12.14.tgz", - "integrity": "sha512-4KXa4nVBIBjbDbd7vfQNuQ25eFxug0aropCQFoI0JdOBuJWamkT1yLVIWReFI8SiTRc+H1hKzaNk+cLk2N9rtQ==", + "version": "2.13.6", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.13.6.tgz", + "integrity": "sha512-GAJbQy8Ra/Ydjt0Hb2MGT2qhzd83J3+QZMHdH85uW7r/XkKc846+Ma2PLif5hGvTm5Yqa+wkcstpim0WeLZU9g==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "@inquirer/confirm": "^5.0.0", - "@mswjs/interceptors": "^0.41.2", - "@open-draft/deferred-promise": "^2.2.0", + "@inquirer/confirm": "^6.0.11", + "@mswjs/interceptors": "^0.41.3", + "@open-draft/deferred-promise": "^3.0.0", "@types/statuses": "^2.0.6", - "cookie": "^1.0.2", - "graphql": "^16.12.0", - "headers-polyfill": "^4.0.2", + "cookie": "^1.1.1", + "graphql": "^16.13.2", + "headers-polyfill": "^5.0.1", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", - "rettime": "^0.10.1", + "rettime": "^0.11.7", "statuses": "^2.0.2", "strict-event-emitter": "^0.5.1", - "tough-cookie": "^6.0.0", - "type-fest": "^5.2.0", + "tough-cookie": "^6.0.1", + "type-fest": "^5.5.0", "until-async": "^3.0.2", "yargs": "^17.7.2" }, @@ -8599,13 +8564,13 @@ } }, "node_modules/mute-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", - "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", + "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/nanoid": { @@ -8697,25 +8662,25 @@ } }, "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz", + "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==", "license": "MIT", "engines": { "node": ">= 0.4.0" } }, "node_modules/node-ical": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.25.6.tgz", - "integrity": "sha512-Wo3GSBGYUNkcxOjHhevKTGa/+eB7ZwNLCq50h/eTX/YHSDZz1nT4KzXArO455CGHLD68gc5vw3nevqWQpOjjLQ==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/node-ical/-/node-ical-0.26.0.tgz", + "integrity": "sha512-tJZY2fMb38Gbj0P05zHMWBr90MslhGZ1qEbOWYnokBYPPX/lYskL/0NnWoeiXTBNod+kRRcTOjxAeB20kfvKyw==", "license": "Apache-2.0", "dependencies": { - "rrule-temporal": "^1.4.7", - "temporal-polyfill": "^0.3.0" + "rrule-temporal": "^1.5.1", + "temporal-polyfill": "^0.3.2" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/normalize-path": { @@ -8740,6 +8705,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/npm-package-arg": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.2.tgz", + "integrity": "sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^9.0.0", + "proc-log": "^6.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/nunjucks": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", @@ -8957,12 +8938,13 @@ } }, "node_modules/package-json-validator": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/package-json-validator/-/package-json-validator-1.3.1.tgz", - "integrity": "sha512-RfUMqyBoLa1qcPsKNAksnVDRuzDvLi//RqfMbf52RNMKsm+cWR/3Cfe6hvrTS/ATtEwvtm/57dPggXsau6++uA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/package-json-validator/-/package-json-validator-1.4.1.tgz", + "integrity": "sha512-kH0EKKflJOfYQeiANow4FkVn2yXVUdxPxtcSiAWSBOVXNopDBdPUShTKGVGy8Dln3n0dZZUETe0p1wGyUQGwBw==", "dev": true, "license": "MIT", "dependencies": { + "npm-package-arg": "^13.0.2", "semver": "^7.7.2", "validate-npm-package-license": "^3.0.4", "validate-npm-package-name": "^7.0.0" @@ -8978,16 +8960,16 @@ "license": "MIT" }, "node_modules/parent-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", - "integrity": "sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { - "callsites": "^3.1.0" + "callsites": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" } }, "node_modules/parse-entities": { @@ -9047,26 +9029,26 @@ "license": "MIT" }, "node_modules/parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", - "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", "dev": true, "license": "MIT", "dependencies": { - "entities": "^6.0.0" + "entities": "^8.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" } }, "node_modules/parse5/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", "dev": true, "license": "BSD-2-Clause", "engines": { - "node": ">=0.12" + "node": ">=20.19.0" }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" @@ -9201,13 +9183,13 @@ "license": "MIT" }, "node_modules/playwright": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", - "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.58.2" + "playwright-core": "1.59.1" }, "bin": { "playwright": "cli.js" @@ -9220,9 +9202,9 @@ } }, "node_modules/playwright-core": { - "version": "1.58.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", - "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -9403,9 +9385,9 @@ } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", + "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", "dev": true, "funding": [ { @@ -9489,9 +9471,9 @@ } }, "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "dev": true, "license": "MIT", "bin": { @@ -9527,6 +9509,16 @@ "prettier": "^3.0.0" } }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -9646,16 +9638,16 @@ } }, "node_modules/qified/node_modules/hookified": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.1.1.tgz", - "integrity": "sha512-AHb76R16GB5EsPBE2J7Ko5kiEyXwviB9P5SMrAKcuAu4vJPZttViAbj9+tZeaQE5zjDme+1vcHP78Yj/WoAveA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz", + "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==", "dev": true, "license": "MIT" }, "node_modules/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -9814,11 +9806,12 @@ } }, "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "license": "MIT", "dependencies": { + "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" @@ -9891,9 +9884,9 @@ } }, "node_modules/rettime": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.10.1.tgz", - "integrity": "sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.11.8.tgz", + "integrity": "sha512-0fERGXktJTyJ+h8fBEiPxHPEFOu0h15JY7JtwrOVqR5K+vb99ho6IyOo7ekLS3h4sJCzIDy4VWKIbZUfe9njmg==", "dev": true, "license": "MIT" }, @@ -9934,14 +9927,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", - "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz", + "integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.122.0", - "@rolldown/pluginutils": "1.0.0-rc.12" + "@oxc-project/types": "=0.127.0", + "@rolldown/pluginutils": "1.0.0-rc.17" }, "bin": { "rolldown": "bin/cli.mjs" @@ -9950,21 +9943,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-x64": "1.0.0-rc.12", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + "@rolldown/binding-android-arm64": "1.0.0-rc.17", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", + "@rolldown/binding-darwin-x64": "1.0.0-rc.17", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" } }, "node_modules/router": { @@ -9984,9 +9977,9 @@ } }, "node_modules/router/node_modules/path-to-regexp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.1.tgz", - "integrity": "sha512-fvU78fIjZ+SBM9YwCknCvKOUKkLVqtWDVctl0s7xIqfmfb38t2TT4ZU2gHm+Z8xGwgW+QWEU3oQSAzIbo89Ggw==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", "funding": { "type": "opencollective", @@ -9994,9 +9987,9 @@ } }, "node_modules/rrule-temporal": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/rrule-temporal/-/rrule-temporal-1.5.1.tgz", - "integrity": "sha512-mV82lZ7OzMOXX5g+SCR8FWRMSvhTnPSznjQ2Vguezjz1nEPEv/Ap/Z/FsIZ/AxA15M9X99m05911/MCXFY+FsA==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/rrule-temporal/-/rrule-temporal-1.5.3.tgz", + "integrity": "sha512-qALnXyu4MKNUeykkkO0r6Xxl5or3rM8Cf6ibKIe/29sgmq3tGm1oNq4G1Ddp8Ku3mnKmvC3+3yFAJ3OgOu6OJw==", "license": "MIT", "dependencies": { "@js-temporal/polyfill": "^0.5.1" @@ -10187,6 +10180,13 @@ "url": "https://opencollective.com/express" } }, + "node_modules/set-cookie-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", + "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", + "dev": true, + "license": "MIT" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -10240,13 +10240,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -10486,12 +10486,12 @@ } }, "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.8.tgz", + "integrity": "sha512-NlGELfPrgX2f1TAAcz0WawlLn+0r3FyhhCRpFFK2CemXenPYvzMWWZINv3eDNo9ucdwme7oCHRY0Jnbs4aIkog==", "license": "MIT", "dependencies": { - "ip-address": "^10.0.1", + "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -10651,9 +10651,9 @@ } }, "node_modules/std-env": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", - "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", "dev": true, "license": "MIT" }, @@ -10675,9 +10675,9 @@ } }, "node_modules/string-width": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", - "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", + "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", "dev": true, "license": "MIT", "dependencies": { @@ -10708,9 +10708,9 @@ } }, "node_modules/stylelint": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.6.0.tgz", - "integrity": "sha512-tokrsMIVAR9vAQ/q3UVEr7S0dGXCi7zkCezPRnS2kqPUulvUh5Vgfwngrk4EoAoW7wnrThqTdnTFN5Ra7CaxIg==", + "version": "17.9.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-17.9.1.tgz", + "integrity": "sha512-THTmnAPJTrg/JhkTWZlSyrO+HUYMx6ELthIHeMyD2WOKqXIJUFQv2Yxn91bvUrZdbBJaW2dUuQdPST2wcQ6C3g==", "dev": true, "funding": [ { @@ -10724,9 +10724,9 @@ ], "license": "MIT", "dependencies": { - "@csstools/css-calc": "^3.1.1", + "@csstools/css-calc": "^3.2.0", "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-syntax-patches-for-csstree": "^1.1.1", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", "@csstools/css-tokenizer": "^4.0.0", "@csstools/media-query-list-parser": "^5.0.0", "@csstools/selector-resolve-nested": "^4.0.0", @@ -10740,7 +10740,7 @@ "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^11.1.2", "global-modules": "^2.0.0", - "globby": "^16.1.1", + "globby": "^16.2.0", "globjoin": "^0.1.4", "html-tags": "^5.1.0", "ignore": "^7.0.5", @@ -10751,7 +10751,7 @@ "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "picocolors": "^1.1.1", - "postcss": "^8.5.8", + "postcss": "^8.5.9", "postcss-safe-parser": "^7.0.1", "postcss-selector-parser": "^7.1.1", "postcss-value-parser": "^4.2.0", @@ -11125,9 +11125,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", - "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", + "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", "dev": true, "license": "MIT", "engines": { @@ -11135,14 +11135,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -11162,22 +11162,22 @@ } }, "node_modules/tldts": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", - "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.29.tgz", + "integrity": "sha512-JIXCerhudr/N6OWLwLF1HVsTTUo7ry6qHa5eWZEkiMuxsIiAACL55tGLfqfHfoH7QaMQUW8fngD7u7TxWexYQg==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^7.0.27" + "tldts-core": "^7.0.29" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.27", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", - "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", + "version": "7.0.29", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.29.tgz", + "integrity": "sha512-W99NuU7b1DcG3uJ3v9k9VztCH3WialNbBkBft5wCs8V8mexu0XQqaZEYb9l9RNNzK8+3EJ9PKWB0/RUtTQ/o+Q==", "dev": true, "license": "MIT" }, @@ -11315,9 +11315,9 @@ } }, "node_modules/type-fest": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.5.0.tgz", - "integrity": "sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.6.0.tgz", + "integrity": "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==", "dev": true, "license": "(MIT OR CC0-1.0)", "dependencies": { @@ -11345,9 +11345,9 @@ } }, "node_modules/typescript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", - "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -11367,12 +11367,12 @@ "license": "MIT" }, "node_modules/undici": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.6.tgz", - "integrity": "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.1.0.tgz", + "integrity": "sha512-E9MkTS4xXLnRPYqxH2e6Hr2/49e7WFDKczKcCaFH4VaZs2iNvHMqeIkyUAD9vM8kujy9TjVrRlQ5KkdEJxB2pw==", "license": "MIT", "engines": { - "node": ">=20.18.1" + "node": ">=22.19.0" } }, "node_modules/undici-types": { @@ -11516,17 +11516,17 @@ } }, "node_modules/vite": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", - "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "version": "8.0.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz", + "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", - "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.12", - "tinyglobby": "^0.2.15" + "postcss": "^8.5.10", + "rolldown": "1.0.0-rc.17", + "tinyglobby": "^0.2.16" }, "bin": { "vite": "bin/vite.js" @@ -11543,7 +11543,7 @@ "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", - "esbuild": "^0.27.0", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", @@ -11609,19 +11609,19 @@ } }, "node_modules/vitest": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.2.tgz", - "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", + "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.2", - "@vitest/mocker": "4.1.2", - "@vitest/pretty-format": "4.1.2", - "@vitest/runner": "4.1.2", - "@vitest/snapshot": "4.1.2", - "@vitest/spy": "4.1.2", - "@vitest/utils": "4.1.2", + "@vitest/expect": "4.1.5", + "@vitest/mocker": "4.1.5", + "@vitest/pretty-format": "4.1.5", + "@vitest/runner": "4.1.5", + "@vitest/snapshot": "4.1.5", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -11649,10 +11649,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.2", - "@vitest/browser-preview": "4.1.2", - "@vitest/browser-webdriverio": "4.1.2", - "@vitest/ui": "4.1.2", + "@vitest/browser-playwright": "4.1.5", + "@vitest/browser-preview": "4.1.5", + "@vitest/browser-webdriverio": "4.1.5", + "@vitest/coverage-istanbul": "4.1.5", + "@vitest/coverage-v8": "4.1.5", + "@vitest/ui": "4.1.5", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -11676,6 +11678,12 @@ "@vitest/browser-webdriverio": { "optional": true }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, "@vitest/ui": { "optional": true }, @@ -12073,19 +12081,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/yoctocolors-cjs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", - "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } } } diff --git a/package.json b/package.json index 5cad85d4a0..b2c0eca59b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "magicmirror", - "version": "2.35.0", + "version": "2.36.0", "description": "The open source modular smart mirror platform.", "keywords": [ "magic mirror", @@ -90,55 +90,55 @@ "@fontsource/roboto": "^5.2.10", "@fontsource/roboto-condensed": "^5.2.8", "@fortawesome/fontawesome-free": "^7.2.0", - "ajv": "^8.18.0", + "ajv": "^8.20.0", "animate.css": "^4.1.1", "croner": "^10.0.1", - "eslint": "^10.1.0", + "eslint": "^10.2.1", "express": "^5.2.1", "feedme": "^2.0.2", - "globals": "^17.4.0", + "globals": "^17.5.0", "helmet": "^8.1.0", "html-to-text": "^9.0.5", "iconv-lite": "^0.7.2", "ipaddr.js": "^2.3.0", "moment": "^2.30.1", - "moment-timezone": "^0.6.1", - "node-ical": "^0.25.6", + "moment-timezone": "^0.6.2", + "node-ical": "^0.26.0", "nunjucks": "^3.2.4", "pm2": "^6.0.14", "socket.io": "^4.8.3", "suncalc": "^1.9.0", "systeminformation": "^5.31.5", - "undici": "^7.24.6", + "undici": "^8.1.0", "weathericons": "^2.1.0" }, "devDependencies": { "@eslint/js": "^10.0.1", "@stylistic/eslint-plugin": "^5.10.0", - "@vitest/coverage-v8": "^4.1.2", - "@vitest/eslint-plugin": "^1.6.14", - "@vitest/ui": "^4.1.2", - "cspell": "^9.7.0", + "@vitest/coverage-v8": "^4.1.5", + "@vitest/eslint-plugin": "^1.6.16", + "@vitest/ui": "^4.1.5", + "cspell": "^10.0.0", "eslint-plugin-import-x": "^4.16.2", - "eslint-plugin-jsdoc": "^62.8.1", - "eslint-plugin-package-json": "^0.91.1", - "eslint-plugin-playwright": "^2.10.1", + "eslint-plugin-jsdoc": "^62.9.0", + "eslint-plugin-package-json": "^0.91.2", + "eslint-plugin-playwright": "^2.10.2", "express-basic-auth": "^1.2.1", "husky": "^9.1.7", - "jsdom": "^29.0.1", + "jsdom": "^29.1.0", "lint-staged": "^16.4.0", - "markdownlint-cli2": "^0.22.0", - "msw": "^2.12.14", - "playwright": "^1.58.2", - "prettier": "^3.8.1", + "markdownlint-cli2": "^0.22.1", + "msw": "^2.13.6", + "playwright": "^1.59.1", + "prettier": "^3.8.3", "prettier-plugin-jinja-template": "^2.1.0", - "stylelint": "^17.6.0", + "stylelint": "^17.9.1", "stylelint-config-standard": "^40.0.0", "stylelint-prettier": "^5.0.3", - "vitest": "^4.1.2" + "vitest": "^4.1.5" }, "optionalDependencies": { - "electron": "^41.1.0" + "electron": "^41.3.0" }, "engines": { "node": ">=22.21.1 <23 || >=24" diff --git a/serveronly/watcher.js b/serveronly/watcher.js index c73be29415..ab8aa0a1e5 100644 --- a/serveronly/watcher.js +++ b/serveronly/watcher.js @@ -35,7 +35,7 @@ function getServerConfig () { port: global.mmPort || config.port || 8080, address: config.address || "localhost" }; - } catch (err) { + } catch { serverConfig = { port: 8080, address: "localhost" }; } @@ -248,7 +248,7 @@ try { Log.warn(`Watch target is not a file (directories not supported): ${targetPath}`); } } -} catch (err) { +} catch { // Config file might not exist or be invalid, use fallback targets Log.warn("Could not load watchTargets from config."); } diff --git a/tests/configs/config_functions.js b/tests/configs/config_functions.js new file mode 100644 index 0000000000..27e265f14d --- /dev/null +++ b/tests/configs/config_functions.js @@ -0,0 +1,35 @@ +/*eslint object-shorthand: ["error", "always", { "methodsIgnorePattern": "^roundToInt2$" }]*/ + +let config = require(`${process.cwd()}/tests/configs/default.js`).configFactory({ + modules: [ + { + module: "clock", + position: "middle_center", + config: { + moduleFunctions: { + roundToInt1: (value) => { + try { + return Math.round(parseFloat(value)); + } catch { + return value; + } + }, + roundToInt2: function (value) { + try { + return Math.round(parseFloat(value)); + } catch { + return value; + } + } + }, + stringWithArrow: "a => b is not a function", + stringWithFunction: "this function keyword is just text" + } + } + ] +}); + +/*************** DO NOT EDIT THE LINE BELOW ***************/ +if (typeof module !== "undefined") { + module.exports = config; +} diff --git a/tests/e2e/config_functions_spec.js b/tests/e2e/config_functions_spec.js new file mode 100644 index 0000000000..b40361cdcb --- /dev/null +++ b/tests/e2e/config_functions_spec.js @@ -0,0 +1,21 @@ +const helpers = require("./helpers/global-setup"); + +describe("config with module function", () => { + beforeAll(async () => { + await helpers.startApplication("tests/configs/config_functions.js"); + }); + + afterAll(async () => { + await helpers.stopApplication(); + }); + + it("config should resolve module functions", () => { + expect(config.modules[0].config.moduleFunctions.roundToInt1(13.3)).toBe(13); + expect(config.modules[0].config.moduleFunctions.roundToInt2(13.3)).toBe(13); + }); + + it("config should not revive plain strings containing arrow or function keywords", () => { + expect(config.modules[0].config.stringWithArrow).toBe("a => b is not a function"); + expect(config.modules[0].config.stringWithFunction).toBe("this function keyword is just text"); + }); +}); diff --git a/tests/e2e/translations_spec.js b/tests/e2e/translations_spec.js index 12a364d089..42a0739115 100644 --- a/tests/e2e/translations_spec.js +++ b/tests/e2e/translations_spec.js @@ -76,7 +76,7 @@ describe("translations", () => { it("should load translation file", async () => { const { Translator, Module, config } = dom.window; config.language = "en"; - Translator.load = vi.fn().mockImplementation((_m, _f, _fb) => null); + Translator.load = vi.fn().mockImplementation(() => null); Module.register("name", { getTranslations: () => translations }); const MMM = Module.create("name"); @@ -89,7 +89,7 @@ describe("translations", () => { it("should load translation + fallback file", async () => { const { Translator, Module } = dom.window; - Translator.load = vi.fn().mockImplementation((_m, _f, _fb) => null); + Translator.load = vi.fn().mockImplementation(() => null); Module.register("name", { getTranslations: () => translations }); const MMM = Module.create("name"); @@ -104,7 +104,7 @@ describe("translations", () => { it("should load translation fallback file", async () => { const { Translator, Module, config } = dom.window; config.language = "--"; - Translator.load = vi.fn().mockImplementation((_m, _f, _fb) => null); + Translator.load = vi.fn().mockImplementation(() => null); Module.register("name", { getTranslations: () => translations }); const MMM = Module.create("name"); diff --git a/tests/electron/helpers/global-setup.js b/tests/electron/helpers/global-setup.js index 91c542d090..8df47c4997 100644 --- a/tests/electron/helpers/global-setup.js +++ b/tests/electron/helpers/global-setup.js @@ -70,7 +70,7 @@ exports.stopApplication = async (timeout = 10000) => { if (electronProcess && !electronProcess.killed) { electronProcess.kill("SIGKILL"); } - } catch (error) { + } catch { // Ignore errors caused by Playwright already tearing down the connection } }; @@ -80,7 +80,7 @@ exports.stopApplication = async (timeout = 10000) => { app.close(), new Promise((_, reject) => setTimeout(() => reject(new Error("Electron close timeout")), timeout)) ]); - } catch (error) { + } catch { killElectron(); } }; diff --git a/tests/electron/modules/calendar_spec.js b/tests/electron/modules/calendar_spec.js index 1be60c4d8f..c37f3a434c 100644 --- a/tests/electron/modules/calendar_spec.js +++ b/tests/electron/modules/calendar_spec.js @@ -26,6 +26,7 @@ describe("Calendar module", () => { * Use this for debugging broken tests, it will console log the text of the calendar module * @returns {Promise} */ + // eslint-disable-next-line no-unused-vars const logAllText = async () => { expect(global.page).not.toBeNull(); const loc = await global.page.locator(".calendar .event"); diff --git a/tests/mocks/testNotification/testNotification.js b/tests/mocks/testNotification/testNotification.js index 735af025a3..a18cc0261a 100644 --- a/tests/mocks/testNotification/testNotification.js +++ b/tests/mocks/testNotification/testNotification.js @@ -42,9 +42,9 @@ Module.register("testNotification", { let tableRow = document.createElement("tr"); table.appendChild(tableRow); - let tablecol1 = this.maketd(tableRow, col1); - let tablecol2 = this.maketd(tableRow, col2); - let tablecol3 = this.maketd(tableRow, col3); + this.maketd(tableRow, col1); + this.maketd(tableRow, col2); + this.maketd(tableRow, col3); return tableRow; }, diff --git a/tests/mocks/weather_owm_current.json b/tests/mocks/weather_owm_current.json new file mode 100644 index 0000000000..021ae166bb --- /dev/null +++ b/tests/mocks/weather_owm_current.json @@ -0,0 +1,28 @@ +{ + "coord": { "lon": 11.58, "lat": 48.14 }, + "weather": [{ "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04d" }], + "base": "stations", + "main": { + "temp": -0.27, + "feels_like": -3.9, + "temp_min": -1.0, + "temp_max": 0.5, + "pressure": 1018, + "humidity": 54 + }, + "visibility": 10000, + "wind": { "speed": 3.09, "deg": 220 }, + "clouds": { "all": 100 }, + "dt": 1744200000, + "sys": { + "type": 2, + "id": 2002112, + "country": "DE", + "sunrise": 1744170000, + "sunset": 1744218000 + }, + "timezone": 7200, + "id": 2867714, + "name": "Munich", + "cod": 200 +} diff --git a/tests/mocks/weather_owm_forecast.json b/tests/mocks/weather_owm_forecast.json new file mode 100644 index 0000000000..d9c3e3e075 --- /dev/null +++ b/tests/mocks/weather_owm_forecast.json @@ -0,0 +1,180 @@ +{ + "cod": "200", + "message": 0, + "cnt": 16, + "list": [ + { + "dt": 1744156800, + "main": { "temp": -1.0, "feels_like": -4.0, "temp_min": -1.5, "temp_max": -0.5, "pressure": 1018, "humidity": 60 }, + "weather": [{ "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04n" }], + "clouds": { "all": 100 }, + "wind": { "speed": 3.0, "deg": 210 }, + "pop": 0.2, + "sys": { "pod": "n" }, + "dt_txt": "2026-04-09 00:00:00" + }, + { + "dt": 1744167600, + "main": { "temp": -1.2, "feels_like": -4.2, "temp_min": -1.5, "temp_max": -0.9, "pressure": 1018, "humidity": 62 }, + "weather": [{ "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04n" }], + "clouds": { "all": 100 }, + "wind": { "speed": 3.1, "deg": 215 }, + "pop": 0.2, + "sys": { "pod": "n" }, + "dt_txt": "2026-04-09 03:00:00" + }, + { + "dt": 1744178400, + "main": { "temp": -0.5, "feels_like": -3.5, "temp_min": -1.0, "temp_max": 0.0, "pressure": 1019, "humidity": 58 }, + "weather": [{ "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04d" }], + "clouds": { "all": 95 }, + "wind": { "speed": 2.8, "deg": 220 }, + "pop": 0.3, + "sys": { "pod": "d" }, + "dt_txt": "2026-04-09 06:00:00" + }, + { + "dt": 1744189200, + "main": { "temp": 1.0, "feels_like": -2.0, "temp_min": 0.5, "temp_max": 1.5, "pressure": 1019, "humidity": 55 }, + "weather": [{ "id": 500, "main": "Rain", "description": "light rain", "icon": "10d" }], + "clouds": { "all": 90 }, + "wind": { "speed": 2.5, "deg": 225 }, + "pop": 0.8, + "rain": { "3h": 0.6 }, + "sys": { "pod": "d" }, + "dt_txt": "2026-04-09 09:00:00" + }, + { + "dt": 1744200000, + "main": { "temp": 2.0, "feels_like": -1.0, "temp_min": 1.5, "temp_max": 2.5, "pressure": 1018, "humidity": 52 }, + "weather": [{ "id": 500, "main": "Rain", "description": "light rain", "icon": "10d" }], + "clouds": { "all": 88 }, + "wind": { "speed": 2.4, "deg": 230 }, + "pop": 0.9, + "rain": { "3h": 0.6 }, + "sys": { "pod": "d" }, + "dt_txt": "2026-04-09 12:00:00" + }, + { + "dt": 1744210800, + "main": { "temp": 1.5, "feels_like": -1.5, "temp_min": 1.0, "temp_max": 2.0, "pressure": 1018, "humidity": 54 }, + "weather": [{ "id": 500, "main": "Rain", "description": "light rain", "icon": "10d" }], + "clouds": { "all": 90 }, + "wind": { "speed": 2.6, "deg": 228 }, + "pop": 0.8, + "sys": { "pod": "d" }, + "dt_txt": "2026-04-09 15:00:00" + }, + { + "dt": 1744221600, + "main": { "temp": 0.8, "feels_like": -2.2, "temp_min": 0.5, "temp_max": 1.2, "pressure": 1018, "humidity": 57 }, + "weather": [{ "id": 500, "main": "Rain", "description": "light rain", "icon": "10d" }], + "clouds": { "all": 92 }, + "wind": { "speed": 2.7, "deg": 222 }, + "pop": 0.6, + "sys": { "pod": "d" }, + "dt_txt": "2026-04-09 18:00:00" + }, + { + "dt": 1744232400, + "main": { "temp": -0.2, "feels_like": -3.2, "temp_min": -0.5, "temp_max": 0.1, "pressure": 1019, "humidity": 60 }, + "weather": [{ "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04n" }], + "clouds": { "all": 95 }, + "wind": { "speed": 2.9, "deg": 218 }, + "pop": 0.3, + "sys": { "pod": "n" }, + "dt_txt": "2026-04-09 21:00:00" + }, + { + "dt": 1744243200, + "main": { "temp": 0.5, "feels_like": -2.5, "temp_min": 0.0, "temp_max": 1.0, "pressure": 1020, "humidity": 58 }, + "weather": [{ "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04n" }], + "clouds": { "all": 85 }, + "wind": { "speed": 2.5, "deg": 200 }, + "pop": 0.1, + "sys": { "pod": "n" }, + "dt_txt": "2026-04-10 00:00:00" + }, + { + "dt": 1744254000, + "main": { "temp": 1.0, "feels_like": -2.0, "temp_min": 0.5, "temp_max": 1.5, "pressure": 1021, "humidity": 56 }, + "weather": [{ "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04n" }], + "clouds": { "all": 80 }, + "wind": { "speed": 2.3, "deg": 205 }, + "pop": 0.1, + "sys": { "pod": "n" }, + "dt_txt": "2026-04-10 03:00:00" + }, + { + "dt": 1744264800, + "main": { "temp": 2.0, "feels_like": -1.0, "temp_min": 1.5, "temp_max": 2.5, "pressure": 1021, "humidity": 53 }, + "weather": [{ "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04d" }], + "clouds": { "all": 75 }, + "wind": { "speed": 2.1, "deg": 210 }, + "pop": 0.1, + "sys": { "pod": "d" }, + "dt_txt": "2026-04-10 06:00:00" + }, + { + "dt": 1744275600, + "main": { "temp": 3.5, "feels_like": 0.5, "temp_min": 3.0, "temp_max": 4.0, "pressure": 1020, "humidity": 50 }, + "weather": [{ "id": 520, "main": "Rain", "description": "light shower rain", "icon": "09d" }], + "clouds": { "all": 70 }, + "wind": { "speed": 2.0, "deg": 215 }, + "pop": 0.5, + "snow": { "3h": 0.5 }, + "sys": { "pod": "d" }, + "dt_txt": "2026-04-10 09:00:00" + }, + { + "dt": 1744286400, + "main": { "temp": 5.0, "feels_like": 2.0, "temp_min": 4.5, "temp_max": 5.5, "pressure": 1019, "humidity": 48 }, + "weather": [{ "id": 520, "main": "Rain", "description": "light shower rain", "icon": "09d" }], + "clouds": { "all": 65 }, + "wind": { "speed": 1.9, "deg": 220 }, + "pop": 0.4, + "sys": { "pod": "d" }, + "dt_txt": "2026-04-10 12:00:00" + }, + { + "dt": 1744297200, + "main": { "temp": 4.5, "feels_like": 1.5, "temp_min": 4.0, "temp_max": 5.0, "pressure": 1019, "humidity": 50 }, + "weather": [{ "id": 520, "main": "Rain", "description": "light shower rain", "icon": "09d" }], + "clouds": { "all": 68 }, + "wind": { "speed": 2.0, "deg": 218 }, + "pop": 0.4, + "sys": { "pod": "d" }, + "dt_txt": "2026-04-10 15:00:00" + }, + { + "dt": 1744308000, + "main": { "temp": 3.0, "feels_like": 0.0, "temp_min": 2.5, "temp_max": 3.5, "pressure": 1019, "humidity": 53 }, + "weather": [{ "id": 520, "main": "Rain", "description": "light shower rain", "icon": "09d" }], + "clouds": { "all": 72 }, + "wind": { "speed": 2.1, "deg": 212 }, + "pop": 0.3, + "sys": { "pod": "d" }, + "dt_txt": "2026-04-10 18:00:00" + }, + { + "dt": 1744318800, + "main": { "temp": 1.5, "feels_like": -1.5, "temp_min": 1.0, "temp_max": 2.0, "pressure": 1020, "humidity": 56 }, + "weather": [{ "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04n" }], + "clouds": { "all": 80 }, + "wind": { "speed": 2.2, "deg": 208 }, + "pop": 0.2, + "sys": { "pod": "n" }, + "dt_txt": "2026-04-10 21:00:00" + } + ], + "city": { + "id": 2867714, + "name": "Munich", + "coord": { "lat": 48.14, "lon": 11.58 }, + "country": "DE", + "population": 1260391, + "timezone": 0, + "sunrise": 1744170000, + "sunset": 1744218000 + } +} diff --git a/tests/mocks/weather_smhi.json b/tests/mocks/weather_smhi.json index c08a6e85b0..6273e29791 100644 --- a/tests/mocks/weather_smhi.json +++ b/tests/mocks/weather_smhi.json @@ -1,1907 +1,1986 @@ { - "approvedTime": "2026-02-06T21:31:33Z", + "createdTime": "2026-02-06T21:31:33Z", "referenceTime": "2026-02-06T21:00:00Z", - "geometry": { "type": "Point", "coordinates": [[18.089437, 59.339222]] }, + "geometry": { + "type": "Point", + "coordinates": [18.089437, 59.339222] + }, "timeSeries": [ { - "validTime": "2026-02-06T22:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.5] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [40] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.3] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.8] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [88] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1013.6] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [12.3] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-06T23:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.4] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [38] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.2] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.7] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [88] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1013.9] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [12.8] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T00:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.3] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [38] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.9] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.7] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [87] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1014.2] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T01:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.2] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [37] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.7] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.2] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [87] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1014.2] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [12.9] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [6] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T02:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.2] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [31] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.5] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.8] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [88] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1014.2] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [12.6] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [5] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T03:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.2] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [33] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.6] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.5] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [88] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1014.1] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [6] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T04:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.2] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [35] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.6] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.8] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [89] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1014.4] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [12.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T05:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.4] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [35] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.8] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.9] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [89] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1014.7] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [11.8] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [5] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T06:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.6] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [37] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.4] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [8.0] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [88] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1015.3] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [12.5] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T07:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.7] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [36] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.6] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [8.5] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [88] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1015.9] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [12.8] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T08:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.5] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [42] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.8] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [8.9] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [86] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1016.5] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [14.1] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [3] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T09:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.2] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [41] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.3] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [8.7] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [82] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1017.3] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [17.1] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [6] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T10:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-4.7] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [44] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.2] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.9] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [77] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1017.8] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [20.1] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [5] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T11:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-4.1] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [48] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.3] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.9] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [64] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1018.1] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [28.8] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [6] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T12:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-3.9] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [47] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.3] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.9] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [50] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1018.1] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [38.9] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [2] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T13:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-3.6] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [42] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.2] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.9] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [52] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1018.1] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [1] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T14:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-3.5] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [38] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.0] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.7] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [71] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1018.4] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [23.8] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T15:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-3.9] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [32] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.7] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.3] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [76] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1018.9] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T16:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-4.2] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [31] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.1] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.6] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [80] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1019.2] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [6] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [6] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T17:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-4.6] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [38] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.4] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [5.6] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [84] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1019.6] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [3] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T18:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.4] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [36] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.6] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.0] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [87] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1020.1] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [13.3] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [1] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [1] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T19:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-6.1] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [33] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.6] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.0] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [86] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1020.5] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [13.7] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T20:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-6.5] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [32] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.0] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.0] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [84] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1021.0] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [15.3] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [1] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [1] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [2] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T21:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-6.8] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [32] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.2] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.3] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [90] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1021.4] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [2] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T22:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-6.3] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [38] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.2] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.3] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [90] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1021.9] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [10.9] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [2] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-07T23:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.9] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [44] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.5] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [8.1] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [89] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1022.3] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [3] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T00:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.9] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [46] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.8] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [8.6] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [86] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1022.5] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [13.9] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [6] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [6] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T01:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.8] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [53] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.9] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [8.9] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [84] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1022.9] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T02:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.8] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [49] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.6] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [8.9] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [84] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1023.2] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T03:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.9] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [39] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.1] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [8.4] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [85] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1023.6] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T04:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-6.0] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [40] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.7] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.6] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [86] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1023.9] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [5.3] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T05:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.8] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [46] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.3] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.8] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [87] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1024.0] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [5.6] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T06:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.4] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [63] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.4] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.2] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [83] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1024.2] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T07:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.3] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [55] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.7] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.9] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [83] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1024.5] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [16.1] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T08:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.2] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [54] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.4] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.7] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [83] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1024.8] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [15.9] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T09:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-4.8] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [53] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.7] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.8] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [85] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1025.1] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [14.7] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T10:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-3.8] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [66] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.5] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.8] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [82] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1025.2] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T11:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-2.6] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [103] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.1] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.4] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [64] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1025.1] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [29.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T12:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-2.2] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [116] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.7] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.2] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [55] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1025.0] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T13:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-2.0] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [118] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.5] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.9] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [54] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1024.9] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [-0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [-0.0] } - ] - }, - { - "validTime": "2026-02-08T14:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-2.1] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [123] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.1] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.4] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [55] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1025.0] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [35.2] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [-0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [-0.0] } - ] - }, - { - "validTime": "2026-02-08T15:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-2.9] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [120] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [2.5] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [5.4] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [60] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1025.0] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [31.5] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [-0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [-0.0] } - ] - }, - { - "validTime": "2026-02-08T16:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-4.2] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [116] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [2.1] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.1] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [65] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1024.8] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [5] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [5] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [-0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [3] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T17:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.2] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [115] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.8] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.1] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [71] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1024.6] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [24.3] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [5] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [5] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [3] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T18:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-6.7] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [107] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.4] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [2.4] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [78] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1024.5] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [19.3] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [5] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [5] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [-0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [3] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T19:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-7.5] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [117] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.3] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.9] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [80] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1024.4] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [17.9] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [6] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [6] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [-0.0] } - ] - }, - { - "validTime": "2026-02-08T20:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-8.1] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [124] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.2] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.7] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [80] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1024.3] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [75.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T21:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-8.2] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [138] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.2] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.7] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [80] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1024.2] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [18.0] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [-0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-08T22:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-8.3] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [157] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.4] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.8] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [80] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1024.0] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [18.2] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [-0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [-0.0] } - ] - }, - { - "validTime": "2026-02-08T23:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-8.3] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [174] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.4] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.9] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [80] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1023.6] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [18.2] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [-0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-09T00:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-8.3] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [182] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.7] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [2.2] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [79] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1023.2] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [18.8] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [-0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-09T03:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-7.5] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [223] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [2.6] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.9] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [78] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1021.3] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [19.5] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [-0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [-0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [-0.0] } - ] - }, - { - "validTime": "2026-02-09T06:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-7.0] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [251] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [2.4] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.8] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [77] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1020.4] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [20.4] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [-0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [-0.0] } - ] - }, - { - "validTime": "2026-02-09T09:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-7.0] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [264] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [2.8] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.7] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [76] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1019.9] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [21.1] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-09T12:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-7.0] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [254] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.5] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.8] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [84] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1017.7] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [19.1] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-09T18:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-9.4] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [250] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [2.3] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.5] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [86] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1015.3] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [22.9] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-10T00:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-9.9] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [271] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [0.8] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [2.0] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [87] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1012.9] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [7.3] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-10T06:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-10.3] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [253] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.3] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.3] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [86] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1009.6] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [8.2] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-10T12:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-9.1] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [249] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [2.3] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [5.3] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [80] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1006.5] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [10.3] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [-9] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [0] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-10T18:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-10.3] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [318] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.0] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.0] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [85] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1003.7] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [13.4] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-11T00:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-13.1] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [314] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [0.5] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.5] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [91] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1001.5] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [13.1] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [1] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [3] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-11T06:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-11.2] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [348] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [1.0] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [2.3] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [88] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [999.3] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [43.7] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.2] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-11T12:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.1] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [344] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [2.0] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.5] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [82] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [998.2] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [49.2] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [0] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.2] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-11T18:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.8] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [52] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [3.7] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [6.5] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [86] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [996.1] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [40.7] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.3] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-12T00:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-6.1] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [49] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.1] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.3] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [87] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [994.7] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [39.1] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.2] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.4] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-12T06:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-6.6] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [56] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.9] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [8.6] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [87] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [993.3] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [29.9] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.3] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-12T12:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-5.7] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [55] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [5.5] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [10.3] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [81] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [993.6] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [31.5] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.5] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-12T18:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-7.1] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [45] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [5.2] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [10.3] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [86] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [994.2] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [31.7] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.2] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.4] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-13T00:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-7.9] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [38] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [5.0] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [9.5] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [86] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [994.7] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [31.1] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [5] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.6] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [88] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [6] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-13T12:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-7.7] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [19] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [5.4] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [10.5] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [80] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [996.6] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [33.8] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [8] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [2] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.5] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-14T00:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-10.0] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [3] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.7] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [9.4] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [85] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [999.6] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [37.5] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [7] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [6] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [2] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.3] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-14T12:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-8.0] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [350] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.9] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [9.6] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [75] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1002.7] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [38.9] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [6] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [5] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [1] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.3] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-15T00:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-11.4] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [321] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.2] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [8.1] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [83] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1007.3] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [40.6] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [5] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [1] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.3] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [3] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-15T12:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-7.9] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [304] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.1] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [8.3] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [72] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1011.2] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [43.3] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [2] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [1] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.4] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [3] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-16T00:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-9.4] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [292] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.2] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [7.7] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [85] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1013.8] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [43.3] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [5] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [2] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [1] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [3] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] - }, - { - "validTime": "2026-02-16T12:00:00Z", - "parameters": [ - { "name": "t", "levelType": "hl", "level": 2, "unit": "Cel", "values": [-4.8] }, - { "name": "wd", "levelType": "hl", "level": 10, "unit": "degree", "values": [295] }, - { "name": "ws", "levelType": "hl", "level": 10, "unit": "m/s", "values": [4.1] }, - { "name": "gust", "levelType": "hl", "level": 10, "unit": "m/s", "values": [8.3] }, - { "name": "r", "levelType": "hl", "level": 2, "unit": "percent", "values": [78] }, - { "name": "msl", "levelType": "hmsl", "level": 0, "unit": "hPa", "values": [1014.7] }, - { "name": "vis", "levelType": "hl", "level": 2, "unit": "km", "values": [45.8] }, - { "name": "tstm", "levelType": "hl", "level": 0, "unit": "percent", "values": [0] }, - { "name": "tcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [6] }, - { "name": "lcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [4] }, - { "name": "mcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [3] }, - { "name": "hcc_mean", "levelType": "hl", "level": 0, "unit": "octas", "values": [2] }, - { "name": "pmean", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "pmin", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.1] }, - { "name": "pmax", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.2] }, - { "name": "pmedian", "levelType": "hl", "level": 0, "unit": "kg/m2/h", "values": [0.0] }, - { "name": "spp", "levelType": "hl", "level": 0, "unit": "percent", "values": [100] }, - { "name": "pcat", "levelType": "hl", "level": 0, "unit": "category", "values": [1] }, - { "name": "Wsymb2", "levelType": "hl", "level": 0, "unit": "category", "values": [4] }, - { "name": "tp", "levelType": "hl", "level": 0, "unit": "kg/m2", "values": [0.0] } - ] + "time": "2026-02-06T22:00:00Z", + "intervalParametersStartTime": "2026-02-06T22:00:00Z", + "data": { + "air_temperature": -5.5, + "wind_from_direction": 40, + "wind_speed": 4.3, + "wind_speed_of_gust": 7.8, + "relative_humidity": 88, + "air_pressure_at_mean_sea_level": 1013.6, + "visibility_in_air": 12.3, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 8, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.1, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-06T23:00:00Z", + "intervalParametersStartTime": "2026-02-06T23:00:00Z", + "data": { + "air_temperature": -5.4, + "wind_from_direction": 38, + "wind_speed": 4.2, + "wind_speed_of_gust": 7.7, + "relative_humidity": 88, + "air_pressure_at_mean_sea_level": 1013.9, + "visibility_in_air": 12.8, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 8, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.1, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T00:00:00Z", + "intervalParametersStartTime": "2026-02-07T00:00:00Z", + "data": { + "air_temperature": -5.3, + "wind_from_direction": 38, + "wind_speed": 3.9, + "wind_speed_of_gust": 7.7, + "relative_humidity": 87, + "air_pressure_at_mean_sea_level": 1014.2, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 7, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.1, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T01:00:00Z", + "intervalParametersStartTime": "2026-02-07T01:00:00Z", + "data": { + "air_temperature": -5.2, + "wind_from_direction": 37, + "wind_speed": 3.7, + "wind_speed_of_gust": 7.2, + "relative_humidity": 87, + "air_pressure_at_mean_sea_level": 1014.2, + "visibility_in_air": 12.9, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 6, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T02:00:00Z", + "intervalParametersStartTime": "2026-02-07T02:00:00Z", + "data": { + "air_temperature": -5.2, + "wind_from_direction": 31, + "wind_speed": 3.5, + "wind_speed_of_gust": 6.8, + "relative_humidity": 88, + "air_pressure_at_mean_sea_level": 1014.2, + "visibility_in_air": 12.6, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 5, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T03:00:00Z", + "intervalParametersStartTime": "2026-02-07T03:00:00Z", + "data": { + "air_temperature": -5.2, + "wind_from_direction": 33, + "wind_speed": 3.6, + "wind_speed_of_gust": 6.5, + "relative_humidity": 88, + "air_pressure_at_mean_sea_level": 1014.1, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 6, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T04:00:00Z", + "intervalParametersStartTime": "2026-02-07T04:00:00Z", + "data": { + "air_temperature": -5.2, + "wind_from_direction": 35, + "wind_speed": 3.6, + "wind_speed_of_gust": 6.8, + "relative_humidity": 89, + "air_pressure_at_mean_sea_level": 1014.4, + "visibility_in_air": 12.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 7, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 8, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T05:00:00Z", + "intervalParametersStartTime": "2026-02-07T05:00:00Z", + "data": { + "air_temperature": -5.4, + "wind_from_direction": 35, + "wind_speed": 3.8, + "wind_speed_of_gust": 6.9, + "relative_humidity": 89, + "air_pressure_at_mean_sea_level": 1014.7, + "visibility_in_air": 11.8, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 5, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 8, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T06:00:00Z", + "intervalParametersStartTime": "2026-02-07T06:00:00Z", + "data": { + "air_temperature": -5.6, + "wind_from_direction": 37, + "wind_speed": 4.4, + "wind_speed_of_gust": 8.0, + "relative_humidity": 88, + "air_pressure_at_mean_sea_level": 1015.3, + "visibility_in_air": 12.5, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 4, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 8, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T07:00:00Z", + "intervalParametersStartTime": "2026-02-07T07:00:00Z", + "data": { + "air_temperature": -5.7, + "wind_from_direction": 36, + "wind_speed": 4.6, + "wind_speed_of_gust": 8.5, + "relative_humidity": 88, + "air_pressure_at_mean_sea_level": 1015.9, + "visibility_in_air": 12.8, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 3, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 8, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T08:00:00Z", + "intervalParametersStartTime": "2026-02-07T08:00:00Z", + "data": { + "air_temperature": -5.5, + "wind_from_direction": 42, + "wind_speed": 4.8, + "wind_speed_of_gust": 8.9, + "relative_humidity": 86, + "air_pressure_at_mean_sea_level": 1016.5, + "visibility_in_air": 14.1, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 3, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 7, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 3, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T09:00:00Z", + "intervalParametersStartTime": "2026-02-07T09:00:00Z", + "data": { + "air_temperature": -5.2, + "wind_from_direction": 41, + "wind_speed": 4.3, + "wind_speed_of_gust": 8.7, + "relative_humidity": 82, + "air_pressure_at_mean_sea_level": 1017.3, + "visibility_in_air": 17.1, + "thunderstorm_probability": 0, + "cloud_area_fraction": 7, + "low_type_cloud_area_fraction": 4, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 6, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T10:00:00Z", + "intervalParametersStartTime": "2026-02-07T10:00:00Z", + "data": { + "air_temperature": -4.7, + "wind_from_direction": 44, + "wind_speed": 4.2, + "wind_speed_of_gust": 7.9, + "relative_humidity": 77, + "air_pressure_at_mean_sea_level": 1017.8, + "visibility_in_air": 20.1, + "thunderstorm_probability": 0, + "cloud_area_fraction": 7, + "low_type_cloud_area_fraction": 5, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 4, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T11:00:00Z", + "intervalParametersStartTime": "2026-02-07T11:00:00Z", + "data": { + "air_temperature": -4.1, + "wind_from_direction": 48, + "wind_speed": 4.3, + "wind_speed_of_gust": 7.9, + "relative_humidity": 64, + "air_pressure_at_mean_sea_level": 1018.1, + "visibility_in_air": 28.8, + "thunderstorm_probability": 0, + "cloud_area_fraction": 7, + "low_type_cloud_area_fraction": 6, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 3, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T12:00:00Z", + "intervalParametersStartTime": "2026-02-07T12:00:00Z", + "data": { + "air_temperature": -3.9, + "wind_from_direction": 47, + "wind_speed": 4.3, + "wind_speed_of_gust": 7.9, + "relative_humidity": 50, + "air_pressure_at_mean_sea_level": 1018.1, + "visibility_in_air": 38.9, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 7, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 2, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T13:00:00Z", + "intervalParametersStartTime": "2026-02-07T13:00:00Z", + "data": { + "air_temperature": -3.6, + "wind_from_direction": 42, + "wind_speed": 4.2, + "wind_speed_of_gust": 7.9, + "relative_humidity": 52, + "air_pressure_at_mean_sea_level": 1018.1, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 1, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T14:00:00Z", + "intervalParametersStartTime": "2026-02-07T14:00:00Z", + "data": { + "air_temperature": -3.5, + "wind_from_direction": 38, + "wind_speed": 4.0, + "wind_speed_of_gust": 7.7, + "relative_humidity": 71, + "air_pressure_at_mean_sea_level": 1018.4, + "visibility_in_air": 23.8, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T15:00:00Z", + "intervalParametersStartTime": "2026-02-07T15:00:00Z", + "data": { + "air_temperature": -3.9, + "wind_from_direction": 32, + "wind_speed": 3.7, + "wind_speed_of_gust": 7.3, + "relative_humidity": 76, + "air_pressure_at_mean_sea_level": 1018.9, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T16:00:00Z", + "intervalParametersStartTime": "2026-02-07T16:00:00Z", + "data": { + "air_temperature": -4.2, + "wind_from_direction": 31, + "wind_speed": 3.1, + "wind_speed_of_gust": 6.6, + "relative_humidity": 80, + "air_pressure_at_mean_sea_level": 1019.2, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 6, + "low_type_cloud_area_fraction": 6, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T17:00:00Z", + "intervalParametersStartTime": "2026-02-07T17:00:00Z", + "data": { + "air_temperature": -4.6, + "wind_from_direction": 38, + "wind_speed": 3.4, + "wind_speed_of_gust": 5.6, + "relative_humidity": 84, + "air_pressure_at_mean_sea_level": 1019.6, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 4, + "low_type_cloud_area_fraction": 4, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 3, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T18:00:00Z", + "intervalParametersStartTime": "2026-02-07T18:00:00Z", + "data": { + "air_temperature": -5.4, + "wind_from_direction": 36, + "wind_speed": 3.6, + "wind_speed_of_gust": 7.0, + "relative_humidity": 87, + "air_pressure_at_mean_sea_level": 1020.1, + "visibility_in_air": 13.3, + "thunderstorm_probability": 0, + "cloud_area_fraction": 1, + "low_type_cloud_area_fraction": 1, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 1, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T19:00:00Z", + "intervalParametersStartTime": "2026-02-07T19:00:00Z", + "data": { + "air_temperature": -6.1, + "wind_from_direction": 33, + "wind_speed": 3.6, + "wind_speed_of_gust": 6.0, + "relative_humidity": 86, + "air_pressure_at_mean_sea_level": 1020.5, + "visibility_in_air": 13.7, + "thunderstorm_probability": 0, + "cloud_area_fraction": 0, + "low_type_cloud_area_fraction": 0, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 1, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T20:00:00Z", + "intervalParametersStartTime": "2026-02-07T20:00:00Z", + "data": { + "air_temperature": -6.5, + "wind_from_direction": 32, + "wind_speed": 4.0, + "wind_speed_of_gust": 7.0, + "relative_humidity": 84, + "air_pressure_at_mean_sea_level": 1021.0, + "visibility_in_air": 15.3, + "thunderstorm_probability": 0, + "cloud_area_fraction": 1, + "low_type_cloud_area_fraction": 1, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 2, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T21:00:00Z", + "intervalParametersStartTime": "2026-02-07T21:00:00Z", + "data": { + "air_temperature": -6.8, + "wind_from_direction": 32, + "wind_speed": 4.2, + "wind_speed_of_gust": 7.3, + "relative_humidity": 90, + "air_pressure_at_mean_sea_level": 1021.4, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 3, + "low_type_cloud_area_fraction": 3, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 2, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T22:00:00Z", + "intervalParametersStartTime": "2026-02-07T22:00:00Z", + "data": { + "air_temperature": -6.3, + "wind_from_direction": 38, + "wind_speed": 4.2, + "wind_speed_of_gust": 7.3, + "relative_humidity": 90, + "air_pressure_at_mean_sea_level": 1021.9, + "visibility_in_air": 10.9, + "thunderstorm_probability": 0, + "cloud_area_fraction": 3, + "low_type_cloud_area_fraction": 3, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 2, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-07T23:00:00Z", + "intervalParametersStartTime": "2026-02-07T23:00:00Z", + "data": { + "air_temperature": -5.9, + "wind_from_direction": 44, + "wind_speed": 4.5, + "wind_speed_of_gust": 8.1, + "relative_humidity": 89, + "air_pressure_at_mean_sea_level": 1022.3, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 4, + "low_type_cloud_area_fraction": 4, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 3, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T00:00:00Z", + "intervalParametersStartTime": "2026-02-08T00:00:00Z", + "data": { + "air_temperature": -5.9, + "wind_from_direction": 46, + "wind_speed": 4.8, + "wind_speed_of_gust": 8.6, + "relative_humidity": 86, + "air_pressure_at_mean_sea_level": 1022.5, + "visibility_in_air": 13.9, + "thunderstorm_probability": 0, + "cloud_area_fraction": 6, + "low_type_cloud_area_fraction": 6, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T01:00:00Z", + "intervalParametersStartTime": "2026-02-08T01:00:00Z", + "data": { + "air_temperature": -5.8, + "wind_from_direction": 53, + "wind_speed": 4.9, + "wind_speed_of_gust": 8.9, + "relative_humidity": 84, + "air_pressure_at_mean_sea_level": 1022.9, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T02:00:00Z", + "intervalParametersStartTime": "2026-02-08T02:00:00Z", + "data": { + "air_temperature": -5.8, + "wind_from_direction": 49, + "wind_speed": 4.6, + "wind_speed_of_gust": 8.9, + "relative_humidity": 84, + "air_pressure_at_mean_sea_level": 1023.2, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T03:00:00Z", + "intervalParametersStartTime": "2026-02-08T03:00:00Z", + "data": { + "air_temperature": -5.9, + "wind_from_direction": 39, + "wind_speed": 4.1, + "wind_speed_of_gust": 8.4, + "relative_humidity": 85, + "air_pressure_at_mean_sea_level": 1023.6, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T04:00:00Z", + "intervalParametersStartTime": "2026-02-08T04:00:00Z", + "data": { + "air_temperature": -6.0, + "wind_from_direction": 40, + "wind_speed": 3.7, + "wind_speed_of_gust": 7.6, + "relative_humidity": 86, + "air_pressure_at_mean_sea_level": 1023.9, + "visibility_in_air": 5.3, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T05:00:00Z", + "intervalParametersStartTime": "2026-02-08T05:00:00Z", + "data": { + "air_temperature": -5.8, + "wind_from_direction": 46, + "wind_speed": 3.3, + "wind_speed_of_gust": 6.8, + "relative_humidity": 87, + "air_pressure_at_mean_sea_level": 1024.0, + "visibility_in_air": 5.6, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T06:00:00Z", + "intervalParametersStartTime": "2026-02-08T06:00:00Z", + "data": { + "air_temperature": -5.4, + "wind_from_direction": 63, + "wind_speed": 3.4, + "wind_speed_of_gust": 6.2, + "relative_humidity": 83, + "air_pressure_at_mean_sea_level": 1024.2, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T07:00:00Z", + "intervalParametersStartTime": "2026-02-08T07:00:00Z", + "data": { + "air_temperature": -5.3, + "wind_from_direction": 55, + "wind_speed": 3.7, + "wind_speed_of_gust": 6.9, + "relative_humidity": 83, + "air_pressure_at_mean_sea_level": 1024.5, + "visibility_in_air": 16.1, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T08:00:00Z", + "intervalParametersStartTime": "2026-02-08T08:00:00Z", + "data": { + "air_temperature": -5.2, + "wind_from_direction": 54, + "wind_speed": 3.4, + "wind_speed_of_gust": 6.7, + "relative_humidity": 83, + "air_pressure_at_mean_sea_level": 1024.8, + "visibility_in_air": 15.9, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T09:00:00Z", + "intervalParametersStartTime": "2026-02-08T09:00:00Z", + "data": { + "air_temperature": -4.8, + "wind_from_direction": 53, + "wind_speed": 3.7, + "wind_speed_of_gust": 6.8, + "relative_humidity": 85, + "air_pressure_at_mean_sea_level": 1025.1, + "visibility_in_air": 14.7, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T10:00:00Z", + "intervalParametersStartTime": "2026-02-08T10:00:00Z", + "data": { + "air_temperature": -3.8, + "wind_from_direction": 66, + "wind_speed": 3.5, + "wind_speed_of_gust": 6.8, + "relative_humidity": 82, + "air_pressure_at_mean_sea_level": 1025.2, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T11:00:00Z", + "intervalParametersStartTime": "2026-02-08T11:00:00Z", + "data": { + "air_temperature": -2.6, + "wind_from_direction": 103, + "wind_speed": 3.1, + "wind_speed_of_gust": 6.4, + "relative_humidity": 64, + "air_pressure_at_mean_sea_level": 1025.1, + "visibility_in_air": 29.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T12:00:00Z", + "intervalParametersStartTime": "2026-02-08T12:00:00Z", + "data": { + "air_temperature": -2.2, + "wind_from_direction": 116, + "wind_speed": 3.7, + "wind_speed_of_gust": 7.2, + "relative_humidity": 55, + "air_pressure_at_mean_sea_level": 1025.0, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T13:00:00Z", + "intervalParametersStartTime": "2026-02-08T13:00:00Z", + "data": { + "air_temperature": -2.0, + "wind_from_direction": 118, + "wind_speed": 3.5, + "wind_speed_of_gust": 6.9, + "relative_humidity": 54, + "air_pressure_at_mean_sea_level": 1024.9, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": -0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": -0.0 + } + }, + { + "time": "2026-02-08T14:00:00Z", + "intervalParametersStartTime": "2026-02-08T14:00:00Z", + "data": { + "air_temperature": -2.1, + "wind_from_direction": 123, + "wind_speed": 3.1, + "wind_speed_of_gust": 6.4, + "relative_humidity": 55, + "air_pressure_at_mean_sea_level": 1025.0, + "visibility_in_air": 35.2, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": -0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": -0.0 + } + }, + { + "time": "2026-02-08T15:00:00Z", + "intervalParametersStartTime": "2026-02-08T15:00:00Z", + "data": { + "air_temperature": -2.9, + "wind_from_direction": 120, + "wind_speed": 2.5, + "wind_speed_of_gust": 5.4, + "relative_humidity": 60, + "air_pressure_at_mean_sea_level": 1025.0, + "visibility_in_air": 31.5, + "thunderstorm_probability": 0, + "cloud_area_fraction": 7, + "low_type_cloud_area_fraction": 7, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": -0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": -0.0 + } + }, + { + "time": "2026-02-08T16:00:00Z", + "intervalParametersStartTime": "2026-02-08T16:00:00Z", + "data": { + "air_temperature": -4.2, + "wind_from_direction": 116, + "wind_speed": 2.1, + "wind_speed_of_gust": 4.1, + "relative_humidity": 65, + "air_pressure_at_mean_sea_level": 1024.8, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 5, + "low_type_cloud_area_fraction": 5, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": -0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 3, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T17:00:00Z", + "intervalParametersStartTime": "2026-02-08T17:00:00Z", + "data": { + "air_temperature": -5.2, + "wind_from_direction": 115, + "wind_speed": 1.8, + "wind_speed_of_gust": 3.1, + "relative_humidity": 71, + "air_pressure_at_mean_sea_level": 1024.6, + "visibility_in_air": 24.3, + "thunderstorm_probability": 0, + "cloud_area_fraction": 5, + "low_type_cloud_area_fraction": 5, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 3, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T18:00:00Z", + "intervalParametersStartTime": "2026-02-08T18:00:00Z", + "data": { + "air_temperature": -6.7, + "wind_from_direction": 107, + "wind_speed": 1.4, + "wind_speed_of_gust": 2.4, + "relative_humidity": 78, + "air_pressure_at_mean_sea_level": 1024.5, + "visibility_in_air": 19.3, + "thunderstorm_probability": 0, + "cloud_area_fraction": 5, + "low_type_cloud_area_fraction": 5, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": -0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 3, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T19:00:00Z", + "intervalParametersStartTime": "2026-02-08T19:00:00Z", + "data": { + "air_temperature": -7.5, + "wind_from_direction": 117, + "wind_speed": 1.3, + "wind_speed_of_gust": 1.9, + "relative_humidity": 80, + "air_pressure_at_mean_sea_level": 1024.4, + "visibility_in_air": 17.9, + "thunderstorm_probability": 0, + "cloud_area_fraction": 6, + "low_type_cloud_area_fraction": 6, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": -0.0 + } + }, + { + "time": "2026-02-08T20:00:00Z", + "intervalParametersStartTime": "2026-02-08T20:00:00Z", + "data": { + "air_temperature": -8.1, + "wind_from_direction": 124, + "wind_speed": 1.2, + "wind_speed_of_gust": 1.7, + "relative_humidity": 80, + "air_pressure_at_mean_sea_level": 1024.3, + "visibility_in_air": 75.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 7, + "low_type_cloud_area_fraction": 7, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T21:00:00Z", + "intervalParametersStartTime": "2026-02-08T21:00:00Z", + "data": { + "air_temperature": -8.2, + "wind_from_direction": 138, + "wind_speed": 1.2, + "wind_speed_of_gust": 1.7, + "relative_humidity": 80, + "air_pressure_at_mean_sea_level": 1024.2, + "visibility_in_air": 18.0, + "thunderstorm_probability": 0, + "cloud_area_fraction": 7, + "low_type_cloud_area_fraction": 7, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": -0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-08T22:00:00Z", + "intervalParametersStartTime": "2026-02-08T22:00:00Z", + "data": { + "air_temperature": -8.3, + "wind_from_direction": 157, + "wind_speed": 1.4, + "wind_speed_of_gust": 1.8, + "relative_humidity": 80, + "air_pressure_at_mean_sea_level": 1024.0, + "visibility_in_air": 18.2, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": -0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": -0.0 + } + }, + { + "time": "2026-02-08T23:00:00Z", + "intervalParametersStartTime": "2026-02-08T23:00:00Z", + "data": { + "air_temperature": -8.3, + "wind_from_direction": 174, + "wind_speed": 1.4, + "wind_speed_of_gust": 1.9, + "relative_humidity": 80, + "air_pressure_at_mean_sea_level": 1023.6, + "visibility_in_air": 18.2, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": -0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-09T00:00:00Z", + "intervalParametersStartTime": "2026-02-09T00:00:00Z", + "data": { + "air_temperature": -8.3, + "wind_from_direction": 182, + "wind_speed": 1.7, + "wind_speed_of_gust": 2.2, + "relative_humidity": 79, + "air_pressure_at_mean_sea_level": 1023.2, + "visibility_in_air": 18.8, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": -0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-09T03:00:00Z", + "intervalParametersStartTime": "2026-02-09T03:00:00Z", + "data": { + "air_temperature": -7.5, + "wind_from_direction": 223, + "wind_speed": 2.6, + "wind_speed_of_gust": 3.9, + "relative_humidity": 78, + "air_pressure_at_mean_sea_level": 1021.3, + "visibility_in_air": 19.5, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": -0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": -0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": -0.0 + } + }, + { + "time": "2026-02-09T06:00:00Z", + "intervalParametersStartTime": "2026-02-09T06:00:00Z", + "data": { + "air_temperature": -7.0, + "wind_from_direction": 251, + "wind_speed": 2.4, + "wind_speed_of_gust": 3.8, + "relative_humidity": 77, + "air_pressure_at_mean_sea_level": 1020.4, + "visibility_in_air": 20.4, + "thunderstorm_probability": 0, + "cloud_area_fraction": 7, + "low_type_cloud_area_fraction": 7, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": -0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": -0.0 + } + }, + { + "time": "2026-02-09T09:00:00Z", + "intervalParametersStartTime": "2026-02-09T09:00:00Z", + "data": { + "air_temperature": -7.0, + "wind_from_direction": 264, + "wind_speed": 2.8, + "wind_speed_of_gust": 4.7, + "relative_humidity": 76, + "air_pressure_at_mean_sea_level": 1019.9, + "visibility_in_air": 21.1, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-09T12:00:00Z", + "intervalParametersStartTime": "2026-02-09T12:00:00Z", + "data": { + "air_temperature": -7.0, + "wind_from_direction": 254, + "wind_speed": 3.5, + "wind_speed_of_gust": 7.8, + "relative_humidity": 84, + "air_pressure_at_mean_sea_level": 1017.7, + "visibility_in_air": 19.1, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-09T18:00:00Z", + "intervalParametersStartTime": "2026-02-09T18:00:00Z", + "data": { + "air_temperature": -9.4, + "wind_from_direction": 250, + "wind_speed": 2.3, + "wind_speed_of_gust": 4.5, + "relative_humidity": 86, + "air_pressure_at_mean_sea_level": 1015.3, + "visibility_in_air": 22.9, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-10T00:00:00Z", + "intervalParametersStartTime": "2026-02-10T00:00:00Z", + "data": { + "air_temperature": -9.9, + "wind_from_direction": 271, + "wind_speed": 0.8, + "wind_speed_of_gust": 2.0, + "relative_humidity": 87, + "air_pressure_at_mean_sea_level": 1012.9, + "visibility_in_air": 7.3, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-10T06:00:00Z", + "intervalParametersStartTime": "2026-02-10T06:00:00Z", + "data": { + "air_temperature": -10.3, + "wind_from_direction": 253, + "wind_speed": 1.3, + "wind_speed_of_gust": 3.3, + "relative_humidity": 86, + "air_pressure_at_mean_sea_level": 1009.6, + "visibility_in_air": 8.2, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 7, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-10T12:00:00Z", + "intervalParametersStartTime": "2026-02-10T12:00:00Z", + "data": { + "air_temperature": -9.1, + "wind_from_direction": 249, + "wind_speed": 2.3, + "wind_speed_of_gust": 5.3, + "relative_humidity": 80, + "air_pressure_at_mean_sea_level": 1006.5, + "visibility_in_air": 10.3, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 8, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": -9, + "predominant_precipitation_type_at_surface": 0, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-10T18:00:00Z", + "intervalParametersStartTime": "2026-02-10T18:00:00Z", + "data": { + "air_temperature": -10.3, + "wind_from_direction": 318, + "wind_speed": 1.0, + "wind_speed_of_gust": 3.0, + "relative_humidity": 85, + "air_pressure_at_mean_sea_level": 1003.7, + "visibility_in_air": 13.4, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.0, + "precipitation_amount_max": 0.0, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-11T00:00:00Z", + "intervalParametersStartTime": "2026-02-11T00:00:00Z", + "data": { + "air_temperature": -13.1, + "wind_from_direction": 314, + "wind_speed": 0.5, + "wind_speed_of_gust": 1.5, + "relative_humidity": 91, + "air_pressure_at_mean_sea_level": 1001.5, + "visibility_in_air": 13.1, + "thunderstorm_probability": 0, + "cloud_area_fraction": 4, + "low_type_cloud_area_fraction": 3, + "medium_type_cloud_area_fraction": 1, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.1, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 3, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-11T06:00:00Z", + "intervalParametersStartTime": "2026-02-11T06:00:00Z", + "data": { + "air_temperature": -11.2, + "wind_from_direction": 348, + "wind_speed": 1.0, + "wind_speed_of_gust": 2.3, + "relative_humidity": 88, + "air_pressure_at_mean_sea_level": 999.3, + "visibility_in_air": 43.7, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 0, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.2, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-11T12:00:00Z", + "intervalParametersStartTime": "2026-02-11T12:00:00Z", + "data": { + "air_temperature": -5.1, + "wind_from_direction": 344, + "wind_speed": 2.0, + "wind_speed_of_gust": 4.5, + "relative_humidity": 82, + "air_pressure_at_mean_sea_level": 998.2, + "visibility_in_air": 49.2, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 7, + "medium_type_cloud_area_fraction": 0, + "high_type_cloud_area_fraction": 8, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.2, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-11T18:00:00Z", + "intervalParametersStartTime": "2026-02-11T18:00:00Z", + "data": { + "air_temperature": -5.8, + "wind_from_direction": 52, + "wind_speed": 3.7, + "wind_speed_of_gust": 6.5, + "relative_humidity": 86, + "air_pressure_at_mean_sea_level": 996.1, + "visibility_in_air": 40.7, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 3, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.3, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-12T00:00:00Z", + "intervalParametersStartTime": "2026-02-12T00:00:00Z", + "data": { + "air_temperature": -6.1, + "wind_from_direction": 49, + "wind_speed": 4.1, + "wind_speed_of_gust": 7.3, + "relative_humidity": 87, + "air_pressure_at_mean_sea_level": 994.7, + "visibility_in_air": 39.1, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 3, + "high_type_cloud_area_fraction": 3, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.2, + "precipitation_amount_max": 0.4, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-12T06:00:00Z", + "intervalParametersStartTime": "2026-02-12T06:00:00Z", + "data": { + "air_temperature": -6.6, + "wind_from_direction": 56, + "wind_speed": 4.9, + "wind_speed_of_gust": 8.6, + "relative_humidity": 87, + "air_pressure_at_mean_sea_level": 993.3, + "visibility_in_air": 29.9, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 3, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.3, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-12T12:00:00Z", + "intervalParametersStartTime": "2026-02-12T12:00:00Z", + "data": { + "air_temperature": -5.7, + "wind_from_direction": 55, + "wind_speed": 5.5, + "wind_speed_of_gust": 10.3, + "relative_humidity": 81, + "air_pressure_at_mean_sea_level": 993.6, + "visibility_in_air": 31.5, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 3, + "precipitation_amount_mean": 0.1, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.5, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-12T18:00:00Z", + "intervalParametersStartTime": "2026-02-12T18:00:00Z", + "data": { + "air_temperature": -7.1, + "wind_from_direction": 45, + "wind_speed": 5.2, + "wind_speed_of_gust": 10.3, + "relative_humidity": 86, + "air_pressure_at_mean_sea_level": 994.2, + "visibility_in_air": 31.7, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 4, + "precipitation_amount_mean": 0.1, + "precipitation_amount_min": 0.2, + "precipitation_amount_max": 0.4, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-13T00:00:00Z", + "intervalParametersStartTime": "2026-02-13T00:00:00Z", + "data": { + "air_temperature": -7.9, + "wind_from_direction": 38, + "wind_speed": 5.0, + "wind_speed_of_gust": 9.5, + "relative_humidity": 86, + "air_pressure_at_mean_sea_level": 994.7, + "visibility_in_air": 31.1, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 8, + "medium_type_cloud_area_fraction": 5, + "high_type_cloud_area_fraction": 4, + "precipitation_amount_mean": 0.1, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.6, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 88, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 6, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-13T12:00:00Z", + "intervalParametersStartTime": "2026-02-13T12:00:00Z", + "data": { + "air_temperature": -7.7, + "wind_from_direction": 19, + "wind_speed": 5.4, + "wind_speed_of_gust": 10.5, + "relative_humidity": 80, + "air_pressure_at_mean_sea_level": 996.6, + "visibility_in_air": 33.8, + "thunderstorm_probability": 0, + "cloud_area_fraction": 8, + "low_type_cloud_area_fraction": 7, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 2, + "precipitation_amount_mean": 0.1, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.5, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-14T00:00:00Z", + "intervalParametersStartTime": "2026-02-14T00:00:00Z", + "data": { + "air_temperature": -10.0, + "wind_from_direction": 3, + "wind_speed": 4.7, + "wind_speed_of_gust": 9.4, + "relative_humidity": 85, + "air_pressure_at_mean_sea_level": 999.6, + "visibility_in_air": 37.5, + "thunderstorm_probability": 0, + "cloud_area_fraction": 7, + "low_type_cloud_area_fraction": 6, + "medium_type_cloud_area_fraction": 4, + "high_type_cloud_area_fraction": 2, + "precipitation_amount_mean": 0.1, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.3, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-14T12:00:00Z", + "intervalParametersStartTime": "2026-02-14T12:00:00Z", + "data": { + "air_temperature": -8.0, + "wind_from_direction": 350, + "wind_speed": 4.9, + "wind_speed_of_gust": 9.6, + "relative_humidity": 75, + "air_pressure_at_mean_sea_level": 1002.7, + "visibility_in_air": 38.9, + "thunderstorm_probability": 0, + "cloud_area_fraction": 6, + "low_type_cloud_area_fraction": 5, + "medium_type_cloud_area_fraction": 3, + "high_type_cloud_area_fraction": 1, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.3, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-15T00:00:00Z", + "intervalParametersStartTime": "2026-02-15T00:00:00Z", + "data": { + "air_temperature": -11.4, + "wind_from_direction": 321, + "wind_speed": 4.2, + "wind_speed_of_gust": 8.1, + "relative_humidity": 83, + "air_pressure_at_mean_sea_level": 1007.3, + "visibility_in_air": 40.6, + "thunderstorm_probability": 0, + "cloud_area_fraction": 5, + "low_type_cloud_area_fraction": 3, + "medium_type_cloud_area_fraction": 3, + "high_type_cloud_area_fraction": 1, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.3, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 3, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-15T12:00:00Z", + "intervalParametersStartTime": "2026-02-15T12:00:00Z", + "data": { + "air_temperature": -7.9, + "wind_from_direction": 304, + "wind_speed": 4.1, + "wind_speed_of_gust": 8.3, + "relative_humidity": 72, + "air_pressure_at_mean_sea_level": 1011.2, + "visibility_in_air": 43.3, + "thunderstorm_probability": 0, + "cloud_area_fraction": 4, + "low_type_cloud_area_fraction": 3, + "medium_type_cloud_area_fraction": 2, + "high_type_cloud_area_fraction": 1, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.4, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 3, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-16T00:00:00Z", + "intervalParametersStartTime": "2026-02-16T00:00:00Z", + "data": { + "air_temperature": -9.4, + "wind_from_direction": 292, + "wind_speed": 4.2, + "wind_speed_of_gust": 7.7, + "relative_humidity": 85, + "air_pressure_at_mean_sea_level": 1013.8, + "visibility_in_air": 43.3, + "thunderstorm_probability": 0, + "cloud_area_fraction": 5, + "low_type_cloud_area_fraction": 4, + "medium_type_cloud_area_fraction": 2, + "high_type_cloud_area_fraction": 1, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.1, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 3, + "precipitation_amount_mean_deterministic": 0.0 + } + }, + { + "time": "2026-02-16T12:00:00Z", + "intervalParametersStartTime": "2026-02-16T12:00:00Z", + "data": { + "air_temperature": -4.8, + "wind_from_direction": 295, + "wind_speed": 4.1, + "wind_speed_of_gust": 8.3, + "relative_humidity": 78, + "air_pressure_at_mean_sea_level": 1014.7, + "visibility_in_air": 45.8, + "thunderstorm_probability": 0, + "cloud_area_fraction": 6, + "low_type_cloud_area_fraction": 4, + "medium_type_cloud_area_fraction": 3, + "high_type_cloud_area_fraction": 2, + "precipitation_amount_mean": 0.0, + "precipitation_amount_min": 0.1, + "precipitation_amount_max": 0.2, + "precipitation_amount_median": 0.0, + "precipitation_frozen_part": 100, + "predominant_precipitation_type_at_surface": 5, + "symbol_code": 4, + "precipitation_amount_mean_deterministic": 0.0 + } } ] } diff --git a/tests/unit/functions/http_fetcher_spec.js b/tests/unit/functions/http_fetcher_spec.js index 046ddb9ca0..122025a26c 100644 --- a/tests/unit/functions/http_fetcher_spec.js +++ b/tests/unit/functions/http_fetcher_spec.js @@ -51,6 +51,31 @@ describe("HTTPFetcher", () => { expect(text).toBe(responseData); }); + it("should treat 304 responses as successful and reset error counters", async () => { + server.use( + http.get(TEST_URL, () => { + return new HttpResponse(null, { status: 304 }); + }) + ); + + fetcher = new HTTPFetcher(TEST_URL, { reloadInterval: 60000 }); + fetcher.serverErrorCount = 2; + fetcher.networkErrorCount = 3; + + const responsePromise = new Promise((resolve) => { + fetcher.on("response", (response) => { + resolve(response); + }); + }); + + fetcher.startPeriodicFetch(); + const response = await responsePromise; + + expect(response.status).toBe(304); + expect(fetcher.serverErrorCount).toBe(0); + expect(fetcher.networkErrorCount).toBe(0); + }); + it("should emit error event on network failure", async () => { server.use( http.get(TEST_URL, () => { @@ -440,3 +465,112 @@ describe("fetch() method", () => { expect(errorInfo.errorType).toBe("NETWORK_ERROR"); }); }); + +describe("selfSignedCert dispatcher", () => { + const { Agent } = require("undici"); + + it("should set rejectUnauthorized=false when selfSignedCert is true", () => { + fetcher = new HTTPFetcher(TEST_URL, { + reloadInterval: 60000, + selfSignedCert: true + }); + + const options = fetcher.getRequestOptions(); + + expect(options.dispatcher).toBeInstanceOf(Agent); + const agentOptionsSymbol = Object.getOwnPropertySymbols(options.dispatcher).find((s) => s.description === "options"); + const dispatcherOptions = options.dispatcher[agentOptionsSymbol]; + expect(dispatcherOptions.connect.rejectUnauthorized).toBe(false); + }); + + it("should not set a dispatcher when selfSignedCert is false", () => { + fetcher = new HTTPFetcher(TEST_URL, { + reloadInterval: 60000, + selfSignedCert: false + }); + + const options = fetcher.getRequestOptions(); + + expect(options.dispatcher).toBeUndefined(); + }); +}); + +describe("Retry exhaustion fallback", () => { + it("should fall back to reloadInterval after network retries exhausted", async () => { + server.use( + http.get(TEST_URL, () => { + return HttpResponse.error(); + }) + ); + + fetcher = new HTTPFetcher(TEST_URL, { reloadInterval: 300000, maxRetries: 3 }); + + const errors = []; + fetcher.on("error", (errorInfo) => errors.push(errorInfo)); + + // Trigger maxRetries + 1 fetches to reach exhaustion + for (let i = 0; i < 4; i++) { + await fetcher.fetch(); + } + + // First retries should use backoff (< reloadInterval) + expect(errors[0].retryAfter).toBe(15000); + expect(errors[1].retryAfter).toBe(30000); + // Third retry hits maxRetries, should fall back to reloadInterval + expect(errors[2].retryAfter).toBe(300000); + // Subsequent errors stay at reloadInterval + expect(errors[3].retryAfter).toBe(300000); + }); + + it("should fall back to reloadInterval after server error retries exhausted", async () => { + server.use( + http.get(TEST_URL, () => { + return new HttpResponse(null, { status: 503 }); + }) + ); + + fetcher = new HTTPFetcher(TEST_URL, { reloadInterval: 300000, maxRetries: 3 }); + + const errors = []; + fetcher.on("error", (errorInfo) => errors.push(errorInfo)); + + for (let i = 0; i < 4; i++) { + await fetcher.fetch(); + } + + // First retries should use backoff (< reloadInterval) + expect(errors[0].retryAfter).toBe(15000); + expect(errors[1].retryAfter).toBe(30000); + // Third retry hits maxRetries, should fall back to reloadInterval + expect(errors[2].retryAfter).toBe(300000); + // Subsequent errors stay at reloadInterval + expect(errors[3].retryAfter).toBe(300000); + }); + + it("should reset network error count on success", async () => { + let requestCount = 0; + server.use( + http.get(TEST_URL, () => { + requestCount++; + if (requestCount <= 2) return HttpResponse.error(); + return HttpResponse.text("ok"); + }) + ); + + fetcher = new HTTPFetcher(TEST_URL, { reloadInterval: 300000, maxRetries: 3 }); + + const errors = []; + fetcher.on("error", (errorInfo) => errors.push(errorInfo)); + + // Two failures with backoff + await fetcher.fetch(); + await fetcher.fetch(); + expect(errors).toHaveLength(2); + expect(errors[0].retryAfter).toBe(15000); + expect(errors[1].retryAfter).toBe(30000); + + // Success resets counter + await fetcher.fetch(); + expect(fetcher.networkErrorCount).toBe(0); + }); +}); diff --git a/tests/unit/functions/server_functions_spec.js b/tests/unit/functions/server_functions_spec.js index 8fd0a01450..8c38cf9f0a 100644 --- a/tests/unit/functions/server_functions_spec.js +++ b/tests/unit/functions/server_functions_spec.js @@ -1,7 +1,18 @@ +// Tests use vi.spyOn on shared module objects (dns, undici). +// vi.spyOn modifies the object property directly on the cached module instance, so it +// is intercepted by server_functions.js regardless of the Module.prototype.require override +// in vitest-setup.js. restoreAllMocks:true auto-restores spies, but may reuse the same +// spy instance — mockClear() is called explicitly in beforeEach to reset call history. +const dns = require("node:dns"); +const undici = require("undici"); const { cors, getUserAgent, replaceSecretPlaceholder } = require("#server_functions"); describe("server_functions tests", () => { - describe("The replaceSecretPlaceholder method", () => { + describe("The replaceSecretPlaceholder method with cors=allowWhitelist", () => { + beforeEach(() => { + global.config = { cors: "allowWhitelist" }; + }); + it("Calls string without secret placeholder", () => { const teststring = "test string without secret placeholder"; const result = replaceSecretPlaceholder(teststring); @@ -18,29 +29,48 @@ describe("server_functions tests", () => { }); }); + describe("The replaceSecretPlaceholder method with cors=allowAll", () => { + beforeEach(() => { + global.config = { cors: "allowAll" }; + }); + + it("Calls string without secret placeholder", () => { + const teststring = "test string without secret placeholder"; + const result = replaceSecretPlaceholder(teststring); + expect(result).toBe(teststring); + }); + + it("Calls string with 2 secret placeholders", () => { + const teststring = "test string with secret1=**SECRET_ONE** and secret2=**SECRET_TWO**"; + const result = replaceSecretPlaceholder(teststring); + expect(result).toBe(teststring); + }); + }); + describe("The cors method", () => { - let fetchResponse; + let fetchSpy; let fetchResponseHeadersGet; let fetchResponseArrayBuffer; let corsResponse; let request; - let fetchMock; beforeEach(() => { + global.config = { cors: "allowAll" }; fetchResponseHeadersGet = vi.fn(() => {}); fetchResponseArrayBuffer = vi.fn(() => {}); - fetchResponse = { - headers: { - get: fetchResponseHeadersGet - }, - arrayBuffer: fetchResponseArrayBuffer, - ok: true - }; - fetch = vi.fn(); - fetch.mockImplementation(() => fetchResponse); + // Mock DNS to return a public IP (SSRF check must pass for these tests) + vi.spyOn(dns.promises, "lookup").mockResolvedValue({ address: "93.184.216.34", family: 4 }); - fetchMock = fetch; + // vi.spyOn may return the same spy instance across tests when restoreAllMocks + // restores-but-reuses; mockClear() explicitly resets call history each time. + fetchSpy = vi.spyOn(undici, "fetch"); + fetchSpy.mockClear(); + fetchSpy.mockImplementation(() => Promise.resolve({ + headers: { get: fetchResponseHeadersGet }, + arrayBuffer: fetchResponseArrayBuffer, + ok: true + })); corsResponse = { set: vi.fn(() => {}), @@ -53,7 +83,7 @@ describe("server_functions tests", () => { }; request = { - url: "/cors?url=www.test.com" + url: "/cors?url=http://www.test.com" }; }); @@ -63,8 +93,8 @@ describe("server_functions tests", () => { await cors(request, corsResponse); - expect(fetchMock.mock.calls).toHaveLength(1); - expect(fetchMock.mock.calls[0][0]).toBe(urlToCall); + expect(fetchSpy.mock.calls).toHaveLength(1); + expect(fetchSpy.mock.calls[0][0]).toBe(urlToCall); }); it("Forwards Content-Type if json", async () => { @@ -126,9 +156,9 @@ describe("server_functions tests", () => { it("Fetches with user agent by default", async () => { await cors(request, corsResponse); - expect(fetchMock.mock.calls).toHaveLength(1); - expect(fetchMock.mock.calls[0][1]).toHaveProperty("headers"); - expect(fetchMock.mock.calls[0][1].headers).toHaveProperty("User-Agent"); + expect(fetchSpy.mock.calls).toHaveLength(1); + expect(fetchSpy.mock.calls[0][1]).toHaveProperty("headers"); + expect(fetchSpy.mock.calls[0][1].headers).toHaveProperty("User-Agent"); }); it("Fetches with specified headers", async () => { @@ -138,10 +168,10 @@ describe("server_functions tests", () => { await cors(request, corsResponse); - expect(fetchMock.mock.calls).toHaveLength(1); - expect(fetchMock.mock.calls[0][1]).toHaveProperty("headers"); - expect(fetchMock.mock.calls[0][1].headers).toHaveProperty("header1", "value1"); - expect(fetchMock.mock.calls[0][1].headers).toHaveProperty("header2", "value2"); + expect(fetchSpy.mock.calls).toHaveLength(1); + expect(fetchSpy.mock.calls[0][1]).toHaveProperty("headers"); + expect(fetchSpy.mock.calls[0][1].headers).toHaveProperty("header1", "value1"); + expect(fetchSpy.mock.calls[0][1].headers).toHaveProperty("header2", "value2"); }); it("Sends specified headers", async () => { @@ -153,8 +183,8 @@ describe("server_functions tests", () => { await cors(request, corsResponse); - expect(fetchMock.mock.calls).toHaveLength(1); - expect(fetchMock.mock.calls[0][1]).toHaveProperty("headers"); + expect(fetchSpy.mock.calls).toHaveLength(1); + expect(fetchSpy.mock.calls[0][1]).toHaveProperty("headers"); expect(corsResponse.set.mock.calls).toHaveLength(3); expect(corsResponse.set.mock.calls[0][0]).toBe("Content-Type"); expect(corsResponse.set.mock.calls[1][0]).toBe("header1"); @@ -182,4 +212,93 @@ describe("server_functions tests", () => { global.config = previousConfig; }); }); + + describe("The cors method blocks SSRF (DNS rebinding safe)", () => { + let response; + + beforeEach(() => { + response = { + set: vi.fn(), + send: vi.fn(), + status: vi.fn(function () { return this; }), + json: vi.fn() + }; + }); + + it("Blocks localhost hostname without DNS", async () => { + await cors({ url: "/cors?url=http://localhost/path" }, response); + expect(response.status).toHaveBeenCalledWith(403); + expect(response.json).toHaveBeenCalledWith({ error: "Forbidden: private or reserved addresses are not allowed" }); + }); + + it("Blocks non-http protocols", async () => { + await cors({ url: "/cors?url=ftp://example.com/file" }, response); + expect(response.status).toHaveBeenCalledWith(403); + }); + + it("Blocks invalid URLs", async () => { + await cors({ url: "/cors?url=not_a_valid_url" }, response); + expect(response.status).toHaveBeenCalledWith(403); + }); + + it("Blocks loopback addresses (127.0.0.1)", async () => { + vi.spyOn(dns.promises, "lookup").mockResolvedValue({ address: "127.0.0.1", family: 4 }); + await cors({ url: "/cors?url=http://example.com/" }, response); + expect(response.status).toHaveBeenCalledWith(403); + }); + + it("Blocks RFC 1918 private addresses (192.168.x.x)", async () => { + vi.spyOn(dns.promises, "lookup").mockResolvedValue({ address: "192.168.1.1", family: 4 }); + await cors({ url: "/cors?url=http://example.com/" }, response); + expect(response.status).toHaveBeenCalledWith(403); + }); + + it("Blocks link-local / cloud metadata addresses (169.254.169.254)", async () => { + vi.spyOn(dns.promises, "lookup").mockResolvedValue({ address: "169.254.169.254", family: 4 }); + await cors({ url: "/cors?url=http://example.com/" }, response); + expect(response.status).toHaveBeenCalledWith(403); + }); + + it("Allows public unicast addresses", async () => { + vi.spyOn(dns.promises, "lookup").mockResolvedValue({ address: "93.184.216.34", family: 4 }); + vi.spyOn(global, "fetch").mockResolvedValue({ + ok: true, + headers: { get: vi.fn() }, + arrayBuffer: vi.fn(() => new ArrayBuffer(0)) + }); + await cors({ url: "/cors?url=http://example.com/" }, response); + expect(response.status).not.toHaveBeenCalledWith(403); + }); + }); + + describe("cors method with allowWhitelist", () => { + let response; + + beforeEach(() => { + response = { + set: vi.fn(), + send: vi.fn(), + status: vi.fn(function () { return this; }), + json: vi.fn() + }; + vi.spyOn(dns.promises, "lookup").mockResolvedValue({ address: "93.184.216.34", family: 4 }); + vi.spyOn(global, "fetch").mockResolvedValue({ + ok: true, + headers: { get: vi.fn() }, + arrayBuffer: vi.fn(() => new ArrayBuffer(0)) + }); + }); + + it("Blocks domains not in whitelist", async () => { + global.config = { cors: "allowWhitelist", corsDomainWhitelist: [] }; + await cors({ url: "/cors?url=http://example.com/api" }, response); + expect(response.status).toHaveBeenCalledWith(403); + }); + + it("Allows domains in whitelist", async () => { + global.config = { cors: "allowWhitelist", corsDomainWhitelist: ["example.com"] }; + await cors({ url: "/cors?url=http://example.com/api" }, response); + expect(response.status).not.toHaveBeenCalledWith(403); + }); + }); }); diff --git a/tests/unit/functions/updatenotification_spec.js b/tests/unit/functions/updatenotification_spec.js index f6617e4bf7..c5d29c570b 100644 --- a/tests/unit/functions/updatenotification_spec.js +++ b/tests/unit/functions/updatenotification_spec.js @@ -4,10 +4,10 @@ import { vi, describe, beforeEach, afterEach, it, expect } from "vitest"; * Creates a fresh GitHelper instance with isolated mocks for each test run. * @param {{ current: import("vitest").Mock | null }} fsStatSyncMockRef reference to the mocked fs.statSync. * @param {{ current: { error: import("vitest").Mock; info: import("vitest").Mock } | null }} loggerMockRef reference to logger stubs. - * @param {{ current: import("vitest").MockInstance | null }} execShellSpyRef reference to the execShell spy. + * @param {{ current: import("vitest").MockInstance | null }} execGitSpyRef reference to the execGit spy. * @returns {Promise} resolved GitHelper instance. */ -async function createGitHelper (fsStatSyncMockRef, loggerMockRef, execShellSpyRef) { +async function createGitHelper (fsStatSyncMockRef, loggerMockRef, execGitSpyRef) { vi.resetModules(); fsStatSyncMockRef.current = vi.fn(); @@ -20,10 +20,10 @@ async function createGitHelper (fsStatSyncMockRef, loggerMockRef, execShellSpyRe vi.doMock("logger", () => loggerMockRef.current); const defaults = await import("../../../js/defaults"); - const gitHelperModule = await import(`../../../${defaults.defaultModulesDir}/updatenotification/git_helper`); + const gitHelperModule = await import(`../../../${defaults.defaultModulesDir}/updatenotification/git_helper.js`); const GitHelper = gitHelperModule.default || gitHelperModule; const instance = new GitHelper(); - execShellSpyRef.current = vi.spyOn(instance, "execShell"); + execGitSpyRef.current = vi.spyOn(instance, "execGit"); instance.__loggerMock = loggerMockRef.current; return instance; } @@ -31,7 +31,7 @@ async function createGitHelper (fsStatSyncMockRef, loggerMockRef, execShellSpyRe describe("Updatenotification", () => { const fsStatSyncMockRef = { current: null }; const loggerMockRef = { current: null }; - const execShellSpyRef = { current: null }; + const execGitSpyRef = { current: null }; let gitHelper; let gitRemoteOut; @@ -43,10 +43,10 @@ describe("Updatenotification", () => { let gitFetchErr; let gitTagListOut; - const getExecutedCommands = () => execShellSpyRef.current.mock.calls.map(([command]) => command); + const getExecutedCommands = () => execGitSpyRef.current.mock.calls.map((call) => call.slice(1).join(" ")); beforeEach(async () => { - gitHelper = await createGitHelper(fsStatSyncMockRef, loggerMockRef, execShellSpyRef); + gitHelper = await createGitHelper(fsStatSyncMockRef, loggerMockRef, execGitSpyRef); fsStatSyncMockRef.current.mockReturnValue({ isDirectory: () => true }); @@ -59,40 +59,42 @@ describe("Updatenotification", () => { gitFetchErr = ""; gitTagListOut = ""; - execShellSpyRef.current.mockImplementation((command) => { - if (command.includes("git remote -v")) { + execGitSpyRef.current.mockImplementation((_folder, ...args) => { + const command = args.join(" "); + + if (command === "remote -v") { return Promise.resolve({ stdout: gitRemoteOut, stderr: "" }); } - if (command.includes("git rev-parse HEAD")) { + if (command === "rev-parse HEAD") { return Promise.resolve({ stdout: gitRevParseOut, stderr: "" }); } - if (command.includes("git status -sb")) { + if (command === "status -sb") { return Promise.resolve({ stdout: gitStatusOut, stderr: "" }); } - if (command.includes("git fetch -n --dry-run")) { + if (command === "fetch -n --dry-run") { return Promise.resolve({ stdout: gitFetchOut, stderr: gitFetchErr }); } - if (command.includes("git rev-list --ancestry-path --count")) { + if (command.startsWith("rev-list --ancestry-path --count ")) { return Promise.resolve({ stdout: gitRevListCountOut, stderr: "" }); } - if (command.includes("git rev-list --ancestry-path")) { + if (command.startsWith("rev-list --ancestry-path ")) { return Promise.resolve({ stdout: gitRevListOut, stderr: "" }); } - if (command.includes("git ls-remote -q --tags --refs")) { + if (command === "ls-remote -q --tags --refs") { return Promise.resolve({ stdout: gitTagListOut, stderr: "" }); } return Promise.resolve({ stdout: "", stderr: "" }); }); - if (gitHelper.execShell !== execShellSpyRef.current) { - throw new Error("execShell spy not applied"); + if (gitHelper.execGit !== execGitSpyRef.current) { + throw new Error("execGit spy not applied"); } }); @@ -119,10 +121,10 @@ describe("Updatenotification", () => { expect(repos[0]).toMatchSnapshot(); expect(getExecutedCommands()).toMatchInlineSnapshot(` [ - "cd mock-path && git rev-parse HEAD", - "cd mock-path && git status -sb", - "cd mock-path && git fetch -n --dry-run", - "cd mock-path && git rev-list --ancestry-path --count 60e0377..332e429 develop", + "rev-parse HEAD", + "status -sb", + "fetch -n --dry-run", + "rev-list --ancestry-path --count 60e0377..332e429 develop", ] `); }); @@ -134,20 +136,20 @@ describe("Updatenotification", () => { expect(repos[0]).toMatchSnapshot(); expect(getExecutedCommands()).toMatchInlineSnapshot(` [ - "cd mock-path && git rev-parse HEAD", - "cd mock-path && git status -sb", + "rev-parse HEAD", + "status -sb", ] `); }); it("excludes repo if status can't be retrieved", async () => { const errorMessage = "Failed to retrieve status"; - execShellSpyRef.current.mockImplementationOnce(() => Promise.reject(new Error(errorMessage))); + execGitSpyRef.current.mockImplementationOnce(() => Promise.reject(new Error(errorMessage))); expect(gitHelper.gitRepos).toHaveLength(1); const repos = await gitHelper.getRepos(); expect(repos).toHaveLength(0); - expect(execShellSpyRef.current.mock.calls.length).toBeGreaterThan(0); + expect(execGitSpyRef.current.mock.calls.length).toBeGreaterThan(0); }); }); @@ -169,12 +171,12 @@ describe("Updatenotification", () => { expect(repos[0]).toMatchSnapshot(); expect(getExecutedCommands()).toMatchInlineSnapshot(` [ - "cd mock-path && git rev-parse HEAD", - "cd mock-path && git status -sb", - "cd mock-path && git fetch -n --dry-run", - "cd mock-path && git rev-list --ancestry-path --count 60e0377..332e429 master", - "cd mock-path && git ls-remote -q --tags --refs", - "cd mock-path && git rev-list --ancestry-path 60e0377..332e429 master", + "rev-parse HEAD", + "status -sb", + "fetch -n --dry-run", + "rev-list --ancestry-path --count 60e0377..332e429 master", + "ls-remote -q --tags --refs", + "rev-list --ancestry-path 60e0377..332e429 master", ] `); }); @@ -185,20 +187,20 @@ describe("Updatenotification", () => { const repos = await gitHelper.getRepos(); expect(repos[0]).toMatchSnapshot(); expect(getExecutedCommands()).toMatchInlineSnapshot(` - [ - "cd mock-path && git rev-parse HEAD", - "cd mock-path && git status -sb", - "cd mock-path && git fetch -n --dry-run", - "cd mock-path && git rev-list --ancestry-path --count 60e0377..332e429 master", - "cd mock-path && git ls-remote -q --tags --refs", - "cd mock-path && git rev-list --ancestry-path 60e0377..332e429 master", - ] - `); + [ + "rev-parse HEAD", + "status -sb", + "fetch -n --dry-run", + "rev-list --ancestry-path --count 60e0377..332e429 master", + "ls-remote -q --tags --refs", + "rev-list --ancestry-path 60e0377..332e429 master", + ] + `); }); it("excludes repo if status can't be retrieved", async () => { const errorMessage = "Failed to retrieve status"; - execShellSpyRef.current.mockImplementationOnce(() => Promise.reject(new Error(errorMessage))); + execGitSpyRef.current.mockImplementationOnce(() => Promise.reject(new Error(errorMessage))); const repos = await gitHelper.getRepos(); expect(repos).toHaveLength(0); @@ -224,15 +226,15 @@ describe("Updatenotification", () => { const repos = await gitHelper.getRepos(); expect(repos[0]).toMatchSnapshot(); expect(getExecutedCommands()).toMatchInlineSnapshot(` - [ - "cd mock-path && git rev-parse HEAD", - "cd mock-path && git status -sb", - "cd mock-path && git fetch -n --dry-run", - "cd mock-path && git rev-list --ancestry-path --count 60e0377..332e429 master", - "cd mock-path && git ls-remote -q --tags --refs", - "cd mock-path && git rev-list --ancestry-path 60e0377..332e429 master", - ] - `); + [ + "rev-parse HEAD", + "status -sb", + "fetch -n --dry-run", + "rev-list --ancestry-path --count 60e0377..332e429 master", + "ls-remote -q --tags --refs", + "rev-list --ancestry-path 60e0377..332e429 master", + ] + `); }); it("returns status information early if isBehindInStatus", async () => { @@ -241,20 +243,20 @@ describe("Updatenotification", () => { const repos = await gitHelper.getRepos(); expect(repos[0]).toMatchSnapshot(); expect(getExecutedCommands()).toMatchInlineSnapshot(` - [ - "cd mock-path && git rev-parse HEAD", - "cd mock-path && git status -sb", - "cd mock-path && git fetch -n --dry-run", - "cd mock-path && git rev-list --ancestry-path --count 60e0377..332e429 master", - "cd mock-path && git ls-remote -q --tags --refs", - "cd mock-path && git rev-list --ancestry-path 60e0377..332e429 master", - ] - `); + [ + "rev-parse HEAD", + "status -sb", + "fetch -n --dry-run", + "rev-list --ancestry-path --count 60e0377..332e429 master", + "ls-remote -q --tags --refs", + "rev-list --ancestry-path 60e0377..332e429 master", + ] + `); }); it("excludes repo if status can't be retrieved", async () => { const errorMessage = "Failed to retrieve status"; - execShellSpyRef.current.mockImplementationOnce(() => Promise.reject(new Error(errorMessage))); + execGitSpyRef.current.mockImplementationOnce(() => Promise.reject(new Error(errorMessage))); const repos = await gitHelper.getRepos(); expect(repos).toHaveLength(0); @@ -280,15 +282,15 @@ describe("Updatenotification", () => { const repos = await gitHelper.getRepos(); expect(repos[0]).toMatchSnapshot(); expect(getExecutedCommands()).toMatchInlineSnapshot(` - [ - "cd mock-path && git rev-parse HEAD", - "cd mock-path && git status -sb", - "cd mock-path && git fetch -n --dry-run", - "cd mock-path && git rev-list --ancestry-path --count 60e0377..332e429 master", - "cd mock-path && git ls-remote -q --tags --refs", - "cd mock-path && git rev-list --ancestry-path 60e0377..332e429 master", - ] - `); + [ + "rev-parse HEAD", + "status -sb", + "fetch -n --dry-run", + "rev-list --ancestry-path --count 60e0377..332e429 master", + "ls-remote -q --tags --refs", + "rev-list --ancestry-path 60e0377..332e429 master", + ] + `); }); it("returns status information early if isBehindInStatus", async () => { @@ -297,20 +299,20 @@ describe("Updatenotification", () => { const repos = await gitHelper.getRepos(); expect(repos[0]).toMatchSnapshot(); expect(getExecutedCommands()).toMatchInlineSnapshot(` - [ - "cd mock-path && git rev-parse HEAD", - "cd mock-path && git status -sb", - "cd mock-path && git fetch -n --dry-run", - "cd mock-path && git rev-list --ancestry-path --count 60e0377..332e429 master", - "cd mock-path && git ls-remote -q --tags --refs", - "cd mock-path && git rev-list --ancestry-path 60e0377..332e429 master", - ] - `); + [ + "rev-parse HEAD", + "status -sb", + "fetch -n --dry-run", + "rev-list --ancestry-path --count 60e0377..332e429 master", + "ls-remote -q --tags --refs", + "rev-list --ancestry-path 60e0377..332e429 master", + ] + `); }); it("excludes repo if status can't be retrieved", async () => { const errorMessage = "Failed to retrieve status"; - execShellSpyRef.current.mockImplementationOnce(() => Promise.reject(new Error(errorMessage))); + execGitSpyRef.current.mockImplementationOnce(() => Promise.reject(new Error(errorMessage))); const repos = await gitHelper.getRepos(); expect(repos).toHaveLength(0); @@ -334,12 +336,12 @@ describe("Updatenotification", () => { const repos = await gitHelper.getRepos(); expect(repos[0]).toMatchSnapshot(); expect(getExecutedCommands()).toMatchInlineSnapshot(` - [ - "cd mock-path && git status -sb", - "cd mock-path && git fetch -n --dry-run", - "cd mock-path && git rev-list --ancestry-path --count 19f7faf..9d83101 master", - ] - `); + [ + "status -sb", + "fetch -n --dry-run", + "rev-list --ancestry-path --count 19f7faf..9d83101 master", + ] + `); }); }); }); diff --git a/tests/unit/modules/default/weather/node_helper_spec.js b/tests/unit/modules/default/weather/node_helper_spec.js new file mode 100644 index 0000000000..2f18e9654a --- /dev/null +++ b/tests/unit/modules/default/weather/node_helper_spec.js @@ -0,0 +1,116 @@ +import Module from "node:module"; +import { afterEach, describe, expect, it, vi } from "vitest"; + +/** + * Creates a fresh weather node helper instance with isolated mocks. + * @returns {Promise} The mocked weather node helper. + */ +async function loadWeatherNodeHelper () { + vi.resetModules(); + + const loggerMock = { + log: vi.fn(), + warn: vi.fn(), + error: vi.fn() + }; + const originalRequire = Module.prototype.require; + + Module.prototype.require = function (id) { + if (id === "node_helper") { + return { + create: vi.fn((definition) => definition) + }; + } + + if (id === "logger") { + return loggerMock; + } + + return originalRequire.apply(this, arguments); + }; + + let helper; + try { + const helperModule = await import("../../../../../defaultmodules/weather/node_helper"); + helper = helperModule.default || helperModule; + } finally { + Module.prototype.require = originalRequire; + } + + helper.providers = {}; + helper.lastData = {}; + helper.sendSocketNotification = vi.fn(); + + return helper; +} + +afterEach(() => { + vi.resetAllMocks(); + vi.resetModules(); +}); + +describe("weather node_helper reconnect handling", () => { + it("re-sends cached weather data when a client reconnects", async () => { + const helper = await loadWeatherNodeHelper(); + const instanceId = "weather-current"; + const cachedPayload = { + instanceId, + type: "current", + data: { temperature: 8.5 } + }; + + helper.providers[instanceId] = { locationName: "Munich, BY" }; + helper.lastData[instanceId] = cachedPayload; + + await helper.initWeatherProvider({ + weatherProvider: "openmeteo", + instanceId, + type: "current" + }); + + expect(helper.sendSocketNotification).toHaveBeenNthCalledWith(1, "WEATHER_INITIALIZED", { + instanceId, + locationName: "Munich, BY" + }); + expect(helper.sendSocketNotification).toHaveBeenNthCalledWith(2, "WEATHER_DATA", cachedPayload); + expect(helper.sendSocketNotification).toHaveBeenCalledTimes(2); + }); + + it("does not send WEATHER_DATA on reconnect when no cached payload exists", async () => { + const helper = await loadWeatherNodeHelper(); + const instanceId = "weather-current"; + + helper.providers[instanceId] = { locationName: "Munich, BY" }; + + await helper.initWeatherProvider({ + weatherProvider: "openmeteo", + instanceId, + type: "current" + }); + + expect(helper.sendSocketNotification).toHaveBeenCalledWith("WEATHER_INITIALIZED", { + instanceId, + locationName: "Munich, BY" + }); + expect(helper.sendSocketNotification).toHaveBeenCalledTimes(1); + }); + + it("cleans up provider and cached data when stopping an instance", async () => { + const helper = await loadWeatherNodeHelper(); + const instanceId = "weather-current"; + const stop = vi.fn(); + + helper.providers[instanceId] = { stop }; + helper.lastData[instanceId] = { + instanceId, + type: "current", + data: { temperature: 8.5 } + }; + + helper.stopWeatherProvider(instanceId); + + expect(stop).toHaveBeenCalledTimes(1); + expect(helper.providers[instanceId]).toBeUndefined(); + expect(helper.lastData[instanceId]).toBeUndefined(); + }); +}); diff --git a/tests/unit/modules/default/weather/providers/openmeteo_spec.js b/tests/unit/modules/default/weather/providers/openmeteo_spec.js index 0377a99013..225a970c88 100644 --- a/tests/unit/modules/default/weather/providers/openmeteo_spec.js +++ b/tests/unit/modules/default/weather/providers/openmeteo_spec.js @@ -144,12 +144,15 @@ describe("OpenMeteoProvider", () => { provider.start(); const result = await dataPromise; + const currentHourUnix = Math.floor(currentData.current_weather.time / 3600) * 3600; + const currentHourIndex = currentData.hourly.time.findIndex((time) => time === currentHourUnix); expect(result).toBeDefined(); expect(result.temperature).toBe(8.5); expect(result.windSpeed).toBeCloseTo(4.7, 1); expect(result.windFromDirection).toBe(9); - expect(result.humidity).toBe(83); + expect(result.humidity).toBe(currentData.hourly.relativehumidity_2m[currentHourIndex]); + expect(result.humidity).not.toBe(currentData.hourly.relativehumidity_2m[0]); }); it("should include sunrise and sunset from daily data", async () => { diff --git a/tests/unit/modules/default/weather/providers/openweathermap_spec.js b/tests/unit/modules/default/weather/providers/openweathermap_spec.js index 7b27fd711f..d5b92ed6d6 100644 --- a/tests/unit/modules/default/weather/providers/openweathermap_spec.js +++ b/tests/unit/modules/default/weather/providers/openweathermap_spec.js @@ -8,6 +8,8 @@ import { setupServer } from "msw/node"; import { describe, it, expect, vi, beforeAll, afterAll, afterEach } from "vitest"; import onecallData from "../../../../../mocks/weather_owm_onecall.json" with { type: "json" }; +import currentData from "../../../../../mocks/weather_owm_current.json" with { type: "json" }; +import forecastData from "../../../../../mocks/weather_owm_forecast.json" with { type: "json" }; let server; @@ -232,4 +234,321 @@ describe("OpenWeatherMapProvider", () => { expect(provider.locationName).toBe("America/New_York"); }); }); + + describe("API v2.5 - Current Weather (/weather endpoint)", () => { + it("should parse current weather from /weather endpoint", async () => { + const provider = new OpenWeatherMapProvider({ + lat: 48.14, + lon: 11.58, + apiKey: "test-key", + apiVersion: "2.5", + weatherEndpoint: "/weather", + type: "current" + }); + + const dataPromise = new Promise((resolve) => { + provider.setCallbacks(resolve, vi.fn()); + }); + + server.use( + http.get("https://api.openweathermap.org/data/2.5/weather", () => { + return HttpResponse.json(currentData); + }) + ); + + await provider.initialize(); + provider.start(); + + const result = await dataPromise; + + expect(result.temperature).toBe(-0.27); + expect(result.feelsLikeTemp).toBe(-3.9); + expect(result.humidity).toBe(54); + expect(result.windSpeed).toBe(3.09); + expect(result.windFromDirection).toBe(220); + expect(result.weatherType).toBe("cloudy-windy"); + expect(result.sunrise).toBeInstanceOf(Date); + expect(result.sunset).toBeInstanceOf(Date); + }); + + it("should set location name from city name and country", async () => { + const provider = new OpenWeatherMapProvider({ + lat: 48.14, + lon: 11.58, + apiKey: "test-key", + apiVersion: "2.5", + weatherEndpoint: "/weather", + type: "current" + }); + + const dataPromise = new Promise((resolve) => { + provider.setCallbacks(resolve, vi.fn()); + }); + + server.use( + http.get("https://api.openweathermap.org/data/2.5/weather", () => { + return HttpResponse.json(currentData); + }) + ); + + await provider.initialize(); + provider.start(); + + await dataPromise; + + expect(provider.locationName).toBe("Munich, DE"); + }); + }); + + describe("API v2.5 - Forecast (/forecast endpoint)", () => { + it("should parse /forecast endpoint into daily grouped forecast", async () => { + const provider = new OpenWeatherMapProvider({ + lat: 48.14, + lon: 11.58, + apiKey: "test-key", + apiVersion: "2.5", + weatherEndpoint: "/forecast", + type: "forecast" + }); + + const dataPromise = new Promise((resolve) => { + provider.setCallbacks(resolve, vi.fn()); + }); + + server.use( + http.get("https://api.openweathermap.org/data/2.5/forecast", () => { + return HttpResponse.json(forecastData); + }) + ); + + await provider.initialize(); + provider.start(); + + const result = await dataPromise; + + expect(Array.isArray(result)).toBe(true); + expect(result).toHaveLength(2); + }); + + it("should correctly aggregate min/max temperatures per day", async () => { + const provider = new OpenWeatherMapProvider({ + lat: 48.14, + lon: 11.58, + apiKey: "test-key", + apiVersion: "2.5", + weatherEndpoint: "/forecast", + type: "forecast" + }); + + const dataPromise = new Promise((resolve) => { + provider.setCallbacks(resolve, vi.fn()); + }); + + server.use( + http.get("https://api.openweathermap.org/data/2.5/forecast", () => { + return HttpResponse.json(forecastData); + }) + ); + + await provider.initialize(); + provider.start(); + + const result = await dataPromise; + + // Day 1: temp_min values: -1.5, -1.5, -1.0, 0.5, 1.5, 1.0, 0.5, -0.5 → min=-1.5 + expect(result[0].minTemperature).toBe(-1.5); + // Day 1: temp_max values: -0.5, -0.9, 0.0, 1.5, 2.5, 2.0, 1.2, 0.1 → max=2.5 + expect(result[0].maxTemperature).toBe(2.5); + // Day 2: temp_min values: 0.0, 0.5, 1.5, 3.0, 4.5, 4.0, 2.5, 1.0 → min=0.0 + expect(result[1].minTemperature).toBe(0.0); + // Day 2: temp_max values: 1.0, 1.5, 2.5, 4.0, 5.5, 5.0, 3.5, 2.0 → max=5.5 + expect(result[1].maxTemperature).toBe(5.5); + }); + + it("should pick daytime weather type (8-17h)", async () => { + const provider = new OpenWeatherMapProvider({ + lat: 48.14, + lon: 11.58, + apiKey: "test-key", + apiVersion: "2.5", + weatherEndpoint: "/forecast", + type: "forecast" + }); + + const dataPromise = new Promise((resolve) => { + provider.setCallbacks(resolve, vi.fn()); + }); + + server.use( + http.get("https://api.openweathermap.org/data/2.5/forecast", () => { + return HttpResponse.json(forecastData); + }) + ); + + await provider.initialize(); + provider.start(); + + const result = await dataPromise; + + // Day 1 daytime entries have icon "10d" → "rain" + expect(result[0].weatherType).toBe("rain"); + // Day 2 daytime entries have icon "09d" → "showers" + expect(result[1].weatherType).toBe("showers"); + }); + + it("should accumulate precipitation per day", async () => { + const provider = new OpenWeatherMapProvider({ + lat: 48.14, + lon: 11.58, + apiKey: "test-key", + apiVersion: "2.5", + weatherEndpoint: "/forecast", + type: "forecast" + }); + + const dataPromise = new Promise((resolve) => { + provider.setCallbacks(resolve, vi.fn()); + }); + + server.use( + http.get("https://api.openweathermap.org/data/2.5/forecast", () => { + return HttpResponse.json(forecastData); + }) + ); + + await provider.initialize(); + provider.start(); + + const result = await dataPromise; + + // Day 1: two rain entries of 0.6 each = 1.2 + expect(result[0].rain).toBeCloseTo(1.2); + expect(result[0].precipitationAmount).toBeCloseTo(1.2); + // Day 2: one snow entry of 0.5 + expect(result[1].snow).toBeCloseTo(0.5); + expect(result[1].precipitationAmount).toBeCloseTo(0.5); + }); + + it("should set location name from city in forecast response", async () => { + const provider = new OpenWeatherMapProvider({ + lat: 48.14, + lon: 11.58, + apiKey: "test-key", + apiVersion: "2.5", + weatherEndpoint: "/forecast", + type: "forecast" + }); + + const dataPromise = new Promise((resolve) => { + provider.setCallbacks(resolve, vi.fn()); + }); + + server.use( + http.get("https://api.openweathermap.org/data/2.5/forecast", () => { + return HttpResponse.json(forecastData); + }) + ); + + await provider.initialize(); + provider.start(); + + await dataPromise; + + expect(provider.locationName).toBe("Munich, DE"); + }); + }); + + describe("API v2.5 - Hourly (/forecast endpoint with type hourly)", () => { + it("should return individual 3h entries instead of aggregating", async () => { + const provider = new OpenWeatherMapProvider({ + lat: 48.14, + lon: 11.58, + apiKey: "test-key", + apiVersion: "2.5", + weatherEndpoint: "/forecast", + type: "hourly" + }); + + const dataPromise = new Promise((resolve) => { + provider.setCallbacks(resolve, vi.fn()); + }); + + server.use( + http.get("https://api.openweathermap.org/data/2.5/forecast", () => { + return HttpResponse.json(forecastData); + }) + ); + + await provider.initialize(); + provider.start(); + + const result = await dataPromise; + + expect(Array.isArray(result)).toBe(true); + expect(result).toHaveLength(forecastData.list.length); + }); + + it("should map temperature and wind from each 3h slot", async () => { + const provider = new OpenWeatherMapProvider({ + lat: 48.14, + lon: 11.58, + apiKey: "test-key", + apiVersion: "2.5", + weatherEndpoint: "/forecast", + type: "hourly" + }); + + const dataPromise = new Promise((resolve) => { + provider.setCallbacks(resolve, vi.fn()); + }); + + server.use( + http.get("https://api.openweathermap.org/data/2.5/forecast", () => { + return HttpResponse.json(forecastData); + }) + ); + + await provider.initialize(); + provider.start(); + + const result = await dataPromise; + + expect(result[0].temperature).toBe(forecastData.list[0].main.temp); + expect(result[0].windSpeed).toBe(forecastData.list[0].wind.speed); + expect(result[0].precipitationProbability).toBe(forecastData.list[0].pop * 100); + }); + + it("should include precipitation when present in a slot", async () => { + const provider = new OpenWeatherMapProvider({ + lat: 48.14, + lon: 11.58, + apiKey: "test-key", + apiVersion: "2.5", + weatherEndpoint: "/forecast", + type: "hourly" + }); + + const dataPromise = new Promise((resolve) => { + provider.setCallbacks(resolve, vi.fn()); + }); + + server.use( + http.get("https://api.openweathermap.org/data/2.5/forecast", () => { + return HttpResponse.json(forecastData); + }) + ); + + await provider.initialize(); + provider.start(); + + const result = await dataPromise; + + // Entry at index 3 has rain: { "3h": 0.6 } + expect(result[3].rain).toBe(0.6); + expect(result[3].precipitationAmount).toBe(0.6); + // Entry at index 11 has snow: { "3h": 0.5 } + expect(result[11].snow).toBe(0.5); + expect(result[11].precipitationAmount).toBe(0.5); + }); + }); }); diff --git a/tests/unit/modules/default/weather/providers/smhi_spec.js b/tests/unit/modules/default/weather/providers/smhi_spec.js index b7e1211785..de5eb04ea4 100644 --- a/tests/unit/modules/default/weather/providers/smhi_spec.js +++ b/tests/unit/modules/default/weather/providers/smhi_spec.js @@ -3,6 +3,8 @@ * * Tests data parsing for current, forecast, and hourly weather types. * SMHI provides data only for Sweden, uses metric system. + * + * Fixture: weather_smhi.json uses SNOW1gv1 format (replaced PMP3gv2 2026-03-31) */ import { http, HttpResponse } from "msw"; import { setupServer } from "msw/node"; @@ -123,11 +125,10 @@ describe("SMHIProvider", () => { provider.setCallbacks(resolve, vi.fn()); }); - // Use data with rain (pcat=3 at index 2) + // Use entry at index 2 which has ptype=5 (snow) but pmedian=0.0 const rainData = JSON.parse(JSON.stringify(smhiData)); - // Make the rain entry the closest to "now" rainData.timeSeries = [rainData.timeSeries[2]]; - rainData.timeSeries[0].validTime = new Date().toISOString(); + rainData.timeSeries[0].time = new Date().toISOString(); server.use( http.get("https://opendata-download-metfcst.smhi.se/*", () => { @@ -140,7 +141,8 @@ describe("SMHIProvider", () => { const result = await dataPromise; - expect(result.rain).toBe(0.0); // pmedian value with pcat=3 (rain) + // pmedian is 0.0 at this entry, so all precipitation amounts are 0 + expect(result.rain).toBe(0); expect(result.precipitationAmount).toBe(0.0); expect(result.snow).toBe(0); }); diff --git a/tests/unit/modules/default/weather/providers/weatherapi_spec.js b/tests/unit/modules/default/weather/providers/weatherapi_spec.js index 413360f61c..e53713cc65 100644 --- a/tests/unit/modules/default/weather/providers/weatherapi_spec.js +++ b/tests/unit/modules/default/weather/providers/weatherapi_spec.js @@ -240,6 +240,7 @@ describe("WeatherAPIProvider", () => { expect(result[0].minTemperature).toBe(-8); expect(result[0].maxTemperature).toBe(-1); expect(result[0].weatherType).toBe("day-sprinkle"); + expect(result[0].uvIndex).toBe(1); expect(result[0].sunrise).toBeInstanceOf(Date); expect(result[0].sunset).toBeInstanceOf(Date); }); @@ -275,6 +276,7 @@ describe("WeatherAPIProvider", () => { expect(result[0].humidity).toBe(85); expect(result[0].windFromDirection).toBe(210); expect(result[0].weatherType).toBe("night-sprinkle"); + expect(result[0].uvIndex).toBe(0); expect(result[0].precipitationProbability).toBe(50); }); }); diff --git a/tests/unit/modules/default/weather/providers/weatherflow_spec.js b/tests/unit/modules/default/weather/providers/weatherflow_spec.js index 2eb2fdb4a4..f6b28cadcb 100644 --- a/tests/unit/modules/default/weather/providers/weatherflow_spec.js +++ b/tests/unit/modules/default/weather/providers/weatherflow_spec.js @@ -85,6 +85,9 @@ describe("WeatherFlowProvider", () => { expect(result).toBeDefined(); expect(result.temperature).toBe(16); expect(result.humidity).toBe(28); + expect(result.precipitationAmount).toBe(0); + expect(result.precipitationUnits).toBe("mm"); + expect(result.precipitationProbability).toBe(0); expect(result.weatherType).not.toBeNull(); }); diff --git a/tests/unit/modules/default/weather/providers/yr_spec.js b/tests/unit/modules/default/weather/providers/yr_spec.js index 4602a8d840..64598f2503 100644 --- a/tests/unit/modules/default/weather/providers/yr_spec.js +++ b/tests/unit/modules/default/weather/providers/yr_spec.js @@ -15,7 +15,6 @@ import { describe, it, expect, vi, beforeAll, beforeEach, afterAll, afterEach } import yrData from "../../../../../mocks/weather_yr.json" with { type: "json" }; const YR_FORECAST_URL = "https://api.met.no/weatherapi/locationforecast/**"; -const YR_SUNRISE_URL = "https://api.met.no/weatherapi/sunrise/**"; // Fixed time: 30 minutes after the first timeseries entry (2026-02-06T21:00:00Z) // This ensures timeseries[0] is always chosen as the closest past entry.