mirror of
https://github.com/apache/superset.git
synced 2026-06-16 04:59:17 +00:00
Compare commits
1 Commits
bump-setup
...
showtime-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab0aa3eb78 |
2
.github/actions/setup-backend/action.yml
vendored
2
.github/actions/setup-backend/action.yml
vendored
@@ -42,7 +42,7 @@ runs:
|
||||
fi
|
||||
echo "python-version=$RESOLVED_VERSION" >> "$GITHUB_OUTPUT"
|
||||
- name: Set up Python ${{ steps.set-python-version.outputs.python-version }}
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
||||
with:
|
||||
python-version: ${{ steps.set-python-version.outputs.python-version }}
|
||||
cache: ${{ inputs.cache }}
|
||||
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: steps.check.outputs.superset-extensions-cli
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
with:
|
||||
file: ./coverage.xml
|
||||
flags: superset-extensions-cli
|
||||
|
||||
2
.github/workflows/superset-frontend.yml
vendored
2
.github/workflows/superset-frontend.yml
vendored
@@ -134,7 +134,7 @@ jobs:
|
||||
run: npx nyc merge coverage/ merged-output/coverage-summary.json
|
||||
|
||||
- name: Upload Code Coverage
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
with:
|
||||
flags: javascript
|
||||
use_oidc: true
|
||||
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
run: |
|
||||
./scripts/python_tests.sh
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
with:
|
||||
flags: python,mysql
|
||||
verbose: true
|
||||
@@ -173,7 +173,7 @@ jobs:
|
||||
run: |
|
||||
./scripts/python_tests.sh
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
with:
|
||||
flags: python,postgres
|
||||
verbose: true
|
||||
@@ -222,7 +222,7 @@ jobs:
|
||||
run: |
|
||||
./scripts/python_tests.sh
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
with:
|
||||
flags: python,sqlite
|
||||
verbose: true
|
||||
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
run: |
|
||||
./scripts/python_tests.sh -m 'chart_data_flow or sql_json_flow'
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
with:
|
||||
flags: python,presto
|
||||
verbose: true
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
pip install -e .[hive]
|
||||
./scripts/python_tests.sh -m 'chart_data_flow or sql_json_flow'
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
with:
|
||||
flags: python,hive
|
||||
verbose: true
|
||||
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
pytest --durations-min=0.5 --cov=superset/sql/ ./tests/unit_tests/sql/ --cache-clear --cov-fail-under=100
|
||||
pytest --durations-min=0.5 --cov=superset/semantic_layers/ ./tests/unit_tests/semantic_layers/ --cache-clear --cov-fail-under=100
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1
|
||||
with:
|
||||
flags: python,unit
|
||||
verbose: true
|
||||
|
||||
@@ -297,7 +297,7 @@ pre-commit run eslint # Frontend linting
|
||||
## Platform-Specific Instructions
|
||||
|
||||
- **[CLAUDE.md](CLAUDE.md)** - For Claude/Anthropic tools
|
||||
- **[.github/copilot-instructions.md](.github/copilot-instructions.md)** - For GitHub Copilot
|
||||
- **[.github/copilot-instructions.md](.github/copilot-instructions.md)** - For GitHub Copilot
|
||||
- **[GEMINI.md](GEMINI.md)** - For Google Gemini tools
|
||||
- **[GPT.md](GPT.md)** - For OpenAI/ChatGPT tools
|
||||
- **[.cursor/rules/dev-standard.mdc](.cursor/rules/dev-standard.mdc)** - For Cursor editor
|
||||
|
||||
@@ -72,8 +72,8 @@
|
||||
"@superset-ui/core": "^0.20.4",
|
||||
"@swc/core": "^1.15.40",
|
||||
"antd": "^6.4.3",
|
||||
"baseline-browser-mapping": "^2.10.34",
|
||||
"caniuse-lite": "^1.0.30001797",
|
||||
"baseline-browser-mapping": "^2.10.33",
|
||||
"caniuse-lite": "^1.0.30001793",
|
||||
"docusaurus-plugin-openapi-docs": "^5.0.2",
|
||||
"docusaurus-theme-openapi-docs": "^5.0.2",
|
||||
"js-yaml": "^4.2.0",
|
||||
|
||||
@@ -5578,10 +5578,10 @@ base64-js@^1.3.1, base64-js@^1.5.1:
|
||||
resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
baseline-browser-mapping@^2.10.34, baseline-browser-mapping@^2.9.0, baseline-browser-mapping@^2.9.19:
|
||||
version "2.10.34"
|
||||
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.34.tgz#dedb606362446777cfe328d30d4ee15056d06303"
|
||||
integrity sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw==
|
||||
baseline-browser-mapping@^2.10.33, baseline-browser-mapping@^2.9.0, baseline-browser-mapping@^2.9.19:
|
||||
version "2.10.33"
|
||||
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.33.tgz#27c299b096404978831958d429f48390424c4f9b"
|
||||
integrity sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==
|
||||
|
||||
batch@0.6.1:
|
||||
version "0.6.1"
|
||||
@@ -5824,10 +5824,10 @@ caniuse-api@^3.0.0:
|
||||
lodash.memoize "^4.1.2"
|
||||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001702, caniuse-lite@^1.0.30001759, caniuse-lite@^1.0.30001797:
|
||||
version "1.0.30001797"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz#1332709e1439f01ff92085dd17001e0a45897ec0"
|
||||
integrity sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001702, caniuse-lite@^1.0.30001759, caniuse-lite@^1.0.30001793:
|
||||
version "1.0.30001793"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz#238887ddf5fcfc8c36d872394d0a78a517312a72"
|
||||
integrity sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==
|
||||
|
||||
ccount@^2.0.0:
|
||||
version "2.0.1"
|
||||
|
||||
34
superset-frontend/.eslintignore
Normal file
34
superset-frontend/.eslintignore
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
**/*{.,-}min.js
|
||||
**/*.sh
|
||||
coverage/**
|
||||
dist/*
|
||||
src/assets/images/*
|
||||
node_modules/*
|
||||
node_modules*/*
|
||||
vendor/*
|
||||
docs/*
|
||||
src/dashboard/deprecated/*
|
||||
src/temp/*
|
||||
**/node_modules
|
||||
*.d.ts
|
||||
coverage/
|
||||
esm/
|
||||
lib/
|
||||
tmp/
|
||||
storybook-static/
|
||||
537
superset-frontend/.eslintrc.js
Normal file
537
superset-frontend/.eslintrc.js
Normal file
@@ -0,0 +1,537 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Register TypeScript require hook so ESLint can load .ts plugin files
|
||||
require('tsx/cjs');
|
||||
|
||||
const packageConfig = require('./package.json');
|
||||
|
||||
const importCoreModules = [];
|
||||
Object.entries(packageConfig.dependencies).forEach(([pkg]) => {
|
||||
if (/@superset-ui/.test(pkg)) {
|
||||
importCoreModules.push(pkg);
|
||||
}
|
||||
});
|
||||
|
||||
// ignore files in production mode
|
||||
let ignorePatterns = [];
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
ignorePatterns = [
|
||||
'*.test.{js,ts,jsx,tsx}',
|
||||
'plugins/**/test/**/*',
|
||||
'packages/**/test/**/*',
|
||||
'packages/generator-superset/**/*',
|
||||
];
|
||||
}
|
||||
|
||||
const restrictedImportsRules = {
|
||||
'no-design-icons': {
|
||||
name: '@ant-design/icons',
|
||||
message:
|
||||
'Avoid importing icons directly from @ant-design/icons. Use the src/components/Icons component instead.',
|
||||
},
|
||||
'no-moment': {
|
||||
name: 'moment',
|
||||
message:
|
||||
'Please use the dayjs library instead of moment.js. See https://day.js.org',
|
||||
},
|
||||
'no-lodash-memoize': {
|
||||
name: 'lodash/memoize',
|
||||
message: 'Lodash Memoize is unsafe! Please use memoize-one instead',
|
||||
},
|
||||
'no-testing-library-react': {
|
||||
name: '@superset-ui/core/spec',
|
||||
message: 'Please use spec/helpers/testing-library instead',
|
||||
},
|
||||
'no-testing-library-react-dom-utils': {
|
||||
name: '@testing-library/react-dom-utils',
|
||||
message: 'Please use spec/helpers/testing-library instead',
|
||||
},
|
||||
'no-antd': {
|
||||
name: 'antd',
|
||||
message: 'Please import Ant components from the index of src/components',
|
||||
},
|
||||
'no-superset-theme': {
|
||||
name: '@superset-ui/core',
|
||||
importNames: ['supersetTheme'],
|
||||
message:
|
||||
'Please use the theme directly from the ThemeProvider rather than importing supersetTheme.',
|
||||
},
|
||||
'no-query-string': {
|
||||
name: 'query-string',
|
||||
message: 'Please use the URLSearchParams API instead of query-string.',
|
||||
},
|
||||
'no-jest-mock-console': {
|
||||
name: 'jest-mock-console',
|
||||
message: 'Please use native Jest spies, i.e. jest.spyOn(console, "warn")',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:import/recommended',
|
||||
'plugin:react-prefer-function-component/recommended',
|
||||
'plugin:storybook/recommended',
|
||||
'prettier',
|
||||
],
|
||||
parser: '@babel/eslint-parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
requireConfigFile: false,
|
||||
babelOptions: {
|
||||
presets: ['@babel/preset-react', '@babel/preset-env'],
|
||||
},
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es2020: true,
|
||||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
node: {
|
||||
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
|
||||
moduleDirectory: ['node_modules', '.'],
|
||||
},
|
||||
typescript: {
|
||||
alwaysTryTypes: true,
|
||||
project: [
|
||||
'./tsconfig.json',
|
||||
'./packages/superset-ui-core/tsconfig.json',
|
||||
'./packages/superset-ui-chart-controls/',
|
||||
'./plugins/*/tsconfig.json',
|
||||
],
|
||||
},
|
||||
},
|
||||
'import/core-modules': importCoreModules,
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
'import',
|
||||
'lodash',
|
||||
'theme-colors',
|
||||
'icons',
|
||||
'i18n-strings',
|
||||
'react-prefer-function-component',
|
||||
'react-you-might-not-need-an-effect',
|
||||
'prettier',
|
||||
],
|
||||
rules: {
|
||||
// === Essential Superset customizations ===
|
||||
|
||||
// Prettier integration
|
||||
'prettier/prettier': 'error',
|
||||
|
||||
// Custom Superset rules
|
||||
'theme-colors/no-literal-colors': 'error',
|
||||
'icons/no-fa-icons-usage': 'error',
|
||||
'i18n-strings/no-template-vars': 'error',
|
||||
'i18n-strings/no-eager-t-in-config': 'off', // enabled only for controlPanel files via overrides below
|
||||
|
||||
// Core ESLint overrides for Superset
|
||||
'no-console': 'warn',
|
||||
'no-unused-vars': 'off', // TypeScript handles this
|
||||
camelcase: [
|
||||
'error',
|
||||
{
|
||||
allow: ['^UNSAFE_', '__REDUX_DEVTOOLS_EXTENSION_COMPOSE__'],
|
||||
properties: 'never',
|
||||
},
|
||||
],
|
||||
'prefer-destructuring': ['error', { object: true, array: false }],
|
||||
'no-prototype-builtins': 0,
|
||||
curly: 'off',
|
||||
|
||||
// Import plugin overrides
|
||||
'import/extensions': [
|
||||
'error',
|
||||
'ignorePackages',
|
||||
{
|
||||
js: 'never',
|
||||
jsx: 'never',
|
||||
ts: 'never',
|
||||
tsx: 'never',
|
||||
},
|
||||
],
|
||||
'import/no-cycle': 0,
|
||||
'import/prefer-default-export': 0,
|
||||
'import/no-named-as-default-member': 0,
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{
|
||||
devDependencies: [
|
||||
'test/**',
|
||||
'tests/**',
|
||||
'spec/**',
|
||||
'**/__tests__/**',
|
||||
'**/__mocks__/**',
|
||||
'*.test.{js,jsx,ts,tsx}',
|
||||
'*.spec.{js,jsx,ts,tsx}',
|
||||
'**/*.test.{js,jsx,ts,tsx}',
|
||||
'**/*.spec.{js,jsx,ts,tsx}',
|
||||
'**/jest.config.js',
|
||||
'**/jest.setup.js',
|
||||
'**/webpack.config.js',
|
||||
'**/webpack.config.*.js',
|
||||
'**/.eslintrc*.js',
|
||||
],
|
||||
optionalDependencies: false,
|
||||
},
|
||||
],
|
||||
|
||||
// React plugin overrides
|
||||
'react-prefer-function-component/react-prefer-function-component': 1,
|
||||
|
||||
// React effect best practices
|
||||
'react-you-might-not-need-an-effect/no-empty-effect': 'error',
|
||||
'react-you-might-not-need-an-effect/no-pass-live-state-to-parent': 'error',
|
||||
'react-you-might-not-need-an-effect/no-initialize-state': 'error',
|
||||
|
||||
// Lodash
|
||||
'lodash/import-scope': [2, 'member'],
|
||||
|
||||
// React effect best practices
|
||||
'react-you-might-not-need-an-effect/no-reset-all-state-on-prop-change':
|
||||
'error',
|
||||
'react-you-might-not-need-an-effect/no-chain-state-updates': 'error',
|
||||
'react-you-might-not-need-an-effect/no-event-handler': 'error',
|
||||
'react-you-might-not-need-an-effect/no-derived-state': 'error',
|
||||
|
||||
// Storybook
|
||||
'storybook/prefer-pascal-case': 'error',
|
||||
|
||||
// File progress
|
||||
'file-progress/activate': 1,
|
||||
|
||||
// React effect rules
|
||||
'react-you-might-not-need-an-effect/no-adjust-state-on-prop-change':
|
||||
'error',
|
||||
'react-you-might-not-need-an-effect/no-pass-data-to-parent': 'error',
|
||||
|
||||
// Restricted imports
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: Object.values(restrictedImportsRules).filter(Boolean),
|
||||
patterns: ['antd/*'],
|
||||
},
|
||||
],
|
||||
|
||||
// Temporarily disabled for migration
|
||||
'no-unsafe-optional-chaining': 0,
|
||||
'no-import-assign': 0,
|
||||
'import/no-relative-packages': 0,
|
||||
'no-promise-executor-return': 0,
|
||||
'import/no-import-module-exports': 0,
|
||||
|
||||
// Restrict certain syntax patterns
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector:
|
||||
"ImportDeclaration[source.value='react'] :matches(ImportDefaultSpecifier, ImportNamespaceSpecifier)",
|
||||
message:
|
||||
'Default React import is not required due to automatic JSX runtime in React 16.4',
|
||||
},
|
||||
{
|
||||
selector: 'ImportNamespaceSpecifier[parent.source.value!=/^(\\.|src)/]',
|
||||
message: 'Wildcard imports are not allowed',
|
||||
},
|
||||
],
|
||||
},
|
||||
overrides: [
|
||||
// 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. Many pre-existing call sites need conversion;
|
||||
// run `eslint --fix` on a controlPanel file to sweep it. Promote to
|
||||
// `'error'` once the codebase is clean.
|
||||
{
|
||||
files: ['**/controlPanel.{ts,tsx,js,jsx}'],
|
||||
rules: {
|
||||
'i18n-strings/no-eager-t-in-config': 'warn',
|
||||
},
|
||||
},
|
||||
// Ban JavaScript files in src/ - all new code must be TypeScript
|
||||
{
|
||||
files: ['src/**/*.js', 'src/**/*.jsx'],
|
||||
rules: {
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector: 'Program',
|
||||
message:
|
||||
'JavaScript files are not allowed in src/. Please use TypeScript (.ts/.tsx) instead.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// Ban JavaScript files in plugins/ - all plugin source code must be TypeScript
|
||||
{
|
||||
files: ['plugins/**/src/**/*.js', 'plugins/**/src/**/*.jsx'],
|
||||
rules: {
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector: 'Program',
|
||||
message:
|
||||
'JavaScript files are not allowed in plugins/. Please use TypeScript (.ts/.tsx) instead.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
// Ban JavaScript files in packages/ - with exceptions for config files and generators
|
||||
{
|
||||
files: ['packages/**/src/**/*.js', 'packages/**/src/**/*.jsx'],
|
||||
excludedFiles: [
|
||||
'packages/generator-superset/**/*', // Yeoman generator templates run via Node
|
||||
'packages/**/__mocks__/**/*', // Test mocks
|
||||
],
|
||||
rules: {
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector: 'Program',
|
||||
message:
|
||||
'JavaScript files are not allowed in packages/. Please use TypeScript (.ts/.tsx) instead.',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
extends: ['plugin:@typescript-eslint/recommended', 'prettier'],
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
rules: {
|
||||
// TypeScript-specific rule overrides
|
||||
'@typescript-eslint/ban-ts-ignore': 0,
|
||||
'@typescript-eslint/ban-ts-comment': 0,
|
||||
'@typescript-eslint/ban-types': 0,
|
||||
'@typescript-eslint/naming-convention': [
|
||||
'error',
|
||||
{
|
||||
selector: 'enum',
|
||||
format: ['PascalCase'],
|
||||
},
|
||||
{
|
||||
selector: 'enumMember',
|
||||
format: ['PascalCase'],
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-empty-function': 0,
|
||||
'@typescript-eslint/no-explicit-any': 0,
|
||||
'@typescript-eslint/no-use-before-define': 'error',
|
||||
'@typescript-eslint/no-non-null-assertion': 0,
|
||||
'@typescript-eslint/explicit-function-return-type': 0,
|
||||
'@typescript-eslint/explicit-module-boundary-types': 0,
|
||||
'@typescript-eslint/no-unused-vars': 'warn',
|
||||
'@typescript-eslint/prefer-optional-chain': 'error',
|
||||
|
||||
// Disable base rules that conflict with TS versions
|
||||
'no-unused-vars': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'no-shadow': 'off',
|
||||
|
||||
// Import overrides for TypeScript
|
||||
'import/extensions': [
|
||||
'error',
|
||||
'ignorePackages',
|
||||
{
|
||||
js: 'never',
|
||||
jsx: 'never',
|
||||
ts: 'never',
|
||||
tsx: 'never',
|
||||
},
|
||||
],
|
||||
},
|
||||
settings: {
|
||||
'import/resolver': {
|
||||
typescript: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/**'],
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{ devDependencies: true },
|
||||
],
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: [
|
||||
restrictedImportsRules['no-moment'],
|
||||
restrictedImportsRules['no-lodash-memoize'],
|
||||
restrictedImportsRules['no-superset-theme'],
|
||||
],
|
||||
patterns: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['plugins/**'],
|
||||
rules: {
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: [
|
||||
restrictedImportsRules['no-moment'],
|
||||
restrictedImportsRules['no-lodash-memoize'],
|
||||
],
|
||||
patterns: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['src/components/**', 'src/theme/**'],
|
||||
rules: {
|
||||
'no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
paths: Object.values(restrictedImportsRules).filter(
|
||||
r => r.name !== 'antd',
|
||||
),
|
||||
patterns: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'*.test.ts',
|
||||
'*.test.tsx',
|
||||
'*.test.js',
|
||||
'*.test.jsx',
|
||||
'*.stories.tsx',
|
||||
'*.stories.jsx',
|
||||
'fixtures.*',
|
||||
'**/test/**/*',
|
||||
'**/tests/**/*',
|
||||
'spec/**/*',
|
||||
'**/fixtures/**/*',
|
||||
'**/__mocks__/**/*',
|
||||
'**/spec/**/*',
|
||||
],
|
||||
excludedFiles: 'cypress-base/cypress/**/*',
|
||||
plugins: ['jest-dom', 'no-only-tests', 'testing-library'],
|
||||
extends: ['plugin:jest-dom/recommended', 'plugin:testing-library/react'],
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{ devDependencies: true },
|
||||
],
|
||||
'prefer-promise-reject-errors': 0,
|
||||
'max-classes-per-file': 0,
|
||||
|
||||
// Temporary for migration
|
||||
'testing-library/await-async-queries': 0,
|
||||
'testing-library/await-async-utils': 0,
|
||||
'testing-library/no-await-sync-events': 0,
|
||||
'testing-library/no-render-in-lifecycle': 0,
|
||||
'testing-library/no-unnecessary-act': 0,
|
||||
'testing-library/no-wait-for-multiple-assertions': 0,
|
||||
'testing-library/prefer-screen-queries': 0,
|
||||
'testing-library/await-async-events': 0,
|
||||
'testing-library/no-node-access': 0,
|
||||
'testing-library/no-wait-for-side-effects': 0,
|
||||
'testing-library/prefer-presence-queries': 0,
|
||||
'testing-library/render-result-naming-convention': 0,
|
||||
'testing-library/no-container': 0,
|
||||
'testing-library/prefer-find-by': 0,
|
||||
'testing-library/no-manual-cleanup': 0,
|
||||
|
||||
'no-restricted-syntax': [
|
||||
'error',
|
||||
{
|
||||
selector:
|
||||
"ImportDeclaration[source.value='react'] :matches(ImportDefaultSpecifier, ImportNamespaceSpecifier)",
|
||||
message:
|
||||
'Default React import is not required due to automatic JSX runtime in React 16.4',
|
||||
},
|
||||
],
|
||||
'no-restricted-imports': 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'*.test.ts',
|
||||
'*.test.tsx',
|
||||
'*.test.js',
|
||||
'*.test.jsx',
|
||||
'*.stories.tsx',
|
||||
'*.stories.jsx',
|
||||
'fixtures.*',
|
||||
'**/test/**/*',
|
||||
'**/tests/**/*',
|
||||
'spec/**/*',
|
||||
'**/fixtures/**/*',
|
||||
'**/__mocks__/**/*',
|
||||
'**/spec/**/*',
|
||||
'cypress-base/cypress/**/*',
|
||||
'Stories.tsx',
|
||||
'packages/superset-ui-core/src/theme/index.tsx',
|
||||
],
|
||||
rules: {
|
||||
'theme-colors/no-literal-colors': 0,
|
||||
'icons/no-fa-icons-usage': 0,
|
||||
'i18n-strings/no-template-vars': 0,
|
||||
'no-restricted-imports': 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'packages/**/*.stories.*',
|
||||
'packages/**/*.overview.*',
|
||||
'packages/**/fixtures.*',
|
||||
],
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['playwright/**/*.ts', 'playwright/**/*.js'],
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': [
|
||||
'error',
|
||||
{ devDependencies: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
ignorePatterns,
|
||||
};
|
||||
124
superset-frontend/.eslintrc.minimal.js
Normal file
124
superset-frontend/.eslintrc.minimal.js
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// Register TypeScript require hook so ESLint can load .ts plugin files
|
||||
require('tsx/cjs');
|
||||
|
||||
/**
|
||||
* MINIMAL ESLint config - ONLY for rules OXC doesn't support
|
||||
* This config is designed to be run alongside OXC linter
|
||||
*
|
||||
* Only covers:
|
||||
* - Custom Superset plugins (theme-colors, icons, i18n)
|
||||
* - Prettier formatting
|
||||
* - File progress indicator
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
// Don't report on eslint-disable comments for rules we don't have
|
||||
reportUnusedDisableDirectives: false,
|
||||
// Simple parser - no TypeScript needed since OXC handles that
|
||||
parser: '@babel/eslint-parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
requireConfigFile: false,
|
||||
babelOptions: {
|
||||
presets: ['@babel/preset-react', '@babel/preset-env'],
|
||||
},
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es2020: true,
|
||||
},
|
||||
plugins: [
|
||||
// ONLY custom Superset plugins that OXC doesn't support
|
||||
'theme-colors',
|
||||
'icons',
|
||||
'i18n-strings',
|
||||
'file-progress',
|
||||
'prettier',
|
||||
],
|
||||
rules: {
|
||||
// === ONLY rules that OXC cannot handle ===
|
||||
|
||||
// Prettier integration (formatting)
|
||||
'prettier/prettier': 'error',
|
||||
|
||||
// Custom Superset plugins
|
||||
'theme-colors/no-literal-colors': 'error',
|
||||
'icons/no-fa-icons-usage': 'error',
|
||||
'i18n-strings/no-template-vars': 'error',
|
||||
'file-progress/activate': 1,
|
||||
|
||||
// Explicitly turn off all other rules to avoid conflicts
|
||||
// when the config gets merged with other configs
|
||||
'import/no-unresolved': 'off',
|
||||
'import/extensions': 'off',
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
// Disable custom rules in test/story files
|
||||
files: [
|
||||
'**/*.test.*',
|
||||
'**/*.spec.*',
|
||||
'**/*.stories.*',
|
||||
'**/test/**',
|
||||
'**/tests/**',
|
||||
'**/spec/**',
|
||||
'**/__tests__/**',
|
||||
'**/__mocks__/**',
|
||||
'cypress-base/**',
|
||||
'packages/superset-ui-core/src/theme/index.tsx',
|
||||
],
|
||||
rules: {
|
||||
'theme-colors/no-literal-colors': 0,
|
||||
'icons/no-fa-icons-usage': 0,
|
||||
'i18n-strings/no-template-vars': 0,
|
||||
'file-progress/activate': 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
// Only check src/ files where theme/icon 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 files
|
||||
'*.generated.*',
|
||||
'*.config.js',
|
||||
'webpack.*',
|
||||
// Temporary analysis files
|
||||
'*.js', // Skip all standalone JS files in root
|
||||
'*.json',
|
||||
],
|
||||
};
|
||||
@@ -14,7 +14,7 @@
|
||||
"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.
|
||||
under the License
|
||||
-->
|
||||
|
||||
# Change Log
|
||||
|
||||
@@ -31,13 +31,12 @@ const plugin: { rules: Record<string, Rule.RuleModule> } = require('.');
|
||||
// Tests
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const ruleTester = new RuleTester({ languageOptions: { ecmaVersion: 6 } });
|
||||
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
|
||||
const rule: Rule.RuleModule = plugin.rules['no-template-vars'];
|
||||
|
||||
const errors: Array<{ message: string }> = [
|
||||
const errors: Array<{ type: string }> = [
|
||||
{
|
||||
message:
|
||||
"Don't use variables in translation string templates. Flask-babel is a static translation service, so it can't handle strings that include variables",
|
||||
type: 'CallExpression',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -31,10 +31,7 @@ const plugin: { rules: Record<string, Rule.RuleModule> } = require('.');
|
||||
// Tests
|
||||
//------------------------------------------------------------------------------
|
||||
const ruleTester = new RuleTester({
|
||||
languageOptions: {
|
||||
ecmaVersion: 6,
|
||||
parserOptions: { ecmaFeatures: { jsx: true } },
|
||||
},
|
||||
parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } },
|
||||
});
|
||||
const rule: Rule.RuleModule = plugin.rules['no-fa-icons-usage'];
|
||||
|
||||
|
||||
@@ -1,137 +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 and
|
||||
* Prettier formatting 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)
|
||||
* - Prettier formatting
|
||||
*/
|
||||
|
||||
// Register the TypeScript require hook so ESLint can load the .ts plugin files
|
||||
// from eslint-rules/*.
|
||||
require('tsx/cjs');
|
||||
|
||||
const tsParser = require('@typescript-eslint/parser');
|
||||
const prettierPlugin = require('eslint-plugin-prettier');
|
||||
const themeColorsPlugin = require('eslint-plugin-theme-colors');
|
||||
const iconsPlugin = require('eslint-plugin-icons');
|
||||
const i18nStringsPlugin = require('eslint-plugin-i18n-strings');
|
||||
|
||||
module.exports = [
|
||||
// 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: {
|
||||
prettier: prettierPlugin,
|
||||
'theme-colors': themeColorsPlugin,
|
||||
icons: iconsPlugin,
|
||||
'i18n-strings': i18nStringsPlugin,
|
||||
},
|
||||
rules: {
|
||||
// Prettier integration (formatting)
|
||||
'prettier/prettier': 'error',
|
||||
|
||||
// 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',
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -287,15 +287,13 @@
|
||||
"ignorePatterns": [
|
||||
"packages/generator-superset/**/*",
|
||||
"cypress-base/**",
|
||||
"**/node_modules/**",
|
||||
"node_modules/**",
|
||||
"build/**",
|
||||
"**/dist/**",
|
||||
"**/lib/**",
|
||||
"**/esm/**",
|
||||
"**/*.min.js",
|
||||
"**/*.d.ts",
|
||||
"dist/**",
|
||||
"lib/**",
|
||||
"esm/**",
|
||||
"*.min.js",
|
||||
"coverage/**",
|
||||
"storybook-static/**",
|
||||
".git/**",
|
||||
"**/*.config.js",
|
||||
"**/*.config.ts"
|
||||
|
||||
622
superset-frontend/package-lock.json
generated
622
superset-frontend/package-lock.json
generated
@@ -121,7 +121,7 @@
|
||||
"query-string": "9.4.0",
|
||||
"re-resizable": "^6.11.2",
|
||||
"react": "^18.2.0",
|
||||
"react-arborist": "^3.10.1",
|
||||
"react-arborist": "^3.8.0",
|
||||
"react-checkbox-tree": "^1.8.0",
|
||||
"react-diff-viewer-continued": "^4.2.2",
|
||||
"react-dnd": "^11.1.3",
|
||||
@@ -197,11 +197,11 @@
|
||||
"@types/content-disposition": "^0.5.9",
|
||||
"@types/dom-to-image": "^2.6.7",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/jquery": "^4.0.1",
|
||||
"@types/jquery": "^4.0.0",
|
||||
"@types/js-levenshtein": "^1.1.3",
|
||||
"@types/json-bigint": "^1.0.4",
|
||||
"@types/mousetrap": "^1.6.15",
|
||||
"@types/node": "^25.9.2",
|
||||
"@types/node": "^25.9.1",
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"@types/react-loadable": "^5.5.11",
|
||||
@@ -214,22 +214,22 @@
|
||||
"@types/rison": "0.1.0",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"@types/unzipper": "^0.10.11",
|
||||
"@typescript-eslint/eslint-plugin": "^8.61.0",
|
||||
"@typescript-eslint/parser": "^8.61.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.60.1",
|
||||
"@typescript-eslint/parser": "^8.59.4",
|
||||
"babel-jest": "^30.4.1",
|
||||
"babel-loader": "^10.1.1",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"baseline-browser-mapping": "^2.10.34",
|
||||
"baseline-browser-mapping": "^2.10.33",
|
||||
"cheerio": "1.2.0",
|
||||
"concurrently": "^10.0.3",
|
||||
"copy-webpack-plugin": "^14.0.0",
|
||||
"cross-env": "^10.1.0",
|
||||
"css-loader": "^7.1.4",
|
||||
"css-minimizer-webpack-plugin": "^8.0.0",
|
||||
"eslint": "^10.4.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^7.2.0",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-import-resolver-typescript": "^4.4.5",
|
||||
"eslint-plugin-cypress": "^3.6.0",
|
||||
@@ -237,11 +237,11 @@
|
||||
"eslint-plugin-icons": "file:eslint-rules/eslint-plugin-icons",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-jest-dom": "^5.5.0",
|
||||
"eslint-plugin-lodash": "^8.0.0",
|
||||
"eslint-plugin-lodash": "^7.4.0",
|
||||
"eslint-plugin-no-only-tests": "^3.4.0",
|
||||
"eslint-plugin-prettier": "^5.5.6",
|
||||
"eslint-plugin-react-prefer-function-component": "^5.0.0",
|
||||
"eslint-plugin-react-you-might-not-need-an-effect": "^1.0.0",
|
||||
"eslint-plugin-react-you-might-not-need-an-effect": "^0.10.4",
|
||||
"eslint-plugin-storybook": "10.4.2",
|
||||
"eslint-plugin-testing-library": "^7.16.2",
|
||||
"eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
|
||||
@@ -3805,108 +3805,92 @@
|
||||
"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/balanced-match": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-array/node_modules/brace-expansion": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"node_modules/@eslint/eslintrc": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
|
||||
"integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
"ajv": "^6.12.4",
|
||||
"debug": "^4.3.2",
|
||||
"espree": "^9.6.0",
|
||||
"globals": "^13.19.0",
|
||||
"ignore": "^5.2.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"minimatch": "^3.1.2",
|
||||
"strip-json-comments": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/config-helpers": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz",
|
||||
"integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==",
|
||||
"node_modules/@eslint/eslintrc/node_modules/ajv": {
|
||||
"version": "6.15.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
|
||||
"integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^1.2.1"
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"node_modules/@eslint/eslintrc/node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/js-yaml": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
|
||||
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/puzrin"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/nodeca"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.15"
|
||||
"argparse": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"node_modules/@eslint/eslintrc/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": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||
}
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz",
|
||||
"integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==",
|
||||
"node_modules/@eslint/js": {
|
||||
"version": "8.57.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
|
||||
"integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@eslint/core": "^1.2.1",
|
||||
"levn": "^0.4.1"
|
||||
},
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@exodus/bytes": {
|
||||
@@ -4085,42 +4069,20 @@
|
||||
"@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==",
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||
"integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
|
||||
"deprecated": "Use @eslint/config-array instead",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@humanfs/types": "^0.15.0"
|
||||
"@humanwhocodes/object-schema": "^2.0.3",
|
||||
"debug": "^4.3.1",
|
||||
"minimatch": "^3.0.5"
|
||||
},
|
||||
"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": ">=10.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/module-importer": {
|
||||
@@ -4137,19 +4099,13 @@
|
||||
"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==",
|
||||
"node_modules/@humanwhocodes/object-schema": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
|
||||
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
|
||||
"deprecated": "Use @eslint/object-schema instead",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=18.18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/nzakas"
|
||||
}
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@hutson/parse-repository-url": {
|
||||
"version": "3.0.2",
|
||||
@@ -11349,26 +11305,6 @@
|
||||
"integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
|
||||
"integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "*",
|
||||
"@types/json-schema": "*"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -11575,9 +11511,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jquery": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-4.0.1.tgz",
|
||||
"integrity": "sha512-9a59A/tycXgYuPABcp6/3spSShn0NT2UOM4EfHvMumjYi4lJWTsK5SZWjhx3yRm9IHGCeWXdV2YfNsrWrft/CA==",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-4.0.0.tgz",
|
||||
"integrity": "sha512-Z+to+A2VkaHq1DfI2oSwsoCdhCHMpTSgjWzNcbNlRGYzksDBpPUgEcAL+RQjOBJRaLoEAOHXxqDGBVP+BblBwg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
@@ -11695,9 +11631,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz",
|
||||
"integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==",
|
||||
"version": "25.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
|
||||
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": ">=7.24.0 <7.24.7"
|
||||
@@ -12161,17 +12097,17 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.0.tgz",
|
||||
"integrity": "sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz",
|
||||
"integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@typescript-eslint/scope-manager": "8.61.0",
|
||||
"@typescript-eslint/type-utils": "8.61.0",
|
||||
"@typescript-eslint/utils": "8.61.0",
|
||||
"@typescript-eslint/visitor-keys": "8.61.0",
|
||||
"@typescript-eslint/scope-manager": "8.60.1",
|
||||
"@typescript-eslint/type-utils": "8.60.1",
|
||||
"@typescript-eslint/utils": "8.60.1",
|
||||
"@typescript-eslint/visitor-keys": "8.60.1",
|
||||
"ignore": "^7.0.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
@@ -12184,7 +12120,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.61.0",
|
||||
"@typescript-eslint/parser": "^8.60.1",
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
@@ -12200,16 +12136,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.0.tgz",
|
||||
"integrity": "sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz",
|
||||
"integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.61.0",
|
||||
"@typescript-eslint/types": "8.61.0",
|
||||
"@typescript-eslint/typescript-estree": "8.61.0",
|
||||
"@typescript-eslint/visitor-keys": "8.61.0",
|
||||
"@typescript-eslint/scope-manager": "8.60.1",
|
||||
"@typescript-eslint/types": "8.60.1",
|
||||
"@typescript-eslint/typescript-estree": "8.60.1",
|
||||
"@typescript-eslint/visitor-keys": "8.60.1",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -12225,14 +12161,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.0.tgz",
|
||||
"integrity": "sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz",
|
||||
"integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.61.0",
|
||||
"@typescript-eslint/types": "^8.61.0",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.60.1",
|
||||
"@typescript-eslint/types": "^8.60.1",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -12247,14 +12183,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.0.tgz",
|
||||
"integrity": "sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz",
|
||||
"integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.61.0",
|
||||
"@typescript-eslint/visitor-keys": "8.61.0"
|
||||
"@typescript-eslint/types": "8.60.1",
|
||||
"@typescript-eslint/visitor-keys": "8.60.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -12265,9 +12201,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.0.tgz",
|
||||
"integrity": "sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz",
|
||||
"integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -12282,15 +12218,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.0.tgz",
|
||||
"integrity": "sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz",
|
||||
"integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.61.0",
|
||||
"@typescript-eslint/typescript-estree": "8.61.0",
|
||||
"@typescript-eslint/utils": "8.61.0",
|
||||
"@typescript-eslint/types": "8.60.1",
|
||||
"@typescript-eslint/typescript-estree": "8.60.1",
|
||||
"@typescript-eslint/utils": "8.60.1",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
},
|
||||
@@ -12307,9 +12243,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.0.tgz",
|
||||
"integrity": "sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz",
|
||||
"integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -12321,16 +12257,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.0.tgz",
|
||||
"integrity": "sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz",
|
||||
"integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.61.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.61.0",
|
||||
"@typescript-eslint/types": "8.61.0",
|
||||
"@typescript-eslint/visitor-keys": "8.61.0",
|
||||
"@typescript-eslint/project-service": "8.60.1",
|
||||
"@typescript-eslint/tsconfig-utils": "8.60.1",
|
||||
"@typescript-eslint/types": "8.60.1",
|
||||
"@typescript-eslint/visitor-keys": "8.60.1",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
@@ -12388,16 +12324,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.0.tgz",
|
||||
"integrity": "sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz",
|
||||
"integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.61.0",
|
||||
"@typescript-eslint/types": "8.61.0",
|
||||
"@typescript-eslint/typescript-estree": "8.61.0"
|
||||
"@typescript-eslint/scope-manager": "8.60.1",
|
||||
"@typescript-eslint/types": "8.60.1",
|
||||
"@typescript-eslint/typescript-estree": "8.60.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -12412,13 +12348,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.61.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.0.tgz",
|
||||
"integrity": "sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==",
|
||||
"version": "8.60.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz",
|
||||
"integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.61.0",
|
||||
"@typescript-eslint/types": "8.60.1",
|
||||
"eslint-visitor-keys": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -14468,9 +14404,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.10.34",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.34.tgz",
|
||||
"integrity": "sha512-IMDedajPifLnHNY0X9n8hKxRTQ6/eTHwr5bDo04WnuqxyKw6LYtQywCuuqPZwhl3aBXMvQpJov42GLCwRRdQzw==",
|
||||
"version": "2.10.33",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.33.tgz",
|
||||
"integrity": "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
@@ -18920,73 +18856,71 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "10.4.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.1.tgz",
|
||||
"integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==",
|
||||
"version": "8.57.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
|
||||
"integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
|
||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@eslint/config-array": "^0.23.5",
|
||||
"@eslint/config-helpers": "^0.6.0",
|
||||
"@eslint/core": "^1.2.1",
|
||||
"@eslint/plugin-kit": "^0.7.2",
|
||||
"@humanfs/node": "^0.16.6",
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
"@eslint/eslintrc": "^2.1.4",
|
||||
"@eslint/js": "8.57.1",
|
||||
"@humanwhocodes/config-array": "^0.13.0",
|
||||
"@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",
|
||||
"@nodelib/fs.walk": "^1.2.8",
|
||||
"@ungap/structured-clone": "^1.2.0",
|
||||
"ajv": "^6.12.4",
|
||||
"chalk": "^4.0.0",
|
||||
"cross-spawn": "^7.0.2",
|
||||
"debug": "^4.3.2",
|
||||
"doctrine": "^3.0.0",
|
||||
"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",
|
||||
"eslint-scope": "^7.2.2",
|
||||
"eslint-visitor-keys": "^3.4.3",
|
||||
"espree": "^9.6.1",
|
||||
"esquery": "^1.4.2",
|
||||
"esutils": "^2.0.2",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"file-entry-cache": "^8.0.0",
|
||||
"file-entry-cache": "^6.0.1",
|
||||
"find-up": "^5.0.0",
|
||||
"glob-parent": "^6.0.2",
|
||||
"globals": "^13.19.0",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.2.0",
|
||||
"imurmurhash": "^0.1.4",
|
||||
"is-glob": "^4.0.0",
|
||||
"is-path-inside": "^3.0.3",
|
||||
"js-yaml": "^4.1.0",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"minimatch": "^10.2.4",
|
||||
"levn": "^0.4.1",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"minimatch": "^3.1.2",
|
||||
"natural-compare": "^1.4.0",
|
||||
"optionator": "^0.9.3"
|
||||
"optionator": "^0.9.3",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"text-table": "^0.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"eslint": "bin/eslint.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://eslint.org/donate"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"jiti": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"jiti": {
|
||||
"optional": true
|
||||
}
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-prettier": {
|
||||
"version": "10.1.8",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
|
||||
"integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-7.2.0.tgz",
|
||||
"integrity": "sha512-rV4Qu0C3nfJKPOAhFujFxB7RMP+URFyQqqOZW9DMRD7ZDTFyjaIlETU3xzHELt++4ugC0+Jm084HQYkkJe+Ivg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/eslint-config-prettier"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=7.0.0"
|
||||
}
|
||||
@@ -19264,9 +19198,9 @@
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-lodash/-/eslint-plugin-lodash-7.4.0.tgz",
|
||||
"integrity": "sha512-Tl83UwVXqe1OVeBRKUeWcfg6/pCW1GTRObbdnbEJgYwjxp5Q92MEWQaH9+dmzbRt6kvYU1Mp893E79nJiCSM8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -19276,7 +19210,7 @@
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=9.0.0"
|
||||
"eslint": ">=2"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-no-only-tests": {
|
||||
@@ -19328,9 +19262,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/eslint-plugin-react-you-might-not-need-an-effect": {
|
||||
"version": "1.0.0",
|
||||
"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.0.tgz",
|
||||
"integrity": "sha512-8exakZ5dCJdZb7TA3P8vD47HlnM3IllA9sjKzU22wyLEx0PZBDjFPxT5+5Rb10tSE6uWmwoBsgClIDNuJ8UW6g==",
|
||||
"version": "0.10.4",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react-you-might-not-need-an-effect/-/eslint-plugin-react-you-might-not-need-an-effect-0.10.4.tgz",
|
||||
"integrity": "sha512-T6UFIOl2yWzVJ7LRk27z6EbJm2pfO4+VCTp2TBRsmAUREkDFUXjtWxoD9NsDcg6NmMFETZLbAD1XzV/w/GOmqw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -19442,56 +19376,38 @@
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/balanced-match": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||
"node_modules/eslint/node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/brace-expansion": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"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==",
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
|
||||
"integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
|
||||
"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"
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"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==",
|
||||
"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": "^20.19.0 || ^22.13.0 || >=24"
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/eslint"
|
||||
@@ -19510,6 +19426,29 @@
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint/node_modules/js-yaml": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
|
||||
"integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/puzrin"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/nodeca"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -19517,35 +19456,19 @@
|
||||
"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==",
|
||||
"version": "9.6.1",
|
||||
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
|
||||
"integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"acorn": "^8.16.0",
|
||||
"acorn": "^8.9.0",
|
||||
"acorn-jsx": "^5.3.2",
|
||||
"eslint-visitor-keys": "^5.0.1"
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || ^22.13.0 || >=24"
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/eslint"
|
||||
@@ -19565,13 +19488,13 @@
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"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": "^20.19.0 || ^22.13.0 || >=24"
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/eslint"
|
||||
@@ -19591,9 +19514,9 @@
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
|
||||
"integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
@@ -20119,30 +20042,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/file-entry-cache": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
||||
"integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
|
||||
"integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"flat-cache": "^4.0.0"
|
||||
"flat-cache": "^3.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/file-entry-cache/node_modules/flat-cache": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
|
||||
"integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"flatted": "^3.2.9",
|
||||
"keyv": "^4.5.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": "^10.12.0 || >=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/file-saver": {
|
||||
@@ -21915,6 +21824,13 @@
|
||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/graphemer": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
|
||||
"integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/h3-js": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/h3-js/-/h3-js-4.2.1.tgz",
|
||||
@@ -23794,6 +23710,16 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-path-inside": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
|
||||
"integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-plain-obj": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
|
||||
@@ -35398,9 +35324,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-arborist": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/react-arborist/-/react-arborist-3.10.1.tgz",
|
||||
"integrity": "sha512-P9WJuj2zvtNGVzaNqTKIIBeS5aDMfvXdYT9KDVOZaALHwcfGJQON+X+gbMscTdAb5frdXDHBWl677HX3XEV1PA==",
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/react-arborist/-/react-arborist-3.8.0.tgz",
|
||||
"integrity": "sha512-66UQK2mWtodjkHg4efiIYjQt0VlFhQ4LXTphcqHi0+1Jc7hAxcxAC1SbmCUCpZYUW7C+14WgsnYgBRqG0AYb1A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"react-dnd": "^14.0.3",
|
||||
@@ -44494,9 +44420,9 @@
|
||||
"@types/d3-scale": "^2.1.1",
|
||||
"@types/d3-time": "^3.0.4",
|
||||
"@types/d3-time-format": "^4.0.3",
|
||||
"@types/jquery": "^4.0.1",
|
||||
"@types/jquery": "^4.0.0",
|
||||
"@types/lodash": "^4.17.24",
|
||||
"@types/node": "^25.9.2",
|
||||
"@types/node": "^25.9.1",
|
||||
"@types/prop-types": "^15.7.15",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@types/react-table": "^7.7.20",
|
||||
@@ -44563,16 +44489,6 @@
|
||||
"react-dom": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"packages/superset-ui-core/node_modules/@types/node": {
|
||||
"version": "25.9.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz",
|
||||
"integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": ">=7.24.0 <7.24.7"
|
||||
}
|
||||
},
|
||||
"packages/superset-ui-core/node_modules/d3-format": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
"query-string": "9.4.0",
|
||||
"re-resizable": "^6.11.2",
|
||||
"react": "^18.2.0",
|
||||
"react-arborist": "^3.10.1",
|
||||
"react-arborist": "^3.8.0",
|
||||
"react-checkbox-tree": "^1.8.0",
|
||||
"react-diff-viewer-continued": "^4.2.2",
|
||||
"react-dnd": "^11.1.3",
|
||||
@@ -280,11 +280,11 @@
|
||||
"@types/content-disposition": "^0.5.9",
|
||||
"@types/dom-to-image": "^2.6.7",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/jquery": "^4.0.1",
|
||||
"@types/jquery": "^4.0.0",
|
||||
"@types/js-levenshtein": "^1.1.3",
|
||||
"@types/json-bigint": "^1.0.4",
|
||||
"@types/mousetrap": "^1.6.15",
|
||||
"@types/node": "^25.9.2",
|
||||
"@types/node": "^25.9.1",
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.0",
|
||||
"@types/react-loadable": "^5.5.11",
|
||||
@@ -297,22 +297,22 @@
|
||||
"@types/rison": "0.1.0",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"@types/unzipper": "^0.10.11",
|
||||
"@typescript-eslint/eslint-plugin": "^8.61.0",
|
||||
"@typescript-eslint/parser": "^8.61.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.60.1",
|
||||
"@typescript-eslint/parser": "^8.59.4",
|
||||
"babel-jest": "^30.4.1",
|
||||
"babel-loader": "^10.1.1",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||
"babel-plugin-lodash": "^3.3.4",
|
||||
"baseline-browser-mapping": "^2.10.34",
|
||||
"baseline-browser-mapping": "^2.10.33",
|
||||
"cheerio": "1.2.0",
|
||||
"concurrently": "^10.0.3",
|
||||
"copy-webpack-plugin": "^14.0.0",
|
||||
"cross-env": "^10.1.0",
|
||||
"css-loader": "^7.1.4",
|
||||
"css-minimizer-webpack-plugin": "^8.0.0",
|
||||
"eslint": "^10.4.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^7.2.0",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-import-resolver-typescript": "^4.4.5",
|
||||
"eslint-plugin-cypress": "^3.6.0",
|
||||
@@ -320,11 +320,11 @@
|
||||
"eslint-plugin-icons": "file:eslint-rules/eslint-plugin-icons",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-jest-dom": "^5.5.0",
|
||||
"eslint-plugin-lodash": "^8.0.0",
|
||||
"eslint-plugin-lodash": "^7.4.0",
|
||||
"eslint-plugin-no-only-tests": "^3.4.0",
|
||||
"eslint-plugin-prettier": "^5.5.6",
|
||||
"eslint-plugin-react-prefer-function-component": "^5.0.0",
|
||||
"eslint-plugin-react-you-might-not-need-an-effect": "^1.0.0",
|
||||
"eslint-plugin-react-you-might-not-need-an-effect": "^0.10.4",
|
||||
"eslint-plugin-storybook": "10.4.2",
|
||||
"eslint-plugin-testing-library": "^7.16.2",
|
||||
"eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
|
||||
@@ -414,16 +414,7 @@
|
||||
"@jest/types": "^30.4.0",
|
||||
"jest-util": "^30.4.0",
|
||||
"jest-circus": "^30.4.0",
|
||||
"jest-environment-node": "^30.4.0",
|
||||
"@babel/eslint-parser": {
|
||||
"eslint": "$eslint"
|
||||
},
|
||||
"eslint-plugin-import": {
|
||||
"eslint": "$eslint"
|
||||
},
|
||||
"eslint-plugin-jest-dom": {
|
||||
"eslint": "$eslint"
|
||||
}
|
||||
"jest-environment-node": "^30.4.0"
|
||||
},
|
||||
"readme": "ERROR: No README data found!",
|
||||
"scarfSettings": {
|
||||
|
||||
@@ -75,9 +75,9 @@
|
||||
"@types/d3-scale": "^2.1.1",
|
||||
"@types/d3-time": "^3.0.4",
|
||||
"@types/d3-time-format": "^4.0.3",
|
||||
"@types/jquery": "^4.0.1",
|
||||
"@types/jquery": "^4.0.0",
|
||||
"@types/lodash": "^4.17.24",
|
||||
"@types/node": "^25.9.2",
|
||||
"@types/node": "^25.9.1",
|
||||
"@types/prop-types": "^15.7.15",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@types/react-table": "^7.7.20",
|
||||
|
||||
@@ -134,11 +134,9 @@ async function runOxlintAndProcess() {
|
||||
console.log('Running minimal ESLint for custom rules...');
|
||||
let eslintOutput = '[]';
|
||||
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.
|
||||
// Run ESLint and capture output directly
|
||||
eslintOutput = execSync(
|
||||
'npx eslint --config eslint.config.minimal.js --no-inline-config --format json src',
|
||||
'npx eslint --no-eslintrc --config .eslintrc.minimal.js --no-inline-config --format json src',
|
||||
{
|
||||
encoding: 'utf8',
|
||||
maxBuffer: 50 * 1024 * 1024,
|
||||
|
||||
@@ -425,7 +425,6 @@ const ResultSet = ({
|
||||
url: makeUrl('/api/v1/sqllab/export_streaming/'),
|
||||
payload: { client_id: query.id },
|
||||
exportType: 'csv',
|
||||
exportSource: 'sqllab',
|
||||
expectedRows: rows,
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -1,113 +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.
|
||||
*/
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { JsonObject, QueryFormData, VizType } from '@superset-ui/core';
|
||||
import { getChartDataRequest } from 'src/components/Chart/chartAction';
|
||||
|
||||
/**
|
||||
* Integration (mocked-network) port of the deprecated Cypress spec
|
||||
* `cypress/e2e/dashboard/_skip.url_params.test.ts` (sc-107448).
|
||||
*
|
||||
* The original test loaded a dashboard with query-string params, intercepted
|
||||
* `/api/v1/chart/data`, and asserted each query in the request body carried
|
||||
* `url_params`. That assertion is request-construction logic — the form_data
|
||||
* → query-context pipeline — which is exercised here without a backend.
|
||||
*
|
||||
* Intentional narrowing: the URL-string → `form_data.url_params` hop (handled
|
||||
* in `src/dashboard/actions/hydrate.ts` via `extractUrlParams`) is not covered
|
||||
* here. This file verifies the chart-data side of the contract only; the
|
||||
* dashboard hydration side is covered by its own unit tests.
|
||||
*/
|
||||
const CHART_DATA_GLOB = 'glob:*/api/v1/chart/data*';
|
||||
const CHART_DATA_ROUTE = 'urlParamsForwarding-chartData';
|
||||
const URL_PARAMS = { param1: '123', param2: 'abc' };
|
||||
|
||||
type ChartDataRequestBody = {
|
||||
queries: JsonObject[];
|
||||
form_data: JsonObject;
|
||||
};
|
||||
|
||||
const buildFormData = (
|
||||
overrides: Partial<QueryFormData> = {},
|
||||
): QueryFormData => ({
|
||||
datasource: '1__table',
|
||||
granularity_sqla: 'ds',
|
||||
viz_type: VizType.Table,
|
||||
url_params: URL_PARAMS,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const lastChartDataBody = (): ChartDataRequestBody => {
|
||||
const calls = fetchMock.callHistory.calls(CHART_DATA_ROUTE);
|
||||
expect(calls.length).toBeGreaterThan(0);
|
||||
return JSON.parse(
|
||||
calls[calls.length - 1].options.body as string,
|
||||
) as ChartDataRequestBody;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock.post(
|
||||
CHART_DATA_GLOB,
|
||||
{ result: [{ data: [] }] },
|
||||
{
|
||||
name: CHART_DATA_ROUTE,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// Remove only this file's route so global routes registered in
|
||||
// setupSupersetClient (e.g. CSRF) survive into the next test.
|
||||
afterEach(() => {
|
||||
fetchMock.clearHistory();
|
||||
fetchMock.removeRoutes({ names: [CHART_DATA_ROUTE] });
|
||||
});
|
||||
|
||||
test('forwards url_params from form_data onto each query in the chart-data request body', async () => {
|
||||
await getChartDataRequest({ formData: buildFormData() });
|
||||
|
||||
const body = lastChartDataBody();
|
||||
expect(Array.isArray(body.queries)).toBe(true);
|
||||
expect(body.queries.length).toBeGreaterThan(0);
|
||||
body.queries.forEach(query => {
|
||||
expect(query.url_params).toEqual(URL_PARAMS);
|
||||
});
|
||||
});
|
||||
|
||||
test('preserves url_params on form_data echoed back in the chart-data request body', async () => {
|
||||
await getChartDataRequest({ formData: buildFormData() });
|
||||
|
||||
const body = lastChartDataBody();
|
||||
expect(body.form_data.url_params).toEqual(URL_PARAMS);
|
||||
});
|
||||
|
||||
// buildQueryObject defaults missing url_params to `{}` (see
|
||||
// packages/superset-ui-core/src/query/buildQueryObject.ts), so the chart-data
|
||||
// request body carries an empty object — not `undefined`. This test documents
|
||||
// that contract; a future change that flips the default should update both.
|
||||
test('emits an empty url_params object on each query when form_data has none', async () => {
|
||||
await getChartDataRequest({
|
||||
formData: buildFormData({ url_params: undefined }),
|
||||
});
|
||||
|
||||
const body = lastChartDataBody();
|
||||
expect(body.queries.length).toBeGreaterThan(0);
|
||||
body.queries.forEach(query => {
|
||||
expect(query.url_params).toEqual({});
|
||||
});
|
||||
});
|
||||
@@ -48,11 +48,11 @@ global.URL.revokeObjectURL = jest.fn();
|
||||
|
||||
global.fetch = jest.fn();
|
||||
|
||||
const { SupersetClient } = jest.requireMock('@superset-ui/core');
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
global.fetch = jest.fn();
|
||||
const { SupersetClient } = jest.requireMock('@superset-ui/core');
|
||||
SupersetClient.getCSRFToken.mockResolvedValue('mock-csrf-token');
|
||||
SupersetClient.getGuestToken.mockReturnValue(undefined);
|
||||
});
|
||||
|
||||
@@ -228,7 +228,6 @@ test('sets ERROR status and calls onError when fetch rejects', async () => {
|
||||
// URL prefix guard tests - prevent regression of missing app root prefix
|
||||
const { applicationRoot } = jest.requireMock('src/utils/getBootstrapData');
|
||||
const { makeUrl } = jest.requireMock('src/utils/pathUtils');
|
||||
const { SupersetClient } = jest.requireMock('@superset-ui/core');
|
||||
|
||||
const createPrefixTestMockFetch = () =>
|
||||
jest.fn().mockResolvedValue({
|
||||
@@ -243,107 +242,6 @@ const createPrefixTestMockFetch = () =>
|
||||
},
|
||||
});
|
||||
|
||||
test('guest-token chart exports skip CSRF fetch and include guest_token form field', async () => {
|
||||
applicationRoot.mockReturnValue('');
|
||||
SupersetClient.getGuestToken.mockReturnValue('guest-token');
|
||||
SupersetClient.getCSRFToken.mockRejectedValue(new Error('CSRF forbidden'));
|
||||
|
||||
const csvData = new TextEncoder().encode('id,name\n1,Alice\n');
|
||||
let readCount = 0;
|
||||
const mockFetch = jest.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
headers: new Headers({
|
||||
'Content-Disposition': 'attachment; filename="embedded.csv"',
|
||||
}),
|
||||
body: {
|
||||
getReader: () => ({
|
||||
read: jest.fn().mockImplementation(() => {
|
||||
readCount += 1;
|
||||
if (readCount === 1) {
|
||||
return Promise.resolve({ done: false, value: csvData });
|
||||
}
|
||||
return Promise.resolve({ done: true, value: undefined });
|
||||
}),
|
||||
}),
|
||||
},
|
||||
});
|
||||
global.fetch = mockFetch;
|
||||
|
||||
const { result } = renderHook(() => useStreamingExport());
|
||||
|
||||
act(() => {
|
||||
result.current.startExport({
|
||||
url: '/api/v1/chart/data',
|
||||
payload: { datasource: '1__table', viz_type: 'table' },
|
||||
exportType: 'csv',
|
||||
exportSource: 'chart',
|
||||
expectedRows: 100000,
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.progress.status).toBe(ExportStatus.COMPLETED);
|
||||
});
|
||||
|
||||
expect(SupersetClient.getCSRFToken).not.toHaveBeenCalled();
|
||||
expect(mockFetch).toHaveBeenCalledTimes(1);
|
||||
const [, requestInit] = mockFetch.mock.calls[0];
|
||||
const body = requestInit.body as URLSearchParams;
|
||||
|
||||
expect(body.get('guest_token')).toBe('guest-token');
|
||||
expect(body.get('expected_rows')).toBe('100000');
|
||||
expect(body.get('form_data')).toBe(
|
||||
JSON.stringify({ datasource: '1__table', viz_type: 'table' }),
|
||||
);
|
||||
});
|
||||
|
||||
test('non-guest chart exports fetch CSRF and include X-CSRFToken header', async () => {
|
||||
applicationRoot.mockReturnValue('');
|
||||
|
||||
const csvData = new TextEncoder().encode('id,name\n1,Alice\n');
|
||||
let readCount = 0;
|
||||
const mockFetch = jest.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
headers: new Headers({
|
||||
'Content-Disposition': 'attachment; filename="chart.csv"',
|
||||
}),
|
||||
body: {
|
||||
getReader: () => ({
|
||||
read: jest.fn().mockImplementation(() => {
|
||||
readCount += 1;
|
||||
if (readCount === 1) {
|
||||
return Promise.resolve({ done: false, value: csvData });
|
||||
}
|
||||
return Promise.resolve({ done: true, value: undefined });
|
||||
}),
|
||||
}),
|
||||
},
|
||||
});
|
||||
global.fetch = mockFetch;
|
||||
|
||||
const { result } = renderHook(() => useStreamingExport());
|
||||
|
||||
act(() => {
|
||||
result.current.startExport({
|
||||
url: '/api/v1/chart/data',
|
||||
payload: { datasource: '1__table', viz_type: 'table' },
|
||||
exportType: 'csv',
|
||||
exportSource: 'chart',
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.progress.status).toBe(ExportStatus.COMPLETED);
|
||||
});
|
||||
|
||||
expect(SupersetClient.getCSRFToken).toHaveBeenCalledTimes(1);
|
||||
const [, requestInit] = mockFetch.mock.calls[0];
|
||||
expect(requestInit.headers).toMatchObject({
|
||||
'X-CSRFToken': 'mock-csrf-token',
|
||||
});
|
||||
expect((requestInit.body as URLSearchParams).has('guest_token')).toBe(false);
|
||||
});
|
||||
|
||||
test('chart streaming export includes guest token in form body when configured', async () => {
|
||||
SupersetClient.getGuestToken.mockReturnValue('guest-token');
|
||||
const mockFetch = createPrefixTestMockFetch();
|
||||
@@ -356,7 +254,6 @@ test('chart streaming export includes guest token in form body when configured',
|
||||
url: '/api/v1/chart/data',
|
||||
payload: { datasource: '1__table', viz_type: 'table' },
|
||||
exportType: 'csv',
|
||||
exportSource: 'chart',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -371,70 +268,6 @@ test('chart streaming export includes guest token in form body when configured',
|
||||
);
|
||||
});
|
||||
|
||||
test('SQL Lab exports fetch CSRF and omit guest_token even when guest token exists', async () => {
|
||||
applicationRoot.mockReturnValue('');
|
||||
SupersetClient.getGuestToken.mockReturnValue('guest-token');
|
||||
|
||||
const mockFetch = createPrefixTestMockFetch();
|
||||
global.fetch = mockFetch;
|
||||
|
||||
const { result } = renderHook(() => useStreamingExport());
|
||||
|
||||
act(() => {
|
||||
result.current.startExport({
|
||||
url: '/api/v1/sqllab/export_streaming/',
|
||||
payload: { client_id: 'test-id' },
|
||||
exportType: 'csv',
|
||||
exportSource: 'sqllab',
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockFetch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
expect(SupersetClient.getCSRFToken).toHaveBeenCalledTimes(1);
|
||||
const [, requestInit] = mockFetch.mock.calls[0];
|
||||
const body = requestInit.body as URLSearchParams;
|
||||
|
||||
expect(requestInit.headers).toMatchObject({
|
||||
'X-CSRFToken': 'mock-csrf-token',
|
||||
});
|
||||
expect(body.get('client_id')).toBe('test-id');
|
||||
expect(body.has('guest_token')).toBe(false);
|
||||
});
|
||||
|
||||
test('guest tokens do not bypass CSRF for unclassified non-client exports', async () => {
|
||||
applicationRoot.mockReturnValue('');
|
||||
SupersetClient.getGuestToken.mockReturnValue('guest-token');
|
||||
|
||||
const mockFetch = createPrefixTestMockFetch();
|
||||
global.fetch = mockFetch;
|
||||
|
||||
const { result } = renderHook(() => useStreamingExport());
|
||||
|
||||
act(() => {
|
||||
result.current.startExport({
|
||||
url: '/api/v1/other/export_streaming/',
|
||||
payload: { export_id: 'test-id' },
|
||||
exportType: 'csv',
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockFetch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
expect(SupersetClient.getCSRFToken).toHaveBeenCalledTimes(1);
|
||||
const [, requestInit] = mockFetch.mock.calls[0];
|
||||
const body = requestInit.body as URLSearchParams;
|
||||
|
||||
expect(requestInit.headers).toMatchObject({
|
||||
'X-CSRFToken': 'mock-csrf-token',
|
||||
});
|
||||
expect(body.has('guest_token')).toBe(false);
|
||||
});
|
||||
|
||||
test('URL prefix guard applies prefix to unprefixed relative URL when app root is configured', async () => {
|
||||
const appRoot = '/superset';
|
||||
applicationRoot.mockReturnValue(appRoot);
|
||||
|
||||
@@ -31,8 +31,6 @@ interface StreamingExportPayload {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
type StreamingExportSource = 'chart' | 'sqllab';
|
||||
|
||||
interface StreamingExportParams {
|
||||
/**
|
||||
* The API endpoint URL for the export request.
|
||||
@@ -48,7 +46,6 @@ interface StreamingExportParams {
|
||||
payload: StreamingExportPayload;
|
||||
filename?: string;
|
||||
exportType: 'csv' | 'xlsx';
|
||||
exportSource?: StreamingExportSource;
|
||||
expectedRows?: number;
|
||||
}
|
||||
|
||||
@@ -98,7 +95,6 @@ const createFetchRequest = async (
|
||||
payload: StreamingExportPayload,
|
||||
filename: string | undefined,
|
||||
_exportType: string,
|
||||
exportSource: StreamingExportSource | undefined,
|
||||
expectedRows: number | undefined,
|
||||
signal: AbortSignal,
|
||||
): Promise<RequestInit> => {
|
||||
@@ -106,19 +102,10 @@ const createFetchRequest = async (
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
};
|
||||
|
||||
const guestToken = SupersetClient.getGuestToken();
|
||||
const isGuestTokenChartExport =
|
||||
Boolean(guestToken) &&
|
||||
exportSource === 'chart' &&
|
||||
!('client_id' in payload);
|
||||
|
||||
// Embedded guest sessions cannot fetch CSRF tokens. Guest chart exports are
|
||||
// safe because chart data is CSRF-exempt and auth is carried by guest_token.
|
||||
if (!isGuestTokenChartExport) {
|
||||
const csrfToken = await SupersetClient.getCSRFToken();
|
||||
if (csrfToken) {
|
||||
headers['X-CSRFToken'] = csrfToken;
|
||||
}
|
||||
// Get CSRF token using SupersetClient
|
||||
const csrfToken = await SupersetClient.getCSRFToken();
|
||||
if (csrfToken) {
|
||||
headers['X-CSRFToken'] = csrfToken;
|
||||
}
|
||||
|
||||
const formParams: Record<string, string> = {};
|
||||
@@ -131,7 +118,8 @@ const createFetchRequest = async (
|
||||
formParams.expected_rows = expectedRows.toString();
|
||||
}
|
||||
|
||||
if (guestToken && isGuestTokenChartExport) {
|
||||
const guestToken = SupersetClient.getGuestToken();
|
||||
if (guestToken) {
|
||||
formParams.guest_token = guestToken;
|
||||
}
|
||||
|
||||
@@ -197,8 +185,7 @@ export const useStreamingExport = (options: UseStreamingExportOptions = {}) => {
|
||||
|
||||
const executeExport = useCallback(
|
||||
async (params: StreamingExportParams) => {
|
||||
const { url, payload, filename, exportType, exportSource, expectedRows } =
|
||||
params;
|
||||
const { url, payload, filename, exportType, expectedRows } = params;
|
||||
if (isExportingRef.current) {
|
||||
return;
|
||||
}
|
||||
@@ -223,7 +210,6 @@ export const useStreamingExport = (options: UseStreamingExportOptions = {}) => {
|
||||
payload,
|
||||
filename,
|
||||
exportType,
|
||||
exportSource,
|
||||
expectedRows,
|
||||
abortControllerRef.current.signal,
|
||||
);
|
||||
|
||||
@@ -165,62 +165,6 @@ describe('DashboardBuilder', () => {
|
||||
expect(header).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('should hide DashboardHeader when standalone mode hides nav and title (?standalone=2)', () => {
|
||||
// React-level equivalent of the legacy `cy.get('#app-menu').should('not.exist')`
|
||||
// Cypress assertion. The `#app-menu` node lives in Flask's spa.html template,
|
||||
// gated by `{% if standalone_mode %}`, so RTL cannot reach it directly.
|
||||
// `?standalone=2` maps to DashboardStandaloneMode.HideNavAndTitle, which the
|
||||
// DashboardBuilder honours by suppressing the React-side DashboardHeader.
|
||||
const originalHref = window.location.href;
|
||||
window.history.replaceState({}, '', '/?standalone=2');
|
||||
try {
|
||||
const { queryByTestId } = setup();
|
||||
expect(
|
||||
queryByTestId('dashboard-header-container'),
|
||||
).not.toBeInTheDocument();
|
||||
} finally {
|
||||
window.history.replaceState({}, '', originalHref);
|
||||
}
|
||||
});
|
||||
|
||||
test('should keep the DashboardHeader when standalone mode only hides nav (?standalone=1)', () => {
|
||||
// `?standalone=1` maps to DashboardStandaloneMode.HideNav, which only hides the
|
||||
// Flask-rendered global app menu (#app-menu) — it must NOT suppress the React-side
|
||||
// DashboardHeader. This pins the boundary against HideNavAndTitle (?standalone=2).
|
||||
const originalHref = window.location.href;
|
||||
window.history.replaceState({}, '', '/?standalone=1');
|
||||
try {
|
||||
const { queryByTestId } = setup();
|
||||
expect(queryByTestId('dashboard-header-container')).toBeInTheDocument();
|
||||
} finally {
|
||||
window.history.replaceState({}, '', originalHref);
|
||||
}
|
||||
});
|
||||
|
||||
test('should keep the header hidden in standalone mode (?standalone=2) while editMode is active', () => {
|
||||
// Orthogonality analogue of the legacy `?edit=true&standalone=true` Cypress mount.
|
||||
// editMode is sourced from Redux (state.dashboardState.editMode), not the URL —
|
||||
// DashboardBuilder only reads URL_PARAMS.standalone — so the legacy `edit=true`
|
||||
// param is inert here and is intentionally omitted. Contract under test:
|
||||
// standalone=2 (HideNavAndTitle) suppresses DashboardHeader even while editMode
|
||||
// drives the `dashboard--editing` class on the wrapper.
|
||||
const originalHref = window.location.href;
|
||||
window.history.replaceState({}, '', '/?standalone=2');
|
||||
try {
|
||||
const { getByTestId, queryByTestId } = setup({
|
||||
dashboardState: { ...mockState.dashboardState, editMode: true },
|
||||
});
|
||||
expect(getByTestId('dashboard-content-wrapper')).toHaveClass(
|
||||
'dashboard dashboard--editing',
|
||||
);
|
||||
expect(
|
||||
queryByTestId('dashboard-header-container'),
|
||||
).not.toBeInTheDocument();
|
||||
} finally {
|
||||
window.history.replaceState({}, '', originalHref);
|
||||
}
|
||||
});
|
||||
|
||||
test('should render a Sticky top-level Tabs if the dashboard has tabs', async () => {
|
||||
const { findAllByTestId } = setup({
|
||||
dashboardLayout: undoableDashboardLayoutWithTabs,
|
||||
|
||||
@@ -99,7 +99,6 @@ interface ExportChartParams {
|
||||
url: string | null;
|
||||
payload: QueryFormData | ReturnType<typeof buildQueryContext>;
|
||||
exportType: string;
|
||||
exportSource: 'chart';
|
||||
}) => void)
|
||||
| null;
|
||||
}
|
||||
@@ -395,7 +394,6 @@ export const exportChart = async ({
|
||||
url: url ? ensureAppRoot(url) : url,
|
||||
payload,
|
||||
exportType: resultFormat,
|
||||
exportSource: 'chart',
|
||||
});
|
||||
} else {
|
||||
// SupersetClient.postForm calls getUrl({ endpoint }) internally, which prepends
|
||||
|
||||
@@ -168,31 +168,6 @@ test('non-text chart shows screenshot width and message content', () => {
|
||||
expect(screen.getByText('Screenshot width')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('screenshot width input preserves a typed zero instead of dropping it', () => {
|
||||
const lineChartProps = {
|
||||
...defaultProps,
|
||||
dashboardId: undefined,
|
||||
chart: { id: 1, sliceFormData: { viz_type: VizType.Line } },
|
||||
chartName: 'My Line Chart',
|
||||
creationMethod: 'charts' as const,
|
||||
};
|
||||
render(<ReportModal {...lineChartProps} />, { useRedux: true });
|
||||
|
||||
const widthInput = screen.getByPlaceholderText(
|
||||
'Input custom width in pixels',
|
||||
);
|
||||
|
||||
// The old `|| null` / `|| ''` logic silently coerced a typed 0 to null, so the
|
||||
// invalid width was swallowed instead of being submitted and surfaced by the
|
||||
// server's min-width validation. The field must preserve the literal value.
|
||||
userEvent.type(widthInput, '0');
|
||||
expect(widthInput).toHaveDisplayValue('0');
|
||||
|
||||
// Clearing the field still yields an empty value (parsed NaN → null).
|
||||
userEvent.clear(widthInput);
|
||||
expect(widthInput).toHaveDisplayValue('');
|
||||
});
|
||||
|
||||
test('dashboard report hides message content section', () => {
|
||||
const dashboardProps = {
|
||||
...defaultProps,
|
||||
|
||||
@@ -296,12 +296,11 @@ function ReportModal({
|
||||
<Input
|
||||
type="number"
|
||||
name="custom_width"
|
||||
value={currentReport?.custom_width ?? ''}
|
||||
value={currentReport?.custom_width || ''}
|
||||
placeholder={t('Input custom width in pixels')}
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
const parsedWidth = parseInt(event.target.value, 10);
|
||||
setCurrentReport({
|
||||
custom_width: Number.isNaN(parsedWidth) ? null : parsedWidth,
|
||||
custom_width: parseInt(event.target.value, 10) || null,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
14
superset-websocket/package-lock.json
generated
14
superset-websocket/package-lock.json
generated
@@ -23,7 +23,7 @@
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/lodash": "^4.17.24",
|
||||
"@types/node": "^25.9.2",
|
||||
"@types/node": "^25.9.1",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.60.1",
|
||||
"@typescript-eslint/parser": "^8.60.1",
|
||||
@@ -1798,9 +1798,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz",
|
||||
"integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==",
|
||||
"version": "25.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
|
||||
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -7883,9 +7883,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "25.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz",
|
||||
"integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==",
|
||||
"version": "25.9.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz",
|
||||
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"undici-types": ">=7.24.0 <7.24.7"
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/jsonwebtoken": "^9.0.10",
|
||||
"@types/lodash": "^4.17.24",
|
||||
"@types/node": "^25.9.2",
|
||||
"@types/node": "^25.9.1",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.60.1",
|
||||
"@typescript-eslint/parser": "^8.60.1",
|
||||
|
||||
@@ -25,37 +25,21 @@ import io
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import Any, Callable
|
||||
from typing import Any
|
||||
|
||||
# Must redirect click output BEFORE importing anything that uses it
|
||||
import click
|
||||
|
||||
# Monkey-patch click to redirect output to stderr in stdio mode
|
||||
if os.environ.get("FASTMCP_TRANSPORT", "stdio") == "stdio":
|
||||
original_echo = click.echo
|
||||
original_secho = click.secho
|
||||
|
||||
def redirect_to_stderr(
|
||||
original_func: Callable[..., None],
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
if len(args) >= 2:
|
||||
args = (args[0], sys.stderr, *args[2:])
|
||||
kwargs.pop("file", None)
|
||||
else:
|
||||
kwargs["file"] = sys.stderr
|
||||
def secho_to_stderr(*args: Any, **kwargs: Any) -> Any:
|
||||
kwargs["file"] = sys.stderr
|
||||
return original_secho(*args, **kwargs)
|
||||
|
||||
original_func(*args, **kwargs)
|
||||
|
||||
def echo_to_stderr(*args: Any, **kwargs: Any) -> None:
|
||||
redirect_to_stderr(original_echo, *args, **kwargs)
|
||||
|
||||
def secho_to_stderr(*args: Any, **kwargs: Any) -> None:
|
||||
redirect_to_stderr(original_secho, *args, **kwargs)
|
||||
|
||||
click.echo = echo_to_stderr
|
||||
click.secho = secho_to_stderr
|
||||
click.echo = lambda *args, **kwargs: click.echo(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
from superset.mcp_service.app import init_fastmcp_server, mcp
|
||||
from superset.mcp_service.middleware import create_response_size_guard_middleware
|
||||
|
||||
@@ -21,7 +21,6 @@ from sqlalchemy import or_
|
||||
from sqlalchemy.orm.query import Query
|
||||
|
||||
from superset import db, security_manager
|
||||
from superset.daos.base import _escape_like
|
||||
from superset.reports.models import ReportSchedule
|
||||
from superset.views.base import BaseFilter
|
||||
|
||||
@@ -48,13 +47,11 @@ class ReportScheduleAllTextFilter(BaseFilter): # pylint: disable=too-few-public
|
||||
def apply(self, query: Query, value: Any) -> Query:
|
||||
if not value:
|
||||
return query
|
||||
# ``value`` may arrive as a non-string (e.g. an int in the API ``filters``
|
||||
# array); coerce it so escaping never raises on ``.replace``.
|
||||
ilike_value = f"%{_escape_like(str(value))}%"
|
||||
ilike_value = f"%{value}%"
|
||||
return query.filter(
|
||||
or_(
|
||||
ReportSchedule.name.ilike(ilike_value, escape="\\"),
|
||||
ReportSchedule.description.ilike(ilike_value, escape="\\"),
|
||||
ReportSchedule.sql.ilike(ilike_value, escape="\\"),
|
||||
ReportSchedule.name.ilike(ilike_value),
|
||||
ReportSchedule.description.ilike(ilike_value),
|
||||
ReportSchedule.sql.ilike(ilike_value),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -447,43 +447,6 @@ def test_ownership_check_raises_forbidden(mocker: MockerFixture) -> None:
|
||||
cmd.validate()
|
||||
|
||||
|
||||
# --- Dashboard extra (activeTabs) validation on update ---
|
||||
|
||||
|
||||
def test_update_rejects_invalid_active_tab_ids(mocker: MockerFixture) -> None:
|
||||
"""On PUT, activeTabs must be validated against the model's dashboard layout.
|
||||
|
||||
The dashboard is not in the payload, so validation must fall back to the
|
||||
existing model's dashboard; tab ids absent from position_json are rejected.
|
||||
"""
|
||||
model = _make_model(mocker, model_type=ReportScheduleType.REPORT, database_id=None)
|
||||
model.dashboard.position_json = '{"TAB-valid": {}}'
|
||||
_setup_mocks(mocker, model)
|
||||
|
||||
cmd = UpdateReportScheduleCommand(
|
||||
model_id=1,
|
||||
data={"extra": {"dashboard": {"activeTabs": ["TAB-missing"]}}},
|
||||
)
|
||||
with pytest.raises(ReportScheduleInvalidError) as exc_info:
|
||||
cmd.validate()
|
||||
messages = _get_validation_messages(exc_info)
|
||||
assert "extra" in messages
|
||||
assert "invalid tab ids" in messages["extra"].lower()
|
||||
|
||||
|
||||
def test_update_accepts_valid_active_tab_ids(mocker: MockerFixture) -> None:
|
||||
"""A tab id present in the model dashboard's position_json passes validation."""
|
||||
model = _make_model(mocker, model_type=ReportScheduleType.REPORT, database_id=None)
|
||||
model.dashboard.position_json = '{"TAB-valid": {}}'
|
||||
_setup_mocks(mocker, model)
|
||||
|
||||
cmd = UpdateReportScheduleCommand(
|
||||
model_id=1,
|
||||
data={"extra": {"dashboard": {"activeTabs": ["TAB-valid"]}}},
|
||||
)
|
||||
cmd.validate() # should not raise
|
||||
|
||||
|
||||
# --- Database not found for alert ---
|
||||
|
||||
|
||||
|
||||
@@ -90,40 +90,3 @@ def test_find_by_extra_metadata_escapes_underscore_wildcard(
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0].name == "with-underscore"
|
||||
|
||||
|
||||
def test_find_by_native_filter_id_returns_matching_reports(
|
||||
session: Session,
|
||||
) -> None:
|
||||
extra = json.dumps({"dashboard": {"nativeFilters": "NATIVE_FILTER-abc123"}})
|
||||
_create_report(session, "match", extra_json=extra)
|
||||
_create_report(session, "no-match", extra_json="{}")
|
||||
|
||||
results = ReportScheduleDAO.find_by_native_filter_id("NATIVE_FILTER-abc123")
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0].name == "match"
|
||||
|
||||
|
||||
def test_find_by_native_filter_id_escapes_percent_wildcard(
|
||||
session: Session,
|
||||
) -> None:
|
||||
_create_report(session, "with-percent", extra_json='{"id": "FILTER-100%x"}')
|
||||
_create_report(session, "other", extra_json='{"id": "FILTER-100yx"}')
|
||||
|
||||
results = ReportScheduleDAO.find_by_native_filter_id("FILTER-100%x")
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0].name == "with-percent"
|
||||
|
||||
|
||||
def test_find_by_native_filter_id_escapes_underscore_wildcard(
|
||||
session: Session,
|
||||
) -> None:
|
||||
_create_report(session, "with-underscore", extra_json='{"id": "FILTER-a_b"}')
|
||||
_create_report(session, "other", extra_json='{"id": "FILTER-axb"}')
|
||||
|
||||
results = ReportScheduleDAO.find_by_native_filter_id("FILTER-a_b")
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0].name == "with-underscore"
|
||||
|
||||
@@ -62,47 +62,3 @@ def test_report_schedule_all_text_filter_applies_ilike() -> None:
|
||||
f = ReportScheduleAllTextFilter("name", MagicMock())
|
||||
f.apply(query, "test")
|
||||
query.filter.assert_called_once()
|
||||
|
||||
|
||||
@patch("superset.reports.filters.or_")
|
||||
@patch("superset.reports.filters.ReportSchedule")
|
||||
def test_report_schedule_all_text_filter_escapes_wildcards(
|
||||
mock_report_schedule: MagicMock, mock_or: MagicMock
|
||||
) -> None:
|
||||
"""User-supplied wildcards must be escaped so they match literally."""
|
||||
from superset.reports.filters import ReportScheduleAllTextFilter
|
||||
|
||||
query = MagicMock()
|
||||
f = ReportScheduleAllTextFilter("name", MagicMock())
|
||||
# raw input contains every LIKE special character plus a backslash
|
||||
f.apply(query, "50%_off\\promo")
|
||||
|
||||
# %, _ and \ are all escaped, and the literal is wrapped for a "contains" match
|
||||
expected = "%50\\%\\_off\\\\promo%"
|
||||
for column in (
|
||||
mock_report_schedule.name,
|
||||
mock_report_schedule.description,
|
||||
mock_report_schedule.sql,
|
||||
):
|
||||
column.ilike.assert_called_once_with(expected, escape="\\")
|
||||
|
||||
|
||||
@patch("superset.reports.filters.or_")
|
||||
@patch("superset.reports.filters.ReportSchedule")
|
||||
def test_report_schedule_all_text_filter_coerces_non_string(
|
||||
mock_report_schedule: MagicMock, mock_or: MagicMock
|
||||
) -> None:
|
||||
"""A non-string value (e.g. an int) must not raise when escaping."""
|
||||
from superset.reports.filters import ReportScheduleAllTextFilter
|
||||
|
||||
query = MagicMock()
|
||||
f = ReportScheduleAllTextFilter("name", MagicMock())
|
||||
f.apply(query, 50)
|
||||
|
||||
expected = "%50%"
|
||||
for column in (
|
||||
mock_report_schedule.name,
|
||||
mock_report_schedule.description,
|
||||
mock_report_schedule.sql,
|
||||
):
|
||||
column.ilike.assert_called_once_with(expected, escape="\\")
|
||||
|
||||
@@ -1,108 +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.
|
||||
|
||||
import io
|
||||
import sys
|
||||
from importlib import util
|
||||
from pathlib import Path
|
||||
from types import ModuleType
|
||||
from typing import Any, cast
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import click
|
||||
import pytest
|
||||
|
||||
_ENTRYPOINT_PATH = (
|
||||
Path(__file__).resolve().parents[2] / "superset/mcp_service/__main__.py"
|
||||
)
|
||||
|
||||
|
||||
def _install_entrypoint_stubs(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
superset_module = cast(Any, ModuleType("superset"))
|
||||
superset_module.__path__ = []
|
||||
|
||||
mcp_service_module = cast(Any, ModuleType("superset.mcp_service"))
|
||||
mcp_service_module.__path__ = []
|
||||
|
||||
app_module = cast(Any, ModuleType("superset.mcp_service.app"))
|
||||
app_module.init_fastmcp_server = MagicMock()
|
||||
app_module.mcp = MagicMock()
|
||||
|
||||
middleware_module = cast(Any, ModuleType("superset.mcp_service.middleware"))
|
||||
middleware_module.create_response_size_guard_middleware = lambda: None
|
||||
|
||||
server_module = cast(Any, ModuleType("superset.mcp_service.server"))
|
||||
server_module.build_middleware_list = lambda: []
|
||||
|
||||
monkeypatch.setitem(sys.modules, "superset", superset_module)
|
||||
monkeypatch.setitem(sys.modules, "superset.mcp_service", mcp_service_module)
|
||||
monkeypatch.setitem(sys.modules, "superset.mcp_service.app", app_module)
|
||||
monkeypatch.setitem(
|
||||
sys.modules,
|
||||
"superset.mcp_service.middleware",
|
||||
middleware_module,
|
||||
)
|
||||
monkeypatch.setitem(sys.modules, "superset.mcp_service.server", server_module)
|
||||
|
||||
|
||||
def _load_entrypoint(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
module_name = "superset.mcp_service.__main__"
|
||||
spec = util.spec_from_file_location(module_name, _ENTRYPOINT_PATH)
|
||||
assert spec is not None
|
||||
assert spec.loader is not None
|
||||
|
||||
module = util.module_from_spec(spec)
|
||||
monkeypatch.setitem(sys.modules, module_name, module)
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
|
||||
def test_stdio_click_output_is_redirected_to_stderr(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
) -> None:
|
||||
"""click output should use the saved original functions in stdio mode."""
|
||||
original_echo = click.echo
|
||||
original_secho = click.secho
|
||||
|
||||
monkeypatch.setenv("FASTMCP_TRANSPORT", "stdio")
|
||||
monkeypatch.setattr(click, "echo", original_echo)
|
||||
monkeypatch.setattr(click, "secho", original_secho)
|
||||
_install_entrypoint_stubs(monkeypatch)
|
||||
|
||||
try:
|
||||
_load_entrypoint(monkeypatch)
|
||||
|
||||
other_stream = io.StringIO()
|
||||
click.echo("plain message")
|
||||
click.echo("keyword file message", file=other_stream)
|
||||
click.echo("positional file message", other_stream)
|
||||
click.secho("styled message")
|
||||
click.secho("styled keyword file message", file=other_stream)
|
||||
click.secho("styled positional file message", other_stream)
|
||||
|
||||
captured = capsys.readouterr()
|
||||
assert captured.out == ""
|
||||
assert other_stream.getvalue() == ""
|
||||
assert "plain message" in captured.err
|
||||
assert "keyword file message" in captured.err
|
||||
assert "positional file message" in captured.err
|
||||
assert "styled message" in captured.err
|
||||
assert "styled keyword file message" in captured.err
|
||||
assert "styled positional file message" in captured.err
|
||||
finally:
|
||||
click.echo = original_echo
|
||||
click.secho = original_secho
|
||||
Reference in New Issue
Block a user