You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ha...@apache.org on 2026/10/15 03:11:01 UTC

(superset) branch master updated: feat(build): migrate custom lint rule to Oxlint (#44201)

This is an automated email from the ASF dual-hosted git repository.

hainenber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 8db419ce1ab feat(build): migrate custom lint rule to Oxlint (#44201)
8db419ce1ab is described below

commit 8db419ce1abf88dcfe0faaf42fe60fab6e05b218
Author: Đỗ Trọng Hải <41...@users.noreply.github.com>
AuthorDate: Thu Oct 15 10:10:52 2026 +0700

    feat(build): migrate custom lint rule to Oxlint (#44201)
    
    Signed-off-by: hainenber <do...@gmail.com>
---
 .github/workflows/superset-frontend.yml            |    3 +-
 scripts/eslint.sh                                  |   30 -
 .../i18n-strings/index.js}                         |  116 +-
 .../i18n-strings}/no-eager-t-in-config.test.ts     |   13 +-
 .../i18n-strings}/no-template-vars.test.ts         |   12 +-
 .../i18n-strings}/package.json                     |    9 +-
 .../index.ts => custom-lint-rules/icons/index.js}  |   61 +-
 .../icons}/no-fontawesome.test.ts                  |   13 +-
 .../icons}/package.json                            |    9 +-
 .../theme-colors/colors.js}                        |    7 +-
 .../theme-colors/index.js}                         |  111 +-
 .../theme-colors/no-literal-colors.test.ts         |   81 ++
 .../theme-colors}/package.json                     |   12 +-
 superset-frontend/eslint.config.minimal.js         |  130 --
 superset-frontend/oxlint.custom-lint-rules.mts     |  121 ++
 superset-frontend/oxlint.json                      |   20 +-
 superset-frontend/package-lock.json                | 1473 +-------------------
 superset-frontend/package.json                     |   28 +-
 superset-frontend/scripts/check-custom-rules.js    |    6 +-
 .../scripts/internal/oxlint-metrics-uploader.js    |  174 +--
 .../spec/scripts/oxlint-metrics-uploader.test.ts   |  327 ++++-
 superset-frontend/tsconfig.json                    |    2 +-
 22 files changed, 861 insertions(+), 1897 deletions(-)

diff --git a/.github/workflows/superset-frontend.yml b/.github/workflows/superset-frontend.yml
index 054dd55183e..2a5cb34abb5 100644
--- a/.github/workflows/superset-frontend.yml
+++ b/.github/workflows/superset-frontend.yml
@@ -153,10 +153,11 @@ jobs:
         run: |
           zstd -d < docker-image.tar.zst | docker load
 
+      # The last weird node command is to run tests fo custom Superset lint rules
       - name: lint
         run: |
           docker run --rm $TAG bash -c \
-          "npm i && npm run lint"
+          "npm i && npm run lint && node --import tsx --test custom-lint-rules/**/*.test.ts"
 
       - name: tsc
         run: |
diff --git a/scripts/eslint.sh b/scripts/eslint.sh
deleted file mode 100755
index b5d5dd4915e..00000000000
--- a/scripts/eslint.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env bash
-
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements.  See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License.  You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-set -e
-
-script_dir="$(dirname "$(realpath "$0")")"
-root_dir="$(dirname "$script_dir")"
-frontend_dir=superset-frontend
-
-if [[ ! -d "$root_dir/$frontend_dir" ]]; then
-  echo "Error: $frontend_dir directory not found in $root_dir" >&2
-  exit 1
-fi
-
-cd "$root_dir/$frontend_dir"
-npm run lint-fix -- "${@//$frontend_dir\//}"
diff --git a/superset-frontend/eslint-rules/eslint-plugin-i18n-strings/index.ts b/superset-frontend/custom-lint-rules/i18n-strings/index.js
similarity index 69%
rename from superset-frontend/eslint-rules/eslint-plugin-i18n-strings/index.ts
rename to superset-frontend/custom-lint-rules/i18n-strings/index.js
index 62c3094d8b6..737c7fb33c5 100644
--- a/superset-frontend/eslint-rules/eslint-plugin-i18n-strings/index.ts
+++ b/superset-frontend/custom-lint-rules/i18n-strings/index.js
@@ -22,14 +22,15 @@
  * @author Apache
  */
 
-import type { Rule } from 'eslint';
-import type { Node } from 'estree';
-
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
 
-const plugin: { rules: Record<string, Rule.RuleModule> } = {
+/** @type {{ rules: Record<string, import('oxlint').Rule.RuleModule> }} */
+const plugin = {
+  meta: {
+    name: 'i18n-strings',
+  },
   rules: {
     'no-template-vars': {
       meta: {
@@ -39,17 +40,22 @@ const plugin: { rules: Record<string, Rule.RuleModule> } = {
         },
         schema: [],
       },
-      create(context: Rule.RuleContext): Rule.RuleListener {
-        function handler(node: Node): void {
-          const callNode = node as Node & {
-            arguments: Array<Node & { type: string; expressions?: Node[] }>;
-          };
+      /** @param {import('eslint').Rule.RuleContext} context */
+      create(context) {
+        /** @param {import('estree').Node} node */
+        function handler(node) {
+          /**
+           * @type {import('estree').Node & {
+           *   arguments: Array<import('estree').Node & {
+           *     type: string;
+           *     expressions?: import('estree').Node[];
+           *   }>;
+           * }}
+           */
+          const callNode = node;
           // Check all arguments (e.g., tn has singular and plural templates)
           for (const arg of callNode.arguments ?? []) {
-            if (
-              arg.type === 'TemplateLiteral' &&
-              (arg as Node & { expressions?: Node[] }).expressions?.length
-            ) {
+            if (arg.type === 'TemplateLiteral' && arg.expressions?.length) {
               context.report({
                 node,
                 message:
@@ -90,23 +96,29 @@ const plugin: { rules: Record<string, Rule.RuleModule> } = {
             'Eager `{{property}}: {{fn}}(...)` is evaluated at module load, before i18n is initialized. Wrap in an arrow function: `{{property}}: () => {{fn}}(...)`.',
         },
       },
-      create(context: Rule.RuleContext): Rule.RuleListener {
-        const watchedProps: string[] = context.options[0]?.properties ?? [
+      /** @param {import('oxlint').Rule.RuleContext} context */
+      create(context) {
+        /** @type {string[]} */
+        const watchedProps = context?.options?.[0]?.properties ?? [
           'label',
           'description',
         ];
         const TRANSLATE_FNS = new Set(['t', 'tn']);
 
-        function handler(node: Node): void {
-          const prop = node as Node & {
-            key: { type: string; name?: string; value?: string };
-            value: Node & {
-              type: string;
-              callee?: { type: string; name?: string };
-            };
-            shorthand?: boolean;
-            computed?: boolean;
-          };
+        /** @param {import('estree').Node} node */
+        function handler(node) {
+          /**
+           * @type {import('estree').Node & {
+           *   key: { type: string; name?: string; value?: string };
+           *   value: import('estree').Node & {
+           *     type: string;
+           *     callee?: { type: string; name?: string };
+           *   };
+           *   shorthand?: boolean;
+           *   computed?: boolean;
+           * }}
+           */
+          const prop = node;
           if (prop.shorthand || prop.computed) return;
 
           const keyName =
@@ -153,21 +165,22 @@ const plugin: { rules: Record<string, Rule.RuleModule> } = {
         },
         schema: [],
       },
-      create(context: Rule.RuleContext): Rule.RuleListener {
-        function isTitleCase(str: string): boolean {
+      /** @param {import('oxlint').Rule.RuleContext} context */
+      create(context) {
+        /** @param {string} str */
+        function isTitleCase(str) {
           // Match "Delete Dataset", "Create Chart", etc. (2+ title-cased words)
           return /^[A-Z][a-z]+(\s+[A-Z][a-z]*)+$/.test(str);
         }
 
-        function isButtonContext(node: Node & { parent?: Node }): boolean {
-          const { parent } = node as Node & {
-            parent?: Node & Record<string, unknown>;
-          };
+        /** @param {import('estree').Node & { parent?: import('estree').Node }} node */
+        function isButtonContext(node) {
+          const { parent } = node;
           if (!parent) return false;
 
           // Check for button-specific props
           if (parent.type === 'Property') {
-            const key = (parent as unknown as { key: { name: string } }).key
+            const key = /** @type {{ key: { name: string } }} */ (parent).key
               .name;
             return [
               'primaryButtonName',
@@ -179,14 +192,15 @@ const plugin: { rules: Record<string, Rule.RuleModule> } = {
 
           // Check for Button components
           // Cast to string because ESTree Node type doesn't include JSX types
-          if ((parent.type as string) === 'JSXExpressionContainer') {
-            const jsx = (parent as Node & { parent?: Node }).parent as
-              | (Node & {
-                  type: string;
-                  openingElement?: { name: { name: string } };
-                })
-              | undefined;
-            if ((jsx?.type as string) === 'JSXElement') {
+          if (parent.type === 'JSXExpressionContainer') {
+            /**
+             * @type {(import('estree').Node & {
+             *   type: string;
+             *   openingElement?: { name: { name: string } };
+             * }) | undefined}
+             */
+            const jsx = parent.parent;
+            if (jsx?.type === 'JSXElement') {
               const elementName = jsx?.openingElement?.name.name;
               return elementName === 'Button';
             }
@@ -195,22 +209,26 @@ const plugin: { rules: Record<string, Rule.RuleModule> } = {
           return false;
         }
 
-        function handler(node: Node): void {
-          const callNode = node as Node & {
-            arguments: Array<Node & { type: string; value?: unknown }>;
-          };
+        /** @param {import('estree').Node} node */
+        function handler(node) {
+          /**
+           * @type {import('estree').Node & {
+           *   arguments: Array<import('estree').Node & {
+           *     type: string;
+           *     value?: unknown;
+           *   }>;
+           * }}
+           */
+          const callNode = node;
           // Check all string literal arguments (e.g., tn has singular and plural)
           for (const arg of callNode.arguments ?? []) {
             if (arg.type === 'Literal' && typeof arg.value === 'string') {
               const text = arg.value;
 
-              if (
-                isButtonContext(node as Node & { parent?: Node }) &&
-                isTitleCase(text)
-              ) {
+              if (isButtonContext(node) && isTitleCase(text)) {
                 const sentenceCase = text
                   .toLowerCase()
-                  .replace(/^\w/, (c: string) => c.toUpperCase());
+                  .replace(/^\w/, c => c.toUpperCase());
                 context.report({
                   node: arg,
                   message: `Button text should use sentence case: "${text}" should be "${sentenceCase}"`,
diff --git a/superset-frontend/eslint-rules/eslint-plugin-i18n-strings/no-eager-t-in-config.test.ts b/superset-frontend/custom-lint-rules/i18n-strings/no-eager-t-in-config.test.ts
similarity index 92%
rename from superset-frontend/eslint-rules/eslint-plugin-i18n-strings/no-eager-t-in-config.test.ts
rename to superset-frontend/custom-lint-rules/i18n-strings/no-eager-t-in-config.test.ts
index e9096087cb3..4278deb2fc0 100644
--- a/superset-frontend/eslint-rules/eslint-plugin-i18n-strings/no-eager-t-in-config.test.ts
+++ b/superset-frontend/custom-lint-rules/i18n-strings/no-eager-t-in-config.test.ts
@@ -16,13 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { type Rule, RuleTester } from 'eslint';
+
+import { RuleTester } from 'oxlint/plugins-dev';
+import { describe, it } from 'node:test';
 import plugin from '.';
 
-const ruleTester = new RuleTester({
-  parserOptions: { ecmaVersion: 2020, sourceType: 'module' },
-} as any);
-const rule: Rule.RuleModule = plugin.rules['no-eager-t-in-config'];
+RuleTester.describe = describe;
+RuleTester.it = it;
+
+const ruleTester = new RuleTester();
+const rule = plugin.rules['no-eager-t-in-config'];
 
 ruleTester.run('no-eager-t-in-config', rule, {
   valid: [
diff --git a/superset-frontend/eslint-rules/eslint-plugin-i18n-strings/no-template-vars.test.ts b/superset-frontend/custom-lint-rules/i18n-strings/no-template-vars.test.ts
similarity index 88%
rename from superset-frontend/eslint-rules/eslint-plugin-i18n-strings/no-template-vars.test.ts
rename to superset-frontend/custom-lint-rules/i18n-strings/no-template-vars.test.ts
index 8a075939141..995c0470967 100644
--- a/superset-frontend/eslint-rules/eslint-plugin-i18n-strings/no-template-vars.test.ts
+++ b/superset-frontend/custom-lint-rules/i18n-strings/no-template-vars.test.ts
@@ -21,16 +21,20 @@
  * @fileoverview Rule to warn about translation template variables
  * @author Apache
  */
-/* eslint-disable no-template-curly-in-string */
-import { type Rule, RuleTester } from 'eslint';
+
+import { RuleTester } from 'oxlint/plugins-dev';
+import { describe, it } from 'node:test';
 import plugin from '.';
 
+RuleTester.describe = describe;
+RuleTester.it = it;
+
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
 
-const ruleTester = new RuleTester({ languageOptions: { ecmaVersion: 6 } });
-const rule: Rule.RuleModule = plugin.rules['no-template-vars'];
+const ruleTester = new RuleTester();
+const rule = plugin.rules['no-template-vars'];
 
 const errors: Array<{ message: string }> = [
   {
diff --git a/superset-frontend/eslint-rules/eslint-plugin-i18n-strings/package.json b/superset-frontend/custom-lint-rules/i18n-strings/package.json
similarity index 64%
rename from superset-frontend/eslint-rules/eslint-plugin-i18n-strings/package.json
rename to superset-frontend/custom-lint-rules/i18n-strings/package.json
index 70a5353c9fc..ea6c25c978a 100644
--- a/superset-frontend/eslint-rules/eslint-plugin-i18n-strings/package.json
+++ b/superset-frontend/custom-lint-rules/i18n-strings/package.json
@@ -1,17 +1,20 @@
 {
-  "name": "@superset-ui/eslint-plugin-i18n-strings",
+  "name": "@superset-ui/lint-plugin-i18n-strings",
   "version": "1.0.0",
   "description": "Warns about translation variables",
   "keywords": [],
   "license": "Apache-2.0",
   "author": "Apache",
   "type": "module",
-  "main": "index.ts",
+  "main": "index.js",
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1"
   },
   "dependencies": {},
   "peerDependencies": {
-    "eslint": ">=0.8.0"
+    "@oxlint/plugins": "*",
+    "@types/estree": "*",
+    "jest": "*",
+    "oxlint": "*"
   }
 }
diff --git a/superset-frontend/eslint-rules/eslint-plugin-icons/index.ts b/superset-frontend/custom-lint-rules/icons/index.js
similarity index 66%
rename from superset-frontend/eslint-rules/eslint-plugin-icons/index.ts
rename to superset-frontend/custom-lint-rules/icons/index.js
index 6a5c186d3f7..b22ce322a19 100644
--- a/superset-frontend/eslint-rules/eslint-plugin-icons/index.ts
+++ b/superset-frontend/custom-lint-rules/icons/index.js
@@ -22,33 +22,43 @@
  * @author Apache
  */
 
-import type { Rule } from 'eslint';
-import type { Node } from 'estree';
-
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
 
-interface JSXAttribute {
-  name?: { name: string };
-  value?: { type: string; value?: string; expression?: { value: string } };
-}
+/**
+ * @typedef {Object} JSXAttribute
+ * @property {Object} [name]
+ * @property {string} [name.name]
+ * @property {Object} [value]
+ * @property {string} [value.type]
+ * @property {string} [value.value]
+ * @property {Object} [value.expression]
+ * @property {string} [value.expression.value]
+ */
 
-interface JSXOpeningElement {
-  name: { name: string };
-  attributes: JSXAttribute[];
-}
+/**
+ * @typedef {Object} JSXOpeningElement
+ * @property {Object} name
+ * @property {string} name.name
+ * @property {JSXAttribute[]} attributes
+ */
 
-interface JSXElementNode {
-  type: string;
-  openingElement: JSXOpeningElement;
-}
+/**
+ * @typedef {Object} JSXElementNode
+ * @property {string} type
+ * @property {JSXOpeningElement} openingElement
+ */
 
+/** @type {{ rules: Record<string, import('oxlint').Rule.RuleModule> }} */
 const plugin = {
+  meta: {
+    name: 'icons',
+  },
   rules: {
     'no-fa-icons-usage': {
       meta: {
-        type: 'problem' as const,
+        type: 'problem',
         docs: {
           description:
             'Disallow the usage of FontAwesome icons in the codebase',
@@ -56,16 +66,25 @@ const plugin = {
         },
         schema: [],
       },
-      create(context: Rule.RuleContext): Rule.RuleListener {
+      /**
+       * @param {import('oxlint').Rule.RuleContext} context
+       * @returns {import('oxlint').Rule.RuleListener}
+       */
+      createOnce(context) {
         return {
-          // Check for JSX elements with class names containing "fa"
-          JSXElement(node: Node): void {
-            const jsxNode = node as unknown as JSXElementNode;
+          /**
+           * Check for JSX elements with class names containing "fa"
+           * @param {import('estree').Node} node
+           * @returns {void}
+           */
+          JSXElement(node) {
+            /** @type {JSXElementNode} */
+            const jsxNode = node;
             if (
               jsxNode.openingElement &&
               jsxNode.openingElement.name.name === 'i' &&
               jsxNode.openingElement.attributes &&
-              jsxNode.openingElement.attributes.some((attr: JSXAttribute) => {
+              jsxNode.openingElement.attributes.some(attr => {
                 if (attr.name?.name !== 'className') return false;
                 // Handle className="fa fa-home"
                 if (attr.value?.type === 'Literal') {
diff --git a/superset-frontend/eslint-rules/eslint-plugin-icons/no-fontawesome.test.ts b/superset-frontend/custom-lint-rules/icons/no-fontawesome.test.ts
similarity index 88%
rename from superset-frontend/eslint-rules/eslint-plugin-icons/no-fontawesome.test.ts
rename to superset-frontend/custom-lint-rules/icons/no-fontawesome.test.ts
index 69239980b42..bd029d8ccd9 100644
--- a/superset-frontend/eslint-rules/eslint-plugin-icons/no-fontawesome.test.ts
+++ b/superset-frontend/custom-lint-rules/icons/no-fontawesome.test.ts
@@ -22,16 +22,23 @@
  * @author Apache
  */
 
-import { RuleTester } from 'eslint';
+import { RuleTester } from 'oxlint/plugins-dev';
+import { describe, it } from 'node:test';
 import plugin from '.';
 
+RuleTester.describe = describe;
+RuleTester.it = it;
+
 //------------------------------------------------------------------------------
 // Tests
 //------------------------------------------------------------------------------
 const ruleTester = new RuleTester({
   languageOptions: {
-    ecmaVersion: 6,
-    parserOptions: { ecmaFeatures: { jsx: true } },
+    parserOptions: {
+      ecmaFeatures: {
+        jsx: true,
+      },
+    },
   },
 });
 const rule = plugin.rules['no-fa-icons-usage'];
diff --git a/superset-frontend/eslint-rules/eslint-plugin-icons/package.json b/superset-frontend/custom-lint-rules/icons/package.json
similarity index 66%
rename from superset-frontend/eslint-rules/eslint-plugin-icons/package.json
rename to superset-frontend/custom-lint-rules/icons/package.json
index bfd59be3855..df04bdbed4a 100644
--- a/superset-frontend/eslint-rules/eslint-plugin-icons/package.json
+++ b/superset-frontend/custom-lint-rules/icons/package.json
@@ -1,17 +1,20 @@
 {
-  "name": "@superset-ui/eslint-plugin-icons",
+  "name": "@superset-ui/lint-plugin-icons",
   "version": "1.0.0",
   "description": "Warns about direct usage of Ant Design icons",
   "keywords": [],
   "license": "Apache-2.0",
   "author": "Apache",
   "type": "module",
-  "main": "index.ts",
+  "main": "index.js",
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1"
   },
   "dependencies": {},
   "peerDependencies": {
-    "eslint": ">=0.8.0"
+    "@oxlint/plugins": "*",
+    "@types/estree": "*",
+    "jest": "*",
+    "oxlint": "*"
   }
 }
diff --git a/superset-frontend/eslint-rules/eslint-plugin-theme-colors/colors.ts b/superset-frontend/custom-lint-rules/theme-colors/colors.js
similarity index 97%
rename from superset-frontend/eslint-rules/eslint-plugin-theme-colors/colors.ts
rename to superset-frontend/custom-lint-rules/theme-colors/colors.js
index 46974c7331f..ec2a8eed4ca 100644
--- a/superset-frontend/eslint-rules/eslint-plugin-theme-colors/colors.ts
+++ b/superset-frontend/custom-lint-rules/theme-colors/colors.js
@@ -18,7 +18,8 @@
  */
 
 // https://www.w3.org/wiki/CSS/Properties/color/keywords
-const COLOR_KEYWORDS: string[] = [
+/** @type {string[]} */
+export const COLOR_KEYWORDS = [
   'black',
   'silver',
   'gray',
@@ -153,6 +154,7 @@ const COLOR_KEYWORDS: string[] = [
   'seagreen',
   'seashell',
   'sienna',
+  'silver',
   'skyblue',
   'slateblue',
   'slategray',
@@ -167,8 +169,7 @@ const COLOR_KEYWORDS: string[] = [
   'turquoise',
   'violet',
   'wheat',
+  'white',
   'whitesmoke',
   'yellowgreen',
 ];
-
-export default COLOR_KEYWORDS;
diff --git a/superset-frontend/eslint-rules/eslint-plugin-theme-colors/index.ts b/superset-frontend/custom-lint-rules/theme-colors/index.js
similarity index 67%
rename from superset-frontend/eslint-rules/eslint-plugin-theme-colors/index.ts
rename to superset-frontend/custom-lint-rules/theme-colors/index.js
index fb8af4eea87..d9a8b0aad70 100644
--- a/superset-frontend/eslint-rules/eslint-plugin-theme-colors/index.ts
+++ b/superset-frontend/custom-lint-rules/theme-colors/index.js
@@ -22,24 +22,34 @@
  * @author Apache
  */
 
-import type { Rule } from 'eslint';
-import type { Node, SourceLocation } from 'estree';
+import { COLOR_KEYWORDS } from './colors.js';
 
-import COLOR_KEYWORDS from './colors';
-
-function hasHexColor(quasi: string): boolean {
+/**
+ * @param {string} quasi
+ * @returns {boolean}
+ */
+function hasHexColor(quasi) {
   const regex = /#([a-f0-9]{3}|[a-f0-9]{4}(?:[a-f0-9]{2}){0,2})\b/gi;
   return !!quasi.match(regex);
 }
 
-function hasRgbColor(quasi: string): boolean {
+/**
+ * @param {string} quasi
+ * @returns {boolean}
+ */
+function hasRgbColor(quasi) {
   const regex = /rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)/i;
   return !!quasi.match(regex);
 }
 
-function hasLiteralColor(quasi: string, strict: boolean = false): boolean {
+/**
+ * @param {string} quasi
+ * @param {boolean} [strict=false]
+ * @returns {boolean}
+ */
+function hasLiteralColor(quasi, strict = false) {
   // matches literal colors at the start or end of a CSS prop
-  return COLOR_KEYWORDS.some((color: string) => {
+  return COLOR_KEYWORDS.some(color => {
     const regexColon = new RegExp(`: ${color}`);
     const regexSemicolon = new RegExp(` ${color};`);
     return (
@@ -50,31 +60,41 @@ function hasLiteralColor(quasi: string, strict: boolean = false): boolean {
   });
 }
 
-const WARNING_MESSAGE: string =
+/** @type {string} */
+const WARNING_MESSAGE =
   'Theme color variables are preferred over rgb(a)/hex/literal colors';
 
 //------------------------------------------------------------------------------
 // Rule Definition
 //------------------------------------------------------------------------------
 
-interface TemplateElementNode {
-  type: string;
-  value?: { raw: string };
-  loc?: SourceLocation | null;
-  parent?: {
-    type: string;
-    parent?: { type: string; loc?: SourceLocation | null };
-  };
-}
+/**
+ * @typedef {Object} TemplateElementNode
+ * @property {string} type
+ * @property {Object} [value]
+ * @property {string} [value.raw]
+ * @property {Object} [loc]
+ * @property {Object} [parent]
+ * @property {string} [parent.type]
+ * @property {Object} [parent.parent]
+ * @property {string} [parent.parent.type]
+ * @property {Object} [parent.parent.loc]
+ */
 
-interface LiteralNode {
-  type: string;
-  value?: unknown;
-  loc?: SourceLocation | null;
-  parent?: { type: string };
-}
+/**
+ * @typedef {Object} LiteralNode
+ * @property {string} type
+ * @property {unknown} [value]
+ * @property {Object} [loc]
+ * @property {Object} [parent]
+ * @property {string} [parent.type]
+ */
 
-const plugin: { rules: Record<string, Rule.RuleModule> } = {
+/** @type {{ rules: Record<string, import('oxlint').Rule.RuleModule> }} */
+const plugin = {
+  meta: {
+    name: 'theme-colors',
+  },
   rules: {
     'no-literal-colors': {
       meta: {
@@ -85,11 +105,22 @@ const plugin: { rules: Record<string, Rule.RuleModule> } = {
         },
         schema: [],
       },
-      create(context: Rule.RuleContext): Rule.RuleListener {
-        const warned: string[] = [];
+      /**
+       * @param {import('oxlint').Rule.RuleContext} context
+       * @returns {import('oxlint').Rule.RuleListener}
+       */
+      create(context) {
+        /** @type {string[]} */
+        const warned = [];
+
         return {
-          TemplateElement(node: Node): void {
-            const templateNode = node as TemplateElementNode;
+          /**
+           * @param {import('estree').Node} node
+           * @returns {void}
+           */
+          TemplateElement(node) {
+            /** @type {TemplateElementNode} */
+            const templateNode = node;
             const rawValue = templateNode?.value?.raw;
             const isChildParentTagged =
               templateNode?.parent?.parent?.type === 'TaggedTemplateExpression';
@@ -100,6 +131,7 @@ const plugin: { rules: Record<string, Rule.RuleModule> } = {
             const loc = templateNode?.parent?.parent?.loc;
             const locId = loc && JSON.stringify(loc);
             const hasWarned = locId ? warned.includes(locId) : false;
+
             if (
               !hasWarned &&
               (isChildParentTagged ||
@@ -111,7 +143,7 @@ const plugin: { rules: Record<string, Rule.RuleModule> } = {
             ) {
               context.report({
                 node,
-                ...(loc && { loc: loc as SourceLocation }),
+                ...(loc && { loc }),
                 message: WARNING_MESSAGE,
               });
               if (locId) {
@@ -119,17 +151,22 @@ const plugin: { rules: Record<string, Rule.RuleModule> } = {
               }
             }
           },
-          Literal(node: Node): void {
-            const literalNode = node as LiteralNode;
+
+          /**
+           * @param {import('estree').Node} node
+           * @returns {void}
+           */
+          Literal(node) {
+            /** @type {LiteralNode} */
+            const literalNode = node;
             const value = literalNode?.value;
+
             // Only process string literals (not numbers, booleans, null, or RegExp)
             if (typeof value !== 'string') {
               return;
             }
-            const parent = literalNode?.parent as Node & {
-              type: string;
-              value?: Node;
-            };
+
+            const parent = literalNode?.parent;
             // Only check property values, not keys (e.g., { color: 'red' } not { red: 1 })
             const isPropertyValue =
               parent?.type === 'Property' && parent.value === node;
@@ -145,7 +182,7 @@ const plugin: { rules: Record<string, Rule.RuleModule> } = {
             ) {
               context.report({
                 node,
-                ...(node.loc && { loc: node.loc as SourceLocation }),
+                ...(node.loc && { loc: node.loc }),
                 message: WARNING_MESSAGE,
               });
               if (locId) {
diff --git a/superset-frontend/custom-lint-rules/theme-colors/no-literal-colors.test.ts b/superset-frontend/custom-lint-rules/theme-colors/no-literal-colors.test.ts
new file mode 100644
index 00000000000..c94479c32b5
--- /dev/null
+++ b/superset-frontend/custom-lint-rules/theme-colors/no-literal-colors.test.ts
@@ -0,0 +1,81 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { RuleTester } from 'oxlint/plugins-dev';
+import { describe, it } from 'node:test';
+import plugin from '.';
+
+RuleTester.describe = describe;
+RuleTester.it = it;
+
+const ruleTester = new RuleTester();
+const rule = plugin.rules['no-literal-colors'];
+
+const errors: Array<{ message: string }> = [
+  {
+    message:
+      'Theme color variables are preferred over rgb(a)/hex/literal colors',
+  },
+];
+
+const doubleErrors: Array<{ message: string }> = [
+  {
+    message:
+      'Theme color variables are preferred over rgb(a)/hex/literal colors',
+  },
+  {
+    message:
+      'Theme color variables are preferred over rgb(a)/hex/literal colors',
+  },
+];
+
+ruleTester.run('no-literal-colors', rule, {
+  valid: [
+    'const styles = { color: theme.colorText, background: theme.colorBg };',
+    "const colors = { red: 'not a property value' };",
+    "const color = 'red';",
+    'styled.div`color: ${theme.colorText};`',
+  ],
+  invalid: [
+    {
+      code: "const styles = { color: 'red' };",
+      errors,
+    },
+    {
+      code: "const styles = { color: 'red', secondColor: 'tan' };",
+      errors: doubleErrors,
+    },
+    {
+      code: "const styles = { background: '#fff' };",
+      errors,
+    },
+    {
+      code: "const styles = { color: 'rgb(1, 2, 3)' };",
+      errors,
+    },
+    {
+      code: 'styled.div`color: red;`',
+      errors,
+    },
+    {
+      code: 'const styles = () => `background: #fff;`;',
+      errors,
+    },
+  ],
+});
diff --git a/superset-frontend/eslint-rules/eslint-plugin-theme-colors/package.json b/superset-frontend/custom-lint-rules/theme-colors/package.json
similarity index 54%
rename from superset-frontend/eslint-rules/eslint-plugin-theme-colors/package.json
rename to superset-frontend/custom-lint-rules/theme-colors/package.json
index 5d355318733..3b2e853d656 100644
--- a/superset-frontend/eslint-rules/eslint-plugin-theme-colors/package.json
+++ b/superset-frontend/custom-lint-rules/theme-colors/package.json
@@ -1,14 +1,20 @@
 {
-  "name": "@superset-ui/eslint-plugin-theme-colors",
+  "name": "@superset-ui/lint-plugin-theme-colors",
   "version": "1.0.0",
   "description": "Warns about rgb(a)/hex/literal colors",
   "keywords": [],
   "license": "Apache-2.0",
   "author": "Apache",
   "type": "module",
-  "main": "index.ts",
+  "main": "index.js",
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1"
   },
-  "dependencies": {}
+  "dependencies": {},
+  "peerDependencies": {
+    "@oxlint/plugins": "*",
+    "@types/estree": "*",
+    "jest": "*",
+    "oxlint": "*"
+  }
 }
diff --git a/superset-frontend/eslint.config.minimal.js b/superset-frontend/eslint.config.minimal.js
deleted file mode 100644
index 8e766e9e655..00000000000
--- a/superset-frontend/eslint.config.minimal.js
+++ /dev/null
@@ -1,130 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/**
- * MINIMAL ESLint flat config - ONLY for rules OXC doesn't support.
- *
- * This config is run alongside the OXC (oxlint) linter, which handles the
- * bulk of linting. ESLint here only covers the custom Superset plugins that
- * oxlint cannot express. It is consumed by
- * `scripts/oxlint-metrics-uploader.js` (`npm run lint-stats`).
- *
- * Migrated from the legacy `.eslintrc.minimal.js` (eslintrc) format to flat
- * config for ESLint v9+/v10, where eslintrc is no longer supported.
- *
- * Only covers:
- * - Custom Superset plugins (theme-colors, icons, i18n-strings)
- */
-
-// Register the TypeScript require hook so ESLint can load the .ts plugin files
-// from eslint-rules/*.
-import 'tsx/cjs';
-
-import tsParser from '@typescript-eslint/parser';
-import themeColorsPlugin from '@superset-ui/eslint-plugin-theme-colors';
-import iconsPlugin from '@superset-ui/eslint-plugin-icons';
-import i18nStringsPlugin from '@superset-ui/eslint-plugin-i18n-strings';
-
-export default [
-  // Files this config applies to. Flat config has no `--ext`; globs live here.
-  // Only check src/ files where the theme/icon/i18n rules matter.
-  {
-    ignores: [
-      'node_modules/**',
-      'dist/**',
-      'build/**',
-      '.next/**',
-      'coverage/**',
-      '**/*.min.js',
-      'vendor/**',
-      // Skip packages/plugins since they have different theming rules
-      'packages/**',
-      'plugins/**',
-      // Skip generated/external/config files
-      '**/*.generated.*',
-      '**/*.config.js',
-      '**/webpack.*',
-      '*.json',
-    ],
-  },
-  {
-    files: ['**/*.{js,jsx,ts,tsx}'],
-    languageOptions: {
-      ecmaVersion: 2020,
-      sourceType: 'module',
-      // The @typescript-eslint parser handles both TS/TSX and plain JS/JSX and
-      // is compatible with ESLint v10's scope manager. (The legacy
-      // @babel/eslint-parser does not support ESLint v10.) The custom rules
-      // here are pure AST visitors and do not require type information, so no
-      // `project` is configured — this keeps parsing fast.
-      parser: tsParser,
-      parserOptions: {
-        ecmaFeatures: {
-          jsx: true,
-        },
-      },
-    },
-    // Don't report on eslint-disable comments for rules we don't have.
-    linterOptions: {
-      reportUnusedDisableDirectives: false,
-    },
-    plugins: {
-      'theme-colors': themeColorsPlugin,
-      icons: iconsPlugin,
-      'i18n-strings': i18nStringsPlugin,
-    },
-    rules: {
-      // Custom Superset plugins
-      'theme-colors/no-literal-colors': 'error',
-      'icons/no-fa-icons-usage': 'error',
-      'i18n-strings/no-template-vars': 'error',
-      // Enabled only for controlPanel files via the override below.
-      'i18n-strings/no-eager-t-in-config': 'off',
-    },
-  },
-  {
-    // Eager t()/tn() in `label`/`description` config props is captured at
-    // module-load time, before i18n initializes — labels stay in the fallback
-    // language even after the user switches. Surfaced as a warning (with
-    // autofix to `() => t(...)`) wherever this is a real foot-gun:
-    // controlPanel files. Promote to `'error'` once the codebase is clean.
-    files: ['**/controlPanel.{ts,tsx,js,jsx}'],
-    rules: {
-      'i18n-strings/no-eager-t-in-config': 'warn',
-    },
-  },
-  {
-    // Disable custom rules in test/story files
-    files: [
-      '**/*.test.*',
-      '**/*.spec.*',
-      '**/*.stories.*',
-      '**/test/**',
-      '**/tests/**',
-      '**/spec/**',
-      '**/__tests__/**',
-      '**/__mocks__/**',
-    ],
-    rules: {
-      'theme-colors/no-literal-colors': 'off',
-      'icons/no-fa-icons-usage': 'off',
-      'i18n-strings/no-template-vars': 'off',
-    },
-  },
-];
diff --git a/superset-frontend/oxlint.custom-lint-rules.mts b/superset-frontend/oxlint.custom-lint-rules.mts
new file mode 100644
index 00000000000..7a4b78d91bf
--- /dev/null
+++ b/superset-frontend/oxlint.custom-lint-rules.mts
@@ -0,0 +1,121 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * MINIMAL Oxlint config - ONLY for custom rules
+ *
+ * This config is run alongside the OXC (oxlint) linter, which handles the
+ * bulk of linting. Oxlint here only covers the custom Superset plugins that
+ * oxlint cannot express. It is consumed by
+ * `scripts/oxlint-metrics-uploader.js` (`npm run lint-stats`).
+ *
+ * 1. Migrated from the legacy `.eslintrc.minimal.js` (eslintrc) format to flat
+ * config for ESLint v9+/v10, where eslintrc is no longer supported.
+ * 2. Migrated from legacy `eslint.config.minimal.js` to `oxlint.custom-lint-rules.mts`
+ * for speed
+ *
+ * Only covers:
+ * - Custom Superset plugins (theme-colors, icons, i18n-strings)
+ */
+
+import { defineConfig } from 'oxlint';
+
+export default defineConfig({
+  // Files this config applies to. Flat config has no `--ext`; globs live here.
+  // Only check src/ files where the theme/icon/i18n rules matter.
+  ignorePatterns: [
+    'node_modules/**',
+    'dist/**',
+    'build/**',
+    '.next/**',
+    'coverage/**',
+    '**/*.min.js',
+    'vendor/**',
+    // Skip packages/plugins since they have different theming rules
+    'packages/**',
+    'plugins/**',
+    // Skip generated/external/config files
+    '**/*.generated.*',
+    '**/*.config.js',
+    '**/webpack.*',
+    '*.json',
+  ],
+  plugins: [],
+  jsPlugins: [
+    "./custom-lint-rules/i18n-strings/index.js",
+    "./custom-lint-rules/icons/index.js",
+    "./custom-lint-rules/theme-colors/index.js"
+  ],
+  options: {
+    reportUnusedDisableDirectives: 'off',
+    respectEslintDisableDirectives: false
+  },
+  // Toggle off built-in lint rules that cannot be toggled off by setting empty `plugins` array at L59
+  // TODO: remove once Oxlint has managed to fix said issue and/or it cannot be reproduced in your run.
+  rules: {
+    "no-unused-vars": "off",
+    "no-extra-boolean-cast": "off",
+    "no-shadow-restricted-names": "off",
+    "no-control-regex": "off"
+  },
+  overrides: [
+    {
+      files: ['**/*.{js,jsx,ts,tsx}'],
+      // Don't report on eslint-disable comments for rules we don't have.
+      rules: {
+        // Custom Superset plugins
+        'theme-colors/no-literal-colors': 'error',
+        'icons/no-fa-icons-usage': 'error',
+        'i18n-strings/no-template-vars': 'error',
+        // Enabled only for controlPanel files via the override below.
+        'i18n-strings/no-eager-t-in-config': 'off',
+      },
+    },
+    {
+      // Eager t()/tn() in `label`/`description` config props is captured at
+      // module-load time, before i18n initializes — labels stay in the fallback
+      // language even after the user switches. Surfaced as a warning (with
+      // autofix to `() => t(...)`) wherever this is a real foot-gun:
+      // controlPanel files. Promote to `'error'` once the codebase is clean.
+      files: ['**/controlPanel.{ts,tsx,js,jsx}'],
+      rules: {
+        'i18n-strings/no-eager-t-in-config': 'warn',
+      },
+    },
+    {
+      // Disable custom rules in test/story files
+      files: [
+        '**/*.test.*',
+        '**/*.spec.*',
+        '**/*.stories.*',
+        '**/test/**',
+        '**/tests/**',
+        '**/spec/**',
+        '**/__tests__/**',
+        '**/__mocks__/**',
+        'cypress-base/**',
+      ],
+      rules: {
+        'theme-colors/no-literal-colors': 'off',
+        'icons/no-fa-icons-usage': 'off',
+        'i18n-strings/no-template-vars': 'off',
+      },
+    },
+  ],
+});
diff --git a/superset-frontend/oxlint.json b/superset-frontend/oxlint.json
index 366138af18b..660887c5b2c 100644
--- a/superset-frontend/oxlint.json
+++ b/superset-frontend/oxlint.json
@@ -222,15 +222,15 @@
     "jsx-a11y/tabindex-no-positive": "error",
 
     // === TypeScript rules ===
-    "@typescript-eslint/ban-ts-comment": "off",
-    "@typescript-eslint/ban-types": "off",
-    "@typescript-eslint/no-empty-function": "off",
-    "@typescript-eslint/no-explicit-any": "off",
-    "@typescript-eslint/no-use-before-define": "error",
-    "@typescript-eslint/no-non-null-assertion": "off",
-    "@typescript-eslint/explicit-function-return-type": "off",
-    "@typescript-eslint/explicit-module-boundary-types": "off",
-    "@typescript-eslint/no-unused-vars": [
+    "typescript/ban-ts-comment": "off",
+    "typescript/ban-types": "off",
+    "typescript/no-empty-function": "off",
+    "typescript/no-explicit-any": "off",
+    "typescript/no-use-before-define": "error",
+    "typescript/no-non-null-assertion": "off",
+    "typescript/explicit-function-return-type": "off",
+    "typescript/explicit-module-boundary-types": "off",
+    "typescript/no-unused-vars": [
       "error",
       {
         "argsIgnorePattern": "^_",
@@ -238,7 +238,7 @@
         "caughtErrors": "none"
       }
     ],
-    "@typescript-eslint/prefer-optional-chain": "error",
+    "typescript/prefer-optional-chain": "error",
 
     // === Unicorn rules (bonus coverage) ===
     "unicorn/no-new-array": "error",
diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json
index b952bab3a09..1594a03f2f1 100644
--- a/superset-frontend/package-lock.json
+++ b/superset-frontend/package-lock.json
@@ -157,7 +157,6 @@
         "@babel/cli": "^7.29.7",
         "@babel/compat-data": "^7.28.4",
         "@babel/core": "^7.29.7",
-        "@babel/eslint-parser": "^7.29.9",
         "@babel/node": "^7.29.7",
         "@babel/plugin-syntax-dynamic-import": "^7.8.3",
         "@babel/plugin-transform-export-namespace-from": "^7.29.7",
@@ -174,15 +173,13 @@
         "@emotion/jest": "^11.14.2",
         "@formatjs/intl-durationformat": "^0.11.1",
         "@istanbuljs/nyc-config-typescript": "^1.0.1",
+        "@oxlint/plugins": "^1.81.0",
         "@playwright/test": "^1.62.1",
         "@pmmmwh/react-refresh-webpack-plugin": "^0.6.3",
         "@storybook/addon-docs": "10.6.1",
         "@storybook/addon-links": "10.6.1",
         "@storybook/react-webpack5": "10.6.1",
         "@storybook/test-runner": "0.26.0",
-        "@superset-ui/eslint-plugin-i18n-strings": "file:eslint-rules/eslint-plugin-i18n-strings",
-        "@superset-ui/eslint-plugin-icons": "file:eslint-rules/eslint-plugin-icons",
-        "@superset-ui/eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
         "@svgr/webpack": "^8.1.0",
         "@swc/core": "^1.16.13",
         "@swc/plugin-emotion": "^19.0.0",
@@ -193,6 +190,7 @@
         "@testing-library/user-event": "^14.6.7",
         "@types/content-disposition": "^0.5.9",
         "@types/dom-to-image": "^2.6.7",
+        "@types/estree": "^1.0.9",
         "@types/jest": "^30.0.0",
         "@types/jquery": "^4.0.1",
         "@types/js-levenshtein": "^1.1.3",
@@ -209,8 +207,6 @@
         "@types/rison": "0.1.0",
         "@types/tinycolor2": "^1.4.3",
         "@types/unzipper": "^0.10.11",
-        "@typescript-eslint/eslint-plugin": "^8.71.1",
-        "@typescript-eslint/parser": "^8.63.0",
         "babel-jest": "^30.5.2",
         "babel-loader": "^10.1.1",
         "babel-plugin-dynamic-import-node": "^2.3.3",
@@ -221,17 +217,6 @@
         "copy-webpack-plugin": "^14.0.0",
         "cross-env": "^10.1.0",
         "css-loader": "^7.1.5",
-        "eslint": "^10.10.0",
-        "eslint-import-resolver-alias": "^1.1.2",
-        "eslint-import-resolver-typescript": "^4.4.5",
-        "eslint-plugin-import": "^2.32.0",
-        "eslint-plugin-jest-dom": "^5.10.1",
-        "eslint-plugin-lodash": "^8.0.0",
-        "eslint-plugin-no-only-tests": "^3.4.0",
-        "eslint-plugin-react-prefer-function-component": "^5.0.0",
-        "eslint-plugin-react-you-might-not-need-an-effect": "^1.0.2",
-        "eslint-plugin-storybook": "10.6.1",
-        "eslint-plugin-testing-library": "^7.16.2",
         "fetch-mock": "^12.6.0",
         "fork-ts-checker-webpack-plugin": "^9.1.0",
         "gettext-parser": "^9.1.1",
@@ -288,30 +273,6 @@
         "regenerator-runtime": "^0.14.1"
       }
     },
-    "eslint-rules/eslint-plugin-i18n-strings": {
-      "name": "@superset-ui/eslint-plugin-i18n-strings",
-      "version": "1.0.0",
-      "dev": true,
-      "license": "Apache-2.0",
-      "peerDependencies": {
-        "eslint": ">=0.8.0"
-      }
-    },
-    "eslint-rules/eslint-plugin-icons": {
-      "name": "@superset-ui/eslint-plugin-icons",
-      "version": "1.0.0",
-      "dev": true,
-      "license": "Apache-2.0",
-      "peerDependencies": {
-        "eslint": ">=0.8.0"
-      }
-    },
-    "eslint-rules/eslint-plugin-theme-colors": {
-      "name": "@superset-ui/eslint-plugin-theme-colors",
-      "version": "1.0.0",
-      "dev": true,
-      "license": "Apache-2.0"
-    },
     "node_modules/@adobe/css-tools": {
       "version": "4.4.1",
       "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.1.tgz",
@@ -606,35 +567,6 @@
         "semver": "bin/semver.js"
       }
     },
-    "node_modules/@babel/eslint-parser": {
-      "version": "7.29.9",
-      "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.29.9.tgz",
-      "integrity": "sha512-GmrJTAtiRbNip+eKFqeuSvoHMJhKlH36j8U0yE3Gqn45B+SIO7NvsEiHtsmzkBqV1hLSMqYr41dI1aZLTkJ37g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
-        "eslint-visitor-keys": "^2.1.0",
-        "semver": "^6.3.1"
-      },
-      "engines": {
-        "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
-      },
-      "peerDependencies": {
-        "@babel/core": "^7.11.0",
-        "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
-      }
-    },
-    "node_modules/@babel/eslint-parser/node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
     "node_modules/@babel/generator": {
       "version": "7.29.7",
       "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
@@ -3956,129 +3888,6 @@
         "node": ">=18"
       }
     },
-    "node_modules/@eslint-community/eslint-utils": {
-      "version": "4.9.1",
-      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
-      "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "eslint-visitor-keys": "^3.4.3"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      },
-      "peerDependencies": {
-        "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
-      }
-    },
-    "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
-      "version": "3.4.3",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
-      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/@eslint-community/regexpp": {
-      "version": "4.12.2",
-      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
-      "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
-      }
-    },
-    "node_modules/@eslint/config-array": {
-      "version": "0.23.5",
-      "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz",
-      "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@eslint/object-schema": "^3.0.5",
-        "debug": "^4.3.1",
-        "minimatch": "^10.2.4"
-      },
-      "engines": {
-        "node": "^20.19.0 || ^22.13.0 || >=24"
-      }
-    },
-    "node_modules/@eslint/config-array/node_modules/minimatch": {
-      "version": "10.2.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
-      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
-      "dev": true,
-      "license": "BlueOak-1.0.0",
-      "dependencies": {
-        "brace-expansion": "^5.0.5"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/@eslint/config-helpers": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz",
-      "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@eslint/core": "^1.2.1"
-      },
-      "engines": {
-        "node": "^20.19.0 || ^22.13.0 || >=24"
-      }
-    },
-    "node_modules/@eslint/core": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz",
-      "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@types/json-schema": "^7.0.15"
-      },
-      "engines": {
-        "node": "^20.19.0 || ^22.13.0 || >=24"
-      }
-    },
-    "node_modules/@eslint/object-schema": {
-      "version": "3.0.5",
-      "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz",
-      "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": "^20.19.0 || ^22.13.0 || >=24"
-      }
-    },
-    "node_modules/@eslint/plugin-kit": {
-      "version": "0.7.3",
-      "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.3.tgz",
-      "integrity": "sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@eslint/core": "^1.2.1",
-        "levn": "^0.4.1"
-      },
-      "engines": {
-        "node": "^20.19.0 || ^22.13.0 || >=24"
-      }
-    },
     "node_modules/@floating-ui/core": {
       "version": "1.8.0",
       "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz",
@@ -4265,72 +4074,6 @@
         "@hapi/hoek": "^11.0.2"
       }
     },
-    "node_modules/@humanfs/core": {
-      "version": "0.19.2",
-      "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
-      "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@humanfs/types": "^0.15.0"
-      },
-      "engines": {
-        "node": ">=18.18.0"
-      }
-    },
-    "node_modules/@humanfs/node": {
-      "version": "0.16.8",
-      "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
-      "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@humanfs/core": "^0.19.2",
-        "@humanfs/types": "^0.15.0",
-        "@humanwhocodes/retry": "^0.4.0"
-      },
-      "engines": {
-        "node": ">=18.18.0"
-      }
-    },
-    "node_modules/@humanfs/types": {
-      "version": "0.15.0",
-      "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
-      "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=18.18.0"
-      }
-    },
-    "node_modules/@humanwhocodes/module-importer": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
-      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=12.22"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/nzakas"
-      }
-    },
-    "node_modules/@humanwhocodes/retry": {
-      "version": "0.4.3",
-      "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
-      "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=18.18"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/nzakas"
-      }
-    },
     "node_modules/@inquirer/ansi": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz",
@@ -8360,16 +8103,6 @@
       "license": "MIT",
       "optional": true
     },
-    "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
-      "version": "5.1.1-v1",
-      "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
-      "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "eslint-scope": "5.1.1"
-      }
-    },
     "node_modules/@noble/hashes": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz",
@@ -10739,6 +10472,19 @@
         "node": "^20.19.0 || >=22.12.0"
       }
     },
+    "node_modules/@oxlint/plugins": {
+      "version": "1.86.0",
+      "resolved": "https://registry.npmjs.org/@oxlint/plugins/-/plugins-1.86.0.tgz",
+      "integrity": "sha512-JeAiaaRMyUiUEQHQ7zBsJn1aEdbLyt1HJsOs4XKIIlATNxcvWRrRxneZA+RF4G03cQobq3IhTqSfyLhOFv03qA==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/oxc-project"
+      }
+    },
     "node_modules/@parcel/watcher": {
       "version": "2.6.0",
       "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz",
@@ -12319,13 +12065,6 @@
         "@rjsf/utils": "^6.11.0"
       }
     },
-    "node_modules/@rtsao/scc": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
-      "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/@scarf/scarf": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
@@ -13079,18 +12818,6 @@
       "resolved": "packages/superset-ui-core",
       "link": true
     },
-    "node_modules/@superset-ui/eslint-plugin-i18n-strings": {
-      "resolved": "eslint-rules/eslint-plugin-i18n-strings",
-      "link": true
-    },
-    "node_modules/@superset-ui/eslint-plugin-icons": {
-      "resolved": "eslint-rules/eslint-plugin-icons",
-      "link": true
-    },
-    "node_modules/@superset-ui/eslint-plugin-theme-colors": {
-      "resolved": "eslint-rules/eslint-plugin-theme-colors",
-      "link": true
-    },
     "node_modules/@superset-ui/generator-superset": {
       "resolved": "packages/generator-superset",
       "link": true
@@ -14334,17 +14061,10 @@
       "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==",
       "license": "MIT"
     },
-    "node_modules/@types/esrecurse": {
-      "version": "4.3.1",
-      "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz",
-      "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/@types/estree": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
-      "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+      "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
       "license": "MIT"
     },
     "node_modules/@types/estree-jsx": {
@@ -14583,13 +14303,6 @@
       "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
       "license": "MIT"
     },
-    "node_modules/@types/json5": {
-      "version": "0.0.29",
-      "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
-      "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/@types/lodash": {
       "version": "4.17.25",
       "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.25.tgz",
@@ -15009,265 +14722,6 @@
       "dev": true,
       "license": "MIT"
     },
-    "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "8.71.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.71.1.tgz",
-      "integrity": "sha512-nNlBf6HuotqMgZvSkWD5hNdemiBIxQ2NmrlgfcUIeZRs+BbakjscCQRq/Hb74EFVnFiSWWdL12JruqwBy3ERDw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@eslint-community/regexpp": "^4.12.2",
-        "@typescript-eslint/scope-manager": "8.71.1",
-        "@typescript-eslint/type-utils": "8.71.1",
-        "@typescript-eslint/utils": "8.71.1",
-        "@typescript-eslint/visitor-keys": "8.71.1",
-        "ignore": "^7.0.5",
-        "natural-compare": "^1.4.0",
-        "ts-api-utils": "^2.5.0"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "@typescript-eslint/parser": "^8.71.1",
-        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
-        "typescript": ">=4.8.4 <6.1.0"
-      }
-    },
-    "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
-      "version": "7.0.5",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
-      "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4"
-      }
-    },
-    "node_modules/@typescript-eslint/parser": {
-      "version": "8.71.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.71.1.tgz",
-      "integrity": "sha512-oM/5sAqz/l1v/zYK1qTOPrn2+JIrBGC1V6uNYucRO5gh6eT9vWJ/RhPbGE59byvZEUQ4VP6XkLBaS5Jti5jpvw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/scope-manager": "8.71.1",
-        "@typescript-eslint/types": "8.71.1",
-        "@typescript-eslint/typescript-estree": "8.71.1",
-        "@typescript-eslint/visitor-keys": "8.71.1",
-        "debug": "^4.4.3"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
-        "typescript": ">=4.8.4 <6.1.0"
-      }
-    },
-    "node_modules/@typescript-eslint/project-service": {
-      "version": "8.71.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.71.1.tgz",
-      "integrity": "sha512-Oijc9RUsohHjncg6iGi7gCtjnEjYt9FWv9u6ygK8uKcHK5eLjLAkqERQ9BaytbpceOFdk6kifPCS/zAZE+A99g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/tsconfig-utils": "^8.71.1",
-        "@typescript-eslint/types": "^8.71.1",
-        "debug": "^4.4.3"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "typescript": ">=4.8.4 <6.1.0"
-      }
-    },
-    "node_modules/@typescript-eslint/scope-manager": {
-      "version": "8.71.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.71.1.tgz",
-      "integrity": "sha512-1YUDZXdTnXLlob03SFHBipdyMBsDVm7zoSz0ytTJ39dk5J1TgsTK03VlR/dLXG/RLLUrPcYbD4rp23BmHxHP0w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/types": "8.71.1",
-        "@typescript-eslint/visitor-keys": "8.71.1"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      }
-    },
-    "node_modules/@typescript-eslint/tsconfig-utils": {
-      "version": "8.71.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.71.1.tgz",
-      "integrity": "sha512-oTvkml5SxXhgg+WWKhod5qWQICDXtOB+/Hi9VdPKFAANJ2b7YOgMffgvzXuqog7R6A/JtrzRf128dsxummyWhg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "typescript": ">=4.8.4 <6.1.0"
-      }
-    },
-    "node_modules/@typescript-eslint/type-utils": {
-      "version": "8.71.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.71.1.tgz",
-      "integrity": "sha512-kH3t3jZCYv2PZSBEWPKTariLrCaoF1CLoPitYFBJOhAGEwWnSRAtGEifrfrpBa4K9JlwWNZotgA5w3/wLSk/FQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/types": "8.71.1",
-        "@typescript-eslint/typescript-estree": "8.71.1",
-        "@typescript-eslint/utils": "8.71.1",
-        "debug": "^4.4.3",
-        "ts-api-utils": "^2.5.0"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
-        "typescript": ">=4.8.4 <6.1.0"
-      }
-    },
-    "node_modules/@typescript-eslint/types": {
-      "version": "8.71.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.71.1.tgz",
-      "integrity": "sha512-ybYnPTUwg3VQQOFEiBPLs8Jxl1ry2vXdn1kVKguomBbvxpetdu3MKTNDqFNUrlG8ylP9MpZosmXMFYlGQHk1oQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "8.71.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.71.1.tgz",
-      "integrity": "sha512-pSKSEK1JJpHs6KiKVtABCE4iJK5d7FLFzDXQNBQ2oRY7LEd3UwOt3TZemTgu9syTIf56dJWlTn/pdmrO9oKUXQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/project-service": "8.71.1",
-        "@typescript-eslint/tsconfig-utils": "8.71.1",
-        "@typescript-eslint/types": "8.71.1",
-        "@typescript-eslint/visitor-keys": "8.71.1",
-        "debug": "^4.4.3",
-        "minimatch": "^10.2.2",
-        "semver": "^7.7.3",
-        "tinyglobby": "^0.2.15",
-        "ts-api-utils": "^2.5.0"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "typescript": ">=4.8.4 <6.1.0"
-      }
-    },
-    "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
-      "version": "10.2.6",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
-      "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
-      "dev": true,
-      "license": "BlueOak-1.0.0",
-      "dependencies": {
-        "brace-expansion": "^5.0.8"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/@typescript-eslint/utils": {
-      "version": "8.71.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.71.1.tgz",
-      "integrity": "sha512-CBjT6gfAz3DW2j9Q4moFnqt208Aq+506sVGXfpRcyAeqn0EpayL0mjdWoCO2t3RC7GewNrFxdeehuEaPg6DMzQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@eslint-community/eslint-utils": "^4.9.1",
-        "@typescript-eslint/scope-manager": "8.71.1",
-        "@typescript-eslint/types": "8.71.1",
-        "@typescript-eslint/typescript-estree": "8.71.1"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      },
-      "peerDependencies": {
-        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
-        "typescript": ">=4.8.4 <6.1.0"
-      }
-    },
-    "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "8.71.1",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.71.1.tgz",
-      "integrity": "sha512-0GxiUGqMU0qXJt58cBsiG3DP8lUmBaFo2vChtUX+CT7PnR3v/VGLLrzVP4K9r1lNiZXjHYefMBbPunplr09v6w==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/types": "8.71.1",
-        "eslint-visitor-keys": "^5.0.0"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "funding": {
-        "type": "opencollective",
-        "url": "https://opencollective.com/typescript-eslint"
-      }
-    },
-    "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
-      "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": "^20.19.0 || ^22.13.0 || >=24"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
     "node_modules/@ungap/structured-clone": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
@@ -16567,16 +16021,6 @@
         "node": ">=0.4.0"
       }
     },
-    "node_modules/acorn-jsx": {
-      "version": "5.3.2",
-      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
-      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
-      "dev": true,
-      "license": "MIT",
-      "peerDependencies": {
-        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
-      }
-    },
     "node_modules/acorn-walk": {
       "version": "8.3.5",
       "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz",
@@ -17029,29 +16473,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/array-includes": {
-      "version": "3.1.9",
-      "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
-      "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.4",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.24.0",
-        "es-object-atoms": "^1.1.1",
-        "get-intrinsic": "^1.3.0",
-        "is-string": "^1.1.1",
-        "math-intrinsics": "^1.1.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/array-union": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz",
@@ -17064,66 +16485,6 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
-    "node_modules/array.prototype.findlastindex": {
-      "version": "1.2.6",
-      "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
-      "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.4",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.9",
-        "es-errors": "^1.3.0",
-        "es-object-atoms": "^1.1.1",
-        "es-shim-unscopables": "^1.1.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array.prototype.flat": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
-      "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.8",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.5",
-        "es-shim-unscopables": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
-    "node_modules/array.prototype.flatmap": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
-      "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.8",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.5",
-        "es-shim-unscopables": "^1.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/array.prototype.reduce": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.7.tgz",
@@ -21728,19 +21089,6 @@
         "node": ">= 0.4"
       }
     },
-    "node_modules/es-shim-unscopables": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
-      "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "hasown": "^2.0.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
     "node_modules/es-to-primitive": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
@@ -21877,574 +21225,6 @@
         "node": ">=0.10.0"
       }
     },
-    "node_modules/eslint": {
-      "version": "10.10.0",
-      "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.10.0.tgz",
-      "integrity": "sha512-NPXn6r5zl4uET1DAVPaOwzX3rut4c0wcmw3dWJAfOsTM5+TogXo0DDjz8pwm/hL8cyVNpHqeK4JpN0NjnyFFNw==",
-      "dev": true,
-      "license": "MIT",
-      "workspaces": [
-        "packages/*"
-      ],
-      "dependencies": {
-        "@eslint-community/eslint-utils": "^4.8.0",
-        "@eslint-community/regexpp": "^4.12.2",
-        "@eslint/config-array": "^0.23.5",
-        "@eslint/config-helpers": "^0.7.0",
-        "@eslint/core": "^1.2.1",
-        "@eslint/plugin-kit": "^0.7.3",
-        "@humanfs/node": "^0.16.6",
-        "@humanwhocodes/module-importer": "^1.0.1",
-        "@humanwhocodes/retry": "^0.4.2",
-        "@types/estree": "^1.0.6",
-        "ajv": "^6.14.0",
-        "cross-spawn": "^7.0.6",
-        "debug": "^4.3.2",
-        "escape-string-regexp": "^4.0.0",
-        "eslint-scope": "^9.1.2",
-        "eslint-visitor-keys": "^5.0.1",
-        "espree": "^11.2.0",
-        "esquery": "^1.7.0",
-        "esutils": "^2.0.2",
-        "fast-deep-equal": "^3.1.3",
-        "file-entry-cache": "11.1.5 || >11.1.6 <12",
-        "find-up": "^5.0.0",
-        "glob-parent": "^6.0.2",
-        "ignore": "^5.2.0",
-        "imurmurhash": "^0.1.4",
-        "is-glob": "^4.0.0",
-        "json-stable-stringify-without-jsonify": "^1.0.1",
-        "minimatch": "^10.2.5",
-        "natural-compare": "^1.4.0",
-        "optionator": "^0.9.3"
-      },
-      "bin": {
-        "eslint": "bin/eslint.js"
-      },
-      "engines": {
-        "node": "^20.19.0 || ^22.13.0 || >=24"
-      },
-      "funding": {
-        "url": "https://eslint.org/donate"
-      },
-      "peerDependencies": {
-        "jiti": "*"
-      },
-      "peerDependenciesMeta": {
-        "jiti": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/eslint-import-context": {
-      "version": "0.1.9",
-      "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz",
-      "integrity": "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "get-tsconfig": "^4.10.1",
-        "stable-hash-x": "^0.2.0"
-      },
-      "engines": {
-        "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint-import-context"
-      },
-      "peerDependencies": {
-        "unrs-resolver": "^1.0.0"
-      },
-      "peerDependenciesMeta": {
-        "unrs-resolver": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/eslint-import-resolver-alias": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/eslint-import-resolver-alias/-/eslint-import-resolver-alias-1.1.2.tgz",
-      "integrity": "sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4"
-      },
-      "peerDependencies": {
-        "eslint-plugin-import": ">=1.4.0"
-      }
-    },
-    "node_modules/eslint-import-resolver-node": {
-      "version": "0.3.9",
-      "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
-      "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "^3.2.7",
-        "is-core-module": "^2.13.0",
-        "resolve": "^1.22.4"
-      }
-    },
-    "node_modules/eslint-import-resolver-node/node_modules/debug": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.1"
-      }
-    },
-    "node_modules/eslint-import-resolver-typescript": {
-      "version": "4.4.5",
-      "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.4.5.tgz",
-      "integrity": "sha512-nbE5XLph6TLtGYcu/U6e6ZVXyKBhbDWK5cLGk76eJ7NdZpwf1P9EFkpt1Z01mNZNrrilsAYWKH6zUkL4reoXbw==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "debug": "^4.4.1",
-        "eslint-import-context": "^0.1.8",
-        "get-tsconfig": "^4.10.1",
-        "is-bun-module": "^2.0.0",
-        "stable-hash-x": "^0.2.0",
-        "tinyglobby": "^0.2.14",
-        "unrs-resolver": "^1.7.11"
-      },
-      "engines": {
-        "node": "^16.17.0 || >=18.6.0"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint-import-resolver-typescript"
-      },
-      "peerDependencies": {
-        "eslint": "*",
-        "eslint-plugin-import": "*",
-        "eslint-plugin-import-x": "*"
-      },
-      "peerDependenciesMeta": {
-        "eslint-plugin-import": {
-          "optional": true
-        },
-        "eslint-plugin-import-x": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/eslint-module-utils": {
-      "version": "2.12.1",
-      "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
-      "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "debug": "^3.2.7"
-      },
-      "engines": {
-        "node": ">=4"
-      },
-      "peerDependenciesMeta": {
-        "eslint": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/eslint-module-utils/node_modules/debug": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.1"
-      }
-    },
-    "node_modules/eslint-plugin-import": {
-      "version": "2.32.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
-      "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@rtsao/scc": "^1.1.0",
-        "array-includes": "^3.1.9",
-        "array.prototype.findlastindex": "^1.2.6",
-        "array.prototype.flat": "^1.3.3",
-        "array.prototype.flatmap": "^1.3.3",
-        "debug": "^3.2.7",
-        "doctrine": "^2.1.0",
-        "eslint-import-resolver-node": "^0.3.9",
-        "eslint-module-utils": "^2.12.1",
-        "hasown": "^2.0.2",
-        "is-core-module": "^2.16.1",
-        "is-glob": "^4.0.3",
-        "minimatch": "^3.1.2",
-        "object.fromentries": "^2.0.8",
-        "object.groupby": "^1.0.3",
-        "object.values": "^1.2.1",
-        "semver": "^6.3.1",
-        "string.prototype.trimend": "^1.0.9",
-        "tsconfig-paths": "^3.15.0"
-      },
-      "engines": {
-        "node": ">=4"
-      },
-      "peerDependencies": {
-        "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
-      }
-    },
-    "node_modules/eslint-plugin-import/node_modules/debug": {
-      "version": "3.2.7",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
-      "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "ms": "^2.1.1"
-      }
-    },
-    "node_modules/eslint-plugin-import/node_modules/doctrine": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
-      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "dependencies": {
-        "esutils": "^2.0.2"
-      },
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/eslint-plugin-import/node_modules/json5": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
-      "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "minimist": "^1.2.0"
-      },
-      "bin": {
-        "json5": "lib/cli.js"
-      }
-    },
-    "node_modules/eslint-plugin-import/node_modules/semver": {
-      "version": "6.3.1",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
-      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
-      "dev": true,
-      "license": "ISC",
-      "bin": {
-        "semver": "bin/semver.js"
-      }
-    },
-    "node_modules/eslint-plugin-import/node_modules/strip-bom": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-      "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=4"
-      }
-    },
-    "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": {
-      "version": "3.15.0",
-      "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
-      "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@types/json5": "^0.0.29",
-        "json5": "^1.0.2",
-        "minimist": "^1.2.6",
-        "strip-bom": "^3.0.0"
-      }
-    },
-    "node_modules/eslint-plugin-jest-dom": {
-      "version": "5.10.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-jest-dom/-/eslint-plugin-jest-dom-5.10.1.tgz",
-      "integrity": "sha512-IIJdzbACbhJUJyMAqpA9E9gxp1Gv29TeQ3DtL+pepwP8Oq2WiqjOB2HbnEIs23h0ewdKIZRDI56yLro2eUO+DA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "requireindex": "^1.2.0"
-      },
-      "engines": {
-        "node": "^12.22.0 || ^14.17.0 || >=16.0.0",
-        "npm": ">=6",
-        "yarn": ">=1"
-      },
-      "peerDependencies": {
-        "@testing-library/dom": "^8.0.0 || ^9.0.0 || ^10.0.0",
-        "eslint": "^6.8.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
-      },
-      "peerDependenciesMeta": {
-        "@testing-library/dom": {
-          "optional": true
-        }
-      }
-    },
-    "node_modules/eslint-plugin-lodash": {
-      "version": "8.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-lodash/-/eslint-plugin-lodash-8.0.0.tgz",
-      "integrity": "sha512-7DA8485FolmWRzh+8t4S8Pzin2TTuWfb0ZW3j/2fYElgk82ZanFz8vDcvc4BBPceYdX1p/za+tkbO68maDBGGw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "lodash": "^4.17.21"
-      },
-      "engines": {
-        "node": ">=10"
-      },
-      "peerDependencies": {
-        "eslint": ">=9.0.0"
-      }
-    },
-    "node_modules/eslint-plugin-no-only-tests": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-3.4.0.tgz",
-      "integrity": "sha512-4S3/9Nb7A2tiMcpzEQE9bQSlpeOz6WJkgryBuou/SA8W2x2c8Zf4j0NvTKBjv6qNhF9T79tmkecm/0CHqV0UGg==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=5.0.0"
-      }
-    },
-    "node_modules/eslint-plugin-react-prefer-function-component": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-react-prefer-function-component/-/eslint-plugin-react-prefer-function-component-5.0.0.tgz",
-      "integrity": "sha512-kXRP41OT/hgR8QDlHCO2tOFGbQgXcER58xjV3nlNOggGvRoSZ5u3wdqogWnD/W2lOTmk/noNOLDt3LRD39G9iQ==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/eslint-plugin-react-you-might-not-need-an-effect": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-react-you-might-not-need-an-effect/-/eslint-plugin-react-you-might-not-need-an-effect-1.0.2.tgz",
-      "integrity": "sha512-HgYol2zhH3KbnW9Q4FY/FcIINbsYVL6rwQESChqBC2s9SLWtw1wg05+J7SCrr3BfQpNY2ReYhf8xjpd2JhKJOQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "globals": "^16.2.0"
-      },
-      "engines": {
-        "node": ">=14.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/nickjvandyke"
-      },
-      "peerDependencies": {
-        "eslint": ">=8.40.0"
-      }
-    },
-    "node_modules/eslint-plugin-react-you-might-not-need-an-effect/node_modules/globals": {
-      "version": "16.5.0",
-      "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
-      "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
-    "node_modules/eslint-plugin-storybook": {
-      "version": "10.6.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-10.6.1.tgz",
-      "integrity": "sha512-XBmXuQcI0wzxMYc0rWKIiydvUxgS+GzLesO1YK1adnj97LhMqqsnlalbM+nXfQo1JoH2F2CI12sAH9/dAC99Dw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/types": "^8.60.0",
-        "@typescript-eslint/utils": "^8.60.0"
-      },
-      "peerDependencies": {
-        "eslint": ">=8"
-      }
-    },
-    "node_modules/eslint-plugin-testing-library": {
-      "version": "7.16.2",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-testing-library/-/eslint-plugin-testing-library-7.16.2.tgz",
-      "integrity": "sha512-8gleGnQXK2ZA3hHwjCwpYTZvM+9VsrJ+/9kDI8CjqAQGAdMQOdn/rJNu7ZySENuiWlGKQWyZJ4ZjEg2zamaRHw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "@typescript-eslint/scope-manager": "^8.56.0",
-        "@typescript-eslint/utils": "^8.56.0"
-      },
-      "engines": {
-        "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
-      },
-      "peerDependencies": {
-        "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0"
-      }
-    },
-    "node_modules/eslint-scope": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
-      "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "esrecurse": "^4.3.0",
-        "estraverse": "^4.1.1"
-      },
-      "engines": {
-        "node": ">=8.0.0"
-      }
-    },
-    "node_modules/eslint-scope/node_modules/estraverse": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
-      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "engines": {
-        "node": ">=4.0"
-      }
-    },
-    "node_modules/eslint-visitor-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
-      "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": ">=10"
-      }
-    },
-    "node_modules/eslint/node_modules/ajv": {
-      "version": "6.15.0",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
-      "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "fast-deep-equal": "^3.1.1",
-        "fast-json-stable-stringify": "^2.0.0",
-        "json-schema-traverse": "^0.4.1",
-        "uri-js": "^4.2.2"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/epoberezkin"
-      }
-    },
-    "node_modules/eslint/node_modules/eslint-scope": {
-      "version": "9.1.2",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz",
-      "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "@types/esrecurse": "^4.3.1",
-        "@types/estree": "^1.0.8",
-        "esrecurse": "^4.3.0",
-        "estraverse": "^5.2.0"
-      },
-      "engines": {
-        "node": "^20.19.0 || ^22.13.0 || >=24"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/eslint/node_modules/eslint-visitor-keys": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
-      "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": "^20.19.0 || ^22.13.0 || >=24"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/eslint/node_modules/glob-parent": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
-      "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
-      "dev": true,
-      "license": "ISC",
-      "dependencies": {
-        "is-glob": "^4.0.3"
-      },
-      "engines": {
-        "node": ">=10.13.0"
-      }
-    },
-    "node_modules/eslint/node_modules/json-schema-traverse": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
-      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
-      "dev": true,
-      "license": "MIT"
-    },
-    "node_modules/eslint/node_modules/minimatch": {
-      "version": "10.2.5",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
-      "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
-      "dev": true,
-      "license": "BlueOak-1.0.0",
-      "dependencies": {
-        "brace-expansion": "^5.0.5"
-      },
-      "engines": {
-        "node": "18 || 20 || >=22"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/isaacs"
-      }
-    },
-    "node_modules/espree": {
-      "version": "11.2.0",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz",
-      "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "acorn": "^8.16.0",
-        "acorn-jsx": "^5.3.2",
-        "eslint-visitor-keys": "^5.0.1"
-      },
-      "engines": {
-        "node": "^20.19.0 || ^22.13.0 || >=24"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
-    "node_modules/espree/node_modules/acorn": {
-      "version": "8.16.0",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
-      "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
-      "dev": true,
-      "license": "MIT",
-      "bin": {
-        "acorn": "bin/acorn"
-      },
-      "engines": {
-        "node": ">=0.4.0"
-      }
-    },
-    "node_modules/espree/node_modules/eslint-visitor-keys": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
-      "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
-      "dev": true,
-      "license": "Apache-2.0",
-      "engines": {
-        "node": "^20.19.0 || ^22.13.0 || >=24"
-      },
-      "funding": {
-        "url": "https://opencollective.com/eslint"
-      }
-    },
     "node_modules/esprima": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
@@ -22458,32 +21238,6 @@
         "node": ">=4"
       }
     },
-    "node_modules/esquery": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
-      "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
-      "dev": true,
-      "license": "BSD-3-Clause",
-      "dependencies": {
-        "estraverse": "^5.1.0"
-      },
-      "engines": {
-        "node": ">=0.10"
-      }
-    },
-    "node_modules/esrecurse": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
-      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
-      "dev": true,
-      "license": "BSD-2-Clause",
-      "dependencies": {
-        "estraverse": "^5.2.0"
-      },
-      "engines": {
-        "node": ">=4.0"
-      }
-    },
     "node_modules/estraverse": {
       "version": "5.3.0",
       "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
@@ -24533,19 +23287,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/get-tsconfig": {
-      "version": "4.10.1",
-      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
-      "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "resolve-pkg-maps": "^1.0.0"
-      },
-      "funding": {
-        "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
-      }
-    },
     "node_modules/get-value": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
@@ -25750,16 +24491,6 @@
       ],
       "license": "BSD-3-Clause"
     },
-    "node_modules/ignore": {
-      "version": "5.3.2",
-      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
-      "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 4"
-      }
-    },
     "node_modules/ignore-walk": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz",
@@ -26170,16 +24901,6 @@
       "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
       "license": "MIT"
     },
-    "node_modules/is-bun-module": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz",
-      "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "semver": "^7.7.1"
-      }
-    },
     "node_modules/is-callable": {
       "version": "1.2.7",
       "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
@@ -31181,13 +29902,6 @@
       "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
       "license": "MIT"
     },
-    "node_modules/json-stable-stringify-without-jsonify": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
-      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
-      "dev": true,
-      "license": "MIT"
-    },
     "node_modules/json-stringify-nice": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz",
@@ -32002,20 +30716,6 @@
         "node": ">=6"
       }
     },
-    "node_modules/levn": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
-      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "prelude-ls": "^1.2.1",
-        "type-check": "~0.4.0"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
     "node_modules/libnpmaccess": {
       "version": "10.0.3",
       "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-10.0.3.tgz",
@@ -35579,25 +34279,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/object.fromentries": {
-      "version": "2.0.8",
-      "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
-      "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/object.getownpropertydescriptors": {
       "version": "2.1.8",
       "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.8.tgz",
@@ -35620,40 +34301,6 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
-    "node_modules/object.groupby": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
-      "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.7",
-        "define-properties": "^1.2.1",
-        "es-abstract": "^1.23.2"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      }
-    },
-    "node_modules/object.values": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
-      "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "call-bind": "^1.0.8",
-        "call-bound": "^1.0.3",
-        "define-properties": "^1.2.1",
-        "es-object-atoms": "^1.0.0"
-      },
-      "engines": {
-        "node": ">= 0.4"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/ljharb"
-      }
-    },
     "node_modules/objectorarray": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/objectorarray/-/objectorarray-1.0.5.tgz",
@@ -35848,24 +34495,6 @@
         "node": ">=0.4.0"
       }
     },
-    "node_modules/optionator": {
-      "version": "0.9.4",
-      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
-      "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "deep-is": "^0.1.3",
-        "fast-levenshtein": "^2.0.6",
-        "levn": "^0.4.1",
-        "prelude-ls": "^1.2.1",
-        "type-check": "^0.4.0",
-        "word-wrap": "^1.2.5"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
     "node_modules/ora": {
       "version": "5.4.1",
       "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
@@ -37156,16 +35785,6 @@
         }
       }
     },
-    "node_modules/prelude-ls": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
-      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
     "node_modules/pretty-error": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz",
@@ -40107,16 +38726,6 @@
       "dev": true,
       "license": "ISC"
     },
-    "node_modules/requireindex": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz",
-      "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=0.10.5"
-      }
-    },
     "node_modules/requires-port": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
@@ -40199,16 +38808,6 @@
       "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==",
       "license": "MIT"
     },
-    "node_modules/resolve-pkg-maps": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
-      "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
-      "dev": true,
-      "license": "MIT",
-      "funding": {
-        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
-      }
-    },
     "node_modules/resolve-protobuf-schema": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
@@ -41498,16 +40097,6 @@
         "node": "^18.17.0 || >=20.5.0"
       }
     },
-    "node_modules/stable-hash-x": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz",
-      "integrity": "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=12.0.0"
-      }
-    },
     "node_modules/stack-utils": {
       "version": "2.0.6",
       "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
@@ -43511,19 +42100,6 @@
         "url": "https://github.com/sponsors/wooorm"
       }
     },
-    "node_modules/ts-api-utils": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz",
-      "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==",
-      "dev": true,
-      "license": "MIT",
-      "engines": {
-        "node": ">=18.12"
-      },
-      "peerDependencies": {
-        "typescript": ">=4.8.4"
-      }
-    },
     "node_modules/ts-dedent": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz",
@@ -43712,19 +42288,6 @@
         "node": "^20.17.0 || >=22.9.0"
       }
     },
-    "node_modules/type-check": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
-      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
-      "dev": true,
-      "license": "MIT",
-      "dependencies": {
-        "prelude-ls": "^1.2.1"
-      },
-      "engines": {
-        "node": ">= 0.8.0"
-      }
-    },
     "node_modules/type-detect": {
       "version": "4.0.8",
       "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
diff --git a/superset-frontend/package.json b/superset-frontend/package.json
index 1f52dd4e4d0..9b7c3434736 100644
--- a/superset-frontend/package.json
+++ b/superset-frontend/package.json
@@ -234,7 +234,6 @@
     "@babel/cli": "^7.29.7",
     "@babel/compat-data": "^7.28.4",
     "@babel/core": "^7.29.7",
-    "@babel/eslint-parser": "^7.29.9",
     "@babel/node": "^7.29.7",
     "@babel/plugin-syntax-dynamic-import": "^7.8.3",
     "@babel/plugin-transform-export-namespace-from": "^7.29.7",
@@ -251,15 +250,13 @@
     "@emotion/jest": "^11.14.2",
     "@formatjs/intl-durationformat": "^0.11.1",
     "@istanbuljs/nyc-config-typescript": "^1.0.1",
+    "@oxlint/plugins": "^1.81.0",
     "@playwright/test": "^1.62.1",
     "@pmmmwh/react-refresh-webpack-plugin": "^0.6.3",
     "@storybook/addon-docs": "10.6.1",
     "@storybook/addon-links": "10.6.1",
     "@storybook/react-webpack5": "10.6.1",
     "@storybook/test-runner": "0.26.0",
-    "@superset-ui/eslint-plugin-i18n-strings": "file:eslint-rules/eslint-plugin-i18n-strings",
-    "@superset-ui/eslint-plugin-icons": "file:eslint-rules/eslint-plugin-icons",
-    "@superset-ui/eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
     "@svgr/webpack": "^8.1.0",
     "@swc/core": "^1.16.13",
     "@swc/plugin-emotion": "^19.0.0",
@@ -270,6 +267,7 @@
     "@testing-library/user-event": "^14.6.7",
     "@types/content-disposition": "^0.5.9",
     "@types/dom-to-image": "^2.6.7",
+    "@types/estree": "^1.0.9",
     "@types/jest": "^30.0.0",
     "@types/jquery": "^4.0.1",
     "@types/js-levenshtein": "^1.1.3",
@@ -286,8 +284,6 @@
     "@types/rison": "0.1.0",
     "@types/tinycolor2": "^1.4.3",
     "@types/unzipper": "^0.10.11",
-    "@typescript-eslint/eslint-plugin": "^8.71.1",
-    "@typescript-eslint/parser": "^8.63.0",
     "babel-jest": "^30.5.2",
     "babel-loader": "^10.1.1",
     "babel-plugin-dynamic-import-node": "^2.3.3",
@@ -298,17 +294,6 @@
     "copy-webpack-plugin": "^14.0.0",
     "cross-env": "^10.1.0",
     "css-loader": "^7.1.5",
-    "eslint": "^10.10.0",
-    "eslint-import-resolver-alias": "^1.1.2",
-    "eslint-import-resolver-typescript": "^4.4.5",
-    "eslint-plugin-import": "^2.32.0",
-    "eslint-plugin-jest-dom": "^5.10.1",
-    "eslint-plugin-lodash": "^8.0.0",
-    "eslint-plugin-no-only-tests": "^3.4.0",
-    "eslint-plugin-react-prefer-function-component": "^5.0.0",
-    "eslint-plugin-react-you-might-not-need-an-effect": "^1.0.2",
-    "eslint-plugin-storybook": "10.6.1",
-    "eslint-plugin-testing-library": "^7.16.2",
     "fetch-mock": "^12.6.0",
     "fork-ts-checker-webpack-plugin": "^9.1.0",
     "gettext-parser": "^9.1.1",
@@ -361,9 +346,6 @@
     "regenerator-runtime": "^0.14.1"
   },
   "overrides": {
-    "@babel/eslint-parser": {
-      "eslint": "$eslint"
-    },
     "@deck.gl/aggregation-layers": "~9.4.0",
     "@deck.gl/core": "~9.4.0",
     "@deck.gl/extensions": "~9.4.0",
@@ -395,12 +377,6 @@
     },
     "dompurify": "^3.4.16",
     "esbuild": "^0.28.1",
-    "eslint-plugin-import": {
-      "eslint": "$eslint"
-    },
-    "eslint-plugin-jest-dom": {
-      "eslint": "$eslint"
-    },
     "fast-uri": "^3.1.8",
     "fast-xml-parser": "^5.8.0",
     "http-proxy-middleware": "^2.0.10",
diff --git a/superset-frontend/scripts/check-custom-rules.js b/superset-frontend/scripts/check-custom-rules.js
index d623cd31926..9804bac8af3 100755
--- a/superset-frontend/scripts/check-custom-rules.js
+++ b/superset-frontend/scripts/check-custom-rules.js
@@ -283,15 +283,13 @@ function checkEagerTranslationsInConfig(ast, filepath) {
       if (hasEslintDisable(path, 'i18n-strings/no-eager-t-in-config')) return;
 
       // Warn (not error) because there are many pre-existing violations.
-      // The ESLint plugin provides an autofix so authors can sweep files
-      // as they touch them. Promote to error once the codebase is clean.
+      // Promote to error once the codebase is clean.
       // eslint-disable-next-line no-console
       console.warn(
         `${YELLOW}⚠${RESET} ${filepath}:${node.loc?.start.line ?? '?'}: ` +
           `Eager \`${keyName}: ${value.callee.name}(...)\` is evaluated at ` +
           `module load, before i18n is initialized. Wrap in an arrow ` +
-          `function: \`${keyName}: () => ${value.callee.name}(...)\`. ` +
-          `Run \`eslint --fix\` to autofix.`,
+          `function: \`${keyName}: () => ${value.callee.name}(...)\`. `,
       );
       warningCount += 1;
     },
diff --git a/superset-frontend/scripts/internal/oxlint-metrics-uploader.js b/superset-frontend/scripts/internal/oxlint-metrics-uploader.js
index 3c6b8506514..65293849f88 100644
--- a/superset-frontend/scripts/internal/oxlint-metrics-uploader.js
+++ b/superset-frontend/scripts/internal/oxlint-metrics-uploader.js
@@ -34,6 +34,7 @@ if (SERVICE_ACCOUNT_KEY.client_email) {
 }
 
 const DATETIME = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
+const CUSTOM_PLUGIN_ID_PREFIXES = ['theme-colors', 'icons', 'i18n-strings'];
 
 /**
  * Turn an oxlint diagnostic code into the canonical rule id used by the metrics
@@ -54,22 +55,60 @@ const DATETIME = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
  */
 function parseRuleId(code) {
   if (!code) {
-    return 'unknown';
+    return { parsed: 'unknown', pluginId: 'unknown' };
   }
 
   const match = code.match(/^([\w-]+)\(([^)]+)\)$/);
   if (!match) {
-    return code;
+    return { parsed: code, pluginId: 'unknown' };
   }
 
   const [, namespace, rule] = match;
   if (namespace === 'eslint') {
-    return rule;
+    return { parsed: rule, pluginId: namespace };
   }
 
   // `eslint-plugin-unicorn(...)` is the same rule as `unicorn/...`
   const plugin = namespace.replace(/^eslint-plugin-/, '');
-  return `${plugin}/${rule}`;
+  return { parsed: `${plugin}/${rule}`, pluginId: plugin };
+}
+
+function parseOxlintResult(results, ruleIdPrefixes) {
+  // Process OXC JSON output
+  const metricsByRule = {};
+  const occurrencesData = [];
+
+  // OXC JSON format has diagnostics array
+  if (results.diagnostics && Array.isArray(results.diagnostics)) {
+    results.diagnostics.forEach(diagnostic => {
+      const { parsed: ruleId, pluginId } = parseRuleId(diagnostic.code);
+
+      const file = diagnostic.filename || 'unknown';
+      const line = diagnostic.labels?.[0]?.span?.line || 0;
+      const column = diagnostic.labels?.[0]?.span?.column || 0;
+      const message = diagnostic.message || '';
+
+      const ruleData = metricsByRule[ruleId] || { count: 0 };
+      ruleData.count += 1;
+
+      if (
+        !ruleIdPrefixes ||
+        (ruleIdPrefixes && ruleIdPrefixes.includes(pluginId))
+      ) {
+        metricsByRule[ruleId] = ruleData;
+        occurrencesData.push({
+          rule: ruleId,
+          message,
+          file,
+          line,
+          column,
+          ts: DATETIME,
+        });
+      }
+    });
+  }
+
+  return { metricsByRule, occurrencesData };
 }
 
 async function writeToGoogleSheet(data, range, headers, append = false) {
@@ -120,104 +159,71 @@ async function runOxlintAndProcess() {
     // config has to be passed explicitly or the run reports oxlint's defaults
     // instead of the project's ruleset. Matches the `lint` scripts in
     // package.json.
-    const oxlintOutput = execSync(
-      'npx oxlint --config oxlint.json --format json',
-      {
+    let oxlintOutput = '{}';
+    try {
+      oxlintOutput = execSync('npx oxlint --config oxlint.json --format json', {
         encoding: 'utf8',
         maxBuffer: 50 * 1024 * 1024, // 50MB buffer for large outputs
         stdio: ['pipe', 'pipe', 'ignore'], // Ignore stderr to avoid error output
-      },
-    );
-
-    const results = JSON.parse(oxlintOutput);
-
-    // Process OXC JSON output
-    const metricsByRule = {};
-    let occurrencesData = [];
-
-    // OXC JSON format has diagnostics array
-    if (results.diagnostics && Array.isArray(results.diagnostics)) {
-      results.diagnostics.forEach(diagnostic => {
-        const ruleId = parseRuleId(diagnostic.code);
-
-        const file = diagnostic.filename || 'unknown';
-        const line = diagnostic.labels?.[0]?.span?.line || 0;
-        const column = diagnostic.labels?.[0]?.span?.column || 0;
-        const message = diagnostic.message || '';
-
-        const ruleData = metricsByRule[ruleId] || { count: 0 };
-        ruleData.count += 1;
-        metricsByRule[ruleId] = ruleData;
-
-        occurrencesData.push({
-          rule: ruleId,
-          message,
-          file,
-          line,
-          column,
-          ts: DATETIME,
-        });
       });
+    } catch (error) {
+      if (error.stdout) {
+        oxlintOutput = error.stdout.toString();
+      } else {
+        throw error;
+      }
     }
 
+    const results = JSON.parse(oxlintOutput);
     console.log(
       `OXC found ${results.diagnostics?.length || 0} issues across ${results.number_of_files} files`,
     );
+    const { metricsByRule, occurrencesData } = parseOxlintResult(results);
 
-    // Also run minimal ESLint for custom rules and merge results
-    console.log('Running minimal ESLint for custom rules...');
-    let eslintOutput = '[]';
+    // Also run Oxlint for custom rules and merge results
+    console.log('Running Oxlint for custom rules...');
+    // Run ESLint and capture output directly.
+    // Flat config (oxlint.custom-lint-rules.mts) is explicitly selected via --config
+    let oxlintCustomRuleOutput = '{}';
     try {
-      // Run ESLint and capture output directly.
-      // Flat config (eslint.config.minimal.js) is explicitly selected via
-      // --config; ESLint v9+/v10 no longer support eslintrc or --no-eslintrc.
-      eslintOutput = execSync(
-        'npx eslint --config eslint.config.minimal.js --no-inline-config --format json src',
+      oxlintCustomRuleOutput = execSync(
+        'npx oxlint --config oxlint.custom-lint-rules.mts --format json src',
         {
           encoding: 'utf8',
-          maxBuffer: 50 * 1024 * 1024,
+          maxBuffer: 50 * 1024 * 1024, // 50MB buffer for large outputs
           stdio: ['pipe', 'pipe', 'ignore'], // Ignore stderr
         },
       );
-    } catch (e) {
-      // ESLint exits with non-zero when it finds issues, capture the stdout
-      if (e.stdout) {
-        eslintOutput = e.stdout.toString();
+    } catch (error) {
+      if (error.stdout) {
+        oxlintCustomRuleOutput = error.stdout.toString();
+      } else {
+        throw error;
       }
     }
 
-    // Parse minimal ESLint output
-    try {
-      const eslintResults = JSON.parse(eslintOutput);
-
-      eslintResults.forEach(result => {
-        result.messages.forEach(({ ruleId, line, column, message }) => {
-          const ruleData = metricsByRule[ruleId] || { count: 0 };
-          ruleData.count += 1;
-          metricsByRule[ruleId] = ruleData;
-
-          occurrencesData.push({
-            rule: ruleId,
-            message,
-            file: result.filePath,
-            line,
-            column,
-            ts: DATETIME,
-          });
-        });
-      });
-
-      console.log(
-        `ESLint found ${eslintResults.reduce((sum, r) => sum + r.messages.length, 0)} custom rule violations`,
-      );
-    } catch (e) {
-      console.log('No ESLint issues found or parsing error:', e.message);
-    }
+    // Parse Oxlint output for custom rules
+    // We only want to persist custom rule violations and discard any built-in rule violations that are
+    // not yet toggled off in oxlint.custom-lint-rules.mts
+    const oxlintCustomRuleResults = JSON.parse(oxlintCustomRuleOutput);
+    console.log(
+      `OXC found ${oxlintCustomRuleResults.diagnostics?.length || 0} issues across ${oxlintCustomRuleResults.number_of_files} files for custom rules`,
+    );
+    const {
+      metricsByRule: metricsByCustomRule,
+      occurrencesData: customRuleOccurrencesData,
+    } = parseOxlintResult(oxlintCustomRuleResults, CUSTOM_PLUGIN_ID_PREFIXES);
+
+    const mergedMetricsByRule = { ...metricsByRule, ...metricsByCustomRule };
+    const mergedOccurrencesData = [
+      ...occurrencesData,
+      ...customRuleOccurrencesData,
+    ];
 
     // Transform data for Google Sheets
-    const metricsData = Object.entries(metricsByRule).map(
+    const metricsData = Object.entries(mergedMetricsByRule).map(
       ([rule, { count }]) => [
-        'OXC+ESLint',
+        'OXC',
         rule,
         enrichedRules[rule]?.description || 'N/A',
         `${count}`,
@@ -225,7 +231,7 @@ async function runOxlintAndProcess() {
       ],
     );
 
-    occurrencesData = occurrencesData.map(
+    const finalizedConcurrencesData = mergedOccurrencesData.map(
       ({ rule, message, file, line, column }) => [
         rule,
         enrichedRules[rule]?.description || 'N/A',
@@ -255,7 +261,7 @@ async function runOxlintAndProcess() {
     ];
 
     console.log(
-      `Found ${Object.keys(metricsByRule).length} unique rules with ${occurrencesData.length} total occurrences`,
+      `Found ${Object.keys(metricsByRule).length} unique rules with ${finalizedConcurrencesData.length} total occurrences`,
     );
 
     await writeToGoogleSheet(
@@ -266,7 +272,7 @@ async function runOxlintAndProcess() {
     );
 
     await writeToGoogleSheet(
-      occurrencesData,
+      finalizedConcurrencesData,
       'ESLint Backlog!A:G',
       eslintBacklogHeaders,
     );
@@ -278,4 +284,4 @@ async function runOxlintAndProcess() {
   }
 }
 
-export { parseRuleId, runOxlintAndProcess };
+export { parseRuleId, parseOxlintResult, runOxlintAndProcess };
diff --git a/superset-frontend/spec/scripts/oxlint-metrics-uploader.test.ts b/superset-frontend/spec/scripts/oxlint-metrics-uploader.test.ts
index 74e7201d0f3..41c9287f5fb 100644
--- a/superset-frontend/spec/scripts/oxlint-metrics-uploader.test.ts
+++ b/superset-frontend/spec/scripts/oxlint-metrics-uploader.test.ts
@@ -16,43 +16,320 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { parseRuleId } from '../../scripts/internal/oxlint-metrics-uploader';
+import {
+  parseOxlintResult,
+  parseRuleId,
+} from '../../scripts/internal/oxlint-metrics-uploader';
+
+jest.mock('node:child_process', () => ({
+  execSync: jest.fn(),
+}));
+
+jest.mock('@googleapis/sheets', () => ({
+  __esModule: true,
+  default: {
+    sheets: jest.fn(),
+  },
+}));
+
+jest.mock('google-auth-library', () => ({
+  __esModule: true,
+  GoogleAuth: jest.fn(),
+}));
+
+beforeEach(() => {
+  process.env.SERVICE_ACCOUNT_KEY = JSON.stringify({
+    client_email: 'foo@bar.com',
+  });
+  process.env.SPREADSHEET_ID = 'spreadsheet-id';
+});
+
+afterEach(() => {
+  delete process.env.SERVICE_ACCOUNT_KEY;
+  delete process.env.SPREADSHEET_ID;
+  jest.restoreAllMocks();
+});
 
 test('eslint rules keep their bare name', () => {
-  expect(parseRuleId('eslint(no-console)')).toBe('no-console');
-  expect(parseRuleId('eslint(prefer-destructuring)')).toBe(
-    'prefer-destructuring',
-  );
+  expect(parseRuleId('eslint(no-console)')).toMatchObject({
+    parsed: 'no-console',
+    pluginId: 'eslint',
+  });
+  expect(parseRuleId('eslint(prefer-destructuring)')).toMatchObject({
+    parsed: 'prefer-destructuring',
+    pluginId: 'eslint',
+  });
 });
 
 test('plugin rules are recorded under plugin/rule (#42981)', () => {
   // These are the codes oxlint actually emits. They previously fell through to
   // the raw `react-hooks(exhaustive-deps)` string, so the rows no longer lined
   // up with the ids the same rules were recorded under before the migration.
-  expect(parseRuleId('react-hooks(exhaustive-deps)')).toBe(
-    'react-hooks/exhaustive-deps',
-  );
-  expect(parseRuleId('react-hooks(rules-of-hooks)')).toBe(
-    'react-hooks/rules-of-hooks',
-  );
-  expect(parseRuleId('react(jsx-key)')).toBe('react/jsx-key');
-  expect(parseRuleId('jest(no-conditional-expect)')).toBe(
-    'jest/no-conditional-expect',
-  );
-  expect(parseRuleId('oxc(erasing-op)')).toBe('oxc/erasing-op');
-  expect(parseRuleId('typescript(no-explicit-any)')).toBe(
-    'typescript/no-explicit-any',
-  );
+  expect(parseRuleId('react-hooks(exhaustive-deps)')).toMatchObject({
+    parsed: 'react-hooks/exhaustive-deps',
+    pluginId: 'react-hooks',
+  });
+  expect(parseRuleId('react-hooks(rules-of-hooks)')).toMatchObject({
+    parsed: 'react-hooks/rules-of-hooks',
+    pluginId: 'react-hooks',
+  });
+  expect(parseRuleId('react(jsx-key)')).toMatchObject({
+    parsed: 'react/jsx-key',
+    pluginId: 'react',
+  });
+  expect(parseRuleId('jest(no-conditional-expect)')).toMatchObject({
+    parsed: 'jest/no-conditional-expect',
+    pluginId: 'jest',
+  });
+  expect(parseRuleId('oxc(erasing-op)')).toMatchObject({
+    parsed: 'oxc/erasing-op',
+    pluginId: 'oxc',
+  });
+  expect(parseRuleId('typescript(no-explicit-any)')).toMatchObject({
+    parsed: 'typescript/no-explicit-any',
+    pluginId: 'typescript',
+  });
 });
 
 test('the legacy eslint-plugin- prefix still collapses to the plugin name', () => {
-  expect(parseRuleId('eslint-plugin-unicorn(no-new-array)')).toBe(
-    'unicorn/no-new-array',
-  );
+  expect(parseRuleId('eslint-plugin-unicorn(no-new-array)')).toMatchObject({
+    parsed: 'unicorn/no-new-array',
+    pluginId: 'unicorn',
+  });
 });
 
 test('an unrecognized or missing code is passed through rather than dropped', () => {
-  expect(parseRuleId('something-unparseable')).toBe('something-unparseable');
-  expect(parseRuleId(undefined)).toBe('unknown');
-  expect(parseRuleId('')).toBe('unknown');
+  expect(parseRuleId('something-unparseable')).toMatchObject({
+    parsed: 'something-unparseable',
+    pluginId: 'unknown',
+  });
+  expect(parseRuleId(undefined)).toMatchObject({
+    parsed: 'unknown',
+    pluginId: 'unknown',
+  });
+  expect(parseRuleId('')).toMatchObject({
+    parsed: 'unknown',
+    pluginId: 'unknown',
+  });
+});
+
+test('parseOxlintResult aggregates diagnostics and records their locations', () => {
+  const result = parseOxlintResult({
+    diagnostics: [
+      {
+        code: 'eslint(no-console)',
+        filename: 'src/first.ts',
+        message: 'Unexpected console statement',
+        labels: [{ span: { line: 4, column: 8 } }],
+      },
+      {
+        code: 'eslint(no-console)',
+        filename: 'src/second.ts',
+        message: 'Unexpected console statement',
+        labels: [{ span: { line: 9, column: 2 } }],
+      },
+      {
+        code: 'theme-colors(no-literal-colors)',
+        filename: 'src/theme.ts',
+        message: 'Use a theme color',
+        labels: [{ span: { line: 12, column: 6 } }],
+      },
+    ],
+  });
+
+  expect(result.metricsByRule).toEqual({
+    'no-console': { count: 2 },
+    'theme-colors/no-literal-colors': { count: 1 },
+  });
+  expect(result.occurrencesData).toMatchObject([
+    {
+      rule: 'no-console',
+      message: 'Unexpected console statement',
+      file: 'src/first.ts',
+      line: 4,
+      column: 8,
+    },
+    {
+      rule: 'no-console',
+      message: 'Unexpected console statement',
+      file: 'src/second.ts',
+      line: 9,
+      column: 2,
+    },
+    {
+      rule: 'theme-colors/no-literal-colors',
+      message: 'Use a theme color',
+      file: 'src/theme.ts',
+      line: 12,
+      column: 6,
+    },
+  ]);
+  expect(result.occurrencesData[0].ts).toEqual(expect.any(String));
+});
+
+test('parseOxlintResult filters diagnostics by plugin ID prefixes', () => {
+  const result = parseOxlintResult(
+    {
+      diagnostics: [
+        {
+          code: 'eslint(no-console)',
+          filename: 'src/app.ts',
+          message: 'Unexpected console statement',
+        },
+        {
+          code: 'i18n-strings(no-template-vars)',
+          filename: 'src/translation.ts',
+          message: 'Avoid template variables',
+        },
+      ],
+    },
+    ['i18n-strings'],
+  );
+
+  expect(result.metricsByRule).toEqual({
+    'i18n-strings/no-template-vars': { count: 1 },
+  });
+  expect(result.occurrencesData).toMatchObject([
+    {
+      rule: 'i18n-strings/no-template-vars',
+      file: 'src/translation.ts',
+      message: 'Avoid template variables',
+    },
+  ]);
+});
+
+test('runOxlintAndProcess uploads aggregated and backlog data to Google Sheets when oxc commands exits with nonzero code due to detection of rule violations', async () => {
+  jest.resetModules();
+
+  const appendSheetMock = jest.fn().mockResolvedValue({});
+  const updateSheetMock = jest.fn().mockResolvedValue({});
+  const googleSheetsModule = jest.requireMock('@googleapis/sheets');
+  const googleAuthModule = jest.requireMock('google-auth-library');
+  const mockedExecSync = jest.requireMock('node:child_process')
+    .execSync as jest.Mock;
+
+  googleSheetsModule.default.sheets.mockReturnValue({
+    spreadsheets: {
+      values: {
+        append: appendSheetMock,
+        update: updateSheetMock,
+      },
+    },
+  } as any);
+  googleAuthModule.GoogleAuth.mockImplementation(() => ({}) as any);
+  mockedExecSync.mockImplementation((cmd: string) => {
+    if (cmd.includes('oxlint.json')) {
+      throw {
+        stdout: JSON.stringify({
+          diagnostics: [
+            {
+              message: 'Use Array destructuring.',
+              code: 'eslint(prefer-destructuring)',
+              severity: 'warning',
+              filename:
+                'src/chartCustomizations/components/DeckglLayerVisibility/DeckglLayerVisibilityCustomizationPlugin.test.tsx',
+              labels: [
+                {
+                  span: { offset: 4357, length: 38, line: 155, column: 20 },
+                },
+              ],
+            },
+          ],
+          number_of_files: 1,
+        }),
+      };
+    }
+
+    if (cmd.includes('oxlint.custom-lint-rules.mts')) {
+      throw {
+        stdout: JSON.stringify({
+          diagnostics: [
+            {
+              message:
+                'Eager `label: t(...)` is evaluated at module load, before i18n is initialized. Wrap in an arrow function: `label: () => t(...)`.',
+              code: 'i18n-strings(no-eager-t-in-config)',
+              severity: 'warning',
+              filename: 'src/filters/components/TimeColumn/controlPanel.ts',
+              labels: [
+                {
+                  span: {
+                    offset: 1013,
+                    length: 21,
+                    line: 25,
+                    column: 14,
+                  },
+                },
+              ],
+            },
+          ],
+          number_of_files: 1,
+        }),
+      };
+    }
+
+    throw new Error('Unknown oxlint executions');
+  });
+
+  const { runOxlintAndProcess } =
+    await import('../../scripts/internal/oxlint-metrics-uploader');
+  await runOxlintAndProcess();
+
+  expect(appendSheetMock).toHaveBeenCalledTimes(1);
+  expect(updateSheetMock).toHaveBeenCalledTimes(1);
+
+  const appendCall = appendSheetMock.mock.calls[0][0];
+  const backlogCall = updateSheetMock.mock.calls[0][0];
+
+  expect(appendCall).toMatchObject({
+    spreadsheetId: 'spreadsheet-id',
+    range: 'Aggregated History!A:E',
+    valueInputOption: 'USER_ENTERED',
+  });
+  expect(appendCall.resource.values).toEqual(
+    expect.arrayContaining([
+      ['OXC', 'prefer-destructuring', 'N/A', '1', expect.any(String)],
+      [
+        'OXC',
+        'i18n-strings/no-eager-t-in-config',
+        'N/A',
+        '1',
+        expect.any(String),
+      ],
+    ]),
+  );
+
+  expect(backlogCall).toMatchObject({
+    spreadsheetId: 'spreadsheet-id',
+    range: 'ESLint Backlog!A:G',
+    valueInputOption: 'USER_ENTERED',
+  });
+  expect(backlogCall.resource.values).toEqual([
+    [
+      'Rule',
+      'Rule Description',
+      'ESLint Message',
+      'File',
+      'Line',
+      'Column',
+      'Timestamp',
+    ],
+    [
+      'prefer-destructuring',
+      'N/A',
+      'Use Array destructuring.',
+      'src/chartCustomizations/components/DeckglLayerVisibility/DeckglLayerVisibilityCustomizationPlugin.test.tsx',
+      '155',
+      '20',
+      expect.any(String),
+    ],
+    [
+      'i18n-strings/no-eager-t-in-config',
+      'N/A',
+      'Eager `label: t(...)` is evaluated at module load, before i18n is initialized. Wrap in an arrow function: `label: () => t(...)`.',
+      'src/filters/components/TimeColumn/controlPanel.ts',
+      '25',
+      '14',
+      expect.any(String),
+    ],
+  ]);
 });
diff --git a/superset-frontend/tsconfig.json b/superset-frontend/tsconfig.json
index 16400707368..f4dbd05c713 100644
--- a/superset-frontend/tsconfig.json
+++ b/superset-frontend/tsconfig.json
@@ -58,13 +58,13 @@
       "@apache-superset/core": ["./packages/superset-core/src"],
       "@apache-superset/core/*": ["./packages/superset-core/src/*"],
       "@superset-ui/plugin-chart-*": ["./plugins/plugin-chart-*/src"],
-      "@superset-ui/plugin-chart-*": ["./plugins/plugin-chart-*/src"],
       "@superset-ui/preset-chart-*": ["./plugins/preset-chart-*/src"],
       "echarts/types/src/*": ["./node_modules/echarts/types/src/*"]
     }
   },
   "include": [
     "./src/**/*",
+    "./custom-lint-rules/**/*",
     "./spec/**/*",
     "./plugins/**/*",
     "./packages/**/*",