diff --git a/frontend/package.json b/frontend/package.json
index fa7acf55b..45e8aa050 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -91,7 +91,6 @@
"unplugin-inject-preload": "3.0.0",
"vite": "7.1.12",
"vite-bundle-visualizer": "1.2.1",
- "vite-plugin-checker": "0.11.0",
"vite-plugin-filter-replace": "0.1.14",
"vite-plugin-html-inject": "1.1.2",
"vite-plugin-inspect": "11.3.3",
diff --git a/frontend/src/html/pages/settings.html b/frontend/src/html/pages/settings.html
index 3640a70d7..50d329dcb 100644
--- a/frontend/src/html/pages/settings.html
+++ b/frontend/src/html/pages/settings.html
@@ -192,7 +192,7 @@
diff --git a/frontend/src/ts/modals/edit-preset.ts b/frontend/src/ts/modals/edit-preset.ts
index 7f3771994..443df2f24 100644
--- a/frontend/src/ts/modals/edit-preset.ts
+++ b/frontend/src/ts/modals/edit-preset.ts
@@ -48,7 +48,7 @@ export function show(action: string, id?: string, name?: string): void {
$("#editPresetModal .modal .text").addClass("hidden");
addCheckBoxes();
presetNameEl ??= new ValidatedHtmlInputElement(
- qsr("#editPresetModal .modal input"),
+ qsr("#editPresetModal .modal input[type=text]"),
{
schema: PresetNameSchema,
},
diff --git a/frontend/src/ts/modals/simple-modals.ts b/frontend/src/ts/modals/simple-modals.ts
index c22d32068..1bf579504 100644
--- a/frontend/src/ts/modals/simple-modals.ts
+++ b/frontend/src/ts/modals/simple-modals.ts
@@ -1212,7 +1212,7 @@ list.devGenerateData = new SimpleModal({
const span = document.querySelector(
"#devGenerateData_1 + span",
) as HTMLInputElement;
- span.innerHTML = `if checked, user will be created with ${target.value}@example.com and password: password`;
+ span.innerText = `if checked, user will be created with ${target.value}@example.com and password: password`;
return;
},
validation: {
diff --git a/frontend/src/ts/utils/caret.ts b/frontend/src/ts/utils/caret.ts
index 1b2b658af..47408db78 100644
--- a/frontend/src/ts/utils/caret.ts
+++ b/frontend/src/ts/utils/caret.ts
@@ -300,7 +300,7 @@ export class Caret {
if (options.letterIndex >= letters.length) {
side = "afterLetter";
- if (Config.blindMode) {
+ if (Config.blindMode || Config.hideExtraLetters) {
options.letterIndex = wordText?.length - 1;
} else {
options.letterIndex = letters.length - 1;
diff --git a/frontend/vite-plugins/oxlint-checker.ts b/frontend/vite-plugins/oxlint-checker.ts
new file mode 100644
index 000000000..5af753d29
--- /dev/null
+++ b/frontend/vite-plugins/oxlint-checker.ts
@@ -0,0 +1,312 @@
+import { Plugin, ViteDevServer, normalizePath } from "vite";
+import { spawn, execSync, ChildProcess } from "child_process";
+import { fileURLToPath } from "url";
+
+export type OxlintCheckerOptions = {
+ /** Debounce delay in milliseconds before running lint after file changes. @default 125 */
+ debounceDelay?: number;
+ /** Run type-aware checks (slower but more thorough). @default true */
+ typeAware?: boolean;
+ /** Show browser overlay with lint status. @default true */
+ overlay?: boolean;
+ /** File extensions to watch for changes. @default ['.ts', '.tsx', '.js', '.jsx'] */
+ extensions?: string[];
+};
+
+type LintResult = {
+ errorCount: number;
+ warningCount: number;
+ running: boolean;
+ hadIssues: boolean;
+ typeAware?: boolean;
+};
+
+const OXLINT_SUMMARY_REGEX = /Found (\d+) warnings? and (\d+) errors?/;
+
+export function oxlintChecker(options: OxlintCheckerOptions = {}): Plugin {
+ const {
+ debounceDelay = 125,
+ typeAware = true,
+ overlay = true,
+ extensions = [".ts", ".tsx", ".js", ".jsx"],
+ } = options;
+
+ let currentProcess: ChildProcess | null = null;
+ let debounceTimer: NodeJS.Timeout | null = null;
+ let server: ViteDevServer | null = null;
+ let isProduction = false;
+ let currentRunId = 0;
+ let lastLintResult: LintResult = {
+ errorCount: 0,
+ warningCount: 0,
+ running: false,
+ hadIssues: false,
+ };
+
+ const killCurrentProcess = (): boolean => {
+ if ((currentProcess && !currentProcess.killed) || currentProcess !== null) {
+ currentProcess.kill();
+ currentProcess = null;
+ return true;
+ }
+ return false;
+ };
+
+ const clearDebounceTimer = (): void => {
+ if (debounceTimer) {
+ clearTimeout(debounceTimer);
+ debounceTimer = null;
+ }
+ };
+
+ const parseLintOutput = (
+ output: string,
+ ): Pick => {
+ const summaryMatch = output.match(OXLINT_SUMMARY_REGEX);
+ if (summaryMatch?.[1] !== undefined && summaryMatch?.[2] !== undefined) {
+ return {
+ warningCount: parseInt(summaryMatch[1], 10),
+ errorCount: parseInt(summaryMatch[2], 10),
+ };
+ }
+ return { errorCount: 0, warningCount: 0 };
+ };
+
+ const sendLintResult = (result: Partial): void => {
+ const previousHadIssues = lastLintResult.hadIssues;
+
+ const payload: LintResult = {
+ errorCount: result.errorCount ?? lastLintResult.errorCount,
+ warningCount: result.warningCount ?? lastLintResult.warningCount,
+ running: result.running ?? false,
+ hadIssues: previousHadIssues,
+ typeAware: result.typeAware,
+ };
+
+ // Only update hadIssues when we have actual lint results (not just running status)
+ if (result.running === false) {
+ const currentHasIssues =
+ (result.errorCount ?? 0) > 0 || (result.warningCount ?? 0) > 0;
+ lastLintResult = { ...payload, hadIssues: currentHasIssues };
+ } else {
+ // Keep hadIssues unchanged when just updating running status
+ lastLintResult = { ...payload, hadIssues: previousHadIssues };
+ }
+
+ if (server) {
+ server.ws.send("vite-plugin-oxlint", payload);
+ }
+ };
+
+ /**
+ * Runs an oxlint process with the given arguments and captures its combined output.
+ *
+ * This function is responsible for managing the lifecycle of the current lint process:
+ * - It spawns a new child process via `npx oxlint . ...args`.
+ * - It assigns the spawned process to the shared {@link currentProcess} variable so that
+ * other parts of the plugin can cancel or track the active lint run.
+ * - On process termination (either "error" or "close"), it clears {@link currentProcess}
+ * if it still refers to this child, avoiding interference with any newer process that
+ * may have started in the meantime.
+ *
+ * @param args Additional command-line arguments to pass to `oxlint`.
+ * @returns A promise that resolves with the process exit code (or `null` if
+ * the process exited due to a signal) and the full stdout/stderr output
+ * produced by the lint run.
+ */
+ const runLintProcess = async (
+ args: string[],
+ ): Promise<{ code: number | null; output: string }> => {
+ return new Promise((resolve) => {
+ const childProcess = spawn("npx", ["oxlint", ".", ...args], {
+ cwd: process.cwd(),
+ shell: true,
+ env: { ...process.env, FORCE_COLOR: "3" },
+ });
+
+ currentProcess = childProcess;
+ let output = "";
+
+ childProcess.stdout?.on("data", (data: Buffer) => {
+ output += data.toString();
+ });
+
+ childProcess.stderr?.on("data", (data: Buffer) => {
+ output += data.toString();
+ });
+
+ childProcess.on("error", (error: Error) => {
+ output += `\nError: ${error.message}`;
+ if (currentProcess === childProcess) {
+ currentProcess = null;
+ }
+ resolve({ code: 1, output });
+ });
+
+ childProcess.on("close", (code: number | null) => {
+ if (currentProcess === childProcess) {
+ currentProcess = null;
+ }
+ resolve({ code, output });
+ });
+ });
+ };
+
+ const runOxlint = async (): Promise => {
+ const wasKilled = killCurrentProcess();
+ const runId = ++currentRunId;
+
+ console.log(
+ wasKilled
+ ? "\x1b[36mRunning oxlint...\x1b[0m \x1b[90m(killed previous process)\x1b[0m"
+ : "\x1b[36mRunning oxlint...\x1b[0m",
+ );
+
+ sendLintResult({ running: true });
+
+ // First pass: fast oxlint without type checking
+ const { code, output } = await runLintProcess([]);
+
+ // Check if we were superseded by a newer run
+ if (runId !== currentRunId) {
+ return;
+ }
+
+ if (output) {
+ console.log(output);
+ }
+
+ // If first pass had errors, send them immediately (fast-fail)
+ if (code !== 0) {
+ const counts = parseLintOutput(output);
+ if (counts.errorCount > 0 || counts.warningCount > 0) {
+ sendLintResult({ ...counts, running: false });
+ return;
+ }
+ }
+
+ // First pass clean - run type-aware check if enabled
+ if (!typeAware) {
+ sendLintResult({ errorCount: 0, warningCount: 0, running: false });
+ return;
+ }
+
+ console.log("\x1b[36mRunning type-aware checks...\x1b[0m");
+ sendLintResult({ running: true, typeAware: true });
+ const typeResult = await runLintProcess(["--type-check", "--type-aware"]);
+
+ // Check if we were superseded by a newer run
+ if (runId !== currentRunId) {
+ return;
+ }
+
+ if (typeResult.output) {
+ console.log(typeResult.output);
+ }
+
+ const counts =
+ typeResult.code !== 0
+ ? parseLintOutput(typeResult.output)
+ : { errorCount: 0, warningCount: 0 };
+ sendLintResult({ ...counts, running: false });
+ };
+
+ const debouncedLint = (): void => {
+ clearDebounceTimer();
+ sendLintResult({ running: true });
+ debounceTimer = setTimeout(() => void runOxlint(), debounceDelay);
+ };
+
+ return {
+ name: "vite-plugin-oxlint-checker",
+
+ config(_, { command }) {
+ isProduction = command === "build";
+ },
+
+ configureServer(devServer: ViteDevServer) {
+ server = devServer;
+
+ // Send current lint status to new clients on connection
+ devServer.ws.on("connection", () => {
+ devServer.ws.send("vite-plugin-oxlint", lastLintResult);
+ });
+
+ // Run initial lint
+ void runOxlint();
+
+ // Listen for file changes
+ devServer.watcher.on("change", (file: string) => {
+ // Only lint on relevant file changes
+ if (extensions.some((ext) => file.endsWith(ext))) {
+ debouncedLint();
+ }
+ });
+ },
+
+ transformIndexHtml() {
+ if (!overlay) {
+ return [];
+ }
+
+ // Inject import to the overlay module (actual .ts file processed by Vite)
+ const overlayPath = normalizePath(
+ fileURLToPath(new URL("./oxlint-overlay.ts", import.meta.url)),
+ );
+ return [
+ {
+ tag: "script",
+ attrs: {
+ type: "module",
+ src: `/@fs${overlayPath}`,
+ },
+ injectTo: "body-prepend",
+ },
+ ];
+ },
+
+ buildStart() {
+ // Only run during production builds, not dev server startup
+ if (!isProduction) {
+ return;
+ }
+
+ // Run oxlint synchronously during build
+ console.log("\n\x1b[1mRunning oxlint...\x1b[0m");
+
+ try {
+ const output = execSync(
+ "npx oxlint . && npx oxlint . --type-aware --type-check",
+ {
+ cwd: process.cwd(),
+ encoding: "utf-8",
+ env: { ...process.env, FORCE_COLOR: "3" },
+ },
+ );
+
+ if (output) {
+ console.log(output);
+ }
+ console.log(` \x1b[32m✓ No linting issues found\x1b[0m\n`);
+ } catch (error) {
+ // execSync throws on non-zero exit code (linting errors found)
+ if (error instanceof Error && "stdout" in error) {
+ const execError = error as Error & {
+ stdout?: string;
+ stderr?: string;
+ };
+ if (execError.stdout !== undefined) console.log(execError.stdout);
+ if (execError.stderr !== undefined) console.error(execError.stderr);
+ }
+ console.error("\n\x1b[31mBuild aborted due to linting errors\x1b[0m\n");
+ process.exit(1);
+ }
+ },
+
+ closeBundle() {
+ // Cleanup on server close
+ killCurrentProcess();
+ clearDebounceTimer();
+ },
+ };
+}
diff --git a/frontend/vite-plugins/oxlint-overlay.ts b/frontend/vite-plugins/oxlint-overlay.ts
new file mode 100644
index 000000000..a1d734031
--- /dev/null
+++ b/frontend/vite-plugins/oxlint-overlay.ts
@@ -0,0 +1,126 @@
+// Oxlint overlay client-side code
+let overlay: HTMLDivElement | null = null;
+let hideTimeout: ReturnType | null = null;
+
+function createOverlay(): HTMLDivElement {
+ if (overlay) return overlay;
+
+ overlay = document.createElement("div");
+ overlay.id = "oxlint-error-overlay";
+ overlay.style.cssText = `
+ position: fixed;
+ bottom: 20px;
+ left: 20px;
+ background: #323437;
+ color: #e4dec8ff;
+ padding: 12px 16px;
+ border-radius: 8px;
+ font-family: 'Roboto Mono', monospace;
+ font-size: 14px;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
+ z-index: 10000;
+ display: none;
+ align-items: center;
+ gap: 8px;
+ cursor: pointer;
+ transition: opacity 0.2s ease;
+ `;
+
+ overlay.addEventListener("mouseenter", () => {
+ if (overlay) overlay.style.opacity = "0.5";
+ });
+
+ overlay.addEventListener("mouseleave", () => {
+ if (overlay) overlay.style.opacity = "1";
+ });
+
+ overlay.addEventListener("click", () => {
+ if (overlay) overlay.style.display = "none";
+ });
+
+ document.body.appendChild(overlay);
+ return overlay;
+}
+
+function updateOverlay(data: {
+ errorCount?: number;
+ warningCount?: number;
+ running?: boolean;
+ hadIssues?: boolean;
+ typeAware?: boolean;
+}): void {
+ const overlayEl = createOverlay();
+
+ // Clear any pending hide timeout
+ if (hideTimeout !== null) {
+ clearTimeout(hideTimeout);
+ hideTimeout = null;
+ }
+
+ // Show running icon if linting is running and there were issues before
+ if (data.running) {
+ if (data.hadIssues) {
+ const message = data.typeAware ? "checking type aware..." : "checking...";
+ overlayEl.innerHTML = `
+ ⏳
+ oxlint: ${message}
+ `;
+ overlayEl.style.display = "flex";
+ overlayEl.style.color = "#e4dec8ff";
+ } else {
+ overlayEl.style.display = "none";
+ }
+ return;
+ }
+
+ const { errorCount = 0, warningCount = 0, hadIssues = false } = data;
+ const total = errorCount + warningCount;
+
+ if (total > 0) {
+ overlayEl.innerHTML = `
+ 🚨
+ oxlint: ${errorCount} error${errorCount !== 1 ? "s" : ""}, ${warningCount} warning${warningCount !== 1 ? "s" : ""}
+ `;
+ overlayEl.style.display = "flex";
+
+ if (errorCount > 0) {
+ overlayEl.style.color = "#e4dec8ff";
+ }
+ } else {
+ // Only show success if the previous lint had issues
+ if (hadIssues) {
+ overlayEl.innerHTML = `
+ ✅
+ oxlint: ok
+ `;
+ overlayEl.style.display = "flex";
+ overlayEl.style.color = "#e4dec8ff";
+
+ // Hide after 3 seconds
+ hideTimeout = setTimeout(() => {
+ overlayEl.style.display = "none";
+ hideTimeout = null;
+ }, 3000);
+ } else {
+ // Two good lints in a row - don't show anything
+ overlayEl.style.display = "none";
+ }
+ }
+}
+
+// Initialize overlay on load
+createOverlay();
+
+if (import.meta.hot) {
+ import.meta.hot.on(
+ "vite-plugin-oxlint",
+ (data: {
+ errorCount?: number;
+ warningCount?: number;
+ running?: boolean;
+ hadIssues?: boolean;
+ }) => {
+ updateOverlay(data);
+ },
+ );
+}
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index ed0eb5c81..0b81b5466 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -19,7 +19,7 @@ import { languageHashes } from "./vite-plugins/language-hashes";
import { minifyJson } from "./vite-plugins/minify-json";
import { versionFile } from "./vite-plugins/version-file";
import { jqueryInject } from "./vite-plugins/jquery-inject";
-import { checker } from "vite-plugin-checker";
+import { oxlintChecker } from "./vite-plugins/oxlint-checker";
import Inspect from "vite-plugin-inspect";
import { ViteMinifyPlugin } from "vite-plugin-minify";
import { VitePWA } from "vite-plugin-pwa";
@@ -81,13 +81,10 @@ function getPlugins({
const plugins: PluginOption[] = [
envConfig({ isDevelopment, clientVersion, env }),
languageHashes({ skip: isDevelopment }),
- checker({
- oxlint: {
- lintCommand: "oxlint . --type-aware --type-check",
- },
- overlay: {
- initialIsOpen: false,
- },
+ oxlintChecker({
+ debounceDelay: 125,
+ typeAware: true,
+ overlay: true,
}),
jqueryInject(),
injectHTML(),
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 89ee68fc4..17e59006f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -399,7 +399,7 @@ importers:
version: 5.0.2
'@vitest/coverage-v8':
specifier: 4.0.15
- version: 4.0.15(vitest@4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1))
+ version: 4.0.15(vitest@4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1))
autoprefixer:
specifier: 10.4.20
version: 10.4.20(postcss@8.4.31)
@@ -460,9 +460,6 @@ importers:
vite-bundle-visualizer:
specifier: 1.2.1
version: 1.2.1(rollup@2.79.2)
- vite-plugin-checker:
- specifier: 0.11.0
- version: 0.11.0(eslint@9.39.1)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.34.0(oxlint-tsgolint@0.9.2))(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1))
vite-plugin-filter-replace:
specifier: 0.1.14
version: 0.1.14
@@ -480,7 +477,7 @@ importers:
version: 1.1.0(vite@7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1))(workbox-build@7.1.1)(workbox-window@7.1.0)
vitest:
specifier: 4.0.15
- version: 4.0.15(@opentelemetry/api@1.8.0)(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)
+ version: 4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)
packages/contracts:
dependencies:
@@ -7090,10 +7087,6 @@ packages:
resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
- npm-run-path@6.0.0:
- resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==}
- engines: {node: '>=18'}
-
npmlog@5.0.1:
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
deprecated: This package is no longer supported.
@@ -8632,9 +8625,6 @@ packages:
through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
- tiny-invariant@1.3.3:
- resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==}
-
tiny-lru@8.0.2:
resolution: {integrity: sha512-ApGvZ6vVvTNdsmt676grvCkUCGwzG9IqXma5Z07xJgiC5L7akUMof5U8G2JTI9Rz/ovtVhJBlY6mNhEvtjzOIg==}
engines: {node: '>=6'}
@@ -8959,10 +8949,6 @@ packages:
resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==}
engines: {node: '>=18'}
- unicorn-magic@0.3.0:
- resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
- engines: {node: '>=18'}
-
unique-filename@3.0.0:
resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
@@ -9107,43 +9093,6 @@ packages:
peerDependencies:
vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
- vite-plugin-checker@0.11.0:
- resolution: {integrity: sha512-iUdO9Pl9UIBRPAragwi3as/BXXTtRu4G12L3CMrjx+WVTd9g/MsqNakreib9M/2YRVkhZYiTEwdH2j4Dm0w7lw==}
- engines: {node: '>=16.11'}
- peerDependencies:
- '@biomejs/biome': '>=1.7'
- eslint: '>=7'
- meow: ^13.2.0
- optionator: ^0.9.4
- oxlint: '>=1'
- stylelint: '>=16'
- typescript: '*'
- vite: '>=5.4.20'
- vls: '*'
- vti: '*'
- vue-tsc: ~2.2.10 || ^3.0.0
- peerDependenciesMeta:
- '@biomejs/biome':
- optional: true
- eslint:
- optional: true
- meow:
- optional: true
- optionator:
- optional: true
- oxlint:
- optional: true
- stylelint:
- optional: true
- typescript:
- optional: true
- vls:
- optional: true
- vti:
- optional: true
- vue-tsc:
- optional: true
-
vite-plugin-filter-replace@0.1.14:
resolution: {integrity: sha512-3onEAYgjJQTCJN+/f8+1vOUH3X/tRmXsfxmgH3QtpReGKi+xtP1jMpAEnYsNqw9mI2shZmxFm3LS+n52XczwYQ==}
@@ -9254,9 +9203,6 @@ packages:
vlq@0.2.3:
resolution: {integrity: sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==}
- vscode-uri@3.1.0:
- resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
-
walkdir@0.4.1:
resolution: {integrity: sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ==}
engines: {node: '>=6.0.0'}
@@ -12589,6 +12535,23 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@vitest/coverage-v8@4.0.15(vitest@4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1))':
+ dependencies:
+ '@bcoe/v8-coverage': 1.0.2
+ '@vitest/utils': 4.0.15
+ ast-v8-to-istanbul: 0.3.8
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-report: 3.0.1
+ istanbul-lib-source-maps: 5.0.6
+ istanbul-reports: 3.2.0
+ magicast: 0.5.1
+ obug: 2.1.1
+ std-env: 3.10.0
+ tinyrainbow: 3.0.3
+ vitest: 4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)
+ transitivePeerDependencies:
+ - supports-color
+
'@vitest/expect@4.0.15':
dependencies:
'@standard-schema/spec': 1.0.0
@@ -17021,11 +16984,6 @@ snapshots:
dependencies:
path-key: 4.0.0
- npm-run-path@6.0.0:
- dependencies:
- path-key: 4.0.0
- unicorn-magic: 0.3.0
-
npmlog@5.0.1:
dependencies:
are-we-there-yet: 2.0.0
@@ -18904,8 +18862,6 @@ snapshots:
through@2.3.8: {}
- tiny-invariant@1.3.3: {}
-
tiny-lru@8.0.2: {}
tinybench@2.9.0: {}
@@ -19196,8 +19152,6 @@ snapshots:
unicorn-magic@0.1.0: {}
- unicorn-magic@0.3.0: {}
-
unique-filename@3.0.0:
dependencies:
unique-slug: 4.0.0
@@ -19349,24 +19303,6 @@ snapshots:
dependencies:
vite: 7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)
- vite-plugin-checker@0.11.0(eslint@9.39.1)(meow@13.2.0)(optionator@0.9.4)(oxlint@1.34.0(oxlint-tsgolint@0.9.2))(typescript@5.9.3)(vite@7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)):
- dependencies:
- '@babel/code-frame': 7.27.1
- chokidar: 4.0.3
- npm-run-path: 6.0.0
- picocolors: 1.1.1
- picomatch: 4.0.3
- tiny-invariant: 1.3.3
- tinyglobby: 0.2.15
- vite: 7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)
- vscode-uri: 3.1.0
- optionalDependencies:
- eslint: 9.39.1
- meow: 13.2.0
- optionator: 0.9.4
- oxlint: 1.34.0(oxlint-tsgolint@0.9.2)
- typescript: 5.9.3
-
vite-plugin-filter-replace@0.1.14:
dependencies:
magic-string: 0.30.17
@@ -19514,9 +19450,45 @@ snapshots:
- tsx
- yaml
- vlq@0.2.3: {}
+ vitest@4.0.15(@types/node@24.9.1)(happy-dom@20.0.10)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1):
+ dependencies:
+ '@vitest/expect': 4.0.15
+ '@vitest/mocker': 4.0.15(vite@7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1))
+ '@vitest/pretty-format': 4.0.15
+ '@vitest/runner': 4.0.15
+ '@vitest/snapshot': 4.0.15
+ '@vitest/spy': 4.0.15
+ '@vitest/utils': 4.0.15
+ es-module-lexer: 1.7.0
+ expect-type: 1.2.2
+ magic-string: 0.30.21
+ obug: 2.1.1
+ pathe: 2.0.3
+ picomatch: 4.0.3
+ std-env: 3.10.0
+ tinybench: 2.9.0
+ tinyexec: 1.0.2
+ tinyglobby: 0.2.15
+ tinyrainbow: 3.0.3
+ vite: 7.1.12(@types/node@24.9.1)(sass@1.70.0)(terser@5.44.1)(tsx@4.16.2)(yaml@2.8.1)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@types/node': 24.9.1
+ happy-dom: 20.0.10
+ transitivePeerDependencies:
+ - jiti
+ - less
+ - lightningcss
+ - msw
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - terser
+ - tsx
+ - yaml
- vscode-uri@3.1.0: {}
+ vlq@0.2.3: {}
walkdir@0.4.1: {}