diff --git a/Dockerfile b/Dockerfile index 3355ee81d..2f463d02c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM sitespeedio/webbrowsers:chrome-142.0-firefox-144.0-edge-141.0 +FROM sitespeedio/webbrowsers:chrome-142.0-firefox-144.0-edge-142.0-b ARG TARGETPLATFORM=linux/amd64 diff --git a/Dockerfile-slim b/Dockerfile-slim index 02082eaed..c056e4de1 100644 --- a/Dockerfile-slim +++ b/Dockerfile-slim @@ -1,4 +1,4 @@ -FROM node:22.13.0-bookworm-slim +FROM node:24.11.0-bookworm-slim ARG TARGETPLATFORM=linux/amd64 @@ -15,7 +15,7 @@ RUN echo "deb http://deb.debian.org/debian/ unstable main contrib non-free" >> / apt-get install -y --no-install-recommends firefox tcpdump iproute2 ca-certificates sudo --no-install-recommends --no-install-suggests && \ # Cleanup apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ - && rm -rf /var/lib/apt/lists/* /tmp/* + && rm -rf /var/lib/apt/lists/* /tmp/* # Install sitespeed.io RUN mkdir -p /usr/src/app diff --git a/docker/scripts/start-slim.sh b/docker/scripts/start-slim.sh index 3ffbc24b3..aaa5e912a 100755 --- a/docker/scripts/start-slim.sh +++ b/docker/scripts/start-slim.sh @@ -6,26 +6,45 @@ SITESPEEDIO=/usr/src/app/bin/sitespeed.js MAX_OLD_SPACE_SIZE="${MAX_OLD_SPACE_SIZE:-2048}" -WORKDIR_UID=$(stat -c "%u" .) -WORKDIR_GID=$(stat -c "%g" .) +# write files owned by the user who runs the container +# if your volume is mounted at /sitespeed.io, use it as CWD +[[ -d /sitespeed.io && "$PWD" = "/" ]] && cd /sitespeed.io -# Create user with the same UID and GID as the owner of the working directory, which will be used -# to execute node. This is partly for security and partly so output files won't be owned by root. -groupadd --non-unique --gid $WORKDIR_GID sitespeedio -useradd --non-unique --uid $WORKDIR_UID --gid $WORKDIR_GID --home-dir /tmp sitespeedio +uid=$(stat -c '%u' . 2>/dev/null || echo 0) +gid=$(stat -c '%g' . 2>/dev/null || echo 0) + +if [[ "$uid" -ne 0 && "$gid" -ne 0 ]]; then + if ! getent group "$gid" >/dev/null 2>&1; then + groupadd -g "$gid" sitespeedio-host 2>/dev/null || true + fi + if ! getent passwd "$uid" >/dev/null 2>&1; then + useradd -u "$uid" -g "$gid" -M -d /tmp -s /bin/bash sitespeedio-host 2>/dev/null || true + fi +fi + +run_as_host() { + if [[ "$uid" -ne 0 && "$gid" -ne 0 ]]; then + HOME=/tmp chroot --skip-chdir --userspec="+${uid}:+${gid}" / "$@" + else + HOME=/tmp "$@" + fi +} + +function execNode(){ + run_as_host node "$@" +} # Need to explictly override the HOME directory to prevent dconf errors like: # (firefox:2003): dconf-CRITICAL **: 00:31:23.379: unable to create directory '/root/.cache/dconf': Permission denied. dconf will not work properly. export HOME=/tmp - # Inspired by docker-selenium way of shutting down function shutdown { kill -s SIGTERM ${PID} wait $PID } -chroot --skip-chdir --userspec='sitespeedio:sitespeedio' / node --max-old-space-size=$MAX_OLD_SPACE_SIZE $SITESPEEDIO "$@" & +execNode --max-old-space-size=$MAX_OLD_SPACE_SIZE $SITESPEEDIO "$@" & PID=$! diff --git a/docker/scripts/start.sh b/docker/scripts/start.sh index 222fbd06b..e759d060b 100755 --- a/docker/scripts/start.sh +++ b/docker/scripts/start.sh @@ -1,5 +1,30 @@ #!/bin/bash -# +set -e + +# write files owned by the user who runs the container +# if your volume is mounted at /sitespeed.io, use it as CWD +[[ -d /sitespeed.io && "$PWD" = "/" ]] && cd /sitespeed.io + +uid=$(stat -c '%u' . 2>/dev/null || echo 0) +gid=$(stat -c '%g' . 2>/dev/null || echo 0) + +if [[ "$uid" -ne 0 && "$gid" -ne 0 ]]; then + if ! getent group "$gid" >/dev/null 2>&1; then + groupadd -g "$gid" sitespeedio-host 2>/dev/null || true + fi + if ! getent passwd "$uid" >/dev/null 2>&1; then + useradd -u "$uid" -g "$gid" -M -d /tmp -s /bin/bash sitespeedio-host 2>/dev/null || true + fi +fi + +run_as_host() { + if [[ "$uid" -ne 0 && "$gid" -ne 0 ]]; then + HOME=/tmp chroot --skip-chdir --userspec="+${uid}:+${gid}" / "$@" + else + HOME=/tmp "$@" + fi +} + # All browsers do not exist in all architectures. if [[ `which google-chrome` ]]; then google-chrome --version @@ -40,20 +65,12 @@ else WPR_HTTPS_PORT=${WPR_HTTPS_PORT:-443} fi -WORKDIR_UID=$(stat -c "%u" .) -WORKDIR_GID=$(stat -c "%g" .) - -# Create user with the same UID and GID as the owner of the working directory, which will be used -# to execute node. This is partly for security and partly so output files won't be owned by root. -groupadd --non-unique --gid $WORKDIR_GID sitespeedio -useradd --non-unique --uid $WORKDIR_UID --gid $WORKDIR_GID --home-dir /tmp sitespeedio - # Need to explictly override the HOME directory to prevent dconf errors like: # (firefox:2003): dconf-CRITICAL **: 00:31:23.379: unable to create directory '/root/.cache/dconf': Permission denied. dconf will not work properly. export HOME=/tmp function execNode(){ - chroot --skip-chdir --userspec='sitespeedio:sitespeedio' / node "$@" + run_as_host node "$@" } # If we run Chrome on Android, we need to start the ADB server @@ -111,7 +128,7 @@ function runWebPageReplay() { execNode --max-old-space-size=$MAX_OLD_SPACE_SIZE $SITESPEEDIO --browsertime.firefox.preference security.OCSP.enabled:0 --browsertime.firefox.acceptInsecureCerts true --browsertime.firefox.preference network.dns.forceResolve:127.0.0.1 --browsertime.chrome.webPageReplayHostResolver --browsertime.chrome.webPageReplayHTTPPort $WPR_HTTP_PORT --browsertime.chrome.webPageReplayHTTPSPort $WPR_HTTPS_PORT --browsertime.connectivity.engine throttle --browsertime.connectivity.throttle.localhost --replay --browsertime.connectivity.profile custom --browsertime.connectivity.rtt $LATENCY "$@" & PID=$! - + trap shutdown SIGTERM SIGINT wait $PID EXIT_STATUS=$? diff --git a/docs/_includes/version/browsertime.txt b/docs/_includes/version/browsertime.txt index c0c550c07..3fd5462bd 100644 --- a/docs/_includes/version/browsertime.txt +++ b/docs/_includes/version/browsertime.txt @@ -1 +1 @@ -26.0.0 \ No newline at end of file +26.0.1 \ No newline at end of file diff --git a/lib/cli/cli.js b/lib/cli/cli.js index 18fe557c2..4d4bc545c 100644 --- a/lib/cli/cli.js +++ b/lib/cli/cli.js @@ -2195,7 +2195,9 @@ export async function parseCommandLine() { ); } - let urlsMetaData = getAliases(argv._, argv.urlAlias, argv.groupAlias); + let urlsMetaData = argv.multi + ? {} + : getAliases(argv._, argv.urlAlias, argv.groupAlias); // Copy the alias so it is also used by Browsertime if (argv.urlAlias) { // Browsertime has it own way of handling alias diff --git a/lib/core/resultsStorage/index.js b/lib/core/resultsStorage/index.js index fb0e91779..81fc184f2 100644 --- a/lib/core/resultsStorage/index.js +++ b/lib/core/resultsStorage/index.js @@ -1,15 +1,15 @@ -import { parse, format } from 'node:url'; import path from 'node:path'; import { resultUrls } from './resultUrls.js'; import { storageManager } from './storageManager.js'; function getDomainOrFileName(input) { - let domainOrFile = input; - domainOrFile = domainOrFile.startsWith('http') - ? parse(domainOrFile).hostname - : path.basename(domainOrFile).replaceAll('.', '_'); - return domainOrFile; + if (input.startsWith('http')) { + const url = new URL(input); + return url.hostname; + } + + return path.basename(input).replaceAll('.', '_'); } export function resultsStorage(input, timestamp, options) { @@ -34,10 +34,19 @@ export function resultsStorage(input, timestamp, options) { storagePathPrefix = path.join(...resultsSubFolders); if (resultBaseURL) { - const url = parse(resultBaseURL); - resultsSubFolders.unshift(url.pathname.slice(1)); - url.pathname = resultsSubFolders.join('/'); - resultUrl = format(url); + const url = new URL(resultBaseURL); + + const basePath = url.pathname.slice(1); // drop leading '/' + if (basePath) { + resultsSubFolders.unshift(basePath); + } + + const newPath = resultsSubFolders.join('/'); + + // Ensure leading slash for pathname + url.pathname = newPath.startsWith('/') ? newPath : `/${newPath}`; + + resultUrl = url.toString(); } return { diff --git a/lib/core/resultsStorage/pathToFolder.js b/lib/core/resultsStorage/pathToFolder.js index 2d9a077dc..4b93b782a 100644 --- a/lib/core/resultsStorage/pathToFolder.js +++ b/lib/core/resultsStorage/pathToFolder.js @@ -1,56 +1,75 @@ -import { parse } from 'node:url'; import { createHash } from 'node:crypto'; - +import path from 'node:path'; import { getLogger } from '@sitespeed.io/log'; - import { isEmpty } from '../../support/util.js'; + const log = getLogger('sitespeedio.file'); -function toSafeKey(key) { - // U+2013 : EN DASH – as used on https://en.wikipedia.org/wiki/2019–20_coronavirus_pandemic - return key.replaceAll(/[ %&()+,./:?|~–]|%7C/g, '-'); +function isHttpLikeUrl(s) { + if (typeof s !== 'string' || s.length === 0) return false; + if (s.startsWith('//')) return true; + return /^https?:\/\//iu.test(s); } -export function pathToFolder(url, options, alias) { - const useHash = options.useHash; - const parsedUrl = parse(decodeURIComponent(url)); +function toSafeKey(key) { + return key.replaceAll(/[ %&()+,./:?|~–]|%7C/gu, '-'); +} - const pathSegments = []; - const urlSegments = []; +function md5Hex8(s) { + return createHash('md5').update(s).digest('hex').slice(0, 8); +} - // If a measurements fail and we use a script file and no URL - // has been tested, we don't have a hostname - if (parsedUrl.hostname) { - pathSegments.push('pages', parsedUrl.hostname.split('.').join('_')); +function normalizeFsPath(input) { + let n = path.normalize(input); + if (n.startsWith(`.${path.sep}`)) n = n.slice(2); + return n; +} + +export function pathToFolder(input, options) { + if (options.useSameDir) return ''; + + let hostname = ''; + let pathname = ''; + let search = ''; + let hash = ''; + + const isUrl = isHttpLikeUrl(input); + + if (isUrl) { + const raw = input.startsWith('//') ? `http:${input}` : input; + const u = new URL(raw); + hostname = u.hostname; + pathname = u.pathname; // '/'-separated + search = u.search; // includes '?' + hash = u.hash; // includes '#' + } else { + hostname = 'file'; + const fsNormalized = normalizeFsPath(input); + pathname = `${path.sep}${fsNormalized}`; } - if (options.urlMetaData && options.urlMetaData[url]) { - pathSegments.push(options.urlMetaData[url]); - } else if (alias) { - pathSegments.push(alias); + const pathSegments = ['pages', hostname.split('.').join('_')]; + const urlSegments = []; + + if (options.urlMetaData && options.urlMetaData[input]) { + pathSegments.push(options.urlMetaData[input]); } else { - if (!isEmpty(parsedUrl.pathname)) { - urlSegments.push(...parsedUrl.pathname.split('/').filter(Boolean)); + const parts = isUrl + ? pathname.split('/').filter(Boolean) + : pathname.split(/[\\/]/u).filter(Boolean); + if (!isEmpty(parts)) urlSegments.push(...parts); + + if (isUrl) { + if (options.useHash && !isEmpty(hash)) + urlSegments.push(`hash-${md5Hex8(hash)}`); + if (!isEmpty(search)) urlSegments.push(`query-${md5Hex8(search)}`); } - if (useHash && !isEmpty(parsedUrl.hash)) { - const md5 = createHash('md5'), - hash = md5.update(parsedUrl.hash).digest('hex').slice(0, 8); - urlSegments.push('hash-' + hash); - } - - if (!isEmpty(parsedUrl.search)) { - const md5 = createHash('md5'), - hash = md5.update(parsedUrl.search).digest('hex').slice(0, 8); - urlSegments.push('query-' + hash); - } - - // This is used from sitespeed.io to match URLs on Graphite if (options.storeURLsAsFlatPageOnDisk) { - const folder = toSafeKey(urlSegments.join('_').concat('_')); + const folder = toSafeKey(`${urlSegments.join('_')}_`); if (folder.length > 255) { log.info( - `The URL ${url} hit the 255 character limit used when stored on disk, you may want to give your URL an alias to make sure it will not collide with other URLs.` + `The URL ${input} hit the 255 character limit used when stored on disk, you may want to give your URL an alias to make sure it will not collide with other URLs.` ); pathSegments.push(folder.slice(0, 254)); } else { @@ -63,11 +82,9 @@ export function pathToFolder(url, options, alias) { // pathSegments.push('data'); - for (const [index, segment] of pathSegments.entries()) { - if (segment) { - pathSegments[index] = segment.replaceAll(/[^\w.\u0621-\u064A-]/gi, '-'); - } + for (const [i, seg] of pathSegments.entries()) { + if (seg) pathSegments[i] = seg.replaceAll(/[^\w.\u0621-\u064A-]/giu, '-'); } - return pathSegments.join('/').concat('/'); + return `${path.join(...pathSegments)}${path.sep}`; } diff --git a/lib/core/url-source.js b/lib/core/url-source.js index 52b6bbe82..22412fb5e 100644 --- a/lib/core/url-source.js +++ b/lib/core/url-source.js @@ -1,4 +1,3 @@ -import { parse } from 'node:url'; import { messageMaker } from '../support/messageMaker.js'; const make = messageMaker('url-reader').make; @@ -15,7 +14,7 @@ export function findUrls(queue, options) { options.urlsMetaData[url] && options.urlsMetaData[url].groupAlias ? options.urlsMetaData[url].groupAlias - : parse(url).hostname + : new URL(url).hostname } ) ); diff --git a/lib/plugins/browsertime/index.js b/lib/plugins/browsertime/index.js index 875cb8602..71cc92e67 100644 --- a/lib/plugins/browsertime/index.js +++ b/lib/plugins/browsertime/index.js @@ -1,5 +1,3 @@ -import { parse } from 'node:url'; - // eslint-disable-next-line unicorn/no-named-default import { default as _merge } from 'lodash.merge'; @@ -107,7 +105,7 @@ export default class BrowsertimePlugin extends SitespeedioPlugin { if (this.options.urlMetaData) { for (let url of Object.keys(this.options.urlMetaData)) { const alias = this.options.urlMetaData[url]; - const group = parse(url).hostname; + const group = new URL(url).hostname; this.allAlias[alias] = url; super.sendMessage('browsertime.alias', alias, { url, @@ -190,7 +188,7 @@ export default class BrowsertimePlugin extends SitespeedioPlugin { if (alias) { if (this.scriptOrMultiple) { url = element.info.url; - group = parse(url).hostname; + group = new URL(url).hostname; } this.allAlias[url] = alias; super.sendMessage('browsertime.alias', alias, { @@ -232,7 +230,7 @@ export default class BrowsertimePlugin extends SitespeedioPlugin { if (this.scriptOrMultiple) { url = result[resultIndex].info?.url; if (url) { - group = parse(url).hostname; + group = new URL(url).hostname; } } let runIndex = 0; diff --git a/lib/plugins/domains/aggregator.js b/lib/plugins/domains/aggregator.js index 8875fd54e..4acd481a1 100644 --- a/lib/plugins/domains/aggregator.js +++ b/lib/plugins/domains/aggregator.js @@ -1,5 +1,3 @@ -import { parse } from 'node:url'; - import { Stats } from 'fast-stats'; import { getLogger } from '@sitespeed.io/log'; @@ -18,7 +16,7 @@ const timingNames = [ ]; function parseDomainName(url) { - return parse(url).hostname; + return new URL(url).hostname; } function getDomain(domainName) { diff --git a/lib/plugins/pagexray/index.js b/lib/plugins/pagexray/index.js index 05e18edf5..70771a5c7 100644 --- a/lib/plugins/pagexray/index.js +++ b/lib/plugins/pagexray/index.js @@ -1,4 +1,3 @@ -import { parse } from 'node:url'; import { getLogger } from '@sitespeed.io/log'; import coach from 'coach-core'; import { SitespeedioPlugin } from '@sitespeed.io/plugin'; @@ -102,7 +101,7 @@ export default class PageXrayPlugin extends SitespeedioPlugin { const sentURL = {}; for (let summary of pageSummary) { // The group can be different so take it per url - const myGroup = parse(summary.url).hostname; + const myGroup = new URL(summary.url).hostname; if (sentURL[summary.url]) { sentURL[summary.url] += 1; } else { diff --git a/lib/plugins/thirdparty/index.js b/lib/plugins/thirdparty/index.js index 77c09038b..1dcc39a81 100644 --- a/lib/plugins/thirdparty/index.js +++ b/lib/plugins/thirdparty/index.js @@ -1,5 +1,3 @@ -import { parse } from 'node:url'; - import coach from 'coach-core'; import { SitespeedioPlugin } from '@sitespeed.io/plugin'; @@ -133,7 +131,7 @@ export default class ThirdPartyPlugin extends SitespeedioPlugin { // fallback to domain if (!entity) { const hostname = ent.url.startsWith('http') - ? parse(ent.url).hostname + ? new URL(ent.url).hostname : ent.url; entity = { name: hostname diff --git a/lib/support/flattenMessage.js b/lib/support/flattenMessage.js index 9a4facc20..e3f85b0ef 100644 --- a/lib/support/flattenMessage.js +++ b/lib/support/flattenMessage.js @@ -1,5 +1,5 @@ -import { parse } from 'node:url'; import { getLogger } from '@sitespeed.io/log'; + const log = getLogger('sitespeedio'); function joinNonEmpty(strings, delimeter) { @@ -11,7 +11,12 @@ function toSafeKey(key) { return key.replaceAll(/[ %&()+,./:?|~–]|%7C/g, '_'); } -export function keypathFromUrl(url, includeQueryParameters, useHash, group) { +export function keypathFromUrl( + urlString, + includeQueryParameters, + useHash, + group +) { function flattenQueryParameters(parameters) { return Object.keys(parameters).reduce( (result, key) => joinNonEmpty([result, key, parameters[key]], '_'), @@ -19,16 +24,22 @@ export function keypathFromUrl(url, includeQueryParameters, useHash, group) { ); } - url = parse(url, !!includeQueryParameters); + const url = new URL(urlString); let path = toSafeKey(url.pathname); if (includeQueryParameters) { + const parameters = {}; + for (const [key, value] of url.searchParams) { + parameters[key] = value; + } + path = joinNonEmpty( - [path, toSafeKey(flattenQueryParameters(url.query))], + [path, toSafeKey(flattenQueryParameters(parameters))], '_' ); } + if (useHash && url.hash) { path = joinNonEmpty([path, toSafeKey(url.hash)], '_'); } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index b0321336a..af70cca10 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -17,7 +17,7 @@ "@slack/webhook": "7.0.6", "@tgwf/co2": "0.16.9", "axe-core": "4.11.0", - "browsertime": "25.4.0", + "browsertime": "26.0.1", "coach-core": "8.1.3", "dayjs": "1.11.18", "fast-crc32c": "2.0.0", @@ -1392,104 +1392,445 @@ } }, "node_modules/@jimp/core": { - "version": "0.22.12", - "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.22.12.tgz", - "integrity": "sha512-l0RR0dOPyzMKfjUW1uebzueFEDtCOj9fN6pyTYWWOM/VS4BciXQ1VVrJs8pO3kycGYZxncRKhCoygbNr8eEZQA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-1.6.0.tgz", + "integrity": "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w==", "license": "MIT", "optional": true, "dependencies": { - "@jimp/utils": "^0.22.12", - "any-base": "^1.1.0", - "buffer": "^5.2.0", + "@jimp/file-ops": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "await-to-js": "^3.0.0", "exif-parser": "^0.1.12", - "file-type": "^16.5.4", - "isomorphic-fetch": "^3.0.0", - "pixelmatch": "^4.0.2", - "tinycolor2": "^1.6.0" + "file-type": "^16.0.0", + "mime": "3" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@jimp/custom": { - "version": "0.22.12", - "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz", - "integrity": "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==", + "node_modules/@jimp/diff": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/diff/-/diff-1.6.0.tgz", + "integrity": "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw==", "license": "MIT", "optional": true, "dependencies": { - "@jimp/core": "^0.22.12" + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "pixelmatch": "^5.3.0" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@jimp/jpeg": { - "version": "0.22.12", - "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.22.12.tgz", - "integrity": "sha512-Rq26XC/uQWaQKyb/5lksCTCxXhtY01NJeBN+dQv5yNYedN0i7iYu+fXEoRsfaJ8xZzjoANH8sns7rVP4GE7d/Q==", + "node_modules/@jimp/file-ops": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/file-ops/-/file-ops-1.6.0.tgz", + "integrity": "sha512-Dx/bVDmgnRe1AlniRpCKrGRm5YvGmUwbDzt+MAkgmLGf+jvBT75hmMEZ003n9HQI/aPnm/YKnXjg/hOpzNCpHQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-bmp": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-bmp/-/js-bmp-1.6.0.tgz", + "integrity": "sha512-FU6Q5PC/e3yzLyBDXupR3SnL3htU7S3KEs4e6rjDP6gNEOXRFsWs6YD3hXuXd50jd8ummy+q2WSwuGkr8wi+Gw==", "license": "MIT", "optional": true, "dependencies": { - "@jimp/utils": "^0.22.12", + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "bmp-ts": "^1.0.9" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-gif": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-gif/-/js-gif-1.6.0.tgz", + "integrity": "sha512-N9CZPHOrJTsAUoWkWZstLPpwT5AwJ0wge+47+ix3++SdSL/H2QzyMqxbcDYNFe4MoI5MIhATfb0/dl/wmX221g==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "gifwrap": "^0.10.1", + "omggif": "^1.0.10" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-jpeg": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-jpeg/-/js-jpeg-1.6.0.tgz", + "integrity": "sha512-6vgFDqeusblf5Pok6B2DUiMXplH8RhIKAryj1yn+007SIAQ0khM1Uptxmpku/0MfbClx2r7pnJv9gWpAEJdMVA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", "jpeg-js": "^0.4.4" }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-png": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-png/-/js-png-1.6.0.tgz", + "integrity": "sha512-AbQHScy3hDDgMRNfG0tPjL88AV6qKAILGReIa3ATpW5QFjBKpisvUaOqhzJ7Reic1oawx3Riyv152gaPfqsBVg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "pngjs": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/js-tiff": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/js-tiff/-/js-tiff-1.6.0.tgz", + "integrity": "sha512-zhReR8/7KO+adijj3h0ZQUOiun3mXUv79zYEAKvE0O+rP7EhgtKvWJOZfRzdZSNv0Pu1rKtgM72qgtwe2tFvyw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "utif2": "^4.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-blit": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-1.6.0.tgz", + "integrity": "sha512-M+uRWl1csi7qilnSK8uxK4RJMSuVeBiO1AY0+7APnfUbQNZm6hCe0CCFv1Iyw1D/Dhb8ph8fQgm5mwM0eSxgVA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-blur": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-1.6.0.tgz", + "integrity": "sha512-zrM7iic1OTwUCb0g/rN5y+UnmdEsT3IfuCXCJJNs8SZzP0MkZ1eTvuwK9ZidCuMo4+J3xkzCidRwYXB5CyGZTw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/utils": "1.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-circle": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-1.6.0.tgz", + "integrity": "sha512-xt1Gp+LtdMKAXfDp3HNaG30SPZW6AQ7dtAtTnoRKorRi+5yCJjKqXRgkewS5bvj8DEh87Ko1ydJfzqS3P2tdWw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-color": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-1.6.0.tgz", + "integrity": "sha512-J5q8IVCpkBsxIXM+45XOXTrsyfblyMZg3a9eAo0P7VPH4+CrvyNQwaYatbAIamSIN1YzxmO3DkIZXzRjFSz1SA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "tinycolor2": "^1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-contain": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-1.6.0.tgz", + "integrity": "sha512-oN/n+Vdq/Qg9bB4yOBOxtY9IPAtEfES8J1n9Ddx+XhGBYT1/QTU/JYkGaAkIGoPnyYvmLEDqMz2SGihqlpqfzQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/plugin-blit": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-cover": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-1.6.0.tgz", + "integrity": "sha512-Iow0h6yqSC269YUJ8HC3Q/MpCi2V55sMlbkkTTx4zPvd8mWZlC0ykrNDeAy9IJegrQ7v5E99rJwmQu25lygKLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/plugin-crop": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-crop": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-1.6.0.tgz", + "integrity": "sha512-KqZkEhvs+21USdySCUDI+GFa393eDIzbi1smBqkUPTE+pRwSWMAf01D5OC3ZWB+xZsNla93BDS9iCkLHA8wang==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-displace": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-1.6.0.tgz", + "integrity": "sha512-4Y10X9qwr5F+Bo5ME356XSACEF55485j5nGdiyJ9hYzjQP9nGgxNJaZ4SAOqpd+k5sFaIeD7SQ0Occ26uIng5Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-dither": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-1.6.0.tgz", + "integrity": "sha512-600d1RxY0pKwgyU0tgMahLNKsqEcxGdbgXadCiVCoGd6V6glyCvkNrnnwC0n5aJ56Htkj88PToSdF88tNVZEEQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/types": "1.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-fisheye": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-1.6.0.tgz", + "integrity": "sha512-E5QHKWSCBFtpgZarlmN3Q6+rTQxjirFqo44ohoTjzYVrDI6B6beXNnPIThJgPr0Y9GwfzgyarKvQuQuqCnnfbA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-flip": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-1.6.0.tgz", + "integrity": "sha512-/+rJVDuBIVOgwoyVkBjUFHtP+wmW0r+r5OQ2GpatQofToPVbJw1DdYWXlwviSx7hvixTWLKVgRWQ5Dw862emDg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-hash": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-hash/-/plugin-hash-1.6.0.tgz", + "integrity": "sha512-wWzl0kTpDJgYVbZdajTf+4NBSKvmI3bRI8q6EH9CVeIHps9VWVsUvEyb7rpbcwVLWYuzDtP2R0lTT6WeBNQH9Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/js-bmp": "1.6.0", + "@jimp/js-jpeg": "1.6.0", + "@jimp/js-png": "1.6.0", + "@jimp/js-tiff": "1.6.0", + "@jimp/plugin-color": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "any-base": "^1.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-mask": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-1.6.0.tgz", + "integrity": "sha512-Cwy7ExSJMZszvkad8NV8o/Z92X2kFUFM8mcDAhNVxU0Q6tA0op2UKRJY51eoK8r6eds/qak3FQkXakvNabdLnA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/types": "1.6.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-print": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-1.6.0.tgz", + "integrity": "sha512-zarTIJi8fjoGMSI/M3Xh5yY9T65p03XJmPsuNet19K/Q7mwRU6EV2pfj+28++2PV2NJ+htDF5uecAlnGyxFN2A==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/js-jpeg": "1.6.0", + "@jimp/js-png": "1.6.0", + "@jimp/plugin-blit": "1.6.0", + "@jimp/types": "1.6.0", + "parse-bmfont-ascii": "^1.0.6", + "parse-bmfont-binary": "^1.0.6", + "parse-bmfont-xml": "^1.1.6", + "simple-xml-to-json": "^1.2.2", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/plugin-quantize": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-quantize/-/plugin-quantize-1.6.0.tgz", + "integrity": "sha512-EmzZ/s9StYQwbpG6rUGBCisc3f64JIhSH+ncTJd+iFGtGo0YvSeMdAd+zqgiHpfZoOL54dNavZNjF4otK+mvlg==", + "license": "MIT", + "optional": true, + "dependencies": { + "image-q": "^4.0.0", + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" } }, "node_modules/@jimp/plugin-resize": { - "version": "0.22.12", - "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.12.tgz", - "integrity": "sha512-3NyTPlPbTnGKDIbaBgQ3HbE6wXbAlFfxHVERmrbqAi8R3r6fQPxpCauA8UVDnieg5eo04D0T8nnnNIX//i/sXg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-1.6.0.tgz", + "integrity": "sha512-uSUD1mqXN9i1SGSz5ov3keRZ7S9L32/mAQG08wUwZiEi5FpbV0K8A8l1zkazAIZi9IJzLlTauRNU41Mi8IF9fA==", "license": "MIT", "optional": true, "dependencies": { - "@jimp/utils": "^0.22.12" + "@jimp/core": "1.6.0", + "@jimp/types": "1.6.0", + "zod": "^3.23.8" }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" + "engines": { + "node": ">=18" } }, - "node_modules/@jimp/plugin-scale": { - "version": "0.22.12", - "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.12.tgz", - "integrity": "sha512-dghs92qM6MhHj0HrV2qAwKPMklQtjNpoYgAB94ysYpsXslhRTiPisueSIELRwZGEr0J0VUxpUY7HgJwlSIgGZw==", + "node_modules/@jimp/plugin-rotate": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-1.6.0.tgz", + "integrity": "sha512-JagdjBLnUZGSG4xjCLkIpQOZZ3Mjbg8aGCCi4G69qR+OjNpOeGI7N2EQlfK/WE8BEHOW5vdjSyglNqcYbQBWRw==", "license": "MIT", "optional": true, "dependencies": { - "@jimp/utils": "^0.22.12" + "@jimp/core": "1.6.0", + "@jimp/plugin-crop": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5", - "@jimp/plugin-resize": ">=0.3.5" + "engines": { + "node": ">=18" } }, - "node_modules/@jimp/png": { - "version": "0.22.12", - "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.22.12.tgz", - "integrity": "sha512-Mrp6dr3UTn+aLK8ty/dSKELz+Otdz1v4aAXzV5q53UDD2rbB5joKVJ/ChY310B+eRzNxIovbUF1KVrUsYdE8Hg==", + "node_modules/@jimp/plugin-threshold": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-1.6.0.tgz", + "integrity": "sha512-M59m5dzLoHOVWdM41O8z9SyySzcDn43xHseOH0HavjsfQsT56GGCC4QzU1banJidbUrePhzoEdS42uFE8Fei8w==", "license": "MIT", "optional": true, "dependencies": { - "@jimp/utils": "^0.22.12", - "pngjs": "^6.0.0" + "@jimp/core": "1.6.0", + "@jimp/plugin-color": "1.6.0", + "@jimp/plugin-hash": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0", + "zod": "^3.23.8" }, - "peerDependencies": { - "@jimp/custom": ">=0.3.5" + "engines": { + "node": ">=18" + } + }, + "node_modules/@jimp/types": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-1.6.0.tgz", + "integrity": "sha512-7UfRsiKo5GZTAATxm2qQ7jqmUXP0DxTArztllTcYdyw6Xi5oT4RaoXynVtCD4UyLK5gJgkZJcwonoijrhYFKfg==", + "license": "MIT", + "optional": true, + "dependencies": { + "zod": "^3.23.8" + }, + "engines": { + "node": ">=18" } }, "node_modules/@jimp/utils": { - "version": "0.22.12", - "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.22.12.tgz", - "integrity": "sha512-yJ5cWUknGnilBq97ZXOyOS0HhsHOyAyjHwYfHxGbSyMTohgQI6sVyE8KPgDwH8HHW/nMKXk8TrSwAE71zt716Q==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-gqFTGEosKbOkYF/WFj26jMHOI5OH2jeP1MmC/zbK6BF6VJBf8rIC5898dPfSzZEbSA0wbbV5slbntWVc5PKLFA==", "license": "MIT", "optional": true, "dependencies": { - "regenerator-runtime": "^0.13.3" + "@jimp/types": "1.6.0", + "tinycolor2": "^1.6.0" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@jimp/utils/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "license": "MIT", - "optional": true - }, "node_modules/@jsdoc/salty": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.4.tgz", @@ -3250,6 +3591,7 @@ "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", "dev": true, + "peer": true, "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" @@ -3322,6 +3664,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3429,6 +3772,7 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3863,6 +4207,16 @@ "node": ">=12" } }, + "node_modules/await-to-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/await-to-js/-/await-to-js-3.0.0.tgz", + "integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/axe-core": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz", @@ -3998,6 +4352,13 @@ "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", "dev": true }, + "node_modules/bmp-ts": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bmp-ts/-/bmp-ts-1.0.9.tgz", + "integrity": "sha512-cTEHk2jLrPyi+12M3dhpEbnnPOsaZuq7C45ylbbQIiWgDFZq4UVYPEY5mlqjvsj/6gJv9qX5sa+ebDzLXT28Vw==", + "license": "MIT", + "optional": true + }, "node_modules/bowser": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.1.tgz", @@ -4046,6 +4407,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", @@ -4060,9 +4422,10 @@ } }, "node_modules/browsertime": { - "version": "25.4.0", - "resolved": "https://registry.npmjs.org/browsertime/-/browsertime-25.4.0.tgz", - "integrity": "sha512-X685LO1es9M71JVjxV0FxonHFC+V4b/geD9TZ0DkxwNbcPUfR7Yy5sqC2BE/8PMUUPAqEcfA4Z3sqNUbFcF0AQ==", + "version": "26.0.1", + "resolved": "https://registry.npmjs.org/browsertime/-/browsertime-26.0.1.tgz", + "integrity": "sha512-IveXYyh44aQiNArFOCDCEV7KVs4Lcoj/CikCDuNStskKvy3TWuXF+48gpJIaXcRsYvZ1GW1dI9l7ZWRY2MVPBA==", + "license": "MIT", "dependencies": { "@devicefarmer/adbkit": "3.3.8", "@sitespeed.io/chromedriver": "142.0.7444-59", @@ -4087,39 +4450,10 @@ "node": ">=20.0.0" }, "optionalDependencies": { - "@jimp/custom": "0.22.12", - "@jimp/jpeg": "0.22.12", - "@jimp/plugin-resize": "0.22.12", - "@jimp/plugin-scale": "0.22.12", - "@jimp/png": "0.22.12", + "jimp": "1.6.0", "usb-power-profiling": "1.6.0" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "optional": true, - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", @@ -4944,7 +5278,6 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", "dev": true, - "peer": true, "engines": { "node": ">=0.12" }, @@ -5019,6 +5352,7 @@ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5080,6 +5414,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -6052,6 +6387,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gifwrap": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz", + "integrity": "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw==", + "license": "MIT", + "optional": true, + "dependencies": { + "image-q": "^4.0.0", + "omggif": "^1.0.10" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -6352,6 +6698,16 @@ "node": ">=10 <11 || >=12 <13 || >=14" } }, + "node_modules/image-q": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz", + "integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "16.9.1" + } + }, "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", @@ -6651,17 +7007,6 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, - "node_modules/isomorphic-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", - "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", - "license": "MIT", - "optional": true, - "dependencies": { - "node-fetch": "^2.6.1", - "whatwg-fetch": "^3.4.1" - } - }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -6678,6 +7023,45 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jimp": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-1.6.0.tgz", + "integrity": "sha512-YcwCHw1kiqEeI5xRpDlPPBGL2EOpBKLwO4yIBJcXWHPj5PnA5urGq0jbyhM5KoNpypQ6VboSoxc9D8HyfvngSg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@jimp/core": "1.6.0", + "@jimp/diff": "1.6.0", + "@jimp/js-bmp": "1.6.0", + "@jimp/js-gif": "1.6.0", + "@jimp/js-jpeg": "1.6.0", + "@jimp/js-png": "1.6.0", + "@jimp/js-tiff": "1.6.0", + "@jimp/plugin-blit": "1.6.0", + "@jimp/plugin-blur": "1.6.0", + "@jimp/plugin-circle": "1.6.0", + "@jimp/plugin-color": "1.6.0", + "@jimp/plugin-contain": "1.6.0", + "@jimp/plugin-cover": "1.6.0", + "@jimp/plugin-crop": "1.6.0", + "@jimp/plugin-displace": "1.6.0", + "@jimp/plugin-dither": "1.6.0", + "@jimp/plugin-fisheye": "1.6.0", + "@jimp/plugin-flip": "1.6.0", + "@jimp/plugin-hash": "1.6.0", + "@jimp/plugin-mask": "1.6.0", + "@jimp/plugin-print": "1.6.0", + "@jimp/plugin-quantize": "1.6.0", + "@jimp/plugin-resize": "1.6.0", + "@jimp/plugin-rotate": "1.6.0", + "@jimp/plugin-threshold": "1.6.0", + "@jimp/types": "1.6.0", + "@jimp/utils": "1.6.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/jpeg-js": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", @@ -7114,7 +7498,6 @@ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", "dev": true, - "peer": true, "dependencies": { "uc.micro": "^1.0.1" } @@ -7203,7 +7586,6 @@ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz", "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==", "dev": true, - "peer": true, "dependencies": { "argparse": "^2.0.1", "entities": "~3.0.1", @@ -7229,8 +7611,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "peer": true + "dev": true }, "node_modules/markdown/node_modules/nopt": { "version": "2.1.2", @@ -7308,8 +7689,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", - "dev": true, - "peer": true + "dev": true }, "node_modules/memoize": { "version": "10.1.0", @@ -7668,6 +8048,13 @@ "node": ">=0.10.0" } }, + "node_modules/omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", + "license": "MIT", + "optional": true + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -7861,6 +8248,31 @@ "node": ">=6" } }, + "node_modules/parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", + "license": "MIT", + "optional": true + }, + "node_modules/parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", + "license": "MIT", + "optional": true + }, + "node_modules/parse-bmfont-xml": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", + "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", + "license": "MIT", + "optional": true, + "dependencies": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.5.0" + } + }, "node_modules/parse-ms": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", @@ -7982,26 +8394,26 @@ } }, "node_modules/pixelmatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", - "integrity": "sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-5.3.0.tgz", + "integrity": "sha512-o8mkY4E/+LNUf6LzX96ht6k6CEDi65k9G2rjMtBe9Oo+VPKSvl+0GKHuH/AlG+GA5LPG/i5hrekkxUc3s2HU+Q==", "license": "ISC", "optional": true, "dependencies": { - "pngjs": "^3.0.0" + "pngjs": "^6.0.0" }, "bin": { "pixelmatch": "bin/pixelmatch" } }, "node_modules/pixelmatch/node_modules/pngjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", - "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", + "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", "license": "MIT", "optional": true, "engines": { - "node": ">=4.0.0" + "node": ">=12.13.0" } }, "node_modules/pkg-prebuilds": { @@ -8147,13 +8559,13 @@ } }, "node_modules/pngjs": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", - "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", "license": "MIT", "optional": true, "engines": { - "node": ">=12.13.0" + "node": ">=14.19.0" } }, "node_modules/prelude-ls": { @@ -8171,6 +8583,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -8868,6 +9281,13 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/sax": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz", + "integrity": "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==", + "devOptional": true, + "license": "BlueOak-1.0.0" + }, "node_modules/selenium-webdriver": { "version": "4.38.0", "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.38.0.tgz", @@ -9068,6 +9488,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-xml-to-json": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/simple-xml-to-json/-/simple-xml-to-json-1.2.3.tgz", + "integrity": "sha512-kWJDCr9EWtZ+/EYYM5MareWj2cRnZGF93YDNpH4jQiHB+hBIZnfPFSQiVMzZOdk+zXWqTZ/9fTeQNu2DqeiudA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=20.12.2" + } + }, "node_modules/simplecrawler": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/simplecrawler/-/simplecrawler-1.1.9.tgz", @@ -9765,8 +10195,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz", "integrity": "sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg==", - "dev": true, - "peer": true + "dev": true }, "node_modules/underscore": { "version": "1.13.2", @@ -9877,6 +10306,16 @@ "node": "^18 || ^20 || >= 21" } }, + "node_modules/utif2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/utif2/-/utif2-4.1.0.tgz", + "integrity": "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w==", + "license": "MIT", + "optional": true, + "dependencies": { + "pako": "^1.0.11" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -9949,13 +10388,6 @@ "node": ">=6" } }, - "node_modules/whatwg-fetch": { - "version": "3.6.20", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", - "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", - "license": "MIT", - "optional": true - }, "node_modules/whatwg-url": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", @@ -10149,11 +10581,36 @@ "xml-js": "bin/cli.js" } }, - "node_modules/xml-js/node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "node_modules/xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", + "license": "MIT", + "optional": true + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "license": "MIT", + "optional": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4.0" + } }, "node_modules/xmlcreate": { "version": "2.0.4", @@ -10239,6 +10696,16 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 26e1f91bf..006e8ee6c 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "@tgwf/co2": "0.16.9", "@slack/webhook": "7.0.6", "axe-core": "4.11.0", - "browsertime": "25.4.0", + "browsertime": "26.0.1", "coach-core": "8.1.3", "dayjs": "1.11.18", "fast-crc32c": "2.0.0",