diff --git a/.changeset/hot-middleware-migration.md b/.changeset/hot-middleware-migration.md new file mode 100644 index 000000000..fcc54ef64 --- /dev/null +++ b/.changeset/hot-middleware-migration.md @@ -0,0 +1,5 @@ +--- +"webpack-dev-middleware": minor +--- + +Added a `hot` option that enables hot module replacement, replacing the need for `webpack-hot-middleware`. Pass `hot: true` to enable with defaults, or `hot: { path, heartbeat, log, statsOptions }` to customize. The client runtime is served by the middleware itself. diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 9bd9719de..af069a29a 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -5,10 +5,12 @@ on: branches: - main - next + - hot-middleware pull_request: branches: - main - next + - hot-middleware permissions: contents: read diff --git a/.gitignore b/.gitignore index 13bb32b4c..6b5d2aaf1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,8 @@ logs npm-debug.log* .eslintcache .cspellcache -/dist +/client +dist /local /reports /test/outputs diff --git a/README.md b/README.md index 5b67deb5a..676495526 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ See [below](#other-servers) for an example of use with fastify. | **[`writeToDisk`](#writetodisk)** | `boolean\|Function` | `false` | Instructs the module to write files to the configured location on disk as specified in your `webpack` configuration. | | **[`outputFileSystem`](#outputfilesystem)** | `Object` | [`memfs`](https://github.com/streamich/memfs) | Set the default file system which will be used by webpack as primary destination of generated files. | | **[`modifyResponseData`](#modifyresponsedata)** | `Function` | `undefined` | Allows to set up a callback to change the response data. | +| **[`hot`](#hot)** | `boolean\|Object` | `false` | Enables a Server-Sent Events endpoint that drives the browser HMR client. | | **[`forwardError`](#forwarderror)** | `boolean` | `false` | Enable or disable forwarding errors to the next middleware. | The middleware accepts an `options` Object. The following is a property reference for the Object. @@ -312,6 +313,115 @@ middleware(compiler, { }); ``` +### hot + +Type: `Boolean | Object` +Default: `false` + +Enables hot module replacement by serving a [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) endpoint that publishes the webpack compiler's `building`, `built` and `sync` events to connected clients. When `true`, defaults are used; pass an object to customise. Use this option together with the browser runtime shipped as `webpack-dev-middleware/client`. + +```js +const webpack = require("webpack"); + +const compiler = webpack({ + /* Webpack configuration with HotModuleReplacementPlugin and the client entry */ +}); + +middleware(compiler, { hot: true }); +``` + +#### `hot.path` + +Type: `String` +Default: `'/__webpack_hmr'` + +Path the SSE endpoint is served at. Must match the `path` option used by the client. + +#### `hot.heartbeat` + +Type: `Number` +Default: `10000` + +Heartbeat interval (in milliseconds) used to keep the SSE connection alive when no compilation events are produced. + +#### `hot.statsOptions` + +Type: `Boolean | Object` +Default: `undefined` + +Webpack stats options used when serializing compilation results for the SSE payload. Forwarded to `stats.toJson(...)`. By default only the minimal stats needed by the client are requested (`hash`, `timings`, `errors`, `warnings`) to avoid slowing down rebuilds. Pass `statsOptions: { modules: true }` if you want the module id → name map used for nicer client logging. + +## Hot Module Replacement client + +When the server is configured to serve the hot module replacement endpoint, the bundled application needs a small runtime that subscribes to that stream and applies the updates. `webpack-dev-middleware` ships that runtime under the `./client` subpath. Add it as a webpack entry next to your application code and enable `HotModuleReplacementPlugin`: + +```js +const webpack = require("webpack"); + +module.exports = { + entry: ["webpack-dev-middleware/client", "./src/app.js"], + plugins: [new webpack.HotModuleReplacementPlugin()], +}; +``` + +The runtime connects to `/__webpack_hmr` by default. Any of the options below can be set by adding a query string to the entry path: + +```js +entry: [ + "webpack-dev-middleware/client?reload=false&overlay=false", + "./src/app.js", +]; +``` + +### Client options + +| Name | Type | Default | Description | +| :-----------------: | :-------: | :--------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------- | +| `path` | `string` | `/__webpack_hmr` | Path the SSE endpoint is served at. Must match the server `hot.path`. | +| `timeout` | `number` | `20000` | Reconnection / heartbeat watchdog timeout in milliseconds. | +| `overlay` | `boolean` | `true` | Show compile-time errors in an in-page overlay. | +| `overlayWarnings` | `boolean` | `false` | Also show compile-time warnings in the overlay. | +| `overlayStyles` | `Object` | `{}` | JSON object of CSS overrides for the overlay container. Pass JSON-encoded value via query string. | +| `ansiColors` | `Object` | `{}` | JSON object overriding the ANSI → HTML color map used by the overlay. | +| `reload` | `boolean` | `true` | Fall back to a full page reload when an update cannot be applied through HMR (e.g. recovering from a broken build). Set to `false` to keep HMR-only. | +| `logging` | `string` | `"info"` | Logger level — one of `"none"`, `"error"`, `"warn"`, `"info"`, `"log"`, `"verbose"`. Uses webpack's runtime logger. | +| `name` | `string` | `""` | Restrict updates to a specific compilation name (useful with multi-compiler). | +| `autoConnect` | `boolean` | `true` | Connect on load; set to `false` and call `setOptionsAndConnect()` manually. | +| `dynamicPublicPath` | `boolean` | `false` | Prefix `path` with `__webpack_public_path__` at runtime. | + +### Programmatic API + +`webpack-dev-middleware/client` also exports a few functions for advanced cases: + +```js +const hotClient = require("webpack-dev-middleware/client"); + +// Receive every HMR payload (building / built / sync / custom). +hotClient.subscribeAll((payload) => { + console.log("hot event", payload); +}); + +// Receive payloads whose `action` is not recognised by the client (i.e. custom +// payloads published via the server's `instance.context.hot.publish(...)`). +hotClient.subscribe((payload) => { + // do something +}); + +// Replace the default error overlay with your own implementation. +hotClient.useCustomOverlay({ + showProblems(type, lines) { + /* ... */ + }, + clear() { + /* ... */ + }, +}); + +// Connect manually when `autoConnect=false`. Accepts the same option keys as +// the query-string API above. +hotClient.setOptionsAndConnect({ path: "/__hmr" }); +``` + ## API `webpack-dev-middleware` also provides convenience methods that can be use to diff --git a/babel.config.js b/babel.config.js index 700d9fd7a..dc7d001f7 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,7 +1,4 @@ -const MIN_BABEL_VERSION = 7; - module.exports = (api) => { - api.assertVersion(MIN_BABEL_VERSION); api.cache(true); return { @@ -9,11 +6,28 @@ module.exports = (api) => { [ "@babel/preset-env", { + modules: false, targets: { - node: "20.9.0", + esmodules: true, + node: "0.12", }, }, ], ], + env: { + test: { + presets: [ + [ + "@babel/preset-env", + { + targets: { + node: "18.12.0", + }, + }, + ], + ], + plugins: ["@babel/plugin-transform-runtime"], + }, + }, }; }; diff --git a/client-src/globals.d.ts b/client-src/globals.d.ts new file mode 100644 index 000000000..7f66bdd08 --- /dev/null +++ b/client-src/globals.d.ts @@ -0,0 +1,28 @@ +/* eslint-disable */ + +declare module "ansi-html-community" { + function ansiHtmlCommunity(str: string): string; + namespace ansiHtmlCommunity { + function setColors(colors: Record): void; + } + export = ansiHtmlCommunity; +} + +interface ClientReporter { + cleanProblemsCache(): void; + problems( + type: "errors" | "warnings", + obj: { errors: string[]; warnings: string[]; name?: string }, + ): boolean; + success(): void; + useCustomOverlay(customOverlay: unknown): void; +} + +interface EventSourceWrapper { + addMessageListener(fn: (event: { data: string }) => void): void; +} + +interface Window { + __wdmEventSourceWrapper?: Record; + __webpack_dev_middleware_hot_reporter__?: ClientReporter; +} diff --git a/client-src/index.js b/client-src/index.js new file mode 100644 index 000000000..106ab1f8e --- /dev/null +++ b/client-src/index.js @@ -0,0 +1,361 @@ +/* global __resourceQuery, __webpack_public_path__ */ + +import stripAnsi from "strip-ansi"; + +import configureOverlay from "./overlay.js"; +import applyUpdate from "./process-update.js"; +import { log, setLogLevel } from "./utils/log.js"; + +/** @typedef {import("./utils/log.js").LogLevel} LogLevel */ + +/** + * @typedef {object} ClientOptions + * @property {string} path SSE endpoint path + * @property {number} timeout reconnection timeout in milliseconds + * @property {boolean} overlay enable the in-page error overlay + * @property {boolean} reload reload the page when HMR cannot apply the update + * @property {LogLevel} logging logger level + * @property {string} name limit updates to this compilation name + * @property {boolean} autoConnect connect immediately when the entry runs + * @property {Record} overlayStyles overrides for the overlay container CSS + * @property {boolean} overlayWarnings show warnings in the overlay too + * @property {Record} ansiColors overrides for ANSI → HTML color mapping + */ + +/** @type {ClientOptions} */ +const options = { + path: "/__webpack_hmr", + timeout: 20 * 1000, + overlay: true, + reload: true, + logging: "info", + name: "", + autoConnect: true, + overlayStyles: {}, + overlayWarnings: false, + ansiColors: {}, +}; + +setLogLevel(options.logging); + +/** + * @param {Record} overrides parsed query-string overrides + */ +function setOverrides(overrides) { + if (overrides.autoConnect) { + options.autoConnect = overrides.autoConnect === "true"; + } + if (overrides.path) options.path = overrides.path; + if (overrides.timeout) options.timeout = Number(overrides.timeout); + if (overrides.overlay) options.overlay = overrides.overlay !== "false"; + if (overrides.reload) options.reload = overrides.reload !== "false"; + if (overrides.logging) { + options.logging = /** @type {LogLevel} */ (overrides.logging); + } + if (overrides.name) { + options.name = overrides.name; + } + + if (overrides.dynamicPublicPath) { + options.path = __webpack_public_path__ + options.path; + } + + if (overrides.ansiColors) { + options.ansiColors = JSON.parse(overrides.ansiColors); + } + if (overrides.overlayStyles) { + options.overlayStyles = JSON.parse(overrides.overlayStyles); + } + + if (overrides.overlayWarnings) { + options.overlayWarnings = overrides.overlayWarnings === "true"; + } + + setLogLevel(options.logging); +} + +/** + * @typedef {(event: { data: string }) => void} MessageListener + */ + +/** + * @returns {{ addMessageListener: (fn: MessageListener) => void }} event source wrapper + */ +function createEventSourceWrapper() { + /** @type {EventSource} */ + let source; + let lastActivity = Date.now(); + /** @type {MessageListener[]} */ + const listeners = []; + /** @type {ReturnType} */ + let timer; + + const handleOnline = () => { + log.info("connected"); + lastActivity = Date.now(); + }; + + /** + * @param {{ data: string }} event event + */ + const handleMessage = (event) => { + lastActivity = Date.now(); + for (const listener of listeners) { + listener(event); + } + }; + + const handleDisconnect = () => { + clearInterval(timer); + source.close(); + setTimeout(init, /** @type {number} */ (options.timeout)); + }; + + /** + * Open the EventSource connection. + */ + function init() { + source = new window.EventSource(/** @type {string} */ (options.path)); + source.addEventListener("open", handleOnline); + source.addEventListener("error", handleDisconnect); + source.addEventListener("message", handleMessage); + } + + init(); + timer = setInterval( + () => { + if (Date.now() - lastActivity > /** @type {number} */ (options.timeout)) { + handleDisconnect(); + } + }, + /** @type {number} */ (options.timeout) / 2, + ); + + return { + addMessageListener(fn) { + listeners.push(fn); + }, + }; +} + +const WRAPPER_KEY = "__wdmEventSourceWrapper"; + +/** + * @returns {ReturnType} cached event source wrapper for this path + */ +function getEventSourceWrapper() { + const path = /** @type {string} */ (options.path); + if (!window[WRAPPER_KEY]) { + window[WRAPPER_KEY] = {}; + } + if (!window[WRAPPER_KEY][path]) { + // Cache the wrapper so multiple entries on the same page sharing the same + // `options.path` reuse a single SSE connection. + window[WRAPPER_KEY][path] = createEventSourceWrapper(); + } + return window[WRAPPER_KEY][path]; +} + +/** + * Subscribe the message handler to the shared event source wrapper. + */ +function connect() { + getEventSourceWrapper().addMessageListener((event) => { + if (event.data === "💓") { + return; + } + try { + processMessage(JSON.parse(event.data)); + } catch (err) { + log.warn(`Invalid HMR message: ${event.data}\n${err}`); + } + }); +} + +/** + * @param {Record} overrides overrides + */ +export function setOptionsAndConnect(overrides) { + setOverrides(overrides); + connect(); +} + +// eslint-disable-next-line jsdoc/reject-any-type +/** @typedef {any} EXPECTED_ANY */ + +/** @typedef {{ name?: string, errors: string[], warnings: string[], hash: string, time?: number, modules?: Record, action?: string }} HMRPayload */ + +/** + * @returns {{ + * cleanProblemsCache: () => void, + * problems: (type: "errors" | "warnings", obj: HMRPayload) => boolean, + * success: () => void, + * useCustomOverlay: (customOverlay: EXPECTED_ANY) => void, + * }} reporter + */ +function createReporter() { + /** @type {EXPECTED_ANY} */ + let overlay; + if (typeof document !== "undefined" && options.overlay) { + overlay = configureOverlay({ + ansiColors: options.ansiColors, + overlayStyles: options.overlayStyles, + }); + } + + /** @type {string | null} */ + let previousProblems = null; + + /** + * @param {"errors" | "warnings"} type problem type + * @param {HMRPayload} obj payload + */ + const logProblems = (type, obj) => { + const newProblems = obj[type].map(stripAnsi).join("\n"); + if (previousProblems === newProblems) { + return; + } + previousProblems = newProblems; + + const name = obj.name ? `'${obj.name}' ` : ""; + const title = `bundle ${name}has ${obj[type].length} ${type}`; + if (type === "errors") { + log.error(title); + log.error(newProblems); + } else { + log.warn(title); + log.warn(newProblems); + } + }; + + return { + cleanProblemsCache() { + previousProblems = null; + }, + problems(type, obj) { + logProblems(type, obj); + if (overlay) { + if (options.overlayWarnings || type === "errors") { + overlay.showProblems(type, obj[type]); + return false; + } + overlay.clear(); + } + return true; + }, + success() { + if (overlay) overlay.clear(); + }, + useCustomOverlay(customOverlay) { + overlay = customOverlay; + }, + }; +} + +// The reporter is a singleton on the page so that, when multiple bundles +// include the client, errors are reported once but all clients receive them. +const REPORTER_KEY = "__webpack_dev_middleware_hot_reporter__"; +/** @type {ReturnType | undefined} */ +let reporter; + +/** @type {((obj: HMRPayload) => void) | undefined} */ +let customHandler; +/** @type {((obj: HMRPayload) => void) | undefined} */ +let subscribeAllHandler; + +/** + * @param {HMRPayload} obj payload + */ +function processMessage(obj) { + switch (obj.action) { + case "building": { + log.info(`bundle ${obj.name ? `'${obj.name}' ` : ""}rebuilding`); + break; + } + case "built": + case "sync": { + if (obj.action === "built") { + log.info( + `bundle ${obj.name ? `'${obj.name}' ` : ""}rebuilt in ${obj.time}ms`, + ); + } + if (obj.name && options.name && obj.name !== options.name) { + return; + } + let shouldApply = true; + if (obj.errors.length > 0) { + if (reporter) reporter.problems("errors", obj); + shouldApply = false; + } else if (obj.warnings.length > 0) { + if (reporter) { + shouldApply = reporter.problems("warnings", obj); + } + } else if (reporter) { + reporter.cleanProblemsCache(); + reporter.success(); + } + if (shouldApply) { + applyUpdate(obj.hash, obj.modules, options); + } + break; + } + default: { + if (customHandler) { + customHandler(obj); + } + } + } + + if (subscribeAllHandler) { + subscribeAllHandler(obj); + } +} + +// Bootstrap: parse query string overrides, then connect (if enabled). +if (typeof __resourceQuery === "string" && __resourceQuery.length > 0) { + const params = [...new URLSearchParams(__resourceQuery.slice(1))]; + /** @type {Record} */ + const overrides = {}; + for (const [key, value] of params) { + overrides[key] = value; + } + setOverrides(overrides); +} + +if (typeof window !== "undefined") { + if (!window[REPORTER_KEY]) { + window[REPORTER_KEY] = createReporter(); + } + reporter = window[REPORTER_KEY]; + + if (typeof window.EventSource === "undefined") { + log.warn( + "webpack-dev-middleware's hot client requires EventSource to work. " + + "Include a polyfill if you want to support this browser: " + + "https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events#Tools", + ); + } else if (options.autoConnect) { + connect(); + } +} + +/** + * @param {(obj: HMRPayload) => void} handler called for every incoming HMR message + */ +export function subscribeAll(handler) { + subscribeAllHandler = handler; +} + +/** + * @param {(obj: HMRPayload) => void} handler called for messages whose `action` is not recognized + */ +export function subscribe(handler) { + customHandler = handler; +} + +/** + * @param {EXPECTED_ANY} customOverlay replacement for the default error overlay + */ +export function useCustomOverlay(customOverlay) { + if (reporter) reporter.useCustomOverlay(customOverlay); +} diff --git a/client-src/overlay.js b/client-src/overlay.js new file mode 100644 index 000000000..7e8065cad --- /dev/null +++ b/client-src/overlay.js @@ -0,0 +1,266 @@ +import ansiHTML from "ansi-html-community"; +import { encode as encodeHtmlEntity } from "html-entities"; + +// The backdrop dims the page and centers the error card. +const clientOverlay = document.createElement("div"); +clientOverlay.id = "webpack-dev-middleware-hot-overlay"; + +// The card is the visible panel that holds the problem messages. +const overlayCard = document.createElement("div"); +clientOverlay.append(overlayCard); + +// A close (×) button pinned to the top-right corner of the card. +const closeButton = document.createElement("button"); +closeButton.type = "button"; +closeButton.textContent = "×"; +closeButton.setAttribute("aria-label", "Close"); +closeButton.style.position = "absolute"; +closeButton.style.top = "8px"; +closeButton.style.right = "12px"; +closeButton.style.border = "none"; +closeButton.style.background = "transparent"; +closeButton.style.color = "#999999"; +closeButton.style.fontSize = "22px"; +closeButton.style.lineHeight = "1"; +closeButton.style.cursor = "pointer"; +closeButton.style.padding = "0"; +closeButton.addEventListener("click", () => { + clear(); +}); + +// Dismiss the overlay when clicking the backdrop (but not the card itself). +clientOverlay.addEventListener("click", (event) => { + if (event.target === clientOverlay) { + clear(); + } +}); + +// Dismiss the overlay when pressing Escape. +document.addEventListener("keydown", (event) => { + if (event.key === "Escape") { + clear(); + } +}); + +/** @type {Record} */ +const backdropStyles = { + position: "fixed", + top: 0, + left: 0, + right: 0, + bottom: 0, + zIndex: 9999, + // webpack "Outer Space" (#2B3A42), translucent. + background: "rgba(43,58,66,0.72)", + display: "flex", + alignItems: "center", + justifyContent: "center", + padding: "32px", + boxSizing: "border-box", + overflow: "auto", +}; + +/** @type {Record} */ +const styles = { + // Dark panel; the top accent bar color is set per problem type in showProblems. + position: "relative", + background: "#101619", + color: "#f2f2f2", + lineHeight: "1.6", + whiteSpace: "pre-wrap", + fontFamily: "Menlo, Consolas, 'Courier New', monospace", + fontSize: "14px", + width: "100%", + maxWidth: "960px", + maxHeight: "90vh", + margin: "auto", + padding: "28px 32px", + boxSizing: "border-box", + borderRadius: "8px", + borderTop: "3px solid #ff3348", + boxShadow: "0 8px 40px rgba(0,0,0,0.5)", + overflow: "auto", + direction: "ltr", + textAlign: "left", +}; + +/** @type {Record} */ +const colors = { + reset: ["transparent", "transparent"], + black: "181818", + red: "ff3348", + green: "3fff4f", + yellow: "ffd30e", + blue: "169be0", + magenta: "f840b7", + cyan: "0ad8e9", + lightgrey: "ebe7e3", + darkgrey: "6d7891", +}; + +/** + * @param {"errors" | "warnings"} type problem type + * @returns {string | string[]} hex color (without `#`) for the given type + */ +function problemColor(type) { + /** @type {Record} */ + const problemColors = { + errors: colors.red, + warnings: colors.yellow, + }; + return problemColors[type] || colors.red; +} + +/** + * @param {"errors" | "warnings"} type problem type + * @returns {string} HTML span with a colored badge + */ +function problemType(type) { + const color = problemColor(type); + return ( + `' + + `${type.slice(0, -1).toUpperCase()}` + ); +} + +/** + * Highlight the offending line of a code frame — the one webpack marks with a + * leading `>` gutter — so it stands out from the surrounding context lines. + * @param {string} html message HTML (already entity-encoded, so `>` is `>`) + * @returns {string} HTML with the error line wrapped in a colored span + */ +function highlightCodeFrame(html) { + return html + .split("\n") + .map((line) => + /^\s*>/.test(line) + ? '' + + `${line}` + : line, + ) + .join("\n"); +} + +/** + * Highlight the file references webpack reports. The header reference (the one + * with a `line:col` location, e.g. `./src/render.js 7:2`) is rendered as a file + * chip; bare paths elsewhere are just underlined. + * @param {string} html message HTML + * @returns {string} HTML with file references styled + */ +function highlightFilePath(html) { + return html.replace( + /(\.{1,2}\/[\w./-]+\.\w+)(:\d+:\d+|\s\d+:\d+)?/g, + (match, filePath, location) => { + if (!location) { + return ( + '${match}` + ); + } + + return `${filePath}${location}\n`; + }, + ); +} + +/** + * Turn bare `http(s)` URLs in the message into clickable links. + * @param {string} html message HTML + * @returns {string} HTML with URLs wrapped in anchor tags + */ +function linkify(html) { + return html.replace(/https?:\/\/[^\s<>"]+/g, (url) => { + // Keep trailing punctuation (e.g. a sentence-ending dot) out of the href. + const trailing = url.match(/[.,;:!?)\]}]+$/); + const cut = trailing ? trailing[0] : ""; + const href = url.slice(0, url.length - cut.length); + return ( + `${href}${cut}` + ); + }); +} + +/** + * @param {"errors" | "warnings"} type problem type + * @param {string[]} lines messages to render + */ +export function showProblems(type, lines) { + // Accent the top bar with the problem color (red for errors, yellow for warnings). + overlayCard.style.borderTopColor = `#${problemColor(type)}`; + overlayCard.innerHTML = ""; + overlayCard.append(closeButton); + for (const line of lines) { + const msg = linkify( + highlightFilePath(highlightCodeFrame(ansiHTML(encodeHtmlEntity(line)))), + ); + const div = document.createElement("div"); + div.style.marginBottom = "20px"; + div.innerHTML = `${problemType(type)} in ${msg}`; + overlayCard.append(div); + } + + const hint = document.createElement("div"); + hint.style.marginTop = "4px"; + hint.style.paddingTop = "16px"; + hint.style.borderTop = "1px solid #465e69"; + hint.style.color = "#999999"; + hint.style.fontSize = "13px"; + hint.textContent = "Click outside, press Esc, or fix the code to dismiss."; + overlayCard.append(hint); + + if (document.body) { + document.body.append(clientOverlay); + } +} + +/** + * Remove the overlay container from the DOM. + */ +export function clear() { + if (clientOverlay.parentNode) { + clientOverlay.remove(); + } +} + +/** + * @param {{ ansiColors?: Record, overlayStyles?: Record }} options options + * @returns {{ showProblems: typeof showProblems, clear: typeof clear }} overlay api + */ +export default function configureOverlay(options) { + if (options.ansiColors) { + for (const color of Object.keys(options.ansiColors)) { + if (color in colors) { + colors[color] = options.ansiColors[color]; + } + } + ansiHTML.setColors(colors); + } + + if (options.overlayStyles) { + for (const style of Object.keys(options.overlayStyles)) { + styles[style] = options.overlayStyles[style]; + } + } + + for (const key of Object.keys(backdropStyles)) { + /** @type {EXPECTED_ANY} */ + (clientOverlay.style)[key] = backdropStyles[key]; + } + + for (const key of Object.keys(styles)) { + /** @type {EXPECTED_ANY} */ + (overlayCard.style)[key] = styles[key]; + } + + return { + showProblems, + clear, + }; +} + +// eslint-disable-next-line jsdoc/reject-any-type +/** @typedef {any} EXPECTED_ANY */ diff --git a/client-src/process-update.js b/client-src/process-update.js new file mode 100644 index 000000000..1d00d7c97 --- /dev/null +++ b/client-src/process-update.js @@ -0,0 +1,145 @@ +/* global __webpack_hash__ */ + +import { log } from "./utils/log.js"; + +const hot = import.meta.webpackHot; + +if (!hot) { + throw new Error("[HMR] Hot Module Replacement is disabled."); +} + +const HMR_DOCS_URL = "https://webpack.js.org/concepts/hot-module-replacement/"; + +/** @type {string | undefined} */ +let lastHash; +/** @type {Record} */ +const failureStatuses = { abort: 1, fail: 1 }; + +/** @type {webpack.ApplyOptions} */ +const applyOptions = { + ignoreUnaccepted: true, + ignoreDeclined: true, + ignoreErrored: true, + onUnaccepted(event) { + log.warn( + `Ignored an update to unaccepted module ${event.chain.join(" -> ")}`, + ); + }, + onDeclined(event) { + log.warn( + `Ignored an update to declined module ${event.chain.join(" -> ")}`, + ); + }, + onErrored(event) { + log.error(event.error); + log.warn( + `Ignored an error while updating module ${event.moduleId} (${event.type})`, + ); + }, +}; + +/** + * @param {string=} hash latest webpack compilation hash + * @returns {boolean} true when the current bundle matches the latest hash + */ +function upToDate(hash) { + if (hash) lastHash = hash; + return lastHash === __webpack_hash__; +} + +/** + * @param {string} hash latest hash from the SSE payload + * @param {Record | undefined} moduleMap module id → name map + * @param {{ reload?: boolean }} options client options + */ +export default function applyUpdate(hash, moduleMap, options) { + const { reload } = options; + + /** + * Trigger a full page reload when HMR cannot apply the update. + */ + function performReload() { + if (reload) { + log.warn("Reloading page"); + window.location.reload(); + } + } + + /** + * @param {Error} err error + */ + function handleError(err) { + if (hot.status() in failureStatuses) { + log.warn("Cannot check for update (Full reload needed)"); + log.warn(err.stack || err.message); + performReload(); + return; + } + log.warn(`Update check failed: ${err.stack || err.message}`); + } + + /** + * @param {(string | number)[]} updatedModules ids of modules that were attempted to update + * @param {(string | number)[] | null | undefined} renewedModules ids of modules that were successfully renewed + */ + function logUpdates(updatedModules, renewedModules) { + const unacceptedModules = updatedModules.filter( + (moduleId) => !renewedModules || !renewedModules.includes(moduleId), + ); + + if (unacceptedModules.length > 0) { + log.warn( + "The following modules couldn't be hot updated: " + + "(Full reload needed)\n" + + "This is usually because the modules which have changed " + + "(and their parents) do not know how to hot reload themselves. " + + `See ${HMR_DOCS_URL} for more details.`, + ); + for (const moduleId of unacceptedModules) { + log.warn(` - ${(moduleMap && moduleMap[moduleId]) || moduleId}`); + } + performReload(); + return; + } + + if (!renewedModules || renewedModules.length === 0) { + log.info("Nothing hot updated."); + } else { + log.info("Updated modules:"); + for (const moduleId of renewedModules) { + log.info(` - ${(moduleMap && moduleMap[moduleId]) || moduleId}`); + } + } + + if (upToDate()) { + log.info("App is up to date."); + } + } + + /** + * Ask webpack for the next chunk of HMR updates and apply them. + */ + function check() { + hot + .check(false) + .then((updatedModules) => { + if (!updatedModules) { + log.warn("Cannot find update (Full reload needed)"); + log.warn("(Probably because of restarting the server)"); + performReload(); + return undefined; + } + + return hot.apply(applyOptions).then((renewedModules) => { + if (!upToDate()) check(); + logUpdates(updatedModules, renewedModules); + }); + }) + .catch(handleError); + } + + if (!upToDate(hash) && hot.status() === "idle") { + log.info("Checking for updates on the server..."); + check(); + } +} diff --git a/client-src/utils/log.js b/client-src/utils/log.js new file mode 100644 index 000000000..56f1d82d4 --- /dev/null +++ b/client-src/utils/log.js @@ -0,0 +1,18 @@ +// @ts-expect-error -- no published types for this entry point +import logger from "webpack/lib/logging/runtime.js"; + +const LOGGER_NAME = "webpack-dev-middleware"; +const DEFAULT_LEVEL = "info"; + +/** @typedef {false | true | "none" | "error" | "warn" | "info" | "log" | "verbose"} LogLevel */ + +/** + * @param {LogLevel} level log level (or `false` for off, `true` for default) + */ +export function setLogLevel(level) { + logger.configureDefaultLogger({ level }); +} + +setLogLevel(DEFAULT_LEVEL); + +export const log = logger.getLogger(LOGGER_NAME); diff --git a/cspell.config.json b/cspell.config.json new file mode 100644 index 000000000..46f70162e --- /dev/null +++ b/cspell.config.json @@ -0,0 +1,21 @@ +{ + "ignorePaths": [ + "/client/**", + "/dist/**", + "/node_modules/**", + "/coverage/**", + "/test/outputs/**", + "/test/fixtures/**", + "CHANGELOG.md", + "cspell.config.json" + ], + "words": [ + "Consolas", + "cspellcache", + "darkgrey", + "eslintcache", + "esmodules", + "noopener", + "noreferrer" + ] +} diff --git a/eslint.config.mjs b/eslint.config.mjs index c2e588884..f92345455 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,9 +1,11 @@ -import { defineConfig } from "eslint/config"; +import { defineConfig, globalIgnores } from "eslint/config"; import configs from "eslint-config-webpack/configs.js"; export default defineConfig([ + globalIgnores(["client/**/*", "examples/**/*"]), { extends: [configs["recommended-dirty"]], + ignores: ["client-src/**/*"], }, { files: ["test/helpers/runner.js"], @@ -11,4 +13,13 @@ export default defineConfig([ "n/hashbang": "off", }, }, + { + files: ["client-src/**/*"], + extends: [configs["browser-outdated-recommended-module"]], + rules: { + // Function declarations are hoisted; allow referencing them ahead of + // their definition for readability. + "no-use-before-define": ["error", { functions: false }], + }, + }, ]); diff --git a/examples/hot/README.md b/examples/hot/README.md new file mode 100644 index 000000000..4602cd629 --- /dev/null +++ b/examples/hot/README.md @@ -0,0 +1,43 @@ +# Hot module replacement example + +A minimal [Express](https://expressjs.com/) server that uses +`webpack-dev-middleware` with the `hot` option to enable hot module replacement +over [Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events). + +## What it shows + +- Enabling HMR with a single `{ hot: true }` option. +- Wiring the browser runtime shipped as `webpack-dev-middleware/client` as a + webpack entry, together with `HotModuleReplacementPlugin`. +- Accepting updates with `module.hot.accept` so edits apply without a full + page reload. + +## Files + +| File | Purpose | +| ------------------- | --------------------------------------------------------------- | +| `server.js` | Express server mounting the middleware with `hot: true`. | +| `webpack.config.js` | Adds the client runtime entry and `HotModuleReplacementPlugin`. | +| `src/index.js` | App entry that accepts updates via `module.hot`. | +| `src/render.js` | The module you edit to see HMR in action. | +| `public/index.html` | Demo page that loads the bundle. | + +## Running + +From the repository root, build the package first so `dist/` and `client/` +exist (the example imports `webpack-dev-middleware` and +`webpack-dev-middleware/client` by name): + +```bash +npm run build +node examples/hot/server.js +``` + +Then open and edit `examples/hot/src/render.js`. The +page updates in place — no reload. + +Open your browser's console to see the client runtime log the HMR lifecycle +(`[webpack-dev-middleware] connected`, `App is up to date.`, …). Server-side +logs (`Client connected`, build status) are printed through webpack's +[infrastructure logger](https://webpack.js.org/configuration/other-options/#infrastructurelogging); +set `infrastructureLogging: { level: "log" }` in the webpack config to see them. diff --git a/examples/hot/public/index.html b/examples/hot/public/index.html new file mode 100644 index 000000000..1c4b64b43 --- /dev/null +++ b/examples/hot/public/index.html @@ -0,0 +1,13 @@ + + + + + + webpack-dev-middleware — hot example + + +
+ + + + diff --git a/examples/hot/server.js b/examples/hot/server.js new file mode 100644 index 000000000..a59e08d3f --- /dev/null +++ b/examples/hot/server.js @@ -0,0 +1,25 @@ +const path = require("path"); +const express = require("express"); +const webpack = require("webpack"); +const middleware = require("webpack-dev-middleware"); +const config = require("./webpack.config.js"); + +const compiler = webpack(config); +const app = express(); + +// `hot: true` serves a Server-Sent Events endpoint (at `/__webpack_hmr` by +// default) that the browser runtime subscribes to. The bundled files are still +// served from memory at `output.publicPath`. +app.use(middleware(compiler, { hot: true })); + +// Serve the demo page for any non-asset request. +app.get("/", (req, res) => { + res.sendFile(path.join(__dirname, "public", "index.html")); +}); + +const port = process.env.PORT || 3000; + +app.listen(port, () => { + // eslint-disable-next-line no-console + console.log(`Example app listening on http://localhost:${port}`); +}); diff --git a/examples/hot/src/index.js b/examples/hot/src/index.js new file mode 100644 index 000000000..e0d0aa2a1 --- /dev/null +++ b/examples/hot/src/index.js @@ -0,0 +1,11 @@ +import { render } from "./render.js"; + +render(); + +// Accept updates to `render.js` and re-run it so the DOM reflects the change +// without reloading the page. +if (module.hot) { + module.hot.accept("./render.js", () => { + render(); + }); +} diff --git a/examples/hot/src/render.js b/examples/hot/src/render.js new file mode 100644 index 000000000..e4d3812bf --- /dev/null +++ b/examples/hot/src/render.js @@ -0,0 +1,8 @@ +export function render() { + const root = document.getElementById("root"); + + // Edit this string (or anything below) and save — the page updates in place, + // without a full reload, thanks to hot module replacement. + root.textContent = + "Hello from webpack-dev-middleware hot module replacement!"; +} diff --git a/examples/hot/webpack.config.js b/examples/hot/webpack.config.js new file mode 100644 index 000000000..84827bc84 --- /dev/null +++ b/examples/hot/webpack.config.js @@ -0,0 +1,17 @@ +const path = require("path"); +const webpack = require("webpack"); + +module.exports = { + mode: "development", + // `webpack-dev-middleware/client` is the small runtime that subscribes to the + // SSE endpoint served by the `hot` option and applies the updates. Add it as + // the first entry next to your application code. + entry: ["webpack-dev-middleware/client", "./src/index.js"], + context: __dirname, + output: { + path: path.resolve(__dirname, "dist"), + publicPath: "/", + filename: "main.js", + }, + plugins: [new webpack.HotModuleReplacementPlugin()], +}; diff --git a/lint-staged.config.js b/lint-staged.config.js index 301084338..79bdc6d12 100644 --- a/lint-staged.config.js +++ b/lint-staged.config.js @@ -1,7 +1,7 @@ module.exports = { "*": [ "prettier --cache --write --ignore-unknown", - "cspell --cache --no-must-find-files", + "cspell --cache --no-must-find-files --config cspell.config.json", ], - "*.js": ["eslint --cache --fix"], + "*.js": ["eslint --cache --fix --no-warn-ignored"], }; diff --git a/package-lock.json b/package-lock.json index 55a60b49b..40a84c770 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,15 +9,19 @@ "version": "8.0.3", "license": "MIT", "dependencies": { + "ansi-html-community": "^0.0.8", + "html-entities": "^2.6.0", "memfs": "^4.56.10", "mime-types": "^3.0.2", "on-finished": "^2.4.1", "range-parser": "^1.2.1", - "schema-utils": "^4.3.3" + "schema-utils": "^4.3.3", + "strip-ansi": "^6.0.1" }, "devDependencies": { "@babel/cli": "^7.16.7", "@babel/core": "^7.16.7", + "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.16.7", "@changesets/cli": "^2.30.0", "@changesets/get-github-info": "^0.8.0", @@ -35,7 +39,7 @@ "deepmerge": "^4.2.2", "del-cli": "^7.0.0", "eslint": "^9.28.0", - "eslint-config-webpack": "^4.9.5", + "eslint-config-webpack": "^4.9.6", "execa": "^9.6.1", "express": "^5.1.0", "express-4": "npm:express@^4", @@ -45,6 +49,7 @@ "hono": "^4.12.12", "husky": "^9.1.3", "jest": "^30.1.3", + "jest-environment-jsdom": "^30.4.1", "koa": "^3.0.0", "lint-staged": "^17.0.2", "npm-run-all": "^4.1.5", @@ -70,10 +75,31 @@ } } }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/@babel/cli": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.29.7.tgz", - "integrity": "sha512-/75HwRbAYPqXv/Ax1h7Fg3IZfXgdU98jnA8H93/m/QBaPV3Hp5ICoLqzGYye1yHBCgpmXvtqgSUN8oOKX5tojQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.6.tgz", + "integrity": "sha512-6EUNcuBbNkj08Oj4gAZ+BUU8yLCgKzgVX4gaTh09Ya2C8ICM4P+G30g4m3akRxSYAp3A/gnWchrNst7px4/nUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -101,13 +127,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -116,9 +142,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", - "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", "dev": true, "license": "MIT", "engines": { @@ -126,21 +152,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helpers": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -157,14 +183,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", - "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -174,27 +200,27 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", - "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.7" + "@babel/types": "^7.27.3" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", - "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -204,18 +230,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", - "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz", + "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.29.7", - "@babel/helper-member-expression-to-functions": "^7.29.7", - "@babel/helper-optimise-call-expression": "^7.29.7", - "@babel/helper-replace-supers": "^7.29.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", - "@babel/traverse": "^7.29.7", + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.6", "semver": "^6.3.1" }, "engines": { @@ -226,13 +252,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz", - "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, @@ -261,9 +287,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", - "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, "license": "MIT", "engines": { @@ -271,43 +297,43 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", - "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", - "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", - "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -317,22 +343,22 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", - "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.7" + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", - "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true, "license": "MIT", "engines": { @@ -340,15 +366,15 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz", - "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.29.7", - "@babel/helper-wrap-function": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -358,15 +384,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", - "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.29.7", - "@babel/helper-optimise-call-expression": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -376,23 +402,23 @@ } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", - "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -400,9 +426,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -410,9 +436,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", - "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", "engines": { @@ -420,42 +446,42 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz", - "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", + "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.7" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -465,14 +491,14 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz", - "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", + "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -482,13 +508,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz", - "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -498,13 +524,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz", - "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -514,14 +540,14 @@ } }, "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz", - "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==", + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.3.tgz", + "integrity": "sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -531,15 +557,15 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz", - "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", - "@babel/plugin-transform-optional-chaining": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -549,14 +575,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz", - "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", + "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -634,13 +660,13 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz", - "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", + "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -650,13 +676,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", - "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -851,13 +877,13 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", - "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -867,15 +893,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz", - "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", + "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-remap-async-to-generator": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.29.0" }, "engines": { "node": ">=6.9.0" @@ -885,15 +911,15 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz", - "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", + "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-remap-async-to-generator": "^7.29.7" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -903,13 +929,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz", - "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -919,13 +945,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", - "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", + "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -935,14 +961,14 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", - "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", + "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -952,14 +978,14 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz", - "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", + "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -969,18 +995,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", - "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", + "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-globals": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-replace-supers": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -990,14 +1016,14 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz", - "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", + "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/template": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/template": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1007,14 +1033,14 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", - "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1024,14 +1050,14 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz", - "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", + "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1041,13 +1067,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz", - "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1057,14 +1083,14 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz", - "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1074,13 +1100,13 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz", - "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1090,14 +1116,14 @@ } }, "node_modules/@babel/plugin-transform-explicit-resource-management": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz", - "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", + "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/plugin-transform-destructuring": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1107,13 +1133,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz", - "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", + "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1123,13 +1149,13 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz", - "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1139,14 +1165,14 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz", - "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1156,15 +1182,15 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz", - "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1174,13 +1200,13 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz", - "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", + "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1190,13 +1216,13 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz", - "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1206,13 +1232,13 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz", - "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", + "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1222,13 +1248,13 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz", - "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1238,14 +1264,14 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz", - "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1255,14 +1281,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", - "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1272,16 +1298,16 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz", - "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" }, "engines": { "node": ">=6.9.0" @@ -1291,14 +1317,14 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz", - "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1308,14 +1334,14 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz", - "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1325,13 +1351,13 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz", - "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1341,13 +1367,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", - "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", + "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1357,13 +1383,13 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz", - "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", + "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1373,17 +1399,17 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz", - "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", + "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/plugin-transform-destructuring": "^7.29.7", - "@babel/plugin-transform-parameters": "^7.29.7", - "@babel/traverse": "^7.29.7" + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1393,14 +1419,14 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz", - "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-replace-supers": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1410,13 +1436,13 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz", - "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", + "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1426,14 +1452,14 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", - "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", + "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1443,13 +1469,13 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz", - "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==", + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1459,14 +1485,14 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", - "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", + "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1476,15 +1502,15 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz", - "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", + "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.29.7", - "@babel/helper-create-class-features-plugin": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1494,13 +1520,13 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz", - "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1510,13 +1536,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz", - "integrity": "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", + "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1526,14 +1552,14 @@ } }, "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz", - "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", + "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1543,13 +1569,13 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz", - "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1558,14 +1584,49 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz", + "integrity": "sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz", - "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1575,14 +1636,14 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.7.tgz", - "integrity": "sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", + "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1592,13 +1653,13 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz", - "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1608,13 +1669,13 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz", - "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1624,13 +1685,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz", - "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1640,13 +1701,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz", - "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1656,14 +1717,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz", - "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", + "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1673,14 +1734,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz", - "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1690,14 +1751,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz", - "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", + "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7" + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1707,77 +1768,77 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.7.tgz", - "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-plugin-utils": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.29.7", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.29.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.29.7", - "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.7", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.29.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.29.7", + "version": "7.29.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.5.tgz", + "integrity": "sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.3", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.29.7", - "@babel/plugin-syntax-import-attributes": "^7.29.7", + "@babel/plugin-syntax-import-assertions": "^7.28.6", + "@babel/plugin-syntax-import-attributes": "^7.28.6", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.29.7", - "@babel/plugin-transform-async-generator-functions": "^7.29.7", - "@babel/plugin-transform-async-to-generator": "^7.29.7", - "@babel/plugin-transform-block-scoped-functions": "^7.29.7", - "@babel/plugin-transform-block-scoping": "^7.29.7", - "@babel/plugin-transform-class-properties": "^7.29.7", - "@babel/plugin-transform-class-static-block": "^7.29.7", - "@babel/plugin-transform-classes": "^7.29.7", - "@babel/plugin-transform-computed-properties": "^7.29.7", - "@babel/plugin-transform-destructuring": "^7.29.7", - "@babel/plugin-transform-dotall-regex": "^7.29.7", - "@babel/plugin-transform-duplicate-keys": "^7.29.7", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.7", - "@babel/plugin-transform-dynamic-import": "^7.29.7", - "@babel/plugin-transform-explicit-resource-management": "^7.29.7", - "@babel/plugin-transform-exponentiation-operator": "^7.29.7", - "@babel/plugin-transform-export-namespace-from": "^7.29.7", - "@babel/plugin-transform-for-of": "^7.29.7", - "@babel/plugin-transform-function-name": "^7.29.7", - "@babel/plugin-transform-json-strings": "^7.29.7", - "@babel/plugin-transform-literals": "^7.29.7", - "@babel/plugin-transform-logical-assignment-operators": "^7.29.7", - "@babel/plugin-transform-member-expression-literals": "^7.29.7", - "@babel/plugin-transform-modules-amd": "^7.29.7", - "@babel/plugin-transform-modules-commonjs": "^7.29.7", - "@babel/plugin-transform-modules-systemjs": "^7.29.7", - "@babel/plugin-transform-modules-umd": "^7.29.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.7", - "@babel/plugin-transform-new-target": "^7.29.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.29.7", - "@babel/plugin-transform-numeric-separator": "^7.29.7", - "@babel/plugin-transform-object-rest-spread": "^7.29.7", - "@babel/plugin-transform-object-super": "^7.29.7", - "@babel/plugin-transform-optional-catch-binding": "^7.29.7", - "@babel/plugin-transform-optional-chaining": "^7.29.7", - "@babel/plugin-transform-parameters": "^7.29.7", - "@babel/plugin-transform-private-methods": "^7.29.7", - "@babel/plugin-transform-private-property-in-object": "^7.29.7", - "@babel/plugin-transform-property-literals": "^7.29.7", - "@babel/plugin-transform-regenerator": "^7.29.7", - "@babel/plugin-transform-regexp-modifiers": "^7.29.7", - "@babel/plugin-transform-reserved-words": "^7.29.7", - "@babel/plugin-transform-shorthand-properties": "^7.29.7", - "@babel/plugin-transform-spread": "^7.29.7", - "@babel/plugin-transform-sticky-regex": "^7.29.7", - "@babel/plugin-transform-template-literals": "^7.29.7", - "@babel/plugin-transform-typeof-symbol": "^7.29.7", - "@babel/plugin-transform-unicode-escapes": "^7.29.7", - "@babel/plugin-transform-unicode-property-regex": "^7.29.7", - "@babel/plugin-transform-unicode-regex": "^7.29.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.29.7", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.29.0", + "@babel/plugin-transform-async-to-generator": "^7.28.6", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.6", + "@babel/plugin-transform-class-properties": "^7.28.6", + "@babel/plugin-transform-class-static-block": "^7.28.6", + "@babel/plugin-transform-classes": "^7.28.6", + "@babel/plugin-transform-computed-properties": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-dotall-regex": "^7.28.6", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.6", + "@babel/plugin-transform-exponentiation-operator": "^7.28.6", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.28.6", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.28.6", + "@babel/plugin-transform-modules-systemjs": "^7.29.4", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", + "@babel/plugin-transform-numeric-separator": "^7.28.6", + "@babel/plugin-transform-object-rest-spread": "^7.28.6", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.28.6", + "@babel/plugin-transform-optional-chaining": "^7.28.6", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.28.6", + "@babel/plugin-transform-private-property-in-object": "^7.28.6", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.29.0", + "@babel/plugin-transform-regexp-modifiers": "^7.28.6", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.28.6", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.28.6", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.15", "babel-plugin-polyfill-corejs3": "^0.14.0", @@ -1818,33 +1879,33 @@ } }, "node_modules/@babel/template": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", - "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", - "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", "debug": "^4.3.1" }, "engines": { @@ -1852,14 +1913,14 @@ } }, "node_modules/@babel/types": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -2210,9 +2271,9 @@ } }, "node_modules/@cspell/cspell-bundled-dicts": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-10.0.1.tgz", - "integrity": "sha512-WvkSDNX4Uyyj/ZgbPO6L38iFNMfK1EqsH1FteRiI2qLz6QZMXRFrIt12OqiWIplzZDDaVpBH9FCJOPJll0fjCQ==", + "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": { @@ -2281,22 +2342,22 @@ } }, "node_modules/@cspell/cspell-json-reporter": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-10.0.1.tgz", - "integrity": "sha512-/nes1RGILec3WCBcoMOd0byNTBtnJuPaVz/+ZzqYkLtY7x58VMcBG5kyP6hPyN8cIwjRADE/SR43gwdXuqk/FA==", + "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": "10.0.1" + "@cspell/cspell-types": "10.0.0" }, "engines": { "node": ">=22.18.0" } }, "node_modules/@cspell/cspell-performance-monitor": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-performance-monitor/-/cspell-performance-monitor-10.0.1.tgz", - "integrity": "sha512-9tVcHXwRnbazUv4WSG0h3MqV4+LgmLNgSALAQUflPPW0EMxTf7C4Dmv9cgxJyCEQrdnVKCr58nPPaahhz9LJUg==", + "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": { @@ -2304,9 +2365,9 @@ } }, "node_modules/@cspell/cspell-pipe": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-10.0.1.tgz", - "integrity": "sha512-HPeXMD9AZ3V/qPkvQaPcak+C7cJ2z7JTHN8smd6J8L2aThLRky2cHc2OyeaHPSHB7WA47b4z2n5u5nawZhv5VQ==", + "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": { @@ -2314,9 +2375,9 @@ } }, "node_modules/@cspell/cspell-resolver": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-10.0.1.tgz", - "integrity": "sha512-PIzkZHD1fGUQx1XteK2d1iQ0Mzq/maYcoB4jkvAiiR6WqP3MWYNKFdI9z+R5pOq5KgMfW+5Ig1q0oSR6h8irlA==", + "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": { @@ -2327,9 +2388,9 @@ } }, "node_modules/@cspell/cspell-service-bus": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-10.0.1.tgz", - "integrity": "sha512-y6NcIGP2IdXaBL4PVH8vxsr7K27wzz3Ech87UtUtrDSXAiVEOvXgAIknEOUVp59rTlUE8Rn4IRURC6f/hgMyfw==", + "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": { @@ -2337,9 +2398,9 @@ } }, "node_modules/@cspell/cspell-types": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-10.0.1.tgz", - "integrity": "sha512-kLgLShnWADDVreKC63pBrWkcvxgZzFIfO34Jhx/SWfuOIA3cD8AXT+HjyuLfoGJ7mUb58hv2kUziKzEy4INb1w==", + "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": { @@ -2347,13 +2408,13 @@ } }, "node_modules/@cspell/cspell-worker": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@cspell/cspell-worker/-/cspell-worker-10.0.1.tgz", - "integrity": "sha512-L2bJerfuYOls2wEknm8FmynLtj/G7O4UqX9I/HznRggEW6i2yZIxagDetpVDNowpyavNHJ3SJtUFiyMiZc16Sw==", + "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": "10.0.1" + "cspell-lib": "10.0.0" }, "engines": { "node": ">=22.18.0" @@ -2381,13 +2442,13 @@ "license": "MIT" }, "node_modules/@cspell/dict-bash": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.2.3.tgz", - "integrity": "sha512-ljUZoKHbDqw5Sx0qpL2qTUlmkmr+vhZH/sCNrNaBZKTbdgiswErSnIF1jRbGmEitJNxHRHWsuZyVgnTGfVO1Yw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.2.2.tgz", + "integrity": "sha512-kyWbwtX3TsCf5l49gGQIZkRLaB/P8g73GDRm41Zu8Mv51kjl2H7Au0TsEvHv7jzcsRLS6aUYaZv6Zsvk1fOz+Q==", "dev": true, "license": "MIT", "dependencies": { - "@cspell/dict-shell": "1.2.0" + "@cspell/dict-shell": "1.1.2" } }, "node_modules/@cspell/dict-companies": { @@ -2419,9 +2480,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-css": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.1.2.tgz", - "integrity": "sha512-+ylGoKdwZ2sVOCOnU2Eq5wDZx+RaVX3HoKyNHGGsFvhSw6IidQ6tH/mAPKBDofViHJoWCPNlklE0lTr6MDG3QA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.1.1.tgz", + "integrity": "sha512-y/Vgo6qY08e1t9OqR56qjoFLBCpi4QfWMf2qzD1l9omRZwvSMQGRPz4x0bxkkkU4oocMAeztjzCsmLew//c/8w==", "dev": true, "license": "MIT" }, @@ -2433,9 +2494,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-data-science": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.14.tgz", - "integrity": "sha512-jl6Ds4u5u5JT+yY30pWQpAbdCHfy3lCcNkLbpL/AZKoUaLEoXbaYsps9xQtvD7DyaiXxiLZkdH2yHHXtoFtZyg==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.13.tgz", + "integrity": "sha512-l1HMEhBJkPmw4I2YGVu2eBSKM89K9pVF+N6qIr5Uo5H3O979jVodtuwP8I7LyPrJnC6nz28oxeGRCLh9xC5CVA==", "dev": true, "license": "MIT" }, @@ -2468,9 +2529,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-en_us": { - "version": "4.4.35", - "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.4.35.tgz", - "integrity": "sha512-xWpxBCc/FzzMMo/A+0qwARVaIIhR0Ql8yhhv4rvsvg+GfQF+LG9yzg2GwTM5N2rjvzmM3nKuR9zxFZq2I6fJSg==", + "version": "4.4.33", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.4.33.tgz", + "integrity": "sha512-zWftVqfUStDA37wO1ZNDN1qMJOfcxELa8ucHW8W8wBAZY3TK5Nb6deLogCK/IJi/Qljf30dwwuqqv84Qqle9Tw==", "dev": true, "license": "MIT" }, @@ -2482,9 +2543,9 @@ "license": "CC BY-SA 4.0" }, "node_modules/@cspell/dict-en-gb-mit": { - "version": "3.1.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.24.tgz", - "integrity": "sha512-Oowb/Uzkh7OmDRdCcETzMc9imEb4IpLlHJXoYjX8A8DS2X/54gqSjI915JFB8hKtFjBko5OM0BLQ+6cZhFEMmQ==", + "version": "3.1.22", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.22.tgz", + "integrity": "sha512-xE5Vg6gGdMkZ1Ep6z9SJMMioGkkT1GbxS5Mm0U3Ey1/H68P0G7cJcyiVr1CARxFbLqKE4QUpoV1o6jz1Z5Yl9Q==", "dev": true, "license": "MIT" }, @@ -2629,13 +2690,13 @@ "license": "MIT" }, "node_modules/@cspell/dict-markdown": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.17.tgz", - "integrity": "sha512-H8bAxih6U8NOnSPL7R8My+tqjaB4tmnJTjERuz4zYqmf+cH+5xshX3UVgKlwWFcyjsYfv/zEDuRdMctQv1q6HQ==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.16.tgz", + "integrity": "sha512-976RRqKv6cwhrxdFCQP2DdnBVB86BF57oQtPHy4Zbf4jF/i2Oy29MCrxirnOBalS1W6KQeto7NdfDXRAwkK4PQ==", "dev": true, "license": "MIT", "peerDependencies": { - "@cspell/dict-css": "^4.1.2", + "@cspell/dict-css": "^4.1.1", "@cspell/dict-html": "^4.0.15", "@cspell/dict-html-symbol-entities": "^4.0.5", "@cspell/dict-typescript": "^3.2.3" @@ -2656,9 +2717,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-npm": { - "version": "5.2.41", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.41.tgz", - "integrity": "sha512-To3xsfRmMBYVXtWVEdUgV35M9a/JZ54dSuoY6m6D3uHKKL3I326Wmy4xifZ3PU8MQaWhyEH7zbIcUEtKwTQMcA==", + "version": "5.2.38", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.38.tgz", + "integrity": "sha512-21ucGRPYYhr91C2cDBoMPTrcIOStQv33xOqJB0JLoC5LAs2Sfj9EoPGhGb+gIFVHz6Ia7JQWE2SJsOVFJD1wmg==", "dev": true, "license": "MIT" }, @@ -2684,13 +2745,13 @@ "license": "MIT" }, "node_modules/@cspell/dict-python": { - "version": "4.2.27", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.27.tgz", - "integrity": "sha512-Rj6xQgYS4X6ienjgAZF+njA0GRY4oSPouJWv0vfikCTn6EWlfk0V6Dy1HP3Migj1O+IC2NmespgVq+BZNSp8OA==", + "version": "4.2.26", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.26.tgz", + "integrity": "sha512-hbjN6BjlSgZOG2dA2DtvYNGBM5Aq0i0dHaZjMOI9K/9vRicVvKbcCiBSSrR3b+jwjhQL5ff7HwG5xFaaci0GQA==", "dev": true, "license": "MIT", "dependencies": { - "@cspell/dict-data-science": "^2.0.14" + "@cspell/dict-data-science": "^2.0.13" } }, "node_modules/@cspell/dict-r": { @@ -2722,9 +2783,9 @@ "license": "MIT" }, "node_modules/@cspell/dict-shell": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@cspell/dict-shell/-/dict-shell-1.2.0.tgz", - "integrity": "sha512-PVctvT22lJ49niMiakO8xieY7ELCAzjSqhejWR7bAMb5AZ9F4WDEs+XdGMnoVHWeXq7K5rcepLPmEJb+37zzIw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-shell/-/dict-shell-1.1.2.tgz", + "integrity": "sha512-WqOUvnwcHK1X61wAfwyXq04cn7KYyskg90j4lLg3sGGKMW9Sq13hs91pqrjC44Q+lQLgCobrTkMDw9Wyl9nRFA==", "dev": true, "license": "MIT" }, @@ -2785,13 +2846,13 @@ "license": "MIT" }, "node_modules/@cspell/dynamic-import": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-10.0.1.tgz", - "integrity": "sha512-mP1gdq00aIcH8HxNMqnH11X6BKxLcneDtFgl/ecjIKnaGKwi44m8AndP5Kr4ODaYdl8UUw9O3dJh7KaQXnLHZQ==", + "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": "10.0.1", + "@cspell/url": "10.0.0", "import-meta-resolve": "^4.2.0" }, "engines": { @@ -2799,9 +2860,9 @@ } }, "node_modules/@cspell/filetypes": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-10.0.1.tgz", - "integrity": "sha512-Z5S35giU5IW49fBBq6BksUbE8PC4IYPfaKuwl5Nl9jkf/OkAKiBmCowKX45NzRUQInwK/GSqqIUifrNeI6LdLw==", + "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": { @@ -2809,9 +2870,9 @@ } }, "node_modules/@cspell/rpc": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@cspell/rpc/-/rpc-10.0.1.tgz", - "integrity": "sha512-axSRKv3zEAmBm66iD/FV/MPmE4/Yf7c3PZiwTW894Yd3iEhtn3KPKeTrqQ2/tDrhB1Z2qTsap/Hue0MK4o5WXg==", + "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": { @@ -2819,9 +2880,9 @@ } }, "node_modules/@cspell/strong-weak-map": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-10.0.1.tgz", - "integrity": "sha512-lenN1DVyPi8nJLSMSJJ670ddTjyiruLueuSZO1qLcxBqUhgxDt/mALu9N/1m6WdOVcg6m/5cLiZVg2KOo2UzRw==", + "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": { @@ -2829,31 +2890,146 @@ } }, "node_modules/@cspell/url": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@cspell/url/-/url-10.0.1.tgz", - "integrity": "sha512-abYYgI29wJhWIfWTYrYuzRYDcHQUQ1N5ylnhxYn1NJnIQMqUWGLbDmt12JABtZ+R6h6UNatQrS7rhP86etvJyQ==", + "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": ">=22.18.0" } }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, "license": "MIT", "optional": true, @@ -2862,9 +3038,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, @@ -3160,9 +3336,9 @@ "license": "MIT" }, "node_modules/@fastify/express": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@fastify/express/-/express-4.0.6.tgz", - "integrity": "sha512-jP0UPsT05npbzEHRmTXoVC9r0ATx/FQ2dTk+M/C666h25KUPtAfjN3yBtMjFOy7SmaMnIzKTVWVZhxvYNHRa8g==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@fastify/express/-/express-4.0.5.tgz", + "integrity": "sha512-Sd/6u38XGsOU4ETtw/xwPEZR3cDqEvidL6m+pJQDkvTPPDtXQvcfLZURmbVA1P6uTUhy2HkgV5nFcGatED5+aA==", "dev": true, "funding": [ { @@ -3592,9 +3768,9 @@ } }, "node_modules/@hapi/wreck": { - "version": "18.1.2", - "resolved": "https://registry.npmjs.org/@hapi/wreck/-/wreck-18.1.2.tgz", - "integrity": "sha512-3dMnV2pfhQiyEqu8DL3VBmxkdLiRDiiUDuG79Dp+UK1gL9ZxAfDOUhB6k3D5MLqcgJJ1IARyGFhwoc1NITr/pg==", + "version": "18.1.1", + "resolved": "https://registry.npmjs.org/@hapi/wreck/-/wreck-18.1.1.tgz", + "integrity": "sha512-UwTeGBfAnB/1mkw4gD6IQGI/bgMu7iGmqgT8K+xxye3z4ZHhCZlmS2wuHBJmENhBJSKqvoYzJ71ds3Xfq4gofQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3604,9 +3780,9 @@ } }, "node_modules/@hono/node-server": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.0.4.tgz", - "integrity": "sha512-Ut3y0dMMPWy6bZ2kVfx25EOVbZlm15dhF4mOsezMlhpNHy+4MkU1qN9Y6lnruYi4wPmFzimGX2X7LF/FwHli4A==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.0.2.tgz", + "integrity": "sha512-tXlTi1h/4V7sDe7i97IVP+9re9ZU7wXZZggnR5ucCRclf1+AX6YhGStrR5w8bLj+3Mlyl0pKfBh9gqTqqnGKfQ==", "dev": true, "license": "MIT", "engines": { @@ -3943,6 +4119,34 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/@jest/environment-jsdom-abstract": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/@jest/environment-jsdom-abstract/-/environment-jsdom-abstract-30.4.1.tgz", + "integrity": "sha512-dSlKrqug3siYNHVnjwIldShY12wAH3spwRltO/+8VOjg0X+xEq7vOs3DbBs4LRKsu7OH+NUb9kuZUNBF9Ho3TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/fake-timers": "30.4.1", + "@jest/types": "30.4.1", + "@types/jsdom": "^21.1.7", + "@types/node": "*", + "jest-mock": "30.4.1", + "jest-util": "30.4.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/@jest/expect": { "version": "30.4.1", "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.4.1.tgz", @@ -4380,13 +4584,13 @@ } }, "node_modules/@jsonjoy.com/fs-core": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.7.tgz", - "integrity": "sha512-GDKuYHjP7vAI1kjBo73V+STKr9XIMZknW/xirpRW/EcShX0IKSev/ALafeRfC8Q331nodrXUFu04PugPB0MAhw==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.2.tgz", + "integrity": "sha512-SVjwklkpIV5wrynpYtuYnfYH1QF4/nDuLBX7VXdb+3miglcAgBVZb/5y0cOsehRV/9Vb+3UqhkMq3/NR3ztdkQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.57.7", - "@jsonjoy.com/fs-node-utils": "4.57.7", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", "thingies": "^2.5.0" }, "engines": { @@ -4401,14 +4605,14 @@ } }, "node_modules/@jsonjoy.com/fs-fsa": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.7.tgz", - "integrity": "sha512-1rWsah2nZtRbNeP+c61QcfGfVrJXBmBD0Hm7Akvv4C9MKEasXnbiOS//iH3T3HwUSSBATGrfSp0Xi8nlNhATeQ==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.2.tgz", + "integrity": "sha512-fhO8+iR2I+OCw668ISDJdn1aArc9zx033sWejIyzQ8RBeXa9bDSaUeA3ix0poYOfrj1KdOzytmYNv2/uLDfV6g==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.7", - "@jsonjoy.com/fs-node-builtins": "4.57.7", - "@jsonjoy.com/fs-node-utils": "4.57.7", + "@jsonjoy.com/fs-core": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", "thingies": "^2.5.0" }, "engines": { @@ -4423,16 +4627,16 @@ } }, "node_modules/@jsonjoy.com/fs-node": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.7.tgz", - "integrity": "sha512-xhnyeyEVTiIOibFvda/5n89nChMLCPKHHM2WQ+GGDf6+U/IrQBW3Qx6x+Uq1bkDbxBkybLOdIGoBtVBrE8Nngg==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.2.tgz", + "integrity": "sha512-nX2AdL6cOFwLdju9G4/nbRnYevmCJbh7N7hvR3gGm97Cs60uEjyd0rpR+YBS7cTg175zzl22pGKXR5USaQMvKg==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.7", - "@jsonjoy.com/fs-node-builtins": "4.57.7", - "@jsonjoy.com/fs-node-utils": "4.57.7", - "@jsonjoy.com/fs-print": "4.57.7", - "@jsonjoy.com/fs-snapshot": "4.57.7", + "@jsonjoy.com/fs-core": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/fs-print": "4.57.2", + "@jsonjoy.com/fs-snapshot": "4.57.2", "glob-to-regex.js": "^1.0.0", "thingies": "^2.5.0" }, @@ -4448,9 +4652,9 @@ } }, "node_modules/@jsonjoy.com/fs-node-builtins": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.7.tgz", - "integrity": "sha512-LWqfY1m+uAosjwM1RrKhMkUnP9jcq1RUczHsNO779ovm1E9v8I/pmj04eBAcoBjhC7ltcPbNFGyRJ5JqSJ7Jdg==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.2.tgz", + "integrity": "sha512-xhiegylRmhw43Ki2HO1ZBL7DQ5ja/qpRsL29VtQ2xuUHiuDGbgf2uD4p9Qd8hJI5P6RCtGYD50IXHXVq/Ocjcg==", "license": "Apache-2.0", "engines": { "node": ">=10.0" @@ -4464,14 +4668,14 @@ } }, "node_modules/@jsonjoy.com/fs-node-to-fsa": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.7.tgz", - "integrity": "sha512-9T0zC9LKcAWXDoTLRdLMoJ0seOvJ5bgDKq1tSBoQAFQpPDstQUeV1Oe7PLypdu7F2D3ddRstmwgeNUEN/VaZ4Q==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.2.tgz", + "integrity": "sha512-18LmWTSONhoAPW+IWRuf8w/+zRolPFGPeGwMxlAhhfY11EKzX+5XHDBPAw67dBF5dxDErHJbl40U+3IXSDRXSQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-fsa": "4.57.7", - "@jsonjoy.com/fs-node-builtins": "4.57.7", - "@jsonjoy.com/fs-node-utils": "4.57.7" + "@jsonjoy.com/fs-fsa": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2" }, "engines": { "node": ">=10.0" @@ -4485,12 +4689,12 @@ } }, "node_modules/@jsonjoy.com/fs-node-utils": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.7.tgz", - "integrity": "sha512-jjWSDOsfcog2cZnUCwX5AHmlIq6b6wx5Pz/2LAcNjJ62Rajwg89Fy7ubN+lDHew0/1reLDa9Z5urybYadhh37g==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.2.tgz", + "integrity": "sha512-rsPSJgekz43IlNbLyAM/Ab+ouYLWGp5DDBfYBNNEqDaSpsbXfthBn29Q4muFA9L0F+Z3mKo+CWlgSCXrf+mOyQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.57.7" + "@jsonjoy.com/fs-node-builtins": "4.57.2" }, "engines": { "node": ">=10.0" @@ -4504,12 +4708,12 @@ } }, "node_modules/@jsonjoy.com/fs-print": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.7.tgz", - "integrity": "sha512-mFM4P4Gjq0QQHkLnXzPYPEMFrAoe6a5Myedgb6+CmL+nGd3MKvTxYPuD7N1dLIH9RBy1fLdzxd80qvuK8xrx3Q==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.2.tgz", + "integrity": "sha512-wK9NSow48i4DbDl9F1CQE5TqnyZOJ04elU3WFG5aJ76p+YxO/ulyBBQvKsessPxdo381Bc2pcEoyPujMOhcRqQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-utils": "4.57.7", + "@jsonjoy.com/fs-node-utils": "4.57.2", "tree-dump": "^1.1.0" }, "engines": { @@ -4524,13 +4728,13 @@ } }, "node_modules/@jsonjoy.com/fs-snapshot": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.7.tgz", - "integrity": "sha512-1GS3+plfm2giB3PqokiqyydyqYTPLcCQIKSkp0TdMNRh3KVk7rqRM6U785FLlVRG7XLmkc0KWr215OY+22K3QA==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.2.tgz", + "integrity": "sha512-GdduDZuoP5V/QCgJkx9+BZ6SC0EZ/smXAdTS7PfMqgMTGXLlt/bH/FqMYaqB9JmLf05sJPtO0XRbAwwkEEPbVw==", "license": "Apache-2.0", "dependencies": { "@jsonjoy.com/buffers": "^17.65.0", - "@jsonjoy.com/fs-node-utils": "4.57.7", + "@jsonjoy.com/fs-node-utils": "4.57.2", "@jsonjoy.com/json-pack": "^17.65.0", "@jsonjoy.com/util": "^17.65.0" }, @@ -5113,6 +5317,28 @@ "@types/ms": "*" } }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -5189,6 +5415,18 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jsdom": { + "version": "21.1.7", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz", + "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -5295,6 +5533,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -5320,17 +5565,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.59.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.4.tgz", - "integrity": "sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.3.tgz", + "integrity": "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.59.4", - "@typescript-eslint/type-utils": "8.59.4", - "@typescript-eslint/utils": "8.59.4", - "@typescript-eslint/visitor-keys": "8.59.4", + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/type-utils": "8.59.3", + "@typescript-eslint/utils": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -5343,7 +5588,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.59.4", + "@typescript-eslint/parser": "^8.59.3", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -5359,16 +5604,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.59.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.4.tgz", - "integrity": "sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.3.tgz", + "integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.59.4", - "@typescript-eslint/types": "8.59.4", - "@typescript-eslint/typescript-estree": "8.59.4", - "@typescript-eslint/visitor-keys": "8.59.4", + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", "debug": "^4.4.3" }, "engines": { @@ -5384,14 +5629,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.59.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.4.tgz", - "integrity": "sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.3.tgz", + "integrity": "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.4", - "@typescript-eslint/types": "^8.59.4", + "@typescript-eslint/tsconfig-utils": "^8.59.3", + "@typescript-eslint/types": "^8.59.3", "debug": "^4.4.3" }, "engines": { @@ -5406,14 +5651,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.4.tgz", - "integrity": "sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz", + "integrity": "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.4", - "@typescript-eslint/visitor-keys": "8.59.4" + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5424,9 +5669,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.4.tgz", - "integrity": "sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.3.tgz", + "integrity": "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==", "dev": true, "license": "MIT", "engines": { @@ -5441,15 +5686,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.59.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.4.tgz", - "integrity": "sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.3.tgz", + "integrity": "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.4", - "@typescript-eslint/typescript-estree": "8.59.4", - "@typescript-eslint/utils": "8.59.4", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/utils": "8.59.3", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -5466,9 +5711,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.59.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.4.tgz", - "integrity": "sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.3.tgz", + "integrity": "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==", "dev": true, "license": "MIT", "engines": { @@ -5480,16 +5725,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.4.tgz", - "integrity": "sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.3.tgz", + "integrity": "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.4", - "@typescript-eslint/tsconfig-utils": "8.59.4", - "@typescript-eslint/types": "8.59.4", - "@typescript-eslint/visitor-keys": "8.59.4", + "@typescript-eslint/project-service": "8.59.3", + "@typescript-eslint/tsconfig-utils": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -5547,9 +5792,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", - "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "dev": true, "license": "ISC", "bin": { @@ -5560,16 +5805,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.59.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.4.tgz", - "integrity": "sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.3.tgz", + "integrity": "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.4", - "@typescript-eslint/types": "8.59.4", - "@typescript-eslint/typescript-estree": "8.59.4" + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5584,13 +5829,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.4.tgz", - "integrity": "sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.3.tgz", + "integrity": "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/types": "8.59.3", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -5727,6 +5972,9 @@ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5741,6 +5989,9 @@ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5755,6 +6006,9 @@ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5769,6 +6023,9 @@ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5783,6 +6040,9 @@ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5797,6 +6057,9 @@ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -6122,6 +6385,16 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", @@ -6217,6 +6490,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -7281,9 +7566,9 @@ } }, "node_modules/comment-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-5.0.0.tgz", - "integrity": "sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.6.2.tgz", + "integrity": "sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==", "dev": true, "license": "MIT", "dependencies": { @@ -7508,33 +7793,33 @@ } }, "node_modules/cspell": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/cspell/-/cspell-10.0.1.tgz", - "integrity": "sha512-Gg6w/flT3fKfl3la62hfTnhtNnDQ+9mU7kUhVqw/axl/Ms4oENw0oJMkWFIoj4f6nL/SDPz7KcPXd2XbkKFNmQ==", + "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": "10.0.1", - "@cspell/cspell-performance-monitor": "10.0.1", - "@cspell/cspell-pipe": "10.0.1", - "@cspell/cspell-types": "10.0.1", - "@cspell/cspell-worker": "10.0.1", - "@cspell/dynamic-import": "10.0.1", - "@cspell/url": "10.0.1", + "@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": "10.0.1", - "cspell-dictionary": "10.0.1", - "cspell-gitignore": "10.0.1", - "cspell-glob": "10.0.1", - "cspell-io": "10.0.1", - "cspell-lib": "10.0.1", + "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.4.2", - "semver": "^7.8.1", - "tinyglobby": "^0.2.16" + "semver": "^7.7.4", + "tinyglobby": "^0.2.15" }, "bin": { "cspell": "bin.mjs", @@ -7548,32 +7833,32 @@ } }, "node_modules/cspell-config-lib": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-10.0.1.tgz", - "integrity": "sha512-hMpo/0j6k7pbiqrLDOLJKD2IGP9XwhjKf2miiM6p84Xeo4nyuFZaxxDCQ68R851HSYFrrdltgpoipMbj1h2Tnw==", + "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": "10.0.1", - "comment-json": "^5.0.0", + "@cspell/cspell-types": "10.0.0", + "comment-json": "^4.6.2", "smol-toml": "^1.6.1", - "yaml": "^2.9.0" + "yaml": "^2.8.3" }, "engines": { "node": ">=22.18.0" } }, "node_modules/cspell-dictionary": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-10.0.1.tgz", - "integrity": "sha512-3cZ659vgsZWkzGQJR/sNqGDVt/OnvTSieLKI76V++4t1bHJfochb9ZrrwsuMsb1VPGiyqClUP1/O6WrefF/FVg==", + "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": "10.0.1", - "@cspell/cspell-pipe": "10.0.1", - "@cspell/cspell-types": "10.0.1", - "cspell-trie-lib": "10.0.1", + "@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": { @@ -7581,15 +7866,15 @@ } }, "node_modules/cspell-gitignore": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-10.0.1.tgz", - "integrity": "sha512-wN23U61Mx6qPJN3CesOmBU9vnbJ0jQm/ylK0iaVui3CcnO7Zzl5qLu5mPHUzGQGm8yso6qjyxqo16Ho7LpZGOQ==", + "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": "10.0.1", - "cspell-glob": "10.0.1", - "cspell-io": "10.0.1" + "@cspell/url": "10.0.0", + "cspell-glob": "10.0.0", + "cspell-io": "10.0.0" }, "bin": { "cspell-gitignore": "bin.mjs" @@ -7599,13 +7884,13 @@ } }, "node_modules/cspell-glob": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-10.0.1.tgz", - "integrity": "sha512-7bII9J3aSSpZDwhx7w+zfQXbMxHZQ3be0ilUp5bHrsjz6o07v/NqOHMGcwKdPn1sw2dxDz9sv057xE5pqXnSdw==", + "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": "10.0.1", + "@cspell/url": "10.0.0", "picomatch": "^4.0.4" }, "engines": { @@ -7626,14 +7911,14 @@ } }, "node_modules/cspell-grammar": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-10.0.1.tgz", - "integrity": "sha512-xC9AFYmaI9wsO//a7S5tdDGKGJVD5UEEsTg+Up2fi7lPfXIryisYmV6tePNL1SEg0idYss4ja8LUZ3Mib09BjQ==", + "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": "10.0.1", - "@cspell/cspell-types": "10.0.1" + "@cspell/cspell-pipe": "10.0.0", + "@cspell/cspell-types": "10.0.0" }, "bin": { "cspell-grammar": "bin.mjs" @@ -7643,42 +7928,42 @@ } }, "node_modules/cspell-io": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-10.0.1.tgz", - "integrity": "sha512-8C2ka07faxflnaqEBO3pektS21XViE/SEHT7F5ZD1ou7FyMR5u3xawTBJSczClfsxLt/WYeztBYrpmGAjmjksw==", + "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": "10.0.1", - "@cspell/url": "10.0.1" + "@cspell/cspell-service-bus": "10.0.0", + "@cspell/url": "10.0.0" }, "engines": { "node": ">=22.18.0" } }, "node_modules/cspell-lib": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-10.0.1.tgz", - "integrity": "sha512-RpsIPiLzc4/YMW8BMRKpyJ81x439qjYWcqgdKeXnMkbKM88J9PexzutfFf/4v97v96KzfNitEzMpbI0uj8OeUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspell/cspell-bundled-dicts": "10.0.1", - "@cspell/cspell-performance-monitor": "10.0.1", - "@cspell/cspell-pipe": "10.0.1", - "@cspell/cspell-resolver": "10.0.1", - "@cspell/cspell-types": "10.0.1", - "@cspell/dynamic-import": "10.0.1", - "@cspell/filetypes": "10.0.1", - "@cspell/rpc": "10.0.1", - "@cspell/strong-weak-map": "10.0.1", - "@cspell/url": "10.0.1", - "cspell-config-lib": "10.0.1", - "cspell-dictionary": "10.0.1", - "cspell-glob": "10.0.1", - "cspell-grammar": "10.0.1", - "cspell-io": "10.0.1", - "cspell-trie-lib": "10.0.1", + "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": "^4.0.0", @@ -7705,16 +7990,16 @@ } }, "node_modules/cspell-trie-lib": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-10.0.1.tgz", - "integrity": "sha512-BFvhalSkRQFjKrZ//FKK7fRGrZFpifnxB5AwCkzsIsBZqicsfafcQ1xP21qpb0QqyV/IomjNgviG+tRJs+0rMw==", + "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": ">=22.18.0" }, "peerDependencies": { - "@cspell/cspell-types": "10.0.1" + "@cspell/cspell-types": "10.0.0" } }, "node_modules/cspell/node_modules/chalk": { @@ -7741,9 +8026,9 @@ } }, "node_modules/cspell/node_modules/semver": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", - "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -7753,6 +8038,71 @@ "node": ">=10" } }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -7832,6 +8182,13 @@ } } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/decode-named-character-reference": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", @@ -8230,14 +8587,14 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz", - "integrity": "sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==", + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" + "tapable": "^2.3.0" }, "engines": { "node": ">=10.13.0" @@ -8257,6 +8614,19 @@ "node": ">=8.6" } }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/env-paths": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-4.0.0.tgz", @@ -8415,9 +8785,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, "license": "MIT" }, @@ -8588,9 +8958,9 @@ } }, "node_modules/eslint-compat-utils/node_modules/semver": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", - "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "dev": true, "license": "ISC", "bin": { @@ -8682,9 +9052,9 @@ } }, "node_modules/eslint-config-webpack/node_modules/semver": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", - "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "dev": true, "license": "ISC", "bin": { @@ -8963,9 +9333,9 @@ } }, "node_modules/eslint-plugin-n/node_modules/semver": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", - "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", "dev": true, "license": "ISC", "bin": { @@ -9502,9 +9872,9 @@ } }, "node_modules/express-4/node_modules/body-parser": { - "version": "1.20.5", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", - "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", "dev": true, "license": "MIT", "dependencies": { @@ -9516,7 +9886,7 @@ "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", - "qs": "~6.15.1", + "qs": "~6.14.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" @@ -9526,22 +9896,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/express-4/node_modules/body-parser/node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/express-4/node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -10835,9 +11189,9 @@ } }, "node_modules/hono": { - "version": "4.12.25", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.25.tgz", - "integrity": "sha512-2NFaIyNVgJmBs/ecmtGzlmluTFs5cHEWGTdu0t1HBwYzoGXOL5nUQBRMXsXWla5i4KkG//QMzVP88m1+I3fdAQ==", + "version": "4.12.18", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.18.tgz", + "integrity": "sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ==", "dev": true, "license": "MIT", "engines": { @@ -10851,11 +11205,23 @@ "dev": true, "license": "ISC" }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/html-entities": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", - "dev": true, "funding": [ { "type": "github", @@ -10947,6 +11313,34 @@ "url": "https://opencollective.com/express" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/human-id": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.3.tgz", @@ -11516,6 +11910,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -12269,6 +12670,29 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/jest-environment-jsdom": { + "version": "30.4.1", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-30.4.1.tgz", + "integrity": "sha512-o3nfaN4zej7qgk2X0j8Jhq/S9nAVKs2xK3QeQxeHVvpkEPxaA1yxDGydR+iVI7zPy7Cp62Aq2h3Ja46QvfWHGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.4.1", + "@jest/environment-jsdom-abstract": "30.4.1", + "jsdom": "^26.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/jest-environment-node": { "version": "30.4.1", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.4.1.tgz", @@ -12805,6 +13229,83 @@ "node": ">=20.0.0" } }, + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -13007,9 +13508,9 @@ } }, "node_modules/koa": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/koa/-/koa-3.2.1.tgz", - "integrity": "sha512-e7IpWJrnanNUroVK2taAgMxoEZvHLXdQiNjeExSu/DEIWm83jaKGBgb7tLmu2rMYpA027qFB3iLR/k3AVpFRnA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/koa/-/koa-3.2.0.tgz", + "integrity": "sha512-TrM4/tnNY7uJ1aW55sIIa+dqBvc4V14WRIAlGcWat9wV5pRS9Wr5Zk2ZTjQP1jtfIHDoHiSbPuV08P0fUZo2pg==", "dev": true, "license": "MIT", "dependencies": { @@ -13185,16 +13686,16 @@ "license": "MIT" }, "node_modules/lint-staged": { - "version": "17.0.7", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.0.7.tgz", - "integrity": "sha512-JrSobt+tW3rH8IOMi8tDZd3foorM5yPEkLD/V2NxobgHrFfHWGee4MOLVuZeScgxftEwbHrPHIFA/ZL+nUJeuA==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.0.4.tgz", + "integrity": "sha512-+rU9lSUyVOZ/hDUmRLVGzyS2v73cDdQjX+XQz1AaOdIE4RysLq0HoPW2HrrgeNCLklkhi904VBU1bmgWLHVnkA==", "dev": true, "license": "MIT", "dependencies": { "listr2": "^10.2.1", "picomatch": "^4.0.4", "string-argv": "^0.3.2", - "tinyexec": "^1.2.4" + "tinyexec": "^1.1.2" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -13206,7 +13707,7 @@ "url": "https://opencollective.com/lint-staged" }, "optionalDependencies": { - "yaml": "^2.9.0" + "yaml": "^2.8.4" } }, "node_modules/lint-staged/node_modules/picomatch": { @@ -13290,9 +13791,9 @@ } }, "node_modules/loader-runner": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", - "integrity": "sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "dev": true, "license": "MIT", "engines": { @@ -13842,19 +14343,19 @@ } }, "node_modules/memfs": { - "version": "4.57.7", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.7.tgz", - "integrity": "sha512-YZPphUQZSRGk6ddPlsNuMbztrLwsbUATFNZcqKscSbSJZ4g0+Y3vSZLJ/rfnGZaB1FFhC7SrywZXev6i8lnHgg==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.2.tgz", + "integrity": "sha512-2nWzSsJzrukurSDna4Z0WywuScK4Id3tSKejgu74u8KCdW4uNrseKRSIDg75C6Yw5ZRqBe0F0EtMNlTbUq8bAQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.7", - "@jsonjoy.com/fs-fsa": "4.57.7", - "@jsonjoy.com/fs-node": "4.57.7", - "@jsonjoy.com/fs-node-builtins": "4.57.7", - "@jsonjoy.com/fs-node-to-fsa": "4.57.7", - "@jsonjoy.com/fs-node-utils": "4.57.7", - "@jsonjoy.com/fs-print": "4.57.7", - "@jsonjoy.com/fs-snapshot": "4.57.7", + "@jsonjoy.com/fs-core": "4.57.2", + "@jsonjoy.com/fs-fsa": "4.57.2", + "@jsonjoy.com/fs-node": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-to-fsa": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/fs-print": "4.57.2", + "@jsonjoy.com/fs-snapshot": "4.57.2", "@jsonjoy.com/json-pack": "^1.11.0", "@jsonjoy.com/util": "^1.9.0", "glob-to-regex.js": "^1.0.1", @@ -15026,6 +15527,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/nwsapi": { + "version": "2.2.23", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", + "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -15404,6 +15912,19 @@ "dev": true, "license": "MIT" }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -15642,9 +16163,9 @@ } }, "node_modules/prettier": { - "version": "3.8.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz", - "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==", + "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": { @@ -15803,9 +16324,9 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", + "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -16287,6 +16808,13 @@ "url": "https://opencollective.com/express" } }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -16427,6 +16955,19 @@ "dev": true, "license": "MIT" }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/schema-utils": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", @@ -16658,9 +17199,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", - "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "dev": true, "license": "MIT", "engines": { @@ -17237,7 +17778,6 @@ "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" @@ -17274,7 +17814,6 @@ "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" @@ -17391,6 +17930,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/synckit": { "version": "0.11.12", "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", @@ -17408,9 +17954,9 @@ } }, "node_modules/tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, "license": "MIT", "engines": { @@ -17435,9 +17981,9 @@ } }, "node_modules/terser": { - "version": "5.48.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", - "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", + "version": "5.46.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz", + "integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -17454,9 +18000,9 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.0.tgz", - "integrity": "sha512-Eum+5ajkaOhf5KbM26osvv21kLD7BaGqQ1UA4Ami4arYwylmGUQTgHFpHDdmJod1q4QXa66p0to/FBKID+J1vA==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", + "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", "dev": true, "license": "MIT", "dependencies": { @@ -17476,39 +18022,12 @@ "webpack": "^5.1.0" }, "peerDependenciesMeta": { - "@minify-html/node": { - "optional": true - }, "@swc/core": { "optional": true }, - "@swc/css": { - "optional": true - }, - "@swc/html": { - "optional": true - }, - "clean-css": { - "optional": true - }, - "cssnano": { - "optional": true - }, - "csso": { - "optional": true - }, "esbuild": { "optional": true }, - "html-minifier-terser": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "postcss": { - "optional": true - }, "uglify-js": { "optional": true } @@ -17608,9 +18127,9 @@ } }, "node_modules/tinyexec": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", "dev": true, "license": "MIT", "engines": { @@ -17618,14 +18137,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.4" + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -17665,6 +18184,26 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -17722,6 +18261,19 @@ "node": ">=0.6" } }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -17954,16 +18506,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.59.4", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.4.tgz", - "integrity": "sha512-Rw6+44QNFaXtgHSjPy+Kw8hrJniMYzR85E9yLmOLcfZ91/rz+JXQbDTCmc6ccxMPY6K6PgAq26f0JCBfR7LIPQ==", + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.3.tgz", + "integrity": "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.59.4", - "@typescript-eslint/parser": "8.59.4", - "@typescript-eslint/typescript-estree": "8.59.4", - "@typescript-eslint/utils": "8.59.4" + "@typescript-eslint/eslint-plugin": "8.59.3", + "@typescript-eslint/parser": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/utils": "8.59.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -18301,6 +18853,19 @@ "dev": true, "license": "MIT" }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -18333,12 +18898,13 @@ "license": "BSD-2-Clause" }, "node_modules/webpack": { - "version": "5.107.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.2.tgz", - "integrity": "sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ==", + "version": "5.106.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz", + "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==", "dev": true, "license": "MIT", "dependencies": { + "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", @@ -18348,20 +18914,20 @@ "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.22.0", - "es-module-lexer": "^2.1.0", + "enhanced-resolve": "^5.20.0", + "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", - "loader-runner": "^4.3.2", + "loader-runner": "^4.3.1", "mime-db": "^1.54.0", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", - "terser-webpack-plugin": "^5.5.0", + "terser-webpack-plugin": "^5.3.17", "watchpack": "^2.5.1", - "webpack-sources": "^3.5.0" + "webpack-sources": "^3.3.4" }, "bin": { "webpack": "bin/webpack.js" @@ -18380,9 +18946,9 @@ } }, "node_modules/webpack-sources": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz", - "integrity": "sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", + "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", "dev": true, "license": "MIT", "engines": { @@ -18413,6 +18979,43 @@ "node": ">=4.0" } }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -18651,6 +19254,28 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/ws": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", + "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xdg-basedir": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", @@ -18664,6 +19289,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -18682,9 +19324,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.4.tgz", + "integrity": "sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==", "dev": true, "license": "ISC", "bin": { diff --git a/package.json b/package.json index b87a0ce09..ab9d21e10 100644 --- a/package.json +++ b/package.json @@ -16,25 +16,36 @@ }, "license": "MIT", "author": "Tobias Koppers @sokra", + "exports": { + ".": { + "types": "./types/index.d.ts", + "default": "./dist/index.js" + }, + "./client": "./client/index.js", + "./package.json": "./package.json" + }, "main": "dist/index.js", "types": "types/index.d.ts", "files": [ + "client", "dist", "types" ], "scripts": { "lint:prettier": "prettier --cache --list-different .", "lint:code": "eslint --cache .", - "lint:spelling": "cspell --cache --no-must-find-files --quiet \"**/*.*\"", + "lint:spelling": "cspell --cache --no-must-find-files --quiet --config cspell.config.json \"**/*.*\"", "lint:types": "tsc --pretty --noEmit", + "lint:types-client": "tsc -p tsconfig.client.json --pretty", "lint": "npm-run-all -l -p \"lint:**\"", "fix:js": "npm run lint:code -- --fix", "fix:prettier": "npm run lint:prettier -- --write", "fix": "npm-run-all -l fix:js fix:prettier", - "clean": "del-cli dist types", + "clean": "del-cli client dist types", "prebuild": "npm run clean", "build:types": "tsc && prettier \"types/**/*.ts\" --write", "build:code": "babel src -d dist --copy-files", + "build:client": "babel client-src -d client --copy-files", "build": "npm-run-all -p \"build:**\"", "test:only": "node --experimental-vm-modules ./node_modules/jest-cli/bin/jest", "test:watch": "npm run test:only -- --watch", @@ -46,15 +57,19 @@ "release": "npm run build && changeset publish" }, "dependencies": { + "ansi-html-community": "^0.0.8", + "html-entities": "^2.6.0", "memfs": "^4.56.10", "mime-types": "^3.0.2", "on-finished": "^2.4.1", "range-parser": "^1.2.1", - "schema-utils": "^4.3.3" + "schema-utils": "^4.3.3", + "strip-ansi": "^6.0.1" }, "devDependencies": { "@babel/cli": "^7.16.7", "@babel/core": "^7.16.7", + "@babel/plugin-transform-runtime": "^7.29.0", "@babel/preset-env": "^7.16.7", "@changesets/cli": "^2.30.0", "@changesets/get-github-info": "^0.8.0", @@ -72,7 +87,7 @@ "deepmerge": "^4.2.2", "del-cli": "^7.0.0", "eslint": "^9.28.0", - "eslint-config-webpack": "^4.9.5", + "eslint-config-webpack": "^4.9.6", "execa": "^9.6.1", "express": "^5.1.0", "express-4": "npm:express@^4", @@ -82,6 +97,7 @@ "hono": "^4.12.12", "husky": "^9.1.3", "jest": "^30.1.3", + "jest-environment-jsdom": "^30.4.1", "koa": "^3.0.0", "lint-staged": "^17.0.2", "npm-run-all": "^4.1.5", diff --git a/src/hot.js b/src/hot.js new file mode 100644 index 000000000..dd3f4090b --- /dev/null +++ b/src/hot.js @@ -0,0 +1,340 @@ +/** @typedef {import("webpack").Compiler} Compiler */ +/** @typedef {import("webpack").MultiCompiler} MultiCompiler */ +/** @typedef {ReturnType} Logger */ +/** @typedef {import("webpack").Stats} Stats */ +/** @typedef {import("webpack").MultiStats} MultiStats */ +/** @typedef {import("webpack").StatsCompilation} StatsCompilation */ +/** @typedef {import("webpack").StatsError} StatsError */ +/** @typedef {import("webpack").StatsModule} StatsModule */ +/** @typedef {import("./index.js").IncomingMessage} IncomingMessage */ +/** @typedef {import("./index.js").ServerResponse} ServerResponse */ + +/** @typedef {NonNullable} StatsOptions */ + +/** + * @typedef {object} HotOptions + * @property {string=} path the path the SSE endpoint is served at + * @property {number=} heartbeat heartbeat interval in milliseconds + * @property {StatsOptions=} statsOptions webpack stats options used when serializing compilation results + */ + +/** + * @typedef {object} Payload + * @property {string} action action + * @property {string=} name name + * @property {number=} time time + * @property {string=} hash hash + * @property {string[]=} warnings warnings + * @property {string[]=} errors errors + * @property {Record=} modules modules + */ + +/** + * @typedef {object} EventStream + * @property {(req: IncomingMessage, res: ServerResponse) => void} handler attach a new client + * @property {(payload: Payload | { action: string }) => void} publish publish a payload to every client + * @property {() => void} close end every client and stop the heartbeat + */ + +const HOT_DEFAULT_PATH = "/__webpack_hmr"; +const HOT_DEFAULT_HEARTBEAT = 10 * 1000; +const PLUGIN_NAME = "DevMiddleware"; + +/** + * @param {string | undefined} url url + * @param {string} expected expected pathname + * @returns {boolean} true when the url pathname matches the expected path + */ +function pathMatch(url, expected) { + if (!url) return false; + + try { + return new URL(url, "http://localhost").pathname === expected; + } catch { + return false; + } +} + +/** + * @param {number} heartbeat heartbeat interval in milliseconds + * @param {Logger} logger logger + * @returns {EventStream} event stream + */ +function createEventStream(heartbeat, logger) { + let clientId = 0; + /** @type {Map} */ + let clients = new Map(); + + /** + * @param {(client: ServerResponse) => void} fn each client callback + */ + const everyClient = (fn) => { + for (const client of clients.values()) { + fn(client); + } + }; + + const interval = setInterval(() => { + everyClient((client) => { + client.write("data: 💓\n\n"); + }); + }, heartbeat); + + // Don't block process exit on the heartbeat timer. + if (typeof interval.unref === "function") { + interval.unref(); + } + + return { + close() { + clearInterval(interval); + everyClient((client) => { + if (!client.writableEnded) { + client.end(); + } + }); + clients = new Map(); + }, + handler(req, res) { + /** @type {Record} */ + const headers = { + "Access-Control-Allow-Origin": "*", + "Content-Type": "text/event-stream;charset=utf-8", + "Cache-Control": "no-cache, no-transform", + // While behind nginx, the event stream should not be buffered: + // http://nginx.org/docs/http/ngx_http_proxy_module.html#proxy_buffering + "X-Accel-Buffering": "no", + }; + + const { httpVersion, socket } = req; + const isHttp1 = !(Number.parseInt(httpVersion, 10) >= 2); + + if (isHttp1) { + if (socket && typeof socket.setKeepAlive === "function") { + socket.setKeepAlive(true); + } + headers.Connection = "keep-alive"; + } + + res.writeHead(200, headers); + res.write("\n"); + + const id = clientId++; + clients.set(id, res); + logger.log(`Client connected (${clients.size} active)`); + + req.on("close", () => { + if (!res.writableEnded) { + res.end(); + } + clients.delete(id); + logger.log(`Client disconnected (${clients.size} active)`); + }); + }, + publish(payload) { + everyClient((client) => { + client.write(`data: ${JSON.stringify(payload)}\n\n`); + }); + }, + }; +} + +/** + * @param {(string | StatsError)[]} errors errors or warnings + * @returns {string[]} flat strings + */ +function formatErrors(errors) { + if (!errors || errors.length === 0) { + return []; + } + + if (typeof errors[0] === "string") { + return /** @type {string[]} */ (errors); + } + + return /** @type {StatsError[]} */ (errors).map((error) => { + const moduleName = error.moduleName || ""; + const loc = error.loc || ""; + + return `${moduleName} ${loc}\n${error.message}`; + }); +} + +/** + * @param {Stats} stats stats + * @param {StatsOptions} statsOptions stats options + * @returns {StatsCompilation} json stats with compilation reference attached + */ +function normalizeStats(stats, statsOptions) { + const statsJson = stats.toJson(statsOptions); + + if (stats.compilation) { + statsJson.compilation = stats.compilation; + } + + return statsJson; +} + +/** + * @param {StatsCompilation} stats normalized stats + * @returns {StatsCompilation[]} extracted bundles + */ +function extractBundles(stats) { + if (stats.modules) { + return [stats]; + } + + if (stats.children && stats.children.length > 0) { + return stats.children; + } + + return [stats]; +} + +/** + * @param {StatsModule[]} modules modules + * @returns {Record} module id to name map + */ +function buildModuleMap(modules) { + /** @type {Record} */ + const map = {}; + + for (const item of modules) { + map[/** @type {string | number} */ (item.id)] = /** @type {string} */ ( + item.name + ); + } + + return map; +} + +/** + * @param {string} action action + * @param {Stats | MultiStats} statsResult stats result + * @param {EventStream} eventStream event stream + * @param {StatsOptions | undefined} statsOptions stats options + */ +function publishStats(action, statsResult, eventStream, statsOptions) { + const resultStatsOptions = { + all: false, + hash: true, + timings: true, + errors: true, + warnings: true, + ...(statsOptions && typeof statsOptions === "object" ? statsOptions : {}), + }; + + /** @type {StatsCompilation[]} */ + let bundles; + + // Multi-compiler stats have stats for each child compiler. + if ("stats" in statsResult) { + bundles = statsResult.stats.flatMap((stats) => + extractBundles(normalizeStats(stats, resultStatsOptions)), + ); + } else { + bundles = extractBundles(normalizeStats(statsResult, resultStatsOptions)); + } + + for (const stats of bundles) { + let name = stats.name || ""; + + // Fallback to compilation name when there is a single bundle. + if (!name && stats.compilation) { + name = stats.compilation.name || ""; + } + + eventStream.publish({ + name, + action, + time: stats.time, + hash: stats.hash, + warnings: formatErrors(stats.warnings || []), + errors: formatErrors(stats.errors || []), + modules: buildModuleMap(stats.modules || []), + }); + } +} + +/** + * @typedef {object} HotInstance + * @property {string} path path the SSE endpoint is served at + * @property {(req: IncomingMessage, res: ServerResponse) => void} handle attach the request as a SSE client + * @property {(payload: Payload | { action: string }) => void} publish publish a payload to every client + * @property {() => void} close end every client and detach the heartbeat + */ + +/** + * @param {Compiler | MultiCompiler} compiler compiler + * @param {HotOptions | true} userOptions options + * @returns {HotInstance} hot instance + */ +function createHot(compiler, userOptions) { + const options = userOptions === true ? {} : userOptions; + const path = options.path || HOT_DEFAULT_PATH; + const heartbeat = options.heartbeat || HOT_DEFAULT_HEARTBEAT; + const { statsOptions } = options; + const logger = compiler.getInfrastructureLogger("webpack-dev-middleware"); + + let eventStream = createEventStream(heartbeat, logger); + logger.log(`Hot module replacement enabled, serving events at "${path}"`); + /** @type {Stats | MultiStats | null} */ + let latestStats = null; + let closed = false; + + const onInvalid = () => { + if (closed) return; + + latestStats = null; + + eventStream.publish({ action: "building" }); + }; + + /** @param {Stats | MultiStats} statsResult stats result */ + const onDone = (statsResult) => { + if (closed) return; + + latestStats = statsResult; + publishStats("built", latestStats, eventStream, statsOptions); + }; + + compiler.hooks.invalid.tap(PLUGIN_NAME, onInvalid); + compiler.hooks.done.tap(PLUGIN_NAME, onDone); + + return { + path, + handle(req, res) { + if (closed) return; + + eventStream.handler(req, res); + + if (latestStats) { + publishStats("sync", latestStats, eventStream, statsOptions); + } + }, + publish(payload) { + if (closed) return; + + eventStream.publish(payload); + }, + close() { + if (closed) return; + + // Can't remove compiler plugins, so we set a flag and noop if closed. + // https://github.com/webpack/tapable/issues/32#issuecomment-350644466 + closed = true; + eventStream.close(); + eventStream = /** @type {EventStream} */ (/** @type {unknown} */ (null)); + }, + }; +} + +module.exports = createHot; +module.exports.HOT_DEFAULT_HEARTBEAT = HOT_DEFAULT_HEARTBEAT; +module.exports.HOT_DEFAULT_PATH = HOT_DEFAULT_PATH; +module.exports.buildModuleMap = buildModuleMap; +module.exports.createEventStream = createEventStream; +module.exports.createHot = createHot; +module.exports.formatErrors = formatErrors; +module.exports.pathMatch = pathMatch; +module.exports.publishStats = publishStats; diff --git a/src/index.js b/src/index.js index 053d3b45e..429965524 100644 --- a/src/index.js +++ b/src/index.js @@ -1,8 +1,14 @@ const fs = require("node:fs"); const path = require("node:path"); +// `stream/web` is flagged experimental by the n/eslint plugin until Node 21, +// but `ReadableStream` is stable in practice on Node 20.9+ (our minimum) and +// already pulled in by hono/web frameworks we integrate with. +// eslint-disable-next-line n/no-unsupported-features/node-builtins +const { ReadableStream } = require("node:stream/web"); const memfs = require("memfs"); const mime = require("mime-types"); +const { createHot } = require("./hot"); const middleware = require("./middleware"); const { nodeReadableToWebStream } = require("./utils"); @@ -16,6 +22,8 @@ const noop = () => {}; /** @typedef {import("webpack").MultiStats} MultiStats */ /** @typedef {import("fs").ReadStream} ReadStream */ /** @typedef {import("./middleware").FilenameWithExtra} FilenameWithExtra */ +/** @typedef {import("./hot").HotOptions} HotOptions */ +/** @typedef {import("./hot").HotInstance} HotInstance */ // eslint-disable-next-line jsdoc/reject-any-type /** @typedef {any} EXPECTED_ANY */ @@ -76,6 +84,7 @@ const noop = () => {}; * @property {Watching | MultiWatching} watching watching * @property {Logger} logger logger * @property {OutputFileSystem} outputFileSystem output file system + * @property {HotInstance=} hot hot module replacement instance */ /** @@ -112,6 +121,7 @@ const noop = () => {}; * @property {(boolean | number | string | { maxAge?: number, immutable?: boolean })=} cacheControl options to generate cache headers * @property {boolean=} cacheImmutable is cache immutable * @property {boolean=} forwardError forward error to next middleware + * @property {(boolean | HotOptions)=} hot enable hot module replacement */ /** @@ -504,6 +514,10 @@ function wdm(compiler, options = {}, isPlugin = false) { compiler.hooks.invalid.tap(PLUGIN_NAME, invalid); compiler.hooks.done.tap(PLUGIN_NAME, done); + if (options.hot) { + context.hot = createHot(compiler, options.hot === true ? {} : options.hot); + } + const compilersToModify = isMultipleCompiler(compiler) ? compiler.compilers.filter((item) => item.options.devServer !== false) : [compiler]; @@ -600,6 +614,9 @@ function wdm(compiler, options = {}, isPlugin = false) { }; instance.close = (callback = noop) => { + if (filledContext.hot) { + filledContext.hot.close(); + } filledContext.watching.close(callback); }; @@ -935,6 +952,33 @@ function honoWrapper(compiler, options = {}, usePlugin = false) { let body; let isFinished = false; + // Hot middleware writes raw chunks via `res.writeHead` / `res.write` / `res.end`. + // Hono's response object is Web API-style, so we shim those methods on top of a + // Web ReadableStream that hono streams back to the client. + /** @type {ReadableStreamDefaultController | undefined} */ + let sseController; + let sseClosed = false; + /** @type {(() => void)[]} */ + const closeHandlers = []; + + /** + * @param {string} event event name + * @param {() => void} handler handler + * @returns {EXPECTED_ANY} req + */ + const reqOn = (event, handler) => { + if (event === "close") { + closeHandlers.push(handler); + if (sseClosed) handler(); + } + return req; + }; + + if (typeof (/** @type {EXPECTED_ANY} */ (req).on) !== "function") { + /** @type {EXPECTED_ANY} */ + (req).on = reqOn; + } + try { await new Promise( /** @@ -942,6 +986,72 @@ function honoWrapper(compiler, options = {}, usePlugin = false) { * @param {(reason?: Error) => void} reject reject */ (resolve, reject) => { + /** @type {EXPECTED_ANY} */ + (res).writeHead = + /** + * @param {number} statusCode status code + * @param {Record=} headers headers + */ + (statusCode, headers) => { + status = statusCode; + + if (headers) { + for (const name of Object.keys(headers)) { + context.res.headers.append(name, String(headers[name])); + } + } + + body = new ReadableStream({ + start(controller) { + sseController = controller; + }, + cancel() { + sseClosed = true; + for (const fn of closeHandlers) fn(); + }, + }); + isFinished = true; + resolve(); + }; + + /** @type {EXPECTED_ANY} */ + (res).write = + /** + * @param {string | Buffer} chunk chunk to write + * @returns {boolean} true when written + */ + (chunk) => { + if (!sseController || sseClosed) return false; + try { + sseController.enqueue( + typeof chunk === "string" + ? new TextEncoder().encode(chunk) + : chunk, + ); + } catch { + return false; + } + return true; + }; + + /** @type {EXPECTED_ANY} */ + (res).end = () => { + if (!sseController || sseClosed) return; + sseClosed = true; + try { + sseController.close(); + } catch { + // already closed + } + }; + + Object.defineProperty(res, "writableEnded", { + configurable: true, + get() { + return sseClosed; + }, + }); + /** * @param {import("fs").ReadStream} stream readable stream */ diff --git a/src/middleware.js b/src/middleware.js index e92ec449c..6f84d8a5b 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -4,6 +4,8 @@ const querystring = require("node:querystring"); const mime = require("mime-types"); const onFinishedStream = require("on-finished"); +const { pathMatch: hotPathMatch } = require("./hot"); + const { createReadStreamOrReadFile, destroyStream, @@ -376,6 +378,12 @@ function ready(context, callback, req) { */ function wrapper(context) { return async function middleware(req, res, next) { + // Intercept Server-Sent Events handshake when the `hot` option is enabled. + if (context.hot && hotPathMatch(getRequestURL(req), context.hot.path)) { + context.hot.handle(req, res); + return; + } + /** * @param {NodeJS.ErrnoException=} err an error * @returns {Promise} diff --git a/src/options.json b/src/options.json index 1e83adaa1..950051f7f 100644 --- a/src/options.json +++ b/src/options.json @@ -177,6 +177,43 @@ "description": "Enable or disable forwarding errors to next middleware.", "link": "https://github.com/webpack/webpack-dev-middleware#forwarderrors", "type": "boolean" + }, + "hot": { + "description": "Enable hot module replacement via a Server-Sent Events endpoint.", + "link": "https://github.com/webpack/webpack-dev-middleware#hot", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "description": "The path the SSE endpoint is served at.", + "type": "string", + "minLength": 1 + }, + "heartbeat": { + "description": "Heartbeat interval (in milliseconds) used to keep the SSE connection alive.", + "type": "number", + "minimum": 0 + }, + "statsOptions": { + "description": "Webpack stats options used when serializing compilation results.", + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "additionalProperties": true + } + ] + } + } + } + ] } }, "additionalProperties": false diff --git a/test/__snapshots__/client.test.js.snap.webpack5 b/test/__snapshots__/client.test.js.snap.webpack5 new file mode 100644 index 000000000..e03872bc6 --- /dev/null +++ b/test/__snapshots__/client.test.js.snap.webpack5 @@ -0,0 +1,66 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`client with default options does not show overlay on warning builds by default 1`] = ` +[ + [ + "[webpack-dev-middleware] bundle has 1 warnings", + ], + [ + "[webpack-dev-middleware] This isn't great, but it's not terrible", + ], +] +`; + +exports[`client with default options shows overlay on errored builds 1`] = ` +[ + [ + "[webpack-dev-middleware] bundle has 2 errors", + ], + [ + "[webpack-dev-middleware] Something broke +Actually, 2 things broke", + ], +] +`; + +exports[`client with logging option emits info-level logs (including the [webpack-dev-middleware] prefix) by default 1`] = ` +[ + [ + "[webpack-dev-middleware] bundle rebuilt in 100ms", + ], +] +`; + +exports[`client with logging option logging=error silences info and warn but keeps error 1`] = ` +[ + [ + "[webpack-dev-middleware] bundle has 1 errors", + ], + [ + "[webpack-dev-middleware] boom", + ], +] +`; + +exports[`client with logging option logging=warn silences info but keeps warn 1`] = ` +[ + [ + "[webpack-dev-middleware] bundle has 1 warnings", + ], + [ + "[webpack-dev-middleware] something", + ], +] +`; + +exports[`client with overlayWarnings: true shows overlay on errored builds 1`] = ` +[ + [ + "[webpack-dev-middleware] bundle has 2 errors", + ], + [ + "[webpack-dev-middleware] Something broke +Actually, 2 things broke", + ], +] +`; diff --git a/test/__snapshots__/validation-options.test.js.snap.webpack5 b/test/__snapshots__/validation-options.test.js.snap.webpack5 index ec12f26e9..f95a41978 100644 --- a/test/__snapshots__/validation-options.test.js.snap.webpack5 +++ b/test/__snapshots__/validation-options.test.js.snap.webpack5 @@ -75,6 +75,48 @@ exports[`validation should throw an error on the "headers" option with "true" va * options.headers should be an instance of function." `; +exports[`validation should throw an error on the "hot" option with "{"heartbeat":-1}" value 1`] = ` +"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema. + - options.hot.heartbeat should be >= 0. + -> Heartbeat interval (in milliseconds) used to keep the SSE connection alive." +`; + +exports[`validation should throw an error on the "hot" option with "{"path":""}" value 1`] = ` +"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema. + - options.hot.path should be a non-empty string. + -> The path the SSE endpoint is served at." +`; + +exports[`validation should throw an error on the "hot" option with "{"unknown":true}" value 1`] = ` +"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema. + - options.hot has an unknown property 'unknown'. These properties are valid: + object { path?, heartbeat?, statsOptions? }" +`; + +exports[`validation should throw an error on the "hot" option with "0" value 1`] = ` +"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema. + - options.hot should be one of these: + boolean | object { path?, heartbeat?, statsOptions? } + -> Enable hot module replacement via a Server-Sent Events endpoint. + -> Read more at https://github.com/webpack/webpack-dev-middleware#hot + Details: + * options.hot should be a boolean. + * options.hot should be an object: + object { path?, heartbeat?, statsOptions? }" +`; + +exports[`validation should throw an error on the "hot" option with "foo" value 1`] = ` +"Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema. + - options.hot should be one of these: + boolean | object { path?, heartbeat?, statsOptions? } + -> Enable hot module replacement via a Server-Sent Events endpoint. + -> Read more at https://github.com/webpack/webpack-dev-middleware#hot + Details: + * options.hot should be a boolean. + * options.hot should be an object: + object { path?, heartbeat?, statsOptions? }" +`; + exports[`validation should throw an error on the "index" option with "{}" value 1`] = ` "Invalid options object. Dev Middleware has been initialized using an options object that does not match the API schema. - options.index should be one of these: diff --git a/test/client.test.js b/test/client.test.js new file mode 100644 index 000000000..e310b30b0 --- /dev/null +++ b/test/client.test.js @@ -0,0 +1,701 @@ +/** + * @jest-environment jsdom + */ + +// eslint-disable-next-line jsdoc/reject-any-type +/** @typedef {any} EXPECTED_ANY */ + +/** @type {EXPECTED_ANY} */ +let processUpdate; +/** @type {{ showProblems: jest.Mock, clear: jest.Mock }} */ +let clientOverlay; + +jest.mock("../client-src/process-update", () => { + const fn = jest.fn(); + return fn; +}); + +jest.mock("../client-src/overlay", () => { + const overlay = { showProblems: jest.fn(), clear: jest.fn() }; + const factory = jest.fn(() => overlay); + factory.__getOverlay = () => overlay; + return factory; +}); + +/** + * @param {EXPECTED_ANY} obj message payload + * @returns {{ data: string }} fake SSE event + */ +function makeMessage(obj) { + return { data: typeof obj === "string" ? obj : JSON.stringify(obj) }; +} + +/** + * Stub `EventSource` so each test can drive `message`/`error`/`open` events. + * @returns {EXPECTED_ANY} fake constructor + last instance accessor + */ +function makeEventSourceStub() { + /** @type {EXPECTED_ANY[]} */ + const instances = []; + function EventSourceStub(url) { + this.url = url; + this.listeners = { open: [], error: [], message: [] }; + this.closed = false; + this.addEventListener = (type, fn) => { + if (this.listeners[type]) this.listeners[type].push(fn); + }; + this.dispatch = (type, event) => { + for (const fn of this.listeners[type] || []) fn(event); + }; + this.onmessage = (event) => this.dispatch("message", event); + // eslint-disable-next-line jest/prefer-spy-on + this.close = jest.fn(() => { + this.closed = true; + }); + instances.push(this); + } + EventSourceStub.instances = instances; + EventSourceStub.lastInstance = () => instances[instances.length - 1]; + return EventSourceStub; +} + +/** + * Reset module state so each test loads a fresh client. The per-page + * singletons on `window` are NOT cleared here — the outer `afterEach` handles + * that, so tests that re-require the client on the same "page" can observe + * the wrapper being reused. + * @param {string=} resourceQuery `__resourceQuery` value injected by webpack + * @returns {EXPECTED_ANY} client module + */ +function loadClient(resourceQuery = "") { + jest.resetModules(); + globalThis.__resourceQuery = resourceQuery; + processUpdate = require("../client-src/process-update"); + processUpdate.mockReset(); + + const overlayFactory = require("../client-src/overlay"); + + clientOverlay = overlayFactory.__getOverlay(); + clientOverlay.showProblems.mockReset(); + clientOverlay.clear.mockReset(); + + return require("../client-src"); +} + +describe("client", () => { + afterEach(() => { + delete globalThis.__resourceQuery; + delete globalThis.EventSource; + delete globalThis.__wdmEventSourceWrapper; + delete globalThis.__webpack_dev_middleware_hot_reporter__; + jest.useRealTimers(); + }); + + describe("with default options", () => { + let EventSourceStub; + let client; + + beforeEach(() => { + EventSourceStub = makeEventSourceStub(); + globalThis.EventSource = EventSourceStub; + jest.spyOn(console, "info").mockImplementation(() => {}); + jest.spyOn(console, "log").mockImplementation(() => {}); + jest.spyOn(console, "warn").mockImplementation(() => {}); + jest.spyOn(console, "error").mockImplementation(() => {}); + client = loadClient(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it("connects to /__webpack_hmr", () => { + expect(EventSourceStub.instances).toHaveLength(1); + expect(EventSourceStub.instances[0].url).toBe("/__webpack_hmr"); + }); + + it("triggers webpack on successful builds", () => { + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: [], + modules: [], + }), + ); + expect(processUpdate).toHaveBeenCalledTimes(1); + }); + + it("passes reload:true to the updater by default", () => { + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: [], + modules: [], + }), + ); + expect(processUpdate).toHaveBeenCalledWith( + "1234567890abcdef", + expect.anything(), + expect.objectContaining({ reload: true }), + ); + }); + + it("triggers webpack on successful syncs", () => { + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "sync", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: [], + modules: [], + }), + ); + expect(processUpdate).toHaveBeenCalledTimes(1); + }); + + it("calls subscribeAll handler on default messages", () => { + const spy = jest.fn(); + client.subscribeAll(spy); + const message = { + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: [], + modules: [], + }; + EventSourceStub.lastInstance().onmessage(makeMessage(message)); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith(message); + }); + + it("calls subscribeAll handler on custom messages", () => { + const spy = jest.fn(); + client.subscribeAll(spy); + EventSourceStub.lastInstance().onmessage( + makeMessage({ action: "thingy" }), + ); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith({ action: "thingy" }); + }); + + it("calls only the custom handler for custom messages", () => { + const spy = jest.fn(); + client.subscribe(spy); + EventSourceStub.lastInstance().onmessage( + makeMessage({ custom: "thingy" }), + ); + EventSourceStub.lastInstance().onmessage( + makeMessage({ action: "built" }), + ); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith({ custom: "thingy" }); + expect(processUpdate).not.toHaveBeenCalled(); + }); + + it("does not trigger webpack on errored builds", () => { + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: ["Something broke"], + warnings: [], + modules: [], + }), + ); + expect(processUpdate).not.toHaveBeenCalled(); + }); + + it("shows overlay on errored builds", () => { + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: ["Something broke", "Actually, 2 things broke"], + warnings: [], + modules: [], + }), + ); + expect(clientOverlay.showProblems).toHaveBeenCalledTimes(1); + expect(clientOverlay.showProblems).toHaveBeenCalledWith("errors", [ + "Something broke", + "Actually, 2 things broke", + ]); + expect(console.error.mock.calls).toMatchSnapshot(); + }); + + it("hides overlay after errored build is fixed", () => { + const es = EventSourceStub.lastInstance(); + es.onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: ["Something broke", "Actually, 2 things broke"], + warnings: [], + modules: [], + }), + ); + es.onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef2", + errors: [], + warnings: [], + modules: [], + }), + ); + expect(clientOverlay.showProblems).toHaveBeenCalledTimes(1); + expect(clientOverlay.clear).toHaveBeenCalledTimes(1); + }); + + it("hides overlay after errored build becomes a warning", () => { + const es = EventSourceStub.lastInstance(); + es.onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: ["Something broke", "Actually, 2 things broke"], + warnings: [], + modules: [], + }), + ); + es.onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef2", + errors: [], + warnings: ["This isn't great, but it's not terrible"], + modules: [], + }), + ); + expect(clientOverlay.showProblems).toHaveBeenCalledTimes(1); + expect(clientOverlay.clear).toHaveBeenCalledTimes(1); + }); + + it("triggers webpack on warning builds", () => { + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: ["This isn't great, but it's not terrible"], + modules: [], + }), + ); + expect(processUpdate).toHaveBeenCalledTimes(1); + }); + + it("does not show overlay on warning builds by default", () => { + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: ["This isn't great, but it's not terrible"], + modules: [], + }), + ); + expect(clientOverlay.showProblems).not.toHaveBeenCalled(); + // Warnings still surface through the logger even when the overlay stays hidden. + expect(console.warn.mock.calls).toMatchSnapshot(); + }); + + it("shows overlay after warning build becomes an error", () => { + const es = EventSourceStub.lastInstance(); + es.onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: ["This isn't great, but it's not terrible"], + modules: [], + }), + ); + es.onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef2", + errors: ["Something broke", "Actually, 2 things broke"], + warnings: [], + modules: [], + }), + ); + expect(clientOverlay.showProblems).toHaveBeenCalledTimes(1); + }); + }); + + describe("with overlayWarnings: true", () => { + let EventSourceStub; + + beforeEach(() => { + EventSourceStub = makeEventSourceStub(); + globalThis.EventSource = EventSourceStub; + jest.spyOn(console, "info").mockImplementation(() => {}); + jest.spyOn(console, "log").mockImplementation(() => {}); + jest.spyOn(console, "warn").mockImplementation(() => {}); + jest.spyOn(console, "error").mockImplementation(() => {}); + loadClient("?overlayWarnings=true"); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it("shows overlay on errored builds", () => { + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: ["Something broke", "Actually, 2 things broke"], + warnings: [], + modules: [], + }), + ); + expect(clientOverlay.showProblems).toHaveBeenCalledTimes(1); + expect(clientOverlay.showProblems).toHaveBeenCalledWith("errors", [ + "Something broke", + "Actually, 2 things broke", + ]); + expect(console.error.mock.calls).toMatchSnapshot(); + }); + + it("shows overlay on warning builds", () => { + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: ["This isn't great, but it's not terrible"], + modules: [], + }), + ); + expect(clientOverlay.showProblems).toHaveBeenCalledTimes(1); + expect(clientOverlay.showProblems).toHaveBeenCalledWith("warnings", [ + "This isn't great, but it's not terrible", + ]); + }); + + it("hides overlay after warning build is fixed", () => { + const es = EventSourceStub.lastInstance(); + es.onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: ["This isn't great, but it's not terrible"], + modules: [], + }), + ); + es.onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef2", + errors: [], + warnings: [], + modules: [], + }), + ); + expect(clientOverlay.showProblems).toHaveBeenCalledTimes(1); + expect(clientOverlay.clear).toHaveBeenCalledTimes(1); + }); + + it("updates overlay after errored build becomes a warning", () => { + const es = EventSourceStub.lastInstance(); + es.onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: ["Something broke"], + warnings: [], + modules: [], + }), + ); + es.onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef2", + errors: [], + warnings: ["This isn't great, but it's not terrible"], + modules: [], + }), + ); + expect(clientOverlay.showProblems).toHaveBeenCalledTimes(2); + expect(clientOverlay.showProblems).toHaveBeenNthCalledWith(1, "errors", [ + "Something broke", + ]); + expect(clientOverlay.showProblems).toHaveBeenNthCalledWith( + 2, + "warnings", + ["This isn't great, but it's not terrible"], + ); + }); + }); + + describe("with name option", () => { + let EventSourceStub; + + beforeEach(() => { + EventSourceStub = makeEventSourceStub(); + globalThis.EventSource = EventSourceStub; + jest.spyOn(console, "info").mockImplementation(() => {}); + jest.spyOn(console, "log").mockImplementation(() => {}); + jest.spyOn(console, "warn").mockImplementation(() => {}); + loadClient("?name=test"); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it("does not trigger webpack when event name differs", () => { + EventSourceStub.lastInstance().onmessage( + makeMessage({ + name: "foo", + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: [], + modules: [], + }), + ); + expect(processUpdate).not.toHaveBeenCalled(); + }); + + it("does not trigger webpack on sync when event name differs", () => { + EventSourceStub.lastInstance().onmessage( + makeMessage({ + name: "bar", + action: "sync", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: [], + modules: [], + }), + ); + expect(processUpdate).not.toHaveBeenCalled(); + }); + }); + + describe("with reload disabled", () => { + let EventSourceStub; + + beforeEach(() => { + EventSourceStub = makeEventSourceStub(); + globalThis.EventSource = EventSourceStub; + jest.spyOn(console, "info").mockImplementation(() => {}); + jest.spyOn(console, "log").mockImplementation(() => {}); + jest.spyOn(console, "warn").mockImplementation(() => {}); + loadClient("?reload=false"); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it("passes reload:false to the updater", () => { + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: [], + modules: [], + }), + ); + expect(processUpdate).toHaveBeenCalledWith( + "1234567890abcdef", + expect.anything(), + expect.objectContaining({ reload: false }), + ); + }); + }); + + describe("connection lifecycle", () => { + let EventSourceStub; + let client; + + beforeEach(() => { + EventSourceStub = makeEventSourceStub(); + globalThis.EventSource = EventSourceStub; + jest.spyOn(console, "info").mockImplementation(() => {}); + jest.spyOn(console, "log").mockImplementation(() => {}); + jest.spyOn(console, "warn").mockImplementation(() => {}); + client = loadClient(); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it("ignores heartbeat messages", () => { + const handler = jest.fn(); + client.subscribeAll(handler); + EventSourceStub.lastInstance().dispatch("message", { data: "💓" }); + expect(handler).not.toHaveBeenCalled(); + expect(processUpdate).not.toHaveBeenCalled(); + }); + + it("warns on invalid JSON", () => { + EventSourceStub.lastInstance().dispatch("message", { data: "not-json{" }); + expect( + console.warn.mock.calls.some(([msg]) => + /Invalid HMR message/.test(msg), + ), + ).toBe(true); + }); + + it("reuses the EventSource wrapper across reloads on the same path", () => { + // Re-loading the entry on the same page should reuse the cached SSE + // connection rather than opening a new one. + jest.resetModules(); + require("../client-src"); + expect(EventSourceStub.instances).toHaveLength(1); + }); + + it("closes and re-opens the connection on timeout", () => { + // The watchdog interval is created during the client's first load. Fake + // timers must be enabled before that load so jest can drive it. + jest.useFakeTimers({ doNotFake: ["nextTick"] }); + // Drop the wrapper opened by the outer beforeEach so we get a fresh + // EventSource scheduled under fake timers. + delete globalThis.__wdmEventSourceWrapper; + EventSourceStub.instances.length = 0; + loadClient(); + + const [first] = EventSourceStub.instances; + expect(first.closed).toBe(false); + // The watchdog ticks at `timeout/2` and disconnects when + // `Date.now() - lastActivity > timeout`. 30s is enough to cross that + // boundary regardless of which tick reports it first. + jest.advanceTimersByTime(30 * 1000); + expect(first.closed).toBe(true); + // Reconnect is scheduled after `options.timeout` (20s). + jest.advanceTimersByTime(20 * 1000); + expect(EventSourceStub.instances).toHaveLength(2); + }); + }); + + describe("with logging option", () => { + let EventSourceStub; + + beforeEach(() => { + EventSourceStub = makeEventSourceStub(); + globalThis.EventSource = EventSourceStub; + jest.spyOn(console, "info").mockImplementation(() => {}); + jest.spyOn(console, "warn").mockImplementation(() => {}); + jest.spyOn(console, "error").mockImplementation(() => {}); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it("emits info-level logs (including the [webpack-dev-middleware] prefix) by default", () => { + loadClient(); + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: [], + modules: [], + }), + ); + expect(console.info.mock.calls).toMatchSnapshot(); + }); + + it("logging=none silences every level", () => { + loadClient("?logging=none"); + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: ["boom"], + warnings: [], + modules: [], + }), + ); + expect(console.info).not.toHaveBeenCalled(); + expect(console.warn).not.toHaveBeenCalled(); + expect(console.error).not.toHaveBeenCalled(); + }); + + it("logging=warn silences info but keeps warn", () => { + loadClient("?logging=warn&overlayWarnings=true"); + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: [], + warnings: ["something"], + modules: [], + }), + ); + expect(console.info).not.toHaveBeenCalled(); + expect(console.warn.mock.calls).toMatchSnapshot(); + }); + + it("logging=error silences info and warn but keeps error", () => { + loadClient("?logging=error"); + EventSourceStub.lastInstance().onmessage( + makeMessage({ + action: "built", + time: 100, + hash: "1234567890abcdef", + errors: ["boom"], + warnings: [], + modules: [], + }), + ); + expect(console.info).not.toHaveBeenCalled(); + expect(console.warn).not.toHaveBeenCalled(); + expect(console.error.mock.calls).toMatchSnapshot(); + }); + }); + + describe("with no EventSource", () => { + beforeEach(() => { + delete globalThis.EventSource; + jest.spyOn(console, "warn").mockImplementation(() => {}); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it("emits a warning and does not connect", () => { + loadClient(); + expect(console.warn).toHaveBeenCalledTimes(1); + expect(console.warn.mock.calls[0][0]).toMatch(/EventSource/); + }); + }); +}); diff --git a/test/fixtures/webpack.array.warning.config.js b/test/fixtures/webpack.array.warning.config.js index be0a5557f..415685d4b 100644 --- a/test/fixtures/webpack.array.warning.config.js +++ b/test/fixtures/webpack.array.warning.config.js @@ -9,7 +9,7 @@ module.exports = [ entry: './warning.js', output: { filename: 'bundle.js', - path: path.resolve(__dirname, '../../outputs/array-warning/js1'), + path: path.resolve(__dirname, '../outputs/array-warning/js1'), publicPath: '/static-one/', }, plugins: [ diff --git a/test/helpers/sse.js b/test/helpers/sse.js new file mode 100644 index 000000000..23c98883c --- /dev/null +++ b/test/helpers/sse.js @@ -0,0 +1,108 @@ +import http from "node:http"; + +/** + * @typedef {object} SseEvent + * @property {string=} action event action (building/built/sync/custom) + * @property {string=} name compilation name + * @property {string=} hash compilation hash + * @property {number=} time build time in ms + * @property {string[]=} errors errors + * @property {string[]=} warnings warnings + * @property {Record=} modules module id → name map + */ + +/** + * Open the SSE endpoint just long enough to capture the response headers, then + * force-close it (the stream never ends on its own). + * @param {import("supertest").Test} pending pending supertest request + * @returns {Promise} resolved response + */ +export async function readSseHandshake(pending) { + return new Promise((resolve, reject) => { + pending + .buffer(false) + .parse((res, cb) => { + res.on("data", () => {}); + res.on("end", () => cb(null, "")); + // SSE never closes on its own; force-close after we have headers. + setTimeout(() => res.destroy(), 50); + }) + .end((err, res) => { + if (err && err.code !== "ECONNRESET") { + reject(err); + return; + } + resolve(res); + }); + }); +} + +/** + * Read the SSE stream directly over HTTP (supertest buffers streaming bodies + * differently per framework), then close it and return the parsed payloads + * (heartbeats and the initial handshake newline are skipped). + * @param {import("node:http").Server & { info?: { port: number }, listener?: import("node:http").Server }} listeningServer the running server + * @param {string} requestPath SSE endpoint path + * @param {number} waitMs how long to collect events before closing + * @returns {Promise} parsed event payloads + */ +export async function readSseEvents( + listeningServer, + requestPath, + waitMs = 250, +) { + const httpServer = listeningServer.listener || listeningServer; + const address = + typeof httpServer.address === "function" ? httpServer.address() : null; + const port = + address && typeof address === "object" && address.port + ? address.port + : listeningServer.info && listeningServer.info.port + ? listeningServer.info.port + : 3000; + + return new Promise((resolve, reject) => { + let raw = ""; + const pending = http.get( + { host: "127.0.0.1", port, path: requestPath }, + (res) => { + res.setEncoding("utf8"); + res.on("data", (chunk) => { + raw += chunk; + }); + }, + ); + pending.on("error", (err) => { + if (err.code !== "ECONNRESET") reject(err); + }); + + setTimeout(() => { + pending.destroy(); + + /** @type {SseEvent[]} */ + const events = []; + for (const block of raw.split("\n\n")) { + const line = block.split("\n").find((item) => item.startsWith("data:")); + if (!line) continue; + const payload = line.slice("data:".length).trim(); + if (!payload || payload === "💓") continue; + try { + events.push(JSON.parse(payload)); + } catch { + // Ignore non-JSON frames. + } + } + resolve(events); + }, waitMs); + }); +} + +/** + * @param {{ waitUntilValid: (callback: () => void) => void }} devMiddleware middleware instance + * @returns {Promise} resolves once the first compilation is valid + */ +export async function waitUntilValid(devMiddleware) { + return new Promise((resolve) => { + devMiddleware.waitUntilValid(() => resolve()); + }); +} diff --git a/test/hot.test.js b/test/hot.test.js new file mode 100644 index 000000000..9fb4f01bd --- /dev/null +++ b/test/hot.test.js @@ -0,0 +1,388 @@ +import createHot, { + buildModuleMap, + createEventStream, + formatErrors, + pathMatch, +} from "../src/hot"; + +jest.spyOn(globalThis.console, "log").mockImplementation(); + +// eslint-disable-next-line jsdoc/reject-any-type +/** @typedef {any} EXPECTED_OBJECT */ + +/** @type {EXPECTED_OBJECT} */ +const noopLogger = { log() {} }; + +/** + * Build a minimal compiler-like object so we can drive `invalid`/`done` from + * the test without spinning up webpack. + * @param {EXPECTED_OBJECT=} logger logger returned by getInfrastructureLogger + * @returns {{ hooks: EXPECTED_OBJECT, emitInvalid: () => void, emitDone: (stats: EXPECTED_OBJECT) => void }} fake compiler + */ +function makeFakeCompiler(logger = noopLogger) { + const invalidTaps = []; + const doneTaps = []; + return { + hooks: { + invalid: { tap: (_name, fn) => invalidTaps.push(fn) }, + done: { tap: (_name, fn) => doneTaps.push(fn) }, + }, + getInfrastructureLogger: () => logger, + emitInvalid() { + for (const fn of invalidTaps) fn(); + }, + emitDone(stats) { + for (const fn of doneTaps) fn(stats); + }, + }; +} + +/** + * Build a minimal Stats-like object that satisfies `publishStats`. + * @param {EXPECTED_OBJECT=} overrides field overrides applied on top of the defaults + * @returns {EXPECTED_OBJECT} fake stats + */ +function makeFakeStats(overrides = {}) { + return { + toJson() { + return { + time: 5, + hash: "abc", + warnings: [], + errors: [], + modules: [], + ...overrides, + }; + }, + compilation: undefined, + }; +} + +/** + * Attach a fake response to the given event stream and collect every chunk + * written to it. + * @param {EXPECTED_OBJECT} eventStream stream returned by createEventStream or hot instance + * @param {{ httpVersion?: string }=} reqOverrides overrides for the fake req + * @returns {{ res: EXPECTED_OBJECT, writes: string[], headers: EXPECTED_OBJECT }} captured state + */ +function attachClient(eventStream, reqOverrides = {}) { + const writes = []; + /** @type {EXPECTED_OBJECT | undefined} */ + let headers; + const res = { + writableEnded: false, + write: (chunk) => { + writes.push(chunk); + }, + writeHead: (_code, h) => { + headers = h; + }, + end: () => { + res.writableEnded = true; + }, + }; + const req = { + httpVersion: "1.1", + socket: { setKeepAlive: () => {} }, + on: () => {}, + ...reqOverrides, + }; + eventStream.handler(req, res); + return { res, writes, headers }; +} + +describe("hot middleware (unit)", () => { + describe("pathMatch", () => { + it("matches exact pathname", () => { + expect(pathMatch("/__webpack_hmr", "/__webpack_hmr")).toBe(true); + }); + + it("strips query string", () => { + expect(pathMatch("/__webpack_hmr?name=app", "/__webpack_hmr")).toBe(true); + }); + + it("returns false on mismatch", () => { + expect(pathMatch("/bundle.js", "/__webpack_hmr")).toBe(false); + }); + + it("returns false when the path has a trailing segment", () => { + expect(pathMatch("/__webpack_hmr/extra", "/__webpack_hmr")).toBe(false); + }); + + it("returns false when url is undefined", () => { + expect(pathMatch(undefined, "/__webpack_hmr")).toBe(false); + }); + + it("returns false on malformed urls without throwing", () => { + expect(pathMatch("http://[bad", "/__webpack_hmr")).toBe(false); + }); + }); + + describe("formatErrors", () => { + it("returns an empty array when input is empty", () => { + expect(formatErrors([])).toEqual([]); + }); + + it("passes through string errors unchanged", () => { + expect(formatErrors(["boom"])).toEqual(["boom"]); + }); + + it("formats webpack 5 error objects", () => { + expect( + formatErrors([{ moduleName: "./foo.js", loc: "1:1", message: "boom" }]), + ).toEqual(["./foo.js 1:1\nboom"]); + }); + + it("tolerates missing moduleName and loc", () => { + expect(formatErrors([{ message: "boom" }])).toEqual([" \nboom"]); + }); + }); + + describe("buildModuleMap", () => { + it("maps id to name", () => { + expect( + buildModuleMap([ + { id: 1, name: "./a.js" }, + { id: 2, name: "./b.js" }, + ]), + ).toEqual({ 1: "./a.js", 2: "./b.js" }); + }); + }); + + describe("createEventStream", () => { + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it("emits a heartbeat at the configured interval", () => { + const stream = createEventStream(1000, noopLogger); + const writes = []; + const fakeRes = { + writableEnded: false, + write: (chunk) => writes.push(chunk), + writeHead: () => {}, + end: () => {}, + }; + const fakeReq = { + httpVersion: "1.1", + socket: { setKeepAlive: () => {} }, + on: () => {}, + }; + + stream.handler(fakeReq, fakeRes); + writes.length = 0; + jest.advanceTimersByTime(1000); + + expect(writes.some((w) => w.includes("💓"))).toBe(true); + + stream.close(); + }); + + it("publishes JSON payloads to attached clients", () => { + const stream = createEventStream(5000, noopLogger); + const writes = []; + const fakeRes = { + writableEnded: false, + write: (chunk) => writes.push(chunk), + writeHead: () => {}, + end: () => {}, + }; + const fakeReq = { + httpVersion: "1.1", + socket: { setKeepAlive: () => {} }, + on: () => {}, + }; + + stream.handler(fakeReq, fakeRes); + stream.publish({ action: "built", hash: "abc" }); + + expect(writes.some((w) => w.includes('"action":"built"'))).toBe(true); + expect(writes.some((w) => w.includes('"hash":"abc"'))).toBe(true); + + stream.close(); + }); + + it("close ends connected clients", () => { + const stream = createEventStream(5000, noopLogger); + let ended = false; + const fakeRes = { + writableEnded: false, + write: () => {}, + writeHead: () => {}, + end: () => { + ended = true; + }, + }; + const fakeReq = { + httpVersion: "1.1", + socket: { setKeepAlive: () => {} }, + on: () => {}, + }; + + stream.handler(fakeReq, fakeRes); + stream.close(); + + expect(ended).toBe(true); + }); + + it("sets Connection: keep-alive for HTTP/1 clients", () => { + const stream = createEventStream(5000, noopLogger); + const { headers } = attachClient(stream, { httpVersion: "1.1" }); + expect(headers.Connection).toBe("keep-alive"); + stream.close(); + }); + + it("does not set Connection: keep-alive for HTTP/2 clients", () => { + const stream = createEventStream(5000, noopLogger); + const { headers } = attachClient(stream, { httpVersion: "2.0" }); + expect(headers.Connection).toBeUndefined(); + stream.close(); + }); + + it("broadcasts events to every attached client", () => { + const stream = createEventStream(5000, noopLogger); + const clients = [ + attachClient(stream), + attachClient(stream), + attachClient(stream), + ]; + + for (const c of clients) c.writes.length = 0; + + stream.publish({ action: "built", hash: "xyz" }); + + for (const c of clients) { + expect(c.writes.some((w) => w.includes('"hash":"xyz"'))).toBe(true); + } + + stream.close(); + }); + + it("logs client connect and disconnect with the active count", () => { + const messages = []; + const stream = createEventStream(5000, { + log: (message) => messages.push(message), + }); + /** @type {() => void} */ + let closeHandler = () => {}; + const fakeReq = { + httpVersion: "1.1", + socket: { setKeepAlive: () => {} }, + on: (event, fn) => { + if (event === "close") closeHandler = fn; + }, + }; + const fakeRes = { + writableEnded: false, + write: () => {}, + writeHead: () => {}, + end: () => {}, + }; + + stream.handler(fakeReq, fakeRes); + expect(messages).toContain("Client connected (1 active)"); + + closeHandler(); + expect(messages).toContain("Client disconnected (0 active)"); + + stream.close(); + }); + }); +}); + +describe("createHot", () => { + it("logs that HMR is enabled with the served path", () => { + const messages = []; + const compiler = makeFakeCompiler({ + log: (message) => messages.push(message), + }); + + const hot = createHot(compiler, { path: "/__hmr" }); + + expect(messages).toContain( + 'Hot module replacement enabled, serving events at "/__hmr"', + ); + + hot.close(); + }); + + it("exposes publish() so callers can broadcast custom payloads", () => { + const compiler = makeFakeCompiler(); + const hot = createHot(compiler, {}); + const { writes } = attachClient({ handler: hot.handle }); + + hot.publish({ action: "custom-thing", payload: 42 }); + + expect( + writes.some( + (w) => + w.includes('"action":"custom-thing"') && w.includes('"payload":42'), + ), + ).toBe(true); + + hot.close(); + }); + + it("sends a sync payload to a client that connects after a build", () => { + const compiler = makeFakeCompiler(); + const hot = createHot(compiler, {}); + + // A build finishes BEFORE anyone connects. + compiler.emitDone(makeFakeStats()); + + const { writes } = attachClient({ handler: hot.handle }); + + expect(writes.some((w) => w.includes('"action":"sync"'))).toBe(true); + + hot.close(); + }); + + it("falls back to compilation.name when stats name is empty", () => { + const compiler = makeFakeCompiler(); + const hot = createHot(compiler, {}); + const { writes } = attachClient({ handler: hot.handle }); + + compiler.emitDone({ + toJson() { + return { + time: 1, + hash: "h", + warnings: [], + errors: [], + modules: [], + // no `name` here + }; + }, + compilation: { name: "child-bundle" }, + }); + + expect( + writes.some( + (w) => + w.includes('"name":"child-bundle"') && w.includes('"action":"built"'), + ), + ).toBe(true); + + hot.close(); + }); + + it("stops publishing after close() even if the compiler still emits", () => { + const compiler = makeFakeCompiler(); + const hot = createHot(compiler, {}); + const { writes } = attachClient({ handler: hot.handle }); + + hot.close(); + writes.length = 0; + + // After close, the tap callbacks remain registered (tapable does not + // support removal), but they must noop instead of writing to clients. + compiler.emitInvalid(); + compiler.emitDone(makeFakeStats()); + + expect(writes).toHaveLength(0); + }); +}); diff --git a/test/middleware.test.js b/test/middleware.test.js index 8daed5c93..584bf31e1 100644 --- a/test/middleware.test.js +++ b/test/middleware.test.js @@ -32,6 +32,8 @@ import getCompiler from "./helpers/getCompiler"; import getCompilerHooks from "./helpers/getCompilerHooks"; +import { readSseEvents, readSseHandshake, waitUntilValid } from "./helpers/sse"; + // Suppress unnecessary stats output jest.spyOn(globalThis.console, "log").mockImplementation(); @@ -6946,4 +6948,192 @@ describe.each([ }); }); }); + + describe("hot", () => { + let instance; + let server; + let req; + + afterEach(async () => { + await close(server, instance); + }); + + it("serves SSE on the default hot path", async () => { + const compiler = getCompiler(webpackConfig); + [server, req, instance] = await frameworkFactory( + name, + framework, + compiler, + { hot: true }, + ); + + const res = await readSseHandshake(req.get("/__webpack_hmr")); + + expect(res.status).toBe(200); + expect(res.headers["content-type"]).toMatch(/text\/event-stream/); + expect(res.headers["cache-control"]).toBe("no-cache, no-transform"); + expect(res.headers["x-accel-buffering"]).toBe("no"); + }); + + it("respects a custom hot path", async () => { + const compiler = getCompiler(webpackConfig); + [server, req, instance] = await frameworkFactory( + name, + framework, + compiler, + { + hot: { path: "/__custom_hmr" }, + }, + ); + + const res = await readSseHandshake(req.get("/__custom_hmr")); + + expect(res.status).toBe(200); + expect(res.headers["content-type"]).toMatch(/text\/event-stream/); + }); + + it("does not intercept non-hot requests", async () => { + const compiler = getCompiler(webpackConfig); + [server, req, instance] = await frameworkFactory( + name, + framework, + compiler, + { hot: true }, + ); + + const response = await req.get("/bundle.js"); + + expect(response.statusCode).toBe(200); + expect(response.headers["content-type"] || "").not.toMatch( + /text\/event-stream/, + ); + }); + + it("does not intercept the default hot path when hot is disabled", async () => { + const compiler = getCompiler(webpackConfig); + [server, req, instance] = await frameworkFactory( + name, + framework, + compiler, + {}, + ); + + const response = await req.get("/__webpack_hmr"); + + expect(response.headers["content-type"] || "").not.toMatch( + /text\/event-stream/, + ); + }); + + it("works with MultiCompiler", async () => { + const compiler = getCompiler(webpackMultiConfig); + [server, req, instance] = await frameworkFactory( + name, + framework, + compiler, + { hot: true }, + ); + + const res = await readSseHandshake(req.get("/__webpack_hmr")); + + expect(res.status).toBe(200); + expect(res.headers["content-type"]).toMatch(/text\/event-stream/); + }); + + it("sends a permissive CORS header", async () => { + const compiler = getCompiler(webpackConfig); + [server, req, instance] = await frameworkFactory( + name, + framework, + compiler, + { hot: true }, + ); + + const res = await readSseHandshake(req.get("/__webpack_hmr")); + + expect(res.headers["access-control-allow-origin"]).toBe("*"); + }); + + describe("SSE payload", () => { + it("sends a sync event with the build hash to a client connecting after a build", async () => { + const compiler = getCompiler(webpackConfig); + [server, req, instance] = await frameworkFactory( + name, + framework, + compiler, + { hot: true }, + ); + + await waitUntilValid(instance); + + const events = await readSseEvents(server, "/__webpack_hmr"); + const sync = events.find((event) => event.action === "sync"); + + expect(sync).toBeDefined(); + expect(typeof sync.hash).toBe("string"); + expect(sync.errors).toEqual([]); + }); + + it("sends a sync event per compilation for a MultiCompiler", async () => { + const compiler = getCompiler(webpackMultiConfig); + [server, req, instance] = await frameworkFactory( + name, + framework, + compiler, + { hot: true }, + ); + + await waitUntilValid(instance); + + const events = await readSseEvents(server, "/__webpack_hmr"); + const syncs = events.filter((event) => event.action === "sync"); + + expect(syncs).toHaveLength(webpackMultiConfig.length); + for (const sync of syncs) { + expect(typeof sync.hash).toBe("string"); + } + }); + + it("streams building and built events on recompilation", async () => { + const compiler = getCompiler({ ...webpackConfig, watch: true }); + [server, req, instance] = await frameworkFactory( + name, + framework, + compiler, + { hot: true }, + ); + + await waitUntilValid(instance); + + const pending = readSseEvents(server, "/__webpack_hmr", 2000); + // Trigger a rebuild once the client is listening. + setTimeout(() => instance.invalidate(), 150); + const events = await pending; + const actions = events.map((event) => event.action); + + expect(actions).toContain("building"); + expect(actions).toContain("built"); + }); + + it("broadcasts to every connected client", async () => { + const compiler = getCompiler(webpackConfig); + [server, req, instance] = await frameworkFactory( + name, + framework, + compiler, + { hot: true }, + ); + + await waitUntilValid(instance); + + const [first, second] = await Promise.all([ + readSseEvents(server, "/__webpack_hmr"), + readSseEvents(server, "/__webpack_hmr"), + ]); + + expect(first.some((event) => event.action === "sync")).toBe(true); + expect(second.some((event) => event.action === "sync")).toBe(true); + }); + }); + }); }); diff --git a/test/overlay.test.js b/test/overlay.test.js new file mode 100644 index 000000000..70f866e4d --- /dev/null +++ b/test/overlay.test.js @@ -0,0 +1,183 @@ +/** + * @jest-environment jsdom + */ + +import configureOverlay, { clear, showProblems } from "../client-src/overlay"; + +const OVERLAY_ID = "webpack-dev-middleware-hot-overlay"; + +/** + * @returns {HTMLElement | null} the overlay backdrop, if mounted + */ +function getOverlay() { + return document.getElementById(OVERLAY_ID); +} + +/** + * @returns {HTMLElement} the visible card element inside the backdrop + */ +function getCard() { + return /** @type {HTMLElement} */ ( + /** @type {HTMLElement} */ (getOverlay()).firstElementChild + ); +} + +/** + * @param {string} color expected normalized color + * @returns {HTMLElement | undefined} the first span rendered in that text color + */ +function findSpanByColor(color) { + return [...getCard().querySelectorAll("span")].find( + (span) => span.style.color === color, + ); +} + +describe("overlay", () => { + afterEach(() => { + clear(); + }); + + describe("showProblems", () => { + it("mounts the overlay on the document body", () => { + expect(getOverlay()).toBeNull(); + showProblems("errors", ["./a.js 1:1\nboom"]); + expect(getOverlay()).not.toBeNull(); + }); + + it("renders an ERROR badge in the error color for errors", () => { + showProblems("errors", ["boom"]); + const badge = getCard().querySelector("span"); + expect(badge.textContent).toBe("ERROR"); + expect(badge.style.backgroundColor).toBe("rgb(255, 51, 72)"); + }); + + it("renders a WARNING badge in the warning color for warnings", () => { + showProblems("warnings", ["careful"]); + const badge = getCard().querySelector("span"); + expect(badge.textContent).toBe("WARNING"); + expect(badge.style.backgroundColor).toBe("rgb(255, 211, 14)"); + }); + + it("colors the top accent bar red for errors and yellow for warnings", () => { + showProblems("errors", ["boom"]); + expect(getCard().style.borderTopColor).toBe("rgb(255, 51, 72)"); + showProblems("warnings", ["careful"]); + expect(getCard().style.borderTopColor).toBe("rgb(255, 211, 14)"); + }); + + it("highlights the file path and leaves the location uncolored", () => { + showProblems("errors", ["./src/render.js 7:2\nModule parse failed"]); + const pathSpan = [...getCard().querySelectorAll("span")].find( + (span) => span.textContent === "./src/render.js", + ); + expect(pathSpan).toBeDefined(); + expect(pathSpan.style.color).toBe("rgb(141, 214, 249)"); + // The `7:2` location is rendered as plain text, not inside the span. + expect(getCard().textContent).toContain("./src/render.js 7:2"); + }); + + it("highlights the offending code-frame line", () => { + showProblems("errors", ["./a.js 1:1\n> 1 | const x =\n | ^"]); + expect(findSpanByColor("rgb(255, 107, 107)")).toBeDefined(); + }); + + it("turns URLs into links that open in a new tab", () => { + showProblems("errors", ["See https://webpack.js.org/concepts#loaders"]); + const link = getCard().querySelector("a"); + expect(link).not.toBeNull(); + expect(link.getAttribute("href")).toBe( + "https://webpack.js.org/concepts#loaders", + ); + expect(link.getAttribute("target")).toBe("_blank"); + expect(link.getAttribute("rel")).toBe("noopener noreferrer"); + }); + + it("keeps trailing punctuation out of the link href", () => { + showProblems("errors", ["Docs: https://example.com/a."]); + const link = getCard().querySelector("a"); + expect(link.getAttribute("href")).toBe("https://example.com/a"); + expect(getCard().textContent).toContain("https://example.com/a."); + }); + + it("shows a dismiss hint", () => { + showProblems("errors", ["boom"]); + expect(getCard().textContent).toContain( + "Click outside, press Esc, or fix the code to dismiss.", + ); + }); + + it("replaces previous problems on each call", () => { + showProblems("errors", ["first"]); + showProblems("errors", ["second"]); + expect(getCard().textContent).toContain("second"); + expect(getCard().textContent).not.toContain("first"); + }); + }); + + describe("clear", () => { + it("removes the overlay from the DOM", () => { + showProblems("errors", ["boom"]); + expect(getOverlay()).not.toBeNull(); + clear(); + expect(getOverlay()).toBeNull(); + }); + + it("is a no-op when nothing is shown", () => { + expect(() => clear()).not.toThrow(); + }); + }); + + describe("dismiss", () => { + it("closes when pressing Escape", () => { + showProblems("errors", ["boom"]); + document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" })); + expect(getOverlay()).toBeNull(); + }); + + it("closes when clicking the backdrop", () => { + showProblems("errors", ["boom"]); + getOverlay().click(); + expect(getOverlay()).toBeNull(); + }); + + it("stays open when clicking inside the card", () => { + showProblems("errors", ["boom"]); + getCard().click(); + expect(getOverlay()).not.toBeNull(); + }); + + it("closes when clicking the close button", () => { + showProblems("errors", ["boom"]); + const closeButton = getCard().querySelector("button"); + expect(closeButton).not.toBeNull(); + closeButton.click(); + expect(getOverlay()).toBeNull(); + }); + }); + + describe("configureOverlay", () => { + it("returns the overlay API", () => { + const api = configureOverlay({}); + expect(typeof api.showProblems).toBe("function"); + expect(typeof api.clear).toBe("function"); + }); + + it("applies custom overlay styles to the card", () => { + configureOverlay({ overlayStyles: { maxWidth: "500px" } }); + showProblems("errors", ["boom"]); + expect(getCard().style.maxWidth).toBe("500px"); + }); + + it("honors custom ansi colors for the problem color", () => { + configureOverlay({ ansiColors: { red: "00ff00" } }); + showProblems("errors", ["boom"]); + expect(getCard().querySelector("span").style.backgroundColor).toBe( + "rgb(0, 255, 0)", + ); + expect(getCard().style.borderTopColor).toBe("rgb(0, 255, 0)"); + + // Restore the default so module-level state does not leak. + configureOverlay({ ansiColors: { red: "ff3348" } }); + }); + }); +}); diff --git a/test/validation-options.test.js b/test/validation-options.test.js index 79bb801b3..537b7084a 100644 --- a/test/validation-options.test.js +++ b/test/validation-options.test.js @@ -85,6 +85,18 @@ describe("validation", () => { success: [true, false], failure: ["foo", 0], }, + hot: { + success: [ + true, + false, + {}, + { path: "/__hmr" }, + { heartbeat: 1000 }, + { statsOptions: true }, + { statsOptions: { all: false } }, + ], + failure: ["foo", 0, { path: "" }, { heartbeat: -1 }, { unknown: true }], + }, }; // eslint-disable-next-line jsdoc/reject-any-type diff --git a/tsconfig.client.json b/tsconfig.client.json new file mode 100644 index 000000000..479ffba6d --- /dev/null +++ b/tsconfig.client.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "esnext", + "lib": ["es5", "dom", "webworker", "es2022.error"], + "module": "esnext", + "moduleResolution": "bundler", + "allowJs": true, + "checkJs": true, + "noEmit": true, + "strict": true, + "types": ["node", "webpack/module"], + "skipDefaultLibCheck": true, + "esModuleInterop": true + }, + "include": ["./client-src/**/*"] +} diff --git a/types/hot.d.ts b/types/hot.d.ts new file mode 100644 index 000000000..cac94957c --- /dev/null +++ b/types/hot.d.ts @@ -0,0 +1,215 @@ +export = createHot; +/** + * @typedef {object} HotInstance + * @property {string} path path the SSE endpoint is served at + * @property {(req: IncomingMessage, res: ServerResponse) => void} handle attach the request as a SSE client + * @property {(payload: Payload | { action: string }) => void} publish publish a payload to every client + * @property {() => void} close end every client and detach the heartbeat + */ +/** + * @param {Compiler | MultiCompiler} compiler compiler + * @param {HotOptions | true} userOptions options + * @returns {HotInstance} hot instance + */ +declare function createHot( + compiler: Compiler | MultiCompiler, + userOptions: HotOptions | true, +): HotInstance; +declare namespace createHot { + export { + HOT_DEFAULT_HEARTBEAT, + HOT_DEFAULT_PATH, + buildModuleMap, + createEventStream, + createHot, + formatErrors, + pathMatch, + publishStats, + HotInstance, + Compiler, + MultiCompiler, + Logger, + Stats, + MultiStats, + StatsCompilation, + StatsError, + StatsModule, + IncomingMessage, + ServerResponse, + StatsOptions, + HotOptions, + Payload, + EventStream, + }; +} +declare const HOT_DEFAULT_HEARTBEAT: number; +/** @typedef {import("webpack").Compiler} Compiler */ +/** @typedef {import("webpack").MultiCompiler} MultiCompiler */ +/** @typedef {ReturnType} Logger */ +/** @typedef {import("webpack").Stats} Stats */ +/** @typedef {import("webpack").MultiStats} MultiStats */ +/** @typedef {import("webpack").StatsCompilation} StatsCompilation */ +/** @typedef {import("webpack").StatsError} StatsError */ +/** @typedef {import("webpack").StatsModule} StatsModule */ +/** @typedef {import("./index.js").IncomingMessage} IncomingMessage */ +/** @typedef {import("./index.js").ServerResponse} ServerResponse */ +/** @typedef {NonNullable} StatsOptions */ +/** + * @typedef {object} HotOptions + * @property {string=} path the path the SSE endpoint is served at + * @property {number=} heartbeat heartbeat interval in milliseconds + * @property {StatsOptions=} statsOptions webpack stats options used when serializing compilation results + */ +/** + * @typedef {object} Payload + * @property {string} action action + * @property {string=} name name + * @property {number=} time time + * @property {string=} hash hash + * @property {string[]=} warnings warnings + * @property {string[]=} errors errors + * @property {Record=} modules modules + */ +/** + * @typedef {object} EventStream + * @property {(req: IncomingMessage, res: ServerResponse) => void} handler attach a new client + * @property {(payload: Payload | { action: string }) => void} publish publish a payload to every client + * @property {() => void} close end every client and stop the heartbeat + */ +declare const HOT_DEFAULT_PATH: "/__webpack_hmr"; +/** + * @param {StatsModule[]} modules modules + * @returns {Record} module id to name map + */ +declare function buildModuleMap(modules: StatsModule[]): Record; +/** + * @param {number} heartbeat heartbeat interval in milliseconds + * @param {Logger} logger logger + * @returns {EventStream} event stream + */ +declare function createEventStream( + heartbeat: number, + logger: Logger, +): EventStream; +/** + * @param {(string | StatsError)[]} errors errors or warnings + * @returns {string[]} flat strings + */ +declare function formatErrors(errors: (string | StatsError)[]): string[]; +/** + * @param {string | undefined} url url + * @param {string} expected expected pathname + * @returns {boolean} true when the url pathname matches the expected path + */ +declare function pathMatch(url: string | undefined, expected: string): boolean; +/** + * @param {string} action action + * @param {Stats | MultiStats} statsResult stats result + * @param {EventStream} eventStream event stream + * @param {StatsOptions | undefined} statsOptions stats options + */ +declare function publishStats( + action: string, + statsResult: Stats | MultiStats, + eventStream: EventStream, + statsOptions: StatsOptions | undefined, +): void; +type HotInstance = { + /** + * path the SSE endpoint is served at + */ + path: string; + /** + * attach the request as a SSE client + */ + handle: (req: IncomingMessage, res: ServerResponse) => void; + /** + * publish a payload to every client + */ + publish: ( + payload: + | Payload + | { + action: string; + }, + ) => void; + /** + * end every client and detach the heartbeat + */ + close: () => void; +}; +type Compiler = import("webpack").Compiler; +type MultiCompiler = import("webpack").MultiCompiler; +type Logger = ReturnType; +type Stats = import("webpack").Stats; +type MultiStats = import("webpack").MultiStats; +type StatsCompilation = import("webpack").StatsCompilation; +type StatsError = import("webpack").StatsError; +type StatsModule = import("webpack").StatsModule; +type IncomingMessage = import("./index.js").IncomingMessage; +type ServerResponse = import("./index.js").ServerResponse; +type StatsOptions = NonNullable; +type HotOptions = { + /** + * the path the SSE endpoint is served at + */ + path?: string | undefined; + /** + * heartbeat interval in milliseconds + */ + heartbeat?: number | undefined; + /** + * webpack stats options used when serializing compilation results + */ + statsOptions?: StatsOptions | undefined; +}; +type Payload = { + /** + * action + */ + action: string; + /** + * name + */ + name?: string | undefined; + /** + * time + */ + time?: number | undefined; + /** + * hash + */ + hash?: string | undefined; + /** + * warnings + */ + warnings?: string[] | undefined; + /** + * errors + */ + errors?: string[] | undefined; + /** + * modules + */ + modules?: Record | undefined; +}; +type EventStream = { + /** + * attach a new client + */ + handler: (req: IncomingMessage, res: ServerResponse) => void; + /** + * publish a payload to every client + */ + publish: ( + payload: + | Payload + | { + action: string; + }, + ) => void; + /** + * end every client and stop the heartbeat + */ + close: () => void; +}; diff --git a/types/index.d.ts b/types/index.d.ts index 233a3d8db..7a29ffdd1 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -28,6 +28,8 @@ declare namespace wdm { MultiStats, ReadStream, FilenameWithExtra, + HotOptions, + HotInstance, EXPECTED_ANY, EXPECTED_FUNCTION, ExtendedServerResponse, @@ -128,6 +130,8 @@ type Stats = import("webpack").Stats; type MultiStats = import("webpack").MultiStats; type ReadStream = import("fs").ReadStream; type FilenameWithExtra = import("./middleware").FilenameWithExtra; +type HotOptions = import("./hot").HotOptions; +type HotInstance = import("./hot").HotInstance; type EXPECTED_ANY = any; type EXPECTED_FUNCTION = Function; type ExtendedServerResponse = { @@ -210,6 +214,10 @@ type Context< * output file system */ outputFileSystem: OutputFileSystem; + /** + * hot module replacement instance + */ + hot?: HotInstance | undefined; }; type FilledContext< RequestInternal extends IncomingMessage = import("http").IncomingMessage, @@ -316,6 +324,10 @@ type Options< * forward error to next middleware */ forwardError?: boolean | undefined; + /** + * enable hot module replacement + */ + hot?: (boolean | HotOptions) | undefined; }; type Middleware< RequestInternal extends IncomingMessage = import("http").IncomingMessage,