Compare commits

...

20 Commits

Author SHA1 Message Date
Matt Walsh
0a794eae36 6.5.0 2026-02-16 21:30:23 -06:00
Matt Walsh
8c83736aba remove rss feeds close #184 2026-02-16 21:30:13 -06:00
Matt Walsh
872162080d 6.4.3 2026-02-16 20:40:25 -06:00
Matt Walsh
69d2b0f40b more robust backfilling of current weather properties close #177 2026-02-16 20:40:13 -06:00
Matt Walsh
37193112a7 6.4.2 2026-01-22 22:12:38 -06:00
Matt Walsh
0d9c445919 Fix hourly graph y axis labels close #176 2026-01-22 22:12:32 -06:00
Matt Walsh
6c9fb4cf68 6.4.1 2026-01-17 21:02:33 -06:00
Matt Walsh
59b10ae222 fix icon parsing close #175 2026-01-17 21:02:26 -06:00
Matt Walsh
d18b13821a 6.4.0 2026-01-17 11:42:42 -06:00
Matt Walsh
320d3139c3 add dewpoint to hourly and expand to 36 hours 2026-01-17 11:42:26 -06:00
Matt Walsh
34dedb44c1 6.3.3 2025-12-09 05:03:08 +00:00
Matt Walsh
18633708f9 Slightly denser display of regional cities on map #170 2025-12-09 05:02:58 +00:00
Matt Walsh
9b12255e0a Add image to README 2025-12-08 13:35:13 -06:00
Matt Walsh
f3360772c8 6.3.2 2025-11-30 04:16:09 +00:00
Matt Walsh
767bb8f11d gulp now publishes sourcemaps 2025-11-30 04:16:02 +00:00
Matt Walsh
7586dd7489 update dependencies 2025-11-27 17:32:37 +00:00
Matt Walsh
f37cbd66f7 make kiosk description in readme clearer #165 2025-11-17 17:44:13 -06:00
Matt Walsh
d00262ebbc update dependencies 2025-11-17 00:29:33 +00:00
Matt Walsh
b4646b128a update dependencies 2025-11-10 15:26:35 -06:00
Matt Walsh
9f78761fe8 phone app in readme #163 2025-11-10 13:19:15 -06:00
21 changed files with 1465 additions and 1078 deletions

View File

@@ -2,7 +2,7 @@
"liveSassCompile.settings.formats": [
{
"format": "compressed",
"extensionName": ".css",
"extensionName": ".min.css",
"savePath": "/server/styles",
}
],
@@ -17,4 +17,4 @@
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
}
}

View File

@@ -1,3 +1,5 @@
![Weatherstar 4000+ Current Conditions](https://github.com/netbymatt/ws4kp/blob/main/server/images/social/1200x600.png)
# WeatherStar 4000+
A live version of this project is available at https://weatherstar.netbymatt.com
@@ -200,7 +202,9 @@ https://weatherstar.netbymatt.com/?settings-units-select=metric
```
### Kiosk mode
Kiosk mode can be activated by a checkbox on the page. Note that there is no way out of kiosk mode (except refresh or closing the browser), and the play/pause and other controls will not be available. This is deliberate as a browser's kiosk mode it intended not to be exited or significantly modified. A separate full-screen icon is available in the tool bar to go full-screen on a laptop or mobile browser.
Kiosk mode can be activated by a checkbox on the page. This will start Weatherstar in a fullscreen-like view without the play/volume/etc toolbar and scaled to fill the entire space. This does not activate the browser's fullscreen or kiosk mode. Those can only be activated by user interaction or by launching the browser with specific parameters such as `--start-fullscreen` or `--kiosk`.
When using kiosk mode (via the checkbox), there will be no way to exit the fullscreen-like view of weatherstar. Reloading the page should remove the kiosk checkbox and return you to the normal view. This is deliberate as a browser's kiosk mode it intended not to be exited or significantly modified. A separate full-screen icon is available in the tool bar to go full-screen on a laptop or mobile browser.
It's also possible to enter kiosk mode using a permalink. First generate a [Permalink](#sharing-a-permalink-bookmarking), then to the end of it add `&kiosk=true`. Opening this link will load all of the selected displays included in the Permalink, enter kiosk mode immediately upon loading and start playing the forecast.
@@ -332,9 +336,6 @@ When using Docker:
* **Static deployment**: Mount your `custom.js` file to `/usr/share/nginx/html/scripts/custom.js`
* **Server deployment**: Mount your `custom.js` file to `/app/server/scripts/custom.js`
### RSS feeds and custom scroll
If you would like your Weatherstar to have custom scrolling text in the bottom blue bar, or show headlines from an rss feed turn on the setting for `Enable RSS Feed/Text` and then enter a URL or text in the resulting text box. Then press set.
## Issue reporting and feature requests
Please do not report issues with api.weather.gov being down. It's a new service and not considered fully operational yet. I've also observed that the API can go down on a regional basis (based on NWS office locations). This means that you may have problems getting data for, say, Chicago right now, but Dallas and others are working just fine.
@@ -347,6 +348,14 @@ Note: not all units are converted to metric, if selected. Some text-based produc
This is a known problem with the Ws4kp as it ages. It was a problem with the [actual Weatherstar hardware](https://youtu.be/rcUwlZ4pqh0?feature=shared&t=116) as well.
## Phone App
An Android app is in a closed beta test. It's nothing too special, just a wrapper for displaying the website in a browser.
You can get this functionality without an app on both Andriod and iOS by using the install or add to home screen feature of your browser.
iOS native app? No. I own zero Apple devices and thus have no way to develop, test, compile or verify myself to the app store. That application will have to come from the community.
## Related Projects
Not retro enough? Try the [Weatherstar 3000+](https://github.com/netbymatt/ws3kp)

View File

@@ -729,6 +729,16 @@
"wfo": "LMK"
}
},
{
"city": "Lubbock",
"lat": 33.5836,
"lon": -101.8549,
"point": {
"x": 49,
"y": 34,
"wfo": "LUB"
}
},
{
"city": "Manchester",
"lat": 42.9956,

View File

@@ -364,6 +364,11 @@
"lat": 38.2542,
"lon": -85.7594
},
{
"city": "Lubbock",
"lat": 33.5836,
"lon": -101.8549
},
{
"city": "Manchester",
"lat": 42.9956,

View File

@@ -15,11 +15,16 @@ import { readFile } from 'fs/promises';
import file from 'gulp-file';
import { CloudFrontClient, CreateInvalidationCommand } from '@aws-sdk/client-cloudfront';
import log from 'fancy-log';
import dartSass from 'sass';
import gulpSass from 'gulp-sass';
import sourceMaps from 'gulp-sourcemaps';
import OVERRIDES from '../src/overrides.mjs';
// get cloudfront
import reader from '../src/playlist-reader.mjs';
const sass = gulpSass(dartSass);
const clean = () => deleteAsync(['./dist/**/*', '!./dist/readme.txt']);
const cloudfront = new CloudFrontClient({ region: 'us-east-1' });
@@ -36,6 +41,7 @@ const webpackOptions = {
resolve: {
roots: ['./'],
},
devtool: 'source-map',
optimization: {
minimize: true,
minimizer: [
@@ -80,7 +86,6 @@ const mjsSources = [
'server/scripts/modules/travelforecast.mjs',
'server/scripts/modules/progress.mjs',
'server/scripts/modules/media.mjs',
'server/scripts/modules/custom-rss-feed.mjs',
'server/scripts/index.mjs',
];
@@ -89,10 +94,13 @@ const buildJs = () => src(mjsSources)
.pipe(dest(RESOURCES_PATH));
const cssSources = [
'server/styles/main.css',
'server/styles/scss/**/*.scss',
];
const copyCss = () => src(cssSources)
.pipe(concat('ws.min.css'))
const buildCss = () => src(cssSources)
.pipe(sourceMaps.init())
.pipe(sass({ style: 'compressed' }).on('error', sass.logError))
.pipe(rename({ suffix: '.min' }))
.pipe(sourceMaps.write('./'))
.pipe(dest(RESOURCES_PATH));
const htmlSources = [
@@ -141,7 +149,6 @@ const s3 = s3Upload({
});
const uploadSources = [
'dist/**',
'!dist/**/*.map',
'!dist/images/**/*',
'!dist/fonts/**/*',
];
@@ -209,7 +216,7 @@ const logVersion = async () => {
log(`Version Published: ${version}`);
};
const buildDist = series(clean, parallel(buildJs, compressJsVendor, copyCss, compressHtml, copyOtherFiles, copyDataFiles, copyImageSources, buildPlaylist));
const buildDist = series(clean, parallel(buildJs, compressJsVendor, buildCss, compressHtml, copyOtherFiles, copyDataFiles, copyImageSources, buildPlaylist));
// upload_images could be in parallel with upload, but _images logs a lot and has little changes
// by running upload last the majority of the changes will be at the bottom of the log for easy viewing

1959
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "ws4kp",
"version": "6.3.1",
"version": "6.5.0",
"description": "Welcome to the WeatherStar 4000+ project page!",
"main": "index.mjs",
"type": "module",
@@ -36,7 +36,7 @@
"eslint-plugin-import": "^2.10.0",
"fancy-log": "^2.0.0",
"gulp": "^5.0.0",
"gulp-awspublish": "^8.0.0",
"gulp-awspublish": "^9.0.0",
"gulp-concat": "^2.6.1",
"gulp-ejs": "^5.1.0",
"gulp-file": "^0.4.0",
@@ -44,6 +44,7 @@
"gulp-rename": "^2.0.0",
"gulp-s3-uploader": "^1.0.6",
"gulp-sass": "^6.0.0",
"gulp-sourcemaps": "^3.0.0",
"gulp-terser": "^2.0.0",
"luxon": "^3.0.0",
"metar-taf-parser": "^9.0.0",

View File

@@ -324,7 +324,7 @@ const backfill = (data) => {
// backfill each property
Object.keys(sortedData[0].properties).forEach((key) => {
// qualify the key (must have value)
if (Object.hasOwn(sortedData[0].properties[key], 'value')) {
if (Object.hasOwn(sortedData[0].properties?.[key] ?? {}, 'value')) {
// backfill this property
result[key] = backfillProperty(sortedData, key);
} else {

View File

@@ -1,135 +0,0 @@
import Setting from './utils/setting.mjs';
import { reset as resetScroll, addScreen as addScroll, hazards } from './currentweatherscroll.mjs';
import { json } from './utils/fetch.mjs';
let firstRun = true;
const parser = new DOMParser();
// change of enable handler
const changeEnable = (newValue) => {
let newDisplay;
if (newValue) {
// add the feed to the scroll
parseFeed(customFeed.value);
// show the string box
newDisplay = 'block';
} else {
// set scroll back to original
resetScroll();
// hide the string entry
newDisplay = 'none';
}
const stringEntry = document.getElementById('settings-customFeed-label');
if (stringEntry) {
stringEntry.style.display = newDisplay;
}
};
// parse the feed/text provided
const parseFeed = (textInput) => {
// skip getting the feed on first run
if (firstRun) return;
// test validity
if (textInput === undefined || textInput === '') {
resetScroll();
}
// test for url
if (textInput.match(/https?:\/\//)) {
getFeed(textInput);
return;
}
// add single text scroll after hazards if present
resetScroll();
addScroll(hazards);
addScroll(
() => (
{
type: 'scroll',
text: textInput,
}),
// keep the existing scroll
true,
);
};
// get the rss feed and then swap out the current weather scroll
const getFeed = async (url) => {
// get the text as a string
// it needs to be proxied, use a free service
const rssResponse = await json(`https://api.allorigins.win/get?url=${url}`);
// this returns a data url
// a few sanity checks
if (rssResponse.status.content_type.indexOf('xml') < 0) return;
// determine return type
const isBase64 = rssResponse.status.content_type.substring(0, 8) !== 'text/xml';
// base 64 decode everything after the comma
const rss = isBase64 ? atob(rssResponse.contents.split('base64,')[1]) : rssResponse.contents;
// parse the rss
const doc = parser.parseFromString(rss, 'text/xml');
// get the title
const rssTitle = doc.querySelector('channel title').textContent;
// get each item
const titles = [...doc.querySelectorAll('item title')].map((t) => t.textContent);
// reset the scroll, then add the screens
resetScroll();
// add the hazards scroll first
addScroll(hazards);
titles.forEach((title) => {
// data is provided to the screen handler, so we return a function
addScroll(
() => ({
header: rssTitle,
type: 'scroll',
text: title,
}),
// false parameter does not include the default weather scrolls
false,
);
});
};
// change the feed source and re-load if necessary
const changeFeed = (newValue) => {
// first pass through won't have custom feed enable ready
if (firstRun) return;
if (customFeedEnable.value) {
parseFeed(newValue);
}
};
const customFeed = new Setting('customFeed', {
name: 'Custom RSS Feed',
defaultValue: '',
type: 'string',
changeAction: changeFeed,
placeholder: 'Text or URL',
});
const customFeedEnable = new Setting('customFeedEnable', {
name: 'Enable RSS Feed/Text',
defaultValue: false,
changeAction: changeEnable,
});
// initialize the custom feed inputs on the page
document.addEventListener('DOMContentLoaded', () => {
// add the controls to the page
const settingsSection = document.querySelector('#settings');
settingsSection.append(customFeedEnable.generate(), customFeed.generate());
// clear the first run value
firstRun = false;
// call change enable with the current value to show/hide the url box
// and make the call to get the feed if enabled
changeEnable(customFeedEnable.value);
});

View File

@@ -40,9 +40,10 @@ class HourlyGraph extends WeatherDisplay {
const temperature = data.map((d) => d.temperature);
const probabilityOfPrecipitation = data.map((d) => d.probabilityOfPrecipitation);
const skyCover = data.map((d) => d.skyCover);
const dewpoint = data.map((d) => d.dewpoint);
this.data = {
skyCover, temperature, probabilityOfPrecipitation, temperatureUnit: data[0].temperatureUnit,
skyCover, temperature, probabilityOfPrecipitation, temperatureUnit: data[0].temperatureUnit, dewpoint,
};
this.setStatus(STATUS.loaded);
@@ -63,12 +64,16 @@ class HourlyGraph extends WeatherDisplay {
// calculate time scale
const timeScale = calcScale(0, 5, this.data.temperature.length - 1, availableWidth);
const timeStep = this.data.temperature.length / 4;
const startTime = DateTime.now().startOf('hour');
document.querySelector('.x-axis .l-1').innerHTML = formatTime(startTime);
document.querySelector('.x-axis .l-2').innerHTML = formatTime(startTime.plus({ hour: 6 }));
document.querySelector('.x-axis .l-3').innerHTML = formatTime(startTime.plus({ hour: 12 }));
document.querySelector('.x-axis .l-4').innerHTML = formatTime(startTime.plus({ hour: 18 }));
document.querySelector('.x-axis .l-5').innerHTML = formatTime(startTime.plus({ hour: 24 }));
let prevTime = startTime;
Array(5).fill().forEach((val, idx) => {
// track the previous label so a day of week can be added when it changes
const label = formatTime(startTime.plus({ hour: idx * timeStep }), prevTime);
prevTime = label.ts;
// write to page
document.querySelector(`.x-axis .l-${idx + 1}`).innerHTML = label.formatted;
});
// order is important last line drawn is on top
// clouds
@@ -86,11 +91,22 @@ class HourlyGraph extends WeatherDisplay {
lineWidth: 3,
});
// calculate temperature scale for min and max of dewpoint and temperature
const minScale = Math.min(...this.data.dewpoint, ...this.data.temperature);
const maxScale = Math.max(...this.data.dewpoint, ...this.data.temperature);
const thirdScale = (maxScale - minScale) / 3;
const midScale1 = Math.round(minScale + thirdScale);
const midScale2 = Math.round(minScale + (thirdScale * 2));
const tempScale = calcScale(minScale, availableHeight - 10, maxScale, 10);
// dewpoint
const dewpointPath = createPath(this.data.dewpoint, timeScale, tempScale);
drawPath(dewpointPath, ctx, {
strokeStyle: 'green',
lineWidth: 3,
});
// temperature
const minTemp = Math.min(...this.data.temperature);
const maxTemp = Math.max(...this.data.temperature);
const midTemp = Math.round((minTemp + maxTemp) / 2);
const tempScale = calcScale(minTemp, availableHeight - 10, maxTemp, 10);
const tempPath = createPath(this.data.temperature, timeScale, tempScale);
drawPath(tempPath, ctx, {
strokeStyle: 'red',
@@ -100,15 +116,17 @@ class HourlyGraph extends WeatherDisplay {
// temperature axis labels
// limited to 3 characters, sacraficing degree character
const degree = String.fromCharCode(176);
this.elem.querySelector('.y-axis .l-1').innerHTML = (maxTemp + degree).substring(0, 3);
this.elem.querySelector('.y-axis .l-2').innerHTML = (midTemp + degree).substring(0, 3);
this.elem.querySelector('.y-axis .l-3').innerHTML = (minTemp + degree).substring(0, 3);
this.elem.querySelector('.y-axis .l-1').innerHTML = (maxScale + degree).substring(0, 3);
this.elem.querySelector('.y-axis .l-2').innerHTML = (midScale2 + degree).substring(0, 3);
this.elem.querySelector('.y-axis .l-3').innerHTML = (midScale1 + degree).substring(0, 3);
this.elem.querySelector('.y-axis .l-4').innerHTML = (minScale + degree).substring(0, 3);
// set the image source
this.image.src = canvas.toDataURL();
// change the units in the header
this.elem.querySelector('.temperature').innerHTML = `Temperature ${String.fromCharCode(176)}${this.data.temperatureUnit}`;
this.elem.querySelector('.dewpoint').innerHTML = `Dewpoint ${String.fromCharCode(176)}${this.data.temperatureUnit}`;
super.drawCanvas();
this.finishDraw();
@@ -145,7 +163,18 @@ const drawPath = (path, ctx, options) => {
};
// format as 1p, 12a, etc.
const formatTime = (time) => time.setZone(timeZone()).toFormat('ha').slice(0, -1);
const formatTime = (time, prev) => {
// if the day of the week changes, show the day of the week in the label
let format = 'ha';
if (prev.weekday !== time.weekday) format = 'ccc ha';
const ts = time.setZone(timeZone());
return {
ts,
formatted: ts.toFormat(format).slice(0, -1),
};
};
// register display
registerDisplay(new HourlyGraph(4, 'hourly-graph'));

View File

@@ -75,7 +75,10 @@ class Hourly extends WeatherDisplay {
const startingHour = DateTime.local().setZone(timeZone());
const lines = this.data.map((data, index) => {
// shorten to 24 hours
const shortData = this.data.slice(0, 24);
const lines = shortData.map((data, index) => {
const fillValues = {};
// hour
const hour = startingHour.plus({ hours: index });
@@ -102,7 +105,7 @@ class Hourly extends WeatherDisplay {
const filledRow = this.fillTemplate('hourly-row', fillValues);
// alter the color of the feels like column to reflect wind chill or heat index
if (feelsLike < temperature) {
if (data.apparentTemperature < data.temperature) {
filledRow.querySelector('.like').classList.add('wind-chill');
} else if (feelsLike > temperature) {
filledRow.querySelector('.like').classList.add('heat-index');
@@ -203,6 +206,7 @@ const parseForecast = async (data) => {
const iceAccumulation = expand(data.iceAccumulation.values); // ice icon
const probabilityOfPrecipitation = expand(data.probabilityOfPrecipitation.values); // rain icon
const snowfallAmount = expand(data.snowfallAmount.values); // snow icon
const dewpoint = expand(data.dewpoint.values);
const icons = await determineIcon(skyCover, weather, iceAccumulation, probabilityOfPrecipitation, snowfallAmount, windSpeed);
@@ -216,6 +220,7 @@ const parseForecast = async (data) => {
probabilityOfPrecipitation: probabilityOfPrecipitation[idx],
skyCover: skyCover[idx],
icon: icons[idx],
dewpoint: temperatureConverter(dewpoint[idx]),
}));
};
@@ -233,7 +238,7 @@ const determineIcon = async (skyCover, weather, iceAccumulation, probabilityOfPr
};
// expand a set of values with durations to an hour-by-hour array
const expand = (data, maxHours = 24) => {
const expand = (data, maxHours = 36) => {
const startOfHour = DateTime.utc().startOf('hour').toMillis();
const result = []; // resulting expanded values
data.forEach((item) => {

View File

@@ -13,7 +13,7 @@ const largeIcon = (link, _isNightTime) => {
} catch (error) {
console.warn(`largeIcon: ${error.message}`);
// Return a fallback icon to prevent downstream errors
return addPath(`No-Data.gif?${conditionIcon}${isNightTime ? '-n' : ''}`);
return addPath(`No-Data-Large.gif?${conditionIcon}${isNightTime ? '-n' : ''}`);
}
// find the icon
@@ -102,6 +102,8 @@ const largeIcon = (link, _isNightTime) => {
case 'snow_fzra':
case 'snow_fzra-n':
case 'winter_mix':
case 'winter_mix-n':
return addPath('Freezing-Rain-Snow.gif');
case 'fzra':
@@ -141,6 +143,8 @@ const largeIcon = (link, _isNightTime) => {
return addPath('Thunderstorm.gif');
case 'wind_skc':
case 'wind_':
case 'wind_-n':
return addPath('Windy.gif');
case 'wind_skc-n':
@@ -169,7 +173,7 @@ const largeIcon = (link, _isNightTime) => {
default: {
console.warn(`Unknown weather condition '${conditionIcon}' from ${link}; using fallback icon`);
// Return a reasonable fallback instead of false to prevent downstream errors
return addPath(`No-Data.gif?${conditionIcon}${isNightTime ? '-n' : ''}`);
return addPath(`No-Data-Large.gif?${conditionIcon}${isNightTime ? '-n' : ''}`);
}
}
};

View File

@@ -133,6 +133,7 @@ const smallIcon = (link, _isNightTime) => {
case 'wind_few':
case 'wind_few-n':
case 'wind_':
return addPath('Wind.gif');
case 'wind_sct':
@@ -170,7 +171,7 @@ const smallIcon = (link, _isNightTime) => {
case 'blizzard':
case 'blizzard-n':
return addPath('Blowing Snow.gif');
return addPath('Blowing-Snow.gif');
default:
console.warn(`Unknown weather condition '${conditionIcon}' from ${link}; using fallback icon`);

View File

@@ -51,7 +51,7 @@ class RegionalForecast extends WeatherDisplay {
const minMaxLatLon = utils.getMinMaxLatitudeLongitude(sourceXY.x, sourceXY.y, mapOffsetXY.x, mapOffsetXY.y, this.weatherParameters.state);
// get a target distance
let targetDistance = 2.5;
let targetDistance = 2.4;
if (this.weatherParameters.state === 'HI') targetDistance = 1;
// make station info into an array

View File

@@ -11,7 +11,7 @@
right: 60px;
width: 360px;
font-family: 'Star4000 Small';
font-size: 32px;
font-size: 28px;
@include u.text-shadow();
text-align: right;
@@ -23,6 +23,10 @@
color: red;
}
.dewpoint {
color: green;
}
.cloud {
color: lightgrey;
}
@@ -52,32 +56,33 @@
.x-axis {
bottom: 0px;
left: 0px;
width: 640px;
left: 54px;
width: 532px;
height: 20px;
.label {
text-align: center;
width: 50px;
transform: translateX(-50%);
white-space: nowrap;
&.l-1 {
left: 25px;
left: 0px;
}
&.l-2 {
left: 158px;
left: calc(532px / 4 * 1);
}
&.l-3 {
left: 291px;
left: calc(532px / 4 * 2);
}
&.l-4 {
left: 424px;
left: calc(532px / 4 * 3);
}
&.l-5 {
left: 557px;
left: calc(532px / 4 * 4);
}
}
@@ -110,10 +115,14 @@
}
&.l-2 {
top: 140px;
top: calc(280px / 3);
}
&.l-3 {
bottom: calc(280px / 3 - 11px);
}
&.l-4 {
bottom: 0px;
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

268
tests/package-lock.json generated
View File

@@ -28,26 +28,26 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@puppeteer/browsers": {
"version": "2.10.5",
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.5.tgz",
"integrity": "sha512-eifa0o+i8dERnngJwKrfp3dEq7ia5XFyoqB17S4gK8GhsQE4/P8nxOfQSE0zQHxzzLo/cmF+7+ywEQ7wK7Fb+w==",
"version": "2.10.13",
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.13.tgz",
"integrity": "sha512-a9Ruw3j3qlnB5a/zHRTkruppynxqaeE4H9WNj5eYGRWqw0ZauZ23f4W2ARf3hghF5doozyD+CRtt7XSYuYRI/Q==",
"license": "Apache-2.0",
"dependencies": {
"debug": "^4.4.1",
"debug": "^4.4.3",
"extract-zip": "^2.0.1",
"progress": "^2.0.3",
"proxy-agent": "^6.5.0",
"semver": "^7.7.2",
"tar-fs": "^3.0.8",
"semver": "^7.7.3",
"tar-fs": "^3.1.1",
"yargs": "^17.7.2"
},
"bin": {
@@ -64,13 +64,13 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.15.29",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz",
"integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==",
"version": "24.10.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
"license": "MIT",
"optional": true,
"dependencies": {
"undici-types": "~6.21.0"
"undici-types": "~7.16.0"
}
},
"node_modules/@types/yauzl": {
@@ -84,9 +84,9 @@
}
},
"node_modules/agent-base": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"license": "MIT",
"engines": {
"node": ">= 14"
@@ -135,28 +135,45 @@
}
},
"node_modules/b4a": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
"integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==",
"license": "Apache-2.0"
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz",
"integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==",
"license": "Apache-2.0",
"peerDependencies": {
"react-native-b4a": "*"
},
"peerDependenciesMeta": {
"react-native-b4a": {
"optional": true
}
}
},
"node_modules/bare-events": {
"version": "2.5.4",
"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz",
"integrity": "sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA==",
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz",
"integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==",
"license": "Apache-2.0",
"optional": true
"peerDependencies": {
"bare-abort-controller": "*"
},
"peerDependenciesMeta": {
"bare-abort-controller": {
"optional": true
}
}
},
"node_modules/bare-fs": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.1.5.tgz",
"integrity": "sha512-1zccWBMypln0jEE05LzZt+V/8y8AQsQQqxtklqaIyg5nu6OAYFhZxPXinJTSG+kU5qyNmeLgcn9AW7eHiCHVLA==",
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.1.tgz",
"integrity": "sha512-zGUCsm3yv/ePt2PHNbVxjjn0nNB1MkIaR4wOCxJ2ig5pCf5cCVAYJXVhQg/3OhhJV6DB1ts7Hv0oUaElc2TPQg==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"bare-events": "^2.5.4",
"bare-path": "^3.0.0",
"bare-stream": "^2.6.4"
"bare-stream": "^2.6.4",
"bare-url": "^2.2.2",
"fast-fifo": "^1.3.2"
},
"engines": {
"bare": ">=1.16.0"
@@ -171,9 +188,9 @@
}
},
"node_modules/bare-os": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.1.tgz",
"integrity": "sha512-uaIjxokhFidJP+bmmvKSgiMzj2sV5GPHaZVAIktcxcpCyBFFWO+YlikVAdhmUo2vYFvFhOXIAlldqV29L8126g==",
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz",
"integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==",
"license": "Apache-2.0",
"optional": true,
"engines": {
@@ -191,9 +208,9 @@
}
},
"node_modules/bare-stream": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz",
"integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==",
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz",
"integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
@@ -212,6 +229,16 @@
}
}
},
"node_modules/bare-url": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz",
"integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==",
"license": "Apache-2.0",
"optional": true,
"dependencies": {
"bare-path": "^3.0.0"
}
},
"node_modules/basic-ftp": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
@@ -240,9 +267,9 @@
}
},
"node_modules/chalk": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
"integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
"license": "MIT",
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
@@ -252,9 +279,9 @@
}
},
"node_modules/chromium-bidi": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-5.1.0.tgz",
"integrity": "sha512-9MSRhWRVoRPDG0TgzkHrshFSJJNZzfY5UFqUMuksg7zL1yoZIZ3jLB0YAgHclbiAxPI86pBnwDX1tbzoiV8aFw==",
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-11.0.0.tgz",
"integrity": "sha512-cM3DI+OOb89T3wO8cpPSro80Q9eKYJ7hGVXoGS3GkDPxnYSqiv+6xwpIf6XERyJ9Tdsl09hmNmY94BkgZdVekw==",
"license": "Apache-2.0",
"dependencies": {
"mitt": "^3.0.1",
@@ -332,9 +359,9 @@
}
},
"node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -363,10 +390,11 @@
}
},
"node_modules/devtools-protocol": {
"version": "0.0.1452169",
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1452169.tgz",
"integrity": "sha512-FOFDVMGrAUNp0dDKsAU1TorWJUx2JOU1k9xdgBKKJF3IBh/Uhl2yswG5r3TEAOrCiGY2QRp1e6LVDQrCsTKO4g==",
"license": "BSD-3-Clause"
"version": "0.0.1521046",
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1521046.tgz",
"integrity": "sha512-vhE6eymDQSKWUXwwA37NtTTVEzjtGVfDr3pRbsWEQ5onH/Snp2c+2xZHWJJawG/0hCCJLRGt4xVtEVUVILol4w==",
"license": "BSD-3-Clause",
"peer": true
},
"node_modules/emoji-regex": {
"version": "8.0.0",
@@ -375,9 +403,9 @@
"license": "MIT"
},
"node_modules/end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"license": "MIT",
"dependencies": {
"once": "^1.4.0"
@@ -393,9 +421,9 @@
}
},
"node_modules/error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
"integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
"license": "MIT",
"dependencies": {
"is-arrayish": "^0.2.1"
@@ -462,6 +490,15 @@
"node": ">=0.10.0"
}
},
"node_modules/events-universal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
"integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
"license": "Apache-2.0",
"dependencies": {
"bare-events": "^2.7.0"
}
},
"node_modules/extract-zip": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
@@ -522,9 +559,9 @@
}
},
"node_modules/get-uri": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.4.tgz",
"integrity": "sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==",
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
"integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==",
"license": "MIT",
"dependencies": {
"basic-ftp": "^5.0.2",
@@ -578,14 +615,10 @@
}
},
"node_modules/ip-address": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
"integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"license": "MIT",
"dependencies": {
"jsbn": "1.1.0",
"sprintf-js": "^1.1.3"
},
"engines": {
"node": ">= 12"
}
@@ -612,9 +645,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -623,12 +656,6 @@
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/jsbn": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
"integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
"license": "MIT"
},
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@@ -789,9 +816,9 @@
"license": "MIT"
},
"node_modules/pump": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
"integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
@@ -799,17 +826,17 @@
}
},
"node_modules/puppeteer": {
"version": "24.10.0",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.10.0.tgz",
"integrity": "sha512-Oua9VkGpj0S2psYu5e6mCer6W9AU9POEQh22wRgSXnLXASGH+MwLUVWgLCLeP9QPHHcJ7tySUlg4Sa9OJmaLpw==",
"version": "24.31.0",
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.31.0.tgz",
"integrity": "sha512-q8y5yLxLD8xdZdzNWqdOL43NbfvUOp60SYhaLZQwHC9CdKldxQKXOyJAciOr7oUJfyAH/KgB2wKvqT2sFKoVXA==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@puppeteer/browsers": "2.10.5",
"chromium-bidi": "5.1.0",
"@puppeteer/browsers": "2.10.13",
"chromium-bidi": "11.0.0",
"cosmiconfig": "^9.0.0",
"devtools-protocol": "0.0.1452169",
"puppeteer-core": "24.10.0",
"devtools-protocol": "0.0.1521046",
"puppeteer-core": "24.31.0",
"typed-query-selector": "^2.12.0"
},
"bin": {
@@ -820,17 +847,18 @@
}
},
"node_modules/puppeteer-core": {
"version": "24.10.0",
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.10.0.tgz",
"integrity": "sha512-xX0QJRc8t19iAwRDsAOR38Q/Zx/W6WVzJCEhKCAwp2XMsaWqfNtQ+rBfQW9PlF+Op24d7c8Zlgq9YNmbnA7hdQ==",
"version": "24.31.0",
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.31.0.tgz",
"integrity": "sha512-pnAohhSZipWQoFpXuGV7xCZfaGhqcBR9C4pVrU0QSrcMi7tQMH9J9lDBqBvyMAHQqe8HCARuREqFuVKRQOgTvg==",
"license": "Apache-2.0",
"dependencies": {
"@puppeteer/browsers": "2.10.5",
"chromium-bidi": "5.1.0",
"debug": "^4.4.1",
"devtools-protocol": "0.0.1452169",
"@puppeteer/browsers": "2.10.13",
"chromium-bidi": "11.0.0",
"debug": "^4.4.3",
"devtools-protocol": "0.0.1521046",
"typed-query-selector": "^2.12.0",
"ws": "^8.18.2"
"webdriver-bidi-protocol": "0.3.9",
"ws": "^8.18.3"
},
"engines": {
"node": ">=18"
@@ -855,9 +883,9 @@
}
},
"node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"
@@ -877,12 +905,12 @@
}
},
"node_modules/socks": {
"version": "2.8.4",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.4.tgz",
"integrity": "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==",
"version": "2.8.7",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
"integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
"license": "MIT",
"dependencies": {
"ip-address": "^9.0.5",
"ip-address": "^10.0.1",
"smart-buffer": "^4.2.0"
},
"engines": {
@@ -914,23 +942,15 @@
"node": ">=0.10.0"
}
},
"node_modules/sprintf-js": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
"license": "BSD-3-Clause"
},
"node_modules/streamx": {
"version": "2.22.0",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.0.tgz",
"integrity": "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw==",
"version": "2.23.0",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz",
"integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==",
"license": "MIT",
"dependencies": {
"events-universal": "^1.0.0",
"fast-fifo": "^1.3.2",
"text-decoder": "^1.1.0"
},
"optionalDependencies": {
"bare-events": "^2.2.0"
}
},
"node_modules/string-width": {
@@ -960,9 +980,9 @@
}
},
"node_modules/tar-fs": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.9.tgz",
"integrity": "sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA==",
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz",
"integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==",
"license": "MIT",
"dependencies": {
"pump": "^3.0.0",
@@ -1006,12 +1026,18 @@
"license": "MIT"
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"license": "MIT",
"optional": true
},
"node_modules/webdriver-bidi-protocol": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.9.tgz",
"integrity": "sha512-uIYvlRQ0PwtZR1EzHlTMol1G0lAlmOe6wPykF9a77AK3bkpvZHzIVxRE2ThOx5vjy2zISe0zhwf5rzuUfbo1PQ==",
"license": "Apache-2.0"
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@@ -1036,9 +1062,9 @@
"license": "ISC"
},
"node_modules/ws": {
"version": "8.18.2",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
"integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
"version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
@@ -1103,9 +1129,9 @@
}
},
"node_modules/zod": {
"version": "3.25.49",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.49.tgz",
"integrity": "sha512-JMMPMy9ZBk3XFEdbM3iL1brx4NUSejd6xr3ELrrGEfGb355gjhiAWtG3K5o+AViV/3ZfkIrCzXsZn6SbLwTR8Q==",
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"

View File

@@ -36,7 +36,7 @@
const OVERRIDES = <%- JSON.stringify(OVERRIDES ?? {}) %>;
</script>
<% } else { %>
<link rel="stylesheet" type="text/css" href="styles/main.css" />
<link rel="stylesheet" type="text/css" href="styles/ws.min.css" />
<!--<script type="text/javascript">const OVERRIDES={};</script>-->
<script type="text/javascript">
OVERRIDES = <%- JSON.stringify(OVERRIDES ?? {}) %>;
@@ -62,7 +62,6 @@
<script type="module" src="scripts/modules/radar.mjs"></script>
<script type="module" src="scripts/modules/settings.mjs"></script>
<script type="module" src="scripts/modules/media.mjs"></script>
<script type="module" src="scripts/modules/custom-rss-feed.mjs"></script>
<script type="module" src="scripts/index.mjs"></script>
<% } %>

View File

@@ -2,6 +2,7 @@
<div class="main has-scroll hourly-graph">
<div class="top-right template ">
<div class="temperature">Temperature</div>
<div class="dewpoint">Dewpoint</div>
<div class="cloud">Cloud %</div>
<div class="rain">Precip %</div>
</div>
@@ -9,6 +10,7 @@
<div class="label l-1">75</div>
<div class="label l-2">65</div>
<div class="label l-3">55</div>
<div class="label l-4">45</div>
</div>
<div class="chart">
<img id="chart-area"></img>