mirror of
https://github.com/apache/superset.git
synced 2026-07-05 14:25:32 +00:00
Compare commits
40 Commits
fix_pr_336
...
prefer-bin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d32c8834d | ||
|
|
40164300e5 | ||
|
|
c444eed63e | ||
|
|
1df5e59fdf | ||
|
|
77f66e7434 | ||
|
|
2c81eb6c39 | ||
|
|
09c4afc894 | ||
|
|
229d92590a | ||
|
|
f4f516c64c | ||
|
|
fe1fddde05 | ||
|
|
7e67deead7 | ||
|
|
6e02603098 | ||
|
|
4518f6999c | ||
|
|
aff847b3af | ||
|
|
b24aca0304 | ||
|
|
2c453035e4 | ||
|
|
4fe11869fc | ||
|
|
a0a49f9300 | ||
|
|
29d2fac485 | ||
|
|
0c5da6cb5d | ||
|
|
da6947d295 | ||
|
|
2db8f809ba | ||
|
|
5912fad745 | ||
|
|
dc41c45bec | ||
|
|
88ee90c579 | ||
|
|
bbb2279644 | ||
|
|
1958df6b83 | ||
|
|
58bd3bfcf0 | ||
|
|
d6eb6e08d0 | ||
|
|
96cb6030c8 | ||
|
|
94d47113ea | ||
|
|
f756cee01b | ||
|
|
e8926f177d | ||
|
|
16f4516903 | ||
|
|
000d353ef3 | ||
|
|
83b6f672ff | ||
|
|
0dc48e9b41 | ||
|
|
fe9eef9198 | ||
|
|
8a8248b575 | ||
|
|
42d9a78777 |
125
.cursor/rules/dev-standard.mdc
Normal file
125
.cursor/rules/dev-standard.mdc
Normal file
@@ -0,0 +1,125 @@
|
||||
---
|
||||
description: Apache Superset development standards and guidelines for Cursor IDE
|
||||
globs: ["**/*.py", "**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.sql", "**/*.md"]
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Apache Superset Development Standards for Cursor IDE
|
||||
|
||||
Apache Superset is a data visualization platform with Flask/Python backend and React/TypeScript frontend.
|
||||
|
||||
## ⚠️ CRITICAL: Ongoing Refactors (What NOT to Do)
|
||||
|
||||
**These migrations are actively happening - avoid deprecated patterns:**
|
||||
|
||||
### Frontend Modernization
|
||||
- **NO `any` types** - Use proper TypeScript types
|
||||
- **NO JavaScript files** - Convert to TypeScript (.ts/.tsx)
|
||||
- **NO Enzyme** - Use React Testing Library/Jest (Enzyme fully removed)
|
||||
- **Use @superset-ui/core** - Don't import Ant Design directly
|
||||
|
||||
### Testing Strategy Migration
|
||||
- **Prefer unit tests** over integration tests
|
||||
- **Prefer integration tests** over Cypress end-to-end tests
|
||||
- **Cypress is last resort** - Actively moving away from Cypress
|
||||
- **Use Jest + React Testing Library** for component testing
|
||||
|
||||
### Backend Type Safety
|
||||
- **Add type hints** - All new Python code needs proper typing
|
||||
- **MyPy compliance** - Run `pre-commit run mypy` to validate
|
||||
- **SQLAlchemy typing** - Use proper model annotations
|
||||
|
||||
## Code Standards
|
||||
|
||||
### TypeScript Frontend
|
||||
- **NO `any` types** - Use proper TypeScript
|
||||
- **Functional components** with hooks
|
||||
- **@superset-ui/core** for UI components (not direct antd)
|
||||
- **Jest** for testing (NO Enzyme)
|
||||
- **Redux** for global state, hooks for local
|
||||
|
||||
### Python Backend
|
||||
- **Type hints required** for all new code
|
||||
- **MyPy compliant** - run `pre-commit run mypy`
|
||||
- **SQLAlchemy models** with proper typing
|
||||
- **pytest** for testing
|
||||
|
||||
### Apache License Headers
|
||||
- **New files require ASF license headers** - When creating new code files, include the standard Apache Software Foundation license header
|
||||
- **LLM instruction files are excluded** - Files like LLMS.md, CLAUDE.md, etc. are in `.rat-excludes` to avoid header token overhead
|
||||
|
||||
## Key Directory Structure
|
||||
|
||||
```
|
||||
superset/
|
||||
├── superset/ # Python backend (Flask, SQLAlchemy)
|
||||
│ ├── views/api/ # REST API endpoints
|
||||
│ ├── models/ # Database models
|
||||
│ └── connectors/ # Database connections
|
||||
├── superset-frontend/src/ # React TypeScript frontend
|
||||
│ ├── components/ # Reusable components
|
||||
│ ├── explore/ # Chart builder
|
||||
│ ├── dashboard/ # Dashboard interface
|
||||
│ └── SqlLab/ # SQL editor
|
||||
├── superset-frontend/packages/
|
||||
│ └── superset-ui-core/ # UI component library (USE THIS)
|
||||
├── tests/ # Python/integration tests
|
||||
├── docs/ # Documentation (UPDATE FOR CHANGES)
|
||||
└── UPDATING.md # Breaking changes log
|
||||
```
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Dataset-Centric Approach
|
||||
Charts built from enriched datasets containing:
|
||||
- Dimension columns with labels/descriptions
|
||||
- Predefined metrics as SQL expressions
|
||||
- Self-service analytics within defined contexts
|
||||
|
||||
### Security & Features
|
||||
- **RBAC**: Role-based access via Flask-AppBuilder
|
||||
- **Feature flags**: Control feature rollouts
|
||||
- **Row-level security**: SQL-based data access control
|
||||
|
||||
## Test Utilities
|
||||
|
||||
### Python Test Helpers
|
||||
- **`SupersetTestCase`** - Base class in `tests/integration_tests/base_tests.py`
|
||||
- **`@with_config`** - Config mocking decorator
|
||||
- **`@with_feature_flags`** - Feature flag testing
|
||||
- **`login_as()`, `login_as_admin()`** - Authentication helpers
|
||||
- **`create_dashboard()`, `create_slice()`** - Data setup utilities
|
||||
|
||||
### TypeScript Test Helpers
|
||||
- **`superset-frontend/spec/helpers/testing-library.tsx`** - Custom render() with providers
|
||||
- **`createWrapper()`** - Redux/Router/Theme wrapper
|
||||
- **`selectOption()`** - Select component helper
|
||||
- **React Testing Library** - NO Enzyme (removed)
|
||||
|
||||
## Pre-commit Validation
|
||||
|
||||
**Use pre-commit hooks for quality validation:**
|
||||
|
||||
```bash
|
||||
# Install hooks
|
||||
pre-commit install
|
||||
|
||||
# Quick validation (faster than --all-files)
|
||||
pre-commit run # Staged files only
|
||||
pre-commit run mypy # Python type checking
|
||||
pre-commit run prettier # Code formatting
|
||||
pre-commit run eslint # Frontend linting
|
||||
```
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
- **Documentation**: Update docs/ for any user-facing changes
|
||||
- **Breaking Changes**: Add to UPDATING.md
|
||||
- **Docstrings**: Required for new functions/classes
|
||||
- **Follow existing patterns**: Mimic code style, use existing libraries and utilities
|
||||
- **Type Safety**: This codebase is actively modernizing toward full TypeScript and type safety
|
||||
- **Always run `pre-commit run`** to validate changes before committing
|
||||
|
||||
---
|
||||
|
||||
**Note**: This codebase is actively modernizing toward full TypeScript and type safety. Always run `pre-commit run` to validate changes. Follow the ongoing refactors section to avoid deprecated patterns.
|
||||
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -42,7 +42,7 @@ body:
|
||||
options:
|
||||
- master / latest-dev
|
||||
- "5.0.0"
|
||||
- "4.1.2"
|
||||
- "4.1.3"
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
||||
17
.github/actions/setup-backend/action.yml
vendored
17
.github/actions/setup-backend/action.yml
vendored
@@ -28,7 +28,6 @@ runs:
|
||||
if [ "${{ inputs.python-version }}" = "current" ]; then
|
||||
echo "PYTHON_VERSION=3.11" >> $GITHUB_ENV
|
||||
elif [ "${{ inputs.python-version }}" = "next" ]; then
|
||||
# currently disabled in GHA matrixes because of library compatibility issues
|
||||
echo "PYTHON_VERSION=3.12" >> $GITHUB_ENV
|
||||
elif [ "${{ inputs.python-version }}" = "previous" ]; then
|
||||
echo "PYTHON_VERSION=3.10" >> $GITHUB_ENV
|
||||
@@ -40,7 +39,17 @@ runs:
|
||||
with:
|
||||
python-version: ${{ env.PYTHON_VERSION }}
|
||||
cache: ${{ inputs.cache }}
|
||||
- name: Cache uv packages
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/uv
|
||||
key: uv-${{ runner.os }}-python${{ env.PYTHON_VERSION }}-${{ hashFiles('requirements/development.txt', 'requirements/base.txt') }}
|
||||
restore-keys: |
|
||||
uv-${{ runner.os }}-python${{ env.PYTHON_VERSION }}-
|
||||
- name: Install dependencies
|
||||
env:
|
||||
UV_CACHE_DIR: ~/.cache/uv
|
||||
UV_PREFER_BINARY: "1"
|
||||
run: |
|
||||
if [ "${{ inputs.install-superset }}" = "true" ]; then
|
||||
sudo apt-get update && sudo apt-get -y install libldap2-dev libsasl2-dev
|
||||
@@ -48,11 +57,11 @@ runs:
|
||||
pip install --upgrade pip setuptools wheel uv
|
||||
|
||||
if [ "${{ inputs.requirements-type }}" = "dev" ]; then
|
||||
uv pip install --system -r requirements/development.txt
|
||||
uv pip install --system --prefer-binary -r requirements/development.txt
|
||||
elif [ "${{ inputs.requirements-type }}" = "base" ]; then
|
||||
uv pip install --system -r requirements/base.txt
|
||||
uv pip install --system --prefer-binary -r requirements/base.txt
|
||||
fi
|
||||
|
||||
uv pip install --system -e .
|
||||
uv pip install --system --prefer-binary -e .
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
1
.github/copilot-instructions.md
vendored
Symbolic link
1
.github/copilot-instructions.md
vendored
Symbolic link
@@ -0,0 +1 @@
|
||||
../LLMS.md
|
||||
82
.github/workflows/claude.yml
vendored
Normal file
82
.github/workflows/claude.yml
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
name: Claude PR Assistant
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
check-permissions:
|
||||
if: |
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude'))
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
allowed: ${{ steps.check.outputs.allowed }}
|
||||
steps:
|
||||
- name: Check if user is allowed
|
||||
id: check
|
||||
run: |
|
||||
# List of allowed users
|
||||
ALLOWED_USERS="mistercrunch,rusackas"
|
||||
|
||||
# Get the commenter's username
|
||||
COMMENTER="${{ github.event.comment.user.login }}"
|
||||
|
||||
echo "Checking permissions for user: $COMMENTER"
|
||||
|
||||
# Check if user is in allowed list
|
||||
if [[ ",$ALLOWED_USERS," == *",$COMMENTER,"* ]]; then
|
||||
echo "allowed=true" >> $GITHUB_OUTPUT
|
||||
echo "✅ User $COMMENTER is allowed to use Claude"
|
||||
else
|
||||
echo "allowed=false" >> $GITHUB_OUTPUT
|
||||
echo "❌ User $COMMENTER is not allowed to use Claude"
|
||||
fi
|
||||
|
||||
deny-access:
|
||||
needs: check-permissions
|
||||
if: needs.check-permissions.outputs.allowed == 'false'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Comment access denied
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const message = `👋 Hi @${{ github.event.comment.user.login || github.event.review.user.login || github.event.issue.user.login }}!
|
||||
|
||||
Thanks for trying to use Claude Code, but currently only certain team members have access to this feature.
|
||||
|
||||
If you believe you should have access, please contact a project maintainer.`;
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: message
|
||||
});
|
||||
|
||||
claude-code-action:
|
||||
needs: check-permissions
|
||||
if: needs.check-permissions.outputs.allowed == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude PR Action
|
||||
uses: anthropics/claude-code-action@beta
|
||||
with:
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
timeout_minutes: "60"
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -80,7 +80,7 @@ yarn-error.log
|
||||
*.min.js
|
||||
test-changelog.md
|
||||
*.tsbuildinfo
|
||||
.venv
|
||||
|
||||
# Ignore package-lock in packages
|
||||
plugins/*/package-lock.json
|
||||
packages/*/package-lock.json
|
||||
@@ -92,6 +92,7 @@ scripts/*.zip
|
||||
# IntelliJ
|
||||
*.iml
|
||||
venv
|
||||
.venv
|
||||
@eaDir/
|
||||
|
||||
# PyCharm
|
||||
@@ -126,4 +127,7 @@ docker/*local*
|
||||
# Jest test report
|
||||
test-report.html
|
||||
superset/static/stats/statistics.html
|
||||
|
||||
# LLM-related
|
||||
CLAUDE.local.md
|
||||
.aider*
|
||||
|
||||
@@ -76,3 +76,11 @@ ydb.svg
|
||||
erd.puml
|
||||
erd.svg
|
||||
intro_header.txt
|
||||
|
||||
# for LLMs
|
||||
llm-context.md
|
||||
LLMS.md
|
||||
CLAUDE.md
|
||||
CURSOR.md
|
||||
GEMINI.md
|
||||
GPT.md
|
||||
|
||||
58
CHANGELOG/4.1.3.md
Normal file
58
CHANGELOG/4.1.3.md
Normal file
@@ -0,0 +1,58 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
## Change Log
|
||||
|
||||
### 4.1.3 (Thu May 29 02:31:07 2025 -0500)
|
||||
|
||||
**Database Migrations**
|
||||
|
||||
**Features**
|
||||
|
||||
**Fixes**
|
||||
|
||||
- [#33522](https://github.com/apache/superset/pull/33522) fix(Sqllab): Autocomplete got stuck in UI when open it too fast (@rebenitez1802)
|
||||
- [#33425](https://github.com/apache/superset/pull/33425) fix(table-chart): time shift is not working (@justinpark)
|
||||
- [#32414](https://github.com/apache/superset/pull/32414) fix(api): Added uuid to list api calls (@withnale)
|
||||
- [#33354](https://github.com/apache/superset/pull/33354) fix: loading examples from raw.githubusercontent.com fails with 429 errors (@mistercrunch)
|
||||
- [#32382](https://github.com/apache/superset/pull/32382) fix(pinot): revert join and subquery flags (@yuribogomolov)
|
||||
- [#32473](https://github.com/apache/superset/pull/32473) fix(plugin-chart-echarts): remove erroneous upper bound value (@villebro)
|
||||
- [#33048](https://github.com/apache/superset/pull/33048) fix: improve error type on parse error (@justinpark)
|
||||
- [#32968](https://github.com/apache/superset/pull/32968) fix(pivot-table): Revert "fix(Pivot Table): Fix column width to respect currency config (#31414)" (@justinpark)
|
||||
- [#32795](https://github.com/apache/superset/pull/32795) fix(log): store navigation path to get correct logging path (@justinpark)
|
||||
- [#33216](https://github.com/apache/superset/pull/33216) fix: Downgrade to marshmallow<4 (@amotl)
|
||||
- [#32866](https://github.com/apache/superset/pull/32866) fix: make packages PEP 625 compliant (@sadpandajoe)
|
||||
- [#32035](https://github.com/apache/superset/pull/32035) fix(fe/dashboard-list): display modifier info for `Last modified` data (@hainenber)
|
||||
- [#32708](https://github.com/apache/superset/pull/32708) fix(logging): missing path in event data (@justinpark)
|
||||
- [#32699](https://github.com/apache/superset/pull/32699) fix: Signature of Celery pruner jobs (@michael-s-molina)
|
||||
- [#32681](https://github.com/apache/superset/pull/32681) fix(log): Update recent_activity by event name (@justinpark)
|
||||
- [#32608](https://github.com/apache/superset/pull/32608) fix(welcome): perf on distinct recent activities (@justinpark)
|
||||
- [#32572](https://github.com/apache/superset/pull/32572) fix: Log table retention policy (@michael-s-molina)
|
||||
- [#32406](https://github.com/apache/superset/pull/32406) fix(model/helper): represent RLS filter clause in proper textual SQL string (@hainenber)
|
||||
- [#32240](https://github.com/apache/superset/pull/32240) fix: upgrade to 3.11.11-slim-bookworm to address critical vulnerabilities (@gpchandran)
|
||||
- [#30858](https://github.com/apache/superset/pull/30858) fix(chart data): removing query from /chart/data payload when accessing as guest user (@fisjac)
|
||||
|
||||
**Others**
|
||||
|
||||
- [#33612](https://github.com/apache/superset/pull/33612) chore: update Dockerfile - Upgrade to 3.11.12 (@gpchandran)
|
||||
- [#33435](https://github.com/apache/superset/pull/33435) docs: CVEs fixed on 4.1.2 (@sha174n)
|
||||
- [#33339](https://github.com/apache/superset/pull/33339) chore(🦾): bump python h11 0.14.0 -> 0.16.0 (@github-actions[bot])
|
||||
- [#32745](https://github.com/apache/superset/pull/32745) chore(🦾): bump python sqlglot 26.1.3 -> 26.11.1 (@github-actions[bot])
|
||||
- [#32782](https://github.com/apache/superset/pull/32782) chore: Revert "chore: bump base image in Dockerfile with `ARG PY_VER=3.11.11-slim-bookworm`" (@sadpandajoe)
|
||||
- [#32780](https://github.com/apache/superset/pull/32780) chore: bump base image in Dockerfile with `ARG PY_VER=3.11.11-slim-bookworm` (@gpchandran)
|
||||
@@ -167,7 +167,7 @@ RUN mkdir -p \
|
||||
&& touch superset/static/version_info.json
|
||||
|
||||
# Install Playwright and optionally setup headless browsers
|
||||
ARG INCLUDE_CHROMIUM="true"
|
||||
ARG INCLUDE_CHROMIUM="false"
|
||||
ARG INCLUDE_FIREFOX="false"
|
||||
RUN --mount=type=cache,target=${SUPERSET_HOME}/.cache/uv \
|
||||
if [ "$INCLUDE_CHROMIUM" = "true" ] || [ "$INCLUDE_FIREFOX" = "true" ]; then \
|
||||
@@ -223,7 +223,7 @@ RUN --mount=type=cache,target=${SUPERSET_HOME}/.cache/uv \
|
||||
/app/docker/pip-install.sh --requires-build-essential -r requirements/base.txt
|
||||
# Install the superset package
|
||||
RUN --mount=type=cache,target=${SUPERSET_HOME}/.cache/uv \
|
||||
uv pip install .
|
||||
uv pip install -e .
|
||||
RUN python -m compileall /app/superset
|
||||
|
||||
USER superset
|
||||
@@ -246,7 +246,7 @@ RUN --mount=type=cache,target=${SUPERSET_HOME}/.cache/uv \
|
||||
/app/docker/pip-install.sh --requires-build-essential -r requirements/development.txt
|
||||
# Install the superset package
|
||||
RUN --mount=type=cache,target=${SUPERSET_HOME}/.cache/uv \
|
||||
uv pip install .
|
||||
uv pip install -e .
|
||||
|
||||
RUN uv pip install .[postgres]
|
||||
RUN python -m compileall /app/superset
|
||||
|
||||
148
LLMS.md
Normal file
148
LLMS.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# LLM Context Guide for Apache Superset
|
||||
|
||||
Apache Superset is a data visualization platform with Flask/Python backend and React/TypeScript frontend.
|
||||
|
||||
## ⚠️ CRITICAL: Ongoing Refactors (What NOT to Do)
|
||||
|
||||
**These migrations are actively happening - avoid deprecated patterns:**
|
||||
|
||||
### Frontend Modernization
|
||||
- **NO `any` types** - Use proper TypeScript types
|
||||
- **NO JavaScript files** - Convert to TypeScript (.ts/.tsx)
|
||||
- **Use @superset-ui/core** - Don't import Ant Design directly
|
||||
|
||||
### Testing Strategy Migration
|
||||
- **Prefer unit tests** over integration tests
|
||||
- **Prefer integration tests** over Cypress end-to-end tests
|
||||
- **Cypress is last resort** - Actively moving away from Cypress
|
||||
- **Use Jest + React Testing Library** for component testing
|
||||
|
||||
### Backend Type Safety
|
||||
- **Add type hints** - All new Python code needs proper typing
|
||||
- **MyPy compliance** - Run `pre-commit run mypy` to validate
|
||||
- **SQLAlchemy typing** - Use proper model annotations
|
||||
|
||||
## Key Directories
|
||||
|
||||
```
|
||||
superset/
|
||||
├── superset/ # Python backend (Flask, SQLAlchemy)
|
||||
│ ├── views/api/ # REST API endpoints
|
||||
│ ├── models/ # Database models
|
||||
│ └── connectors/ # Database connections
|
||||
├── superset-frontend/src/ # React TypeScript frontend
|
||||
│ ├── components/ # Reusable components
|
||||
│ ├── explore/ # Chart builder
|
||||
│ ├── dashboard/ # Dashboard interface
|
||||
│ └── SqlLab/ # SQL editor
|
||||
├── superset-frontend/packages/
|
||||
│ └── superset-ui-core/ # UI component library (USE THIS)
|
||||
├── tests/ # Python/integration tests
|
||||
├── docs/ # Documentation (UPDATE FOR CHANGES)
|
||||
└── UPDATING.md # Breaking changes log
|
||||
```
|
||||
|
||||
## Code Standards
|
||||
|
||||
### TypeScript Frontend
|
||||
- **Avoid `any` types** - Use proper TypeScript, reuse existing types
|
||||
- **Functional components** with hooks
|
||||
- **@superset-ui/core** for UI components (not direct antd)
|
||||
- **Jest** for testing (NO Enzyme)
|
||||
- **Redux** for global state where it exists, hooks for local
|
||||
|
||||
### Python Backend
|
||||
- **Type hints required** for all new code
|
||||
- **MyPy compliant** - run `pre-commit run mypy`
|
||||
- **SQLAlchemy models** with proper typing
|
||||
- **pytest** for testing
|
||||
|
||||
### Apache License Headers
|
||||
- **New files require ASF license headers** - When creating new code files, include the standard Apache Software Foundation license header
|
||||
- **LLM instruction files are excluded** - Files like LLMS.md, CLAUDE.md, etc. are in `.rat-excludes` to avoid header token overhead
|
||||
|
||||
## Documentation Requirements
|
||||
|
||||
- **docs/**: Update for any user-facing changes
|
||||
- **UPDATING.md**: Add breaking changes here
|
||||
- **Docstrings**: Required for new functions/classes
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Security & Features
|
||||
- **RBAC**: Role-based access via Flask-AppBuilder
|
||||
- **Feature flags**: Control feature rollouts
|
||||
- **Row-level security**: SQL-based data access control
|
||||
|
||||
## Test Utilities
|
||||
|
||||
### Python Test Helpers
|
||||
- **`SupersetTestCase`** - Base class in `tests/integration_tests/base_tests.py`
|
||||
- **`@with_config`** - Config mocking decorator
|
||||
- **`@with_feature_flags`** - Feature flag testing
|
||||
- **`login_as()`, `login_as_admin()`** - Authentication helpers
|
||||
- **`create_dashboard()`, `create_slice()`** - Data setup utilities
|
||||
|
||||
### TypeScript Test Helpers
|
||||
- **`superset-frontend/spec/helpers/testing-library.tsx`** - Custom render() with providers
|
||||
- **`createWrapper()`** - Redux/Router/Theme wrapper
|
||||
- **`selectOption()`** - Select component helper
|
||||
- **React Testing Library** - NO Enzyme (removed)
|
||||
|
||||
### Running Tests
|
||||
```bash
|
||||
# Frontend
|
||||
npm run test # All tests
|
||||
npm run test -- filename.test.tsx # Single file
|
||||
|
||||
# Backend
|
||||
pytest # All tests
|
||||
pytest tests/unit_tests/specific_test.py # Single file
|
||||
pytest tests/unit_tests/ # Directory
|
||||
|
||||
# If pytest fails with database/setup issues, ask the user to run test environment setup
|
||||
```
|
||||
|
||||
## Environment Validation
|
||||
|
||||
**Quick Setup Check (run this first):**
|
||||
|
||||
```bash
|
||||
# Verify Superset is running
|
||||
curl -f http://localhost:8088/health || echo "❌ Setup required - see https://superset.apache.org/docs/contributing/development#working-with-llms"
|
||||
```
|
||||
|
||||
**If health checks fail:**
|
||||
"It appears you aren't set up properly. Please refer to the [Working with LLMs](https://superset.apache.org/docs/contributing/development#working-with-llms) section in the development docs for setup instructions."
|
||||
|
||||
**Key Project Files:**
|
||||
- `superset-frontend/package.json` - Frontend build scripts (`npm run dev` on port 9000, `npm run test`, `npm run lint`)
|
||||
- `pyproject.toml` - Python tooling (ruff, mypy configs)
|
||||
- `requirements/` folder - Python dependencies (base.txt, development.txt)
|
||||
|
||||
## Pre-commit Validation
|
||||
|
||||
**Use pre-commit hooks for quality validation:**
|
||||
|
||||
```bash
|
||||
# Install hooks
|
||||
pre-commit install
|
||||
|
||||
# Quick validation (faster than --all-files)
|
||||
pre-commit run # Staged files only
|
||||
pre-commit run mypy # Python type checking
|
||||
pre-commit run prettier # Code formatting
|
||||
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
|
||||
- **[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
|
||||
|
||||
---
|
||||
|
||||
**LLM Note**: This codebase is actively modernizing toward full TypeScript and type safety. Always run `pre-commit run` to validate changes. Follow the ongoing refactors section to avoid deprecated patterns.
|
||||
@@ -23,8 +23,8 @@ This file documents any backwards-incompatible changes in Superset and
|
||||
assists people when migrating to a new version.
|
||||
|
||||
## Next
|
||||
|
||||
- [33603](https://github.com/apache/superset/pull/33603) OpenStreetView has been promoted as the new default for Deck.gl visualization since it can be enabled by default without requiring an API key. If you have Mapbox set up and want to disable OpenStreeView in your environment, please follow the steps documented here [https://superset.apache.org/docs/configuration/map-tiles].
|
||||
- [34258](https://github.com/apache/superset/pull/34258) changing the default in Dockerfile to INCLUDE_CHROMIUM="false" (from "true") in the past. This ensures the `lean` layer is lean by default, and people can opt-in to the `chromium` layer by setting the build arg `INCLUDE_CHROMIUM=true`. This is a breaking change for anyone using the `lean` layer, as it will no longer include Chromium by default.
|
||||
- [34204](https://github.com/apache/superset/pull/33603) OpenStreetView has been promoted as the new default for Deck.gl visualization since it can be enabled by default without requiring an API key. If you have Mapbox set up and want to disable OpenStreeView in your environment, please follow the steps documented here [https://superset.apache.org/docs/configuration/map-tiles].
|
||||
- [33116](https://github.com/apache/superset/pull/33116) In Echarts Series charts (e.g. Line, Area, Bar, etc.) charts, the `x_axis_sort_series` and `x_axis_sort_series_ascending` form data items have been renamed with `x_axis_sort` and `x_axis_sort_asc`.
|
||||
There's a migration added that can potentially affect a significant number of existing charts.
|
||||
- [32317](https://github.com/apache/superset/pull/32317) The horizontal filter bar feature is now out of testing/beta development and its feature flag `HORIZONTAL_FILTER_BAR` has been removed.
|
||||
@@ -56,7 +56,6 @@ assists people when migrating to a new version.
|
||||
- [30284](https://github.com/apache/superset/pull/30284) Deprecated GLOBAL_ASYNC_QUERIES_REDIS_CONFIG in favor of the new GLOBAL_ASYNC_QUERIES_CACHE_BACKEND configuration. To leverage Redis Sentinel, set CACHE_TYPE to RedisSentinelCache, or use RedisCache for standalone Redis
|
||||
- [31961](https://github.com/apache/superset/pull/31961) Upgraded React from version 16.13.1 to 17.0.2. If you are using custom frontend extensions or plugins, you may need to update them to be compatible with React 17.
|
||||
- [31260](https://github.com/apache/superset/pull/31260) Docker images now use `uv pip install` instead of `pip install` to manage the python envrionment. Most docker-based deployments will be affected, whether you derive one of the published images, or have custom bootstrap script that install python libraries (drivers)
|
||||
- [32432](https://github.com/apache/superset/pull/31260) Moves the List Roles FAB view to the frontend and requires `FAB_ADD_SECURITY_API` to be enabled in the configuration and `superset init` to be executed.
|
||||
|
||||
### Potential Downtime
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ Affecting the Docker build process:
|
||||
|
||||
- **SUPERSET_BUILD_TARGET (default=dev):** which --target to build, either `lean` or `dev` are commonly used
|
||||
- **INCLUDE_FIREFOX (default=false):** whether to include the Firefox headless browser in the build
|
||||
- **INCLUDE_CHROMIUM (default=false):** whether to include the Firefox headless browser in the build
|
||||
- **INCLUDE_CHROMIUM (default=false):** whether to include the Chromium headless browser in the build
|
||||
- **BUILD_TRANSLATIONS(default=false):** whether to compile the translations from the .po files available
|
||||
- **SUPERSET_LOAD_EXAMPLES (default=yes):** whether to load the examples into the database upon startup,
|
||||
save some precious time on startup by `SUPERSET_LOAD_EXAMPLES=no docker compose up`
|
||||
@@ -194,6 +194,48 @@ You can also run the pre-commit checks manually in various ways:
|
||||
Replace `<hook_id>` with the ID of the specific hook you want to run. You can find the list
|
||||
of available hooks in the `.pre-commit-config.yaml` file.
|
||||
|
||||
## Working with LLMs
|
||||
|
||||
### Environment Setup
|
||||
Ensure Docker Compose is running before starting LLM sessions:
|
||||
```bash
|
||||
docker compose up
|
||||
```
|
||||
|
||||
Validate your environment:
|
||||
```bash
|
||||
curl -f http://localhost:8088/health && echo "✅ Superset ready"
|
||||
```
|
||||
|
||||
### LLM Session Best Practices
|
||||
- Always validate environment setup first using the health checks above
|
||||
- Use focused validation commands: `pre-commit run` (not `--all-files`)
|
||||
- **Read [LLMS.md](https://github.com/apache/superset/blob/master/LLMS.md) first** - Contains comprehensive development guidelines, coding standards, and critical refactor information
|
||||
- **Check platform-specific files** when available:
|
||||
- `CLAUDE.md` - For Claude/Anthropic tools
|
||||
- `CURSOR.md` - For Cursor editor
|
||||
- `GEMINI.md` - For Google Gemini tools
|
||||
- `GPT.md` - For OpenAI/ChatGPT tools
|
||||
- Follow the TypeScript migration guidelines and avoid deprecated patterns listed in LLMS.md
|
||||
|
||||
### Key Development Commands
|
||||
```bash
|
||||
# Frontend development
|
||||
cd superset-frontend
|
||||
npm run dev # Development server on http://localhost:9000
|
||||
npm run test # Run all tests
|
||||
npm run test -- filename.test.tsx # Run single test file
|
||||
npm run lint # Linting and type checking
|
||||
|
||||
# Backend validation
|
||||
pre-commit run mypy # Type checking
|
||||
pytest # Run all tests
|
||||
pytest tests/unit_tests/specific_test.py # Run single test file
|
||||
pytest tests/unit_tests/ # Run all tests in directory
|
||||
```
|
||||
|
||||
For detailed development context, environment setup, and coding guidelines, see [LLMS.md](https://github.com/apache/superset/blob/master/LLMS.md).
|
||||
|
||||
## Alternatives to `docker compose`
|
||||
|
||||
:::caution
|
||||
|
||||
72
docs/eslint.config.js
Normal file
72
docs/eslint.config.js
Normal file
@@ -0,0 +1,72 @@
|
||||
/* eslint-env node */
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
const typescriptEslintParser = require('@typescript-eslint/parser');
|
||||
const typescriptEslintPlugin = require('@typescript-eslint/eslint-plugin');
|
||||
const eslintConfigPrettier = require('eslint-config-prettier');
|
||||
const prettierEslintPlugin = require('eslint-plugin-prettier');
|
||||
const js = require('@eslint/js');
|
||||
const ts = require('typescript-eslint');
|
||||
const react = require('eslint-plugin-react');
|
||||
const globals = require('globals');
|
||||
const { defineConfig, globalIgnores } = require('eslint/config');
|
||||
|
||||
module.exports = defineConfig([
|
||||
globalIgnores(['build/**/*', '.docusaurus/**/*', 'node_modules/**/*']),
|
||||
js.configs.recommended,
|
||||
...ts.configs.recommended,
|
||||
eslintConfigPrettier,
|
||||
{
|
||||
files: ['eslint.config.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
}
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
parser: typescriptEslintParser,
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
},
|
||||
globals: {
|
||||
...globals.browser,
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
typescript: typescriptEslintPlugin,
|
||||
react,
|
||||
prettier: prettierEslintPlugin,
|
||||
},
|
||||
rules: {
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'react/prop-types': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
}
|
||||
])
|
||||
@@ -40,15 +40,18 @@
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "^3.8.1",
|
||||
"@docusaurus/tsconfig": "^3.8.1",
|
||||
"@eslint/js": "^9.31.0",
|
||||
"@types/react": "^19.1.8",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"eslint": "^8.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.37.0",
|
||||
"@typescript-eslint/parser": "^8.37.0",
|
||||
"eslint": "^9.31.0",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-prettier": "^5.5.1",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"prettier": "^2.0.0",
|
||||
"globals": "^16.3.0",
|
||||
"prettier": "^3.6.2",
|
||||
"typescript": "~5.8.3",
|
||||
"typescript-eslint": "^8.37.0",
|
||||
"webpack": "^5.99.9"
|
||||
},
|
||||
"browserslist": {
|
||||
|
||||
@@ -111,7 +111,7 @@ const StyledTitleContainer = styled('div')`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButton = styled(Link as React.ComponentType<any>)`
|
||||
const StyledButton = styled(Link)`
|
||||
border-radius: 10px;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
/* eslint-disable no-undef */
|
||||
import { useEffect } from 'react';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
|
||||
@@ -50,7 +49,9 @@ export default function Root({ children }) {
|
||||
|
||||
// Handle route changes for SPA
|
||||
const handleRouteChange = () => {
|
||||
devMode && console.log('Route changed to:', window.location.pathname);
|
||||
if (devMode) {
|
||||
console.log('Route changed to:', window.location.pathname);
|
||||
}
|
||||
|
||||
// Short timeout to ensure the page has fully rendered
|
||||
setTimeout(() => {
|
||||
@@ -58,11 +59,9 @@ export default function Root({ children }) {
|
||||
const currentTitle = document.title;
|
||||
const currentPath = window.location.pathname;
|
||||
|
||||
devMode &&
|
||||
console.log('Tracking page view:', currentPath, currentTitle);
|
||||
|
||||
// For testing: impersonate real domain - ONLY FOR DEVELOPMENT
|
||||
if (devMode) {
|
||||
console.log('Tracking page view:', currentPath, currentTitle);
|
||||
window._paq.push(['setDomains', ['superset.apache.org']]);
|
||||
window._paq.push([
|
||||
'setCustomUrl',
|
||||
@@ -85,17 +84,22 @@ export default function Root({ children }) {
|
||||
'routeDidUpdate',
|
||||
];
|
||||
|
||||
devMode && console.log('Setting up Docusaurus route listeners');
|
||||
if (devMode) {
|
||||
console.log('Setting up Docusaurus route listeners');
|
||||
}
|
||||
possibleEvents.forEach(eventName => {
|
||||
document.addEventListener(eventName, () => {
|
||||
devMode &&
|
||||
if (devMode) {
|
||||
console.log(`Docusaurus route update detected via ${eventName}`);
|
||||
}
|
||||
handleRouteChange();
|
||||
});
|
||||
});
|
||||
|
||||
// Also set up manual history tracking as fallback
|
||||
devMode && console.log('Setting up manual history tracking as fallback');
|
||||
if (devMode) {
|
||||
console.log('Setting up manual history tracking as fallback');
|
||||
}
|
||||
const originalPushState = window.history.pushState;
|
||||
window.history.pushState = function () {
|
||||
originalPushState.apply(this, arguments);
|
||||
|
||||
482
docs/yarn.lock
482
docs/yarn.lock
@@ -2157,30 +2157,71 @@
|
||||
dependencies:
|
||||
eslint-visitor-keys "^3.4.3"
|
||||
|
||||
"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1":
|
||||
"@eslint-community/eslint-utils@^4.7.0":
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz#607084630c6c033992a082de6e6fbc1a8b52175a"
|
||||
integrity sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==
|
||||
dependencies:
|
||||
eslint-visitor-keys "^3.4.3"
|
||||
|
||||
"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1":
|
||||
version "4.12.1"
|
||||
resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
|
||||
integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
|
||||
|
||||
"@eslint/eslintrc@^2.1.4":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz"
|
||||
integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==
|
||||
"@eslint/config-array@^0.21.0":
|
||||
version "0.21.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.0.tgz#abdbcbd16b124c638081766392a4d6b509f72636"
|
||||
integrity sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==
|
||||
dependencies:
|
||||
"@eslint/object-schema" "^2.1.6"
|
||||
debug "^4.3.1"
|
||||
minimatch "^3.1.2"
|
||||
|
||||
"@eslint/config-helpers@^0.3.0":
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.3.0.tgz#3e09a90dfb87e0005c7694791e58e97077271286"
|
||||
integrity sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==
|
||||
|
||||
"@eslint/core@^0.15.0", "@eslint/core@^0.15.1":
|
||||
version "0.15.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.15.1.tgz#d530d44209cbfe2f82ef86d6ba08760196dd3b60"
|
||||
integrity sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.15"
|
||||
|
||||
"@eslint/eslintrc@^3.3.1":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964"
|
||||
integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==
|
||||
dependencies:
|
||||
ajv "^6.12.4"
|
||||
debug "^4.3.2"
|
||||
espree "^9.6.0"
|
||||
globals "^13.19.0"
|
||||
espree "^10.0.1"
|
||||
globals "^14.0.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"
|
||||
|
||||
"@eslint/js@8.57.1":
|
||||
version "8.57.1"
|
||||
resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz"
|
||||
integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==
|
||||
"@eslint/js@9.31.0", "@eslint/js@^9.31.0":
|
||||
version "9.31.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.31.0.tgz#adb1f39953d8c475c4384b67b67541b0d7206ed8"
|
||||
integrity sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==
|
||||
|
||||
"@eslint/object-schema@^2.1.6":
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f"
|
||||
integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==
|
||||
|
||||
"@eslint/plugin-kit@^0.3.1":
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz#32926b59bd407d58d817941e48b2a7049359b1fd"
|
||||
integrity sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==
|
||||
dependencies:
|
||||
"@eslint/core" "^0.15.1"
|
||||
levn "^0.4.1"
|
||||
|
||||
"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
|
||||
version "9.3.0"
|
||||
@@ -2194,24 +2235,33 @@
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@humanwhocodes/config-array@^0.13.0":
|
||||
version "0.13.0"
|
||||
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz"
|
||||
integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==
|
||||
"@humanfs/core@^0.19.1":
|
||||
version "0.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77"
|
||||
integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==
|
||||
|
||||
"@humanfs/node@^0.16.6":
|
||||
version "0.16.6"
|
||||
resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e"
|
||||
integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==
|
||||
dependencies:
|
||||
"@humanwhocodes/object-schema" "^2.0.3"
|
||||
debug "^4.3.1"
|
||||
minimatch "^3.0.5"
|
||||
"@humanfs/core" "^0.19.1"
|
||||
"@humanwhocodes/retry" "^0.3.0"
|
||||
|
||||
"@humanwhocodes/module-importer@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz"
|
||||
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
|
||||
|
||||
"@humanwhocodes/object-schema@^2.0.3":
|
||||
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==
|
||||
"@humanwhocodes/retry@^0.3.0":
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a"
|
||||
integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==
|
||||
|
||||
"@humanwhocodes/retry@^0.4.2":
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba"
|
||||
integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==
|
||||
|
||||
"@iconify/types@^2.0.0":
|
||||
version "2.0.0"
|
||||
@@ -2353,7 +2403,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
|
||||
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
|
||||
|
||||
"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8":
|
||||
"@nodelib/fs.walk@^1.2.3":
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
|
||||
integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
|
||||
@@ -2361,6 +2411,11 @@
|
||||
"@nodelib/fs.scandir" "2.1.5"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@pkgr/core@^0.2.4":
|
||||
version "0.2.7"
|
||||
resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.7.tgz#eb5014dfd0b03e7f3ba2eeeff506eed89b028058"
|
||||
integrity sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==
|
||||
|
||||
"@pnpm/config.env-replace@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz#ab29da53df41e8948a00f2433f085f54de8b3a4c"
|
||||
@@ -3599,11 +3654,6 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/semver@^7.3.12":
|
||||
version "7.5.8"
|
||||
resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz"
|
||||
integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
|
||||
|
||||
"@types/send@*":
|
||||
version "0.17.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a"
|
||||
@@ -3674,91 +3724,105 @@
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^5.0.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz"
|
||||
integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==
|
||||
"@typescript-eslint/eslint-plugin@8.37.0", "@typescript-eslint/eslint-plugin@^8.37.0":
|
||||
version "8.37.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz#332392883f936137cd6252c8eb236d298e514e70"
|
||||
integrity sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==
|
||||
dependencies:
|
||||
"@eslint-community/regexpp" "^4.4.0"
|
||||
"@typescript-eslint/scope-manager" "5.62.0"
|
||||
"@typescript-eslint/type-utils" "5.62.0"
|
||||
"@typescript-eslint/utils" "5.62.0"
|
||||
debug "^4.3.4"
|
||||
"@eslint-community/regexpp" "^4.10.0"
|
||||
"@typescript-eslint/scope-manager" "8.37.0"
|
||||
"@typescript-eslint/type-utils" "8.37.0"
|
||||
"@typescript-eslint/utils" "8.37.0"
|
||||
"@typescript-eslint/visitor-keys" "8.37.0"
|
||||
graphemer "^1.4.0"
|
||||
ignore "^5.2.0"
|
||||
natural-compare-lite "^1.4.0"
|
||||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
ignore "^7.0.0"
|
||||
natural-compare "^1.4.0"
|
||||
ts-api-utils "^2.1.0"
|
||||
|
||||
"@typescript-eslint/parser@^5.0.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz"
|
||||
integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==
|
||||
"@typescript-eslint/parser@8.37.0", "@typescript-eslint/parser@^8.37.0":
|
||||
version "8.37.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.37.0.tgz#b87f6b61e25ad5cc5bbf8baf809b8da889c89804"
|
||||
integrity sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "5.62.0"
|
||||
"@typescript-eslint/types" "5.62.0"
|
||||
"@typescript-eslint/typescript-estree" "5.62.0"
|
||||
"@typescript-eslint/scope-manager" "8.37.0"
|
||||
"@typescript-eslint/types" "8.37.0"
|
||||
"@typescript-eslint/typescript-estree" "8.37.0"
|
||||
"@typescript-eslint/visitor-keys" "8.37.0"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz"
|
||||
integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==
|
||||
"@typescript-eslint/project-service@8.37.0":
|
||||
version "8.37.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.37.0.tgz#0594352e32a4ac9258591b88af77b5653800cdfe"
|
||||
integrity sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.62.0"
|
||||
"@typescript-eslint/visitor-keys" "5.62.0"
|
||||
|
||||
"@typescript-eslint/type-utils@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz"
|
||||
integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree" "5.62.0"
|
||||
"@typescript-eslint/utils" "5.62.0"
|
||||
"@typescript-eslint/tsconfig-utils" "^8.37.0"
|
||||
"@typescript-eslint/types" "^8.37.0"
|
||||
debug "^4.3.4"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/types@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz"
|
||||
integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz"
|
||||
integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==
|
||||
"@typescript-eslint/scope-manager@8.37.0":
|
||||
version "8.37.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz#a31a3c80ca2ef4ed58de13742debb692e7d4c0a4"
|
||||
integrity sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.62.0"
|
||||
"@typescript-eslint/visitor-keys" "5.62.0"
|
||||
"@typescript-eslint/types" "8.37.0"
|
||||
"@typescript-eslint/visitor-keys" "8.37.0"
|
||||
|
||||
"@typescript-eslint/tsconfig-utils@8.37.0", "@typescript-eslint/tsconfig-utils@^8.37.0":
|
||||
version "8.37.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz#47a2760d265c6125f8e7864bc5c8537cad2bd053"
|
||||
integrity sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==
|
||||
|
||||
"@typescript-eslint/type-utils@8.37.0":
|
||||
version "8.37.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz#2a682e4c6ff5886712dad57e9787b5e417124507"
|
||||
integrity sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.37.0"
|
||||
"@typescript-eslint/typescript-estree" "8.37.0"
|
||||
"@typescript-eslint/utils" "8.37.0"
|
||||
debug "^4.3.4"
|
||||
globby "^11.1.0"
|
||||
ts-api-utils "^2.1.0"
|
||||
|
||||
"@typescript-eslint/types@8.37.0", "@typescript-eslint/types@^8.37.0":
|
||||
version "8.37.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.37.0.tgz#09517aa9625eb3c68941dde3ac8835740587b6ff"
|
||||
integrity sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==
|
||||
|
||||
"@typescript-eslint/typescript-estree@8.37.0":
|
||||
version "8.37.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz#a07e4574d8e6e4355a558f61323730c987f5fcbc"
|
||||
integrity sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==
|
||||
dependencies:
|
||||
"@typescript-eslint/project-service" "8.37.0"
|
||||
"@typescript-eslint/tsconfig-utils" "8.37.0"
|
||||
"@typescript-eslint/types" "8.37.0"
|
||||
"@typescript-eslint/visitor-keys" "8.37.0"
|
||||
debug "^4.3.4"
|
||||
fast-glob "^3.3.2"
|
||||
is-glob "^4.0.3"
|
||||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
minimatch "^9.0.4"
|
||||
semver "^7.6.0"
|
||||
ts-api-utils "^2.1.0"
|
||||
|
||||
"@typescript-eslint/utils@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz"
|
||||
integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==
|
||||
"@typescript-eslint/utils@8.37.0":
|
||||
version "8.37.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.37.0.tgz#189ea59b2709f5d898614611f091a776751ee335"
|
||||
integrity sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.2.0"
|
||||
"@types/json-schema" "^7.0.9"
|
||||
"@types/semver" "^7.3.12"
|
||||
"@typescript-eslint/scope-manager" "5.62.0"
|
||||
"@typescript-eslint/types" "5.62.0"
|
||||
"@typescript-eslint/typescript-estree" "5.62.0"
|
||||
eslint-scope "^5.1.1"
|
||||
semver "^7.3.7"
|
||||
"@eslint-community/eslint-utils" "^4.7.0"
|
||||
"@typescript-eslint/scope-manager" "8.37.0"
|
||||
"@typescript-eslint/types" "8.37.0"
|
||||
"@typescript-eslint/typescript-estree" "8.37.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@5.62.0":
|
||||
version "5.62.0"
|
||||
resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz"
|
||||
integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==
|
||||
"@typescript-eslint/visitor-keys@8.37.0":
|
||||
version "8.37.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz#cdb6a6bd3e8d6dd69bd70c1bdda36e2d18737455"
|
||||
integrity sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.62.0"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
"@typescript-eslint/types" "8.37.0"
|
||||
eslint-visitor-keys "^4.2.1"
|
||||
|
||||
"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.2.0":
|
||||
"@ungap/structured-clone@^1.0.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz#d06bbb384ebcf6c505fde1c3d0ed4ddffe0aaff8"
|
||||
integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==
|
||||
@@ -3914,11 +3978,16 @@ acorn-walk@^8.0.0:
|
||||
dependencies:
|
||||
acorn "^8.11.0"
|
||||
|
||||
acorn@^8.0.0, acorn@^8.0.4, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.8.2, acorn@^8.9.0:
|
||||
acorn@^8.0.0, acorn@^8.0.4, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.8.2:
|
||||
version "8.14.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb"
|
||||
integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==
|
||||
|
||||
acorn@^8.15.0:
|
||||
version "8.15.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816"
|
||||
integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==
|
||||
|
||||
address@^1.0.1:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e"
|
||||
@@ -5006,7 +5075,7 @@ cosmiconfig@^8.1.3, cosmiconfig@^8.3.5:
|
||||
parse-json "^5.2.0"
|
||||
path-type "^4.0.0"
|
||||
|
||||
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
cross-spawn@^7.0.3, cross-spawn@^7.0.6:
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
||||
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
|
||||
@@ -5704,13 +5773,6 @@ doctrine@^2.1.0:
|
||||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
doctrine@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz"
|
||||
integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
|
||||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
docusaurus-plugin-less@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmjs.org/docusaurus-plugin-less/-/docusaurus-plugin-less-2.0.2.tgz"
|
||||
@@ -6104,12 +6166,13 @@ eslint-config-prettier@^10.1.5:
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz#00c18d7225043b6fbce6a665697377998d453782"
|
||||
integrity sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==
|
||||
|
||||
eslint-plugin-prettier@^4.0.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz"
|
||||
integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==
|
||||
eslint-plugin-prettier@^5.5.1:
|
||||
version "5.5.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz#470820964de9aedb37e9ce62c3266d2d26d08d15"
|
||||
integrity sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==
|
||||
dependencies:
|
||||
prettier-linter-helpers "^1.0.0"
|
||||
synckit "^0.11.7"
|
||||
|
||||
eslint-plugin-react@^7.37.5:
|
||||
version "7.37.5"
|
||||
@@ -6135,7 +6198,7 @@ eslint-plugin-react@^7.37.5:
|
||||
string.prototype.matchall "^4.0.12"
|
||||
string.prototype.repeat "^1.0.0"
|
||||
|
||||
eslint-scope@5.1.1, eslint-scope@^5.1.1:
|
||||
eslint-scope@5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
|
||||
integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
|
||||
@@ -6143,80 +6206,82 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1:
|
||||
esrecurse "^4.3.0"
|
||||
estraverse "^4.1.1"
|
||||
|
||||
eslint-scope@^7.2.2:
|
||||
version "7.2.2"
|
||||
resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz"
|
||||
integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==
|
||||
eslint-scope@^8.4.0:
|
||||
version "8.4.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82"
|
||||
integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==
|
||||
dependencies:
|
||||
esrecurse "^4.3.0"
|
||||
estraverse "^5.2.0"
|
||||
|
||||
eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
|
||||
eslint-visitor-keys@^3.4.3:
|
||||
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==
|
||||
|
||||
eslint@^8.0.0:
|
||||
version "8.57.1"
|
||||
resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz"
|
||||
integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==
|
||||
eslint-visitor-keys@^4.2.1:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1"
|
||||
integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==
|
||||
|
||||
eslint@^9.31.0:
|
||||
version "9.31.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.31.0.tgz#9a488e6da75bbe05785cd62e43c5ea99356d21ba"
|
||||
integrity sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==
|
||||
dependencies:
|
||||
"@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"
|
||||
"@eslint-community/regexpp" "^4.12.1"
|
||||
"@eslint/config-array" "^0.21.0"
|
||||
"@eslint/config-helpers" "^0.3.0"
|
||||
"@eslint/core" "^0.15.0"
|
||||
"@eslint/eslintrc" "^3.3.1"
|
||||
"@eslint/js" "9.31.0"
|
||||
"@eslint/plugin-kit" "^0.3.1"
|
||||
"@humanfs/node" "^0.16.6"
|
||||
"@humanwhocodes/module-importer" "^1.0.1"
|
||||
"@nodelib/fs.walk" "^1.2.8"
|
||||
"@ungap/structured-clone" "^1.2.0"
|
||||
"@humanwhocodes/retry" "^0.4.2"
|
||||
"@types/estree" "^1.0.6"
|
||||
"@types/json-schema" "^7.0.15"
|
||||
ajv "^6.12.4"
|
||||
chalk "^4.0.0"
|
||||
cross-spawn "^7.0.2"
|
||||
cross-spawn "^7.0.6"
|
||||
debug "^4.3.2"
|
||||
doctrine "^3.0.0"
|
||||
escape-string-regexp "^4.0.0"
|
||||
eslint-scope "^7.2.2"
|
||||
eslint-visitor-keys "^3.4.3"
|
||||
espree "^9.6.1"
|
||||
esquery "^1.4.2"
|
||||
eslint-scope "^8.4.0"
|
||||
eslint-visitor-keys "^4.2.1"
|
||||
espree "^10.4.0"
|
||||
esquery "^1.5.0"
|
||||
esutils "^2.0.2"
|
||||
fast-deep-equal "^3.1.3"
|
||||
file-entry-cache "^6.0.1"
|
||||
file-entry-cache "^8.0.0"
|
||||
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"
|
||||
levn "^0.4.1"
|
||||
lodash.merge "^4.6.2"
|
||||
minimatch "^3.1.2"
|
||||
natural-compare "^1.4.0"
|
||||
optionator "^0.9.3"
|
||||
strip-ansi "^6.0.1"
|
||||
text-table "^0.2.0"
|
||||
|
||||
espree@^9.6.0, espree@^9.6.1:
|
||||
version "9.6.1"
|
||||
resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz"
|
||||
integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
|
||||
espree@^10.0.1, espree@^10.4.0:
|
||||
version "10.4.0"
|
||||
resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837"
|
||||
integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==
|
||||
dependencies:
|
||||
acorn "^8.9.0"
|
||||
acorn "^8.15.0"
|
||||
acorn-jsx "^5.3.2"
|
||||
eslint-visitor-keys "^3.4.1"
|
||||
eslint-visitor-keys "^4.2.1"
|
||||
|
||||
esprima@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
||||
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
|
||||
|
||||
esquery@^1.4.2:
|
||||
esquery@^1.5.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz"
|
||||
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"
|
||||
integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==
|
||||
dependencies:
|
||||
estraverse "^5.1.0"
|
||||
@@ -6411,7 +6476,7 @@ fast-diff@^1.1.2:
|
||||
resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz"
|
||||
integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
|
||||
|
||||
fast-glob@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.0:
|
||||
fast-glob@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.0, fast-glob@^3.3.2:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
|
||||
integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
|
||||
@@ -6484,12 +6549,12 @@ figures@^3.2.0:
|
||||
dependencies:
|
||||
escape-string-regexp "^1.0.5"
|
||||
|
||||
file-entry-cache@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz"
|
||||
integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
|
||||
file-entry-cache@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f"
|
||||
integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==
|
||||
dependencies:
|
||||
flat-cache "^3.0.4"
|
||||
flat-cache "^4.0.0"
|
||||
|
||||
file-loader@^6.2.0:
|
||||
version "6.2.0"
|
||||
@@ -6548,14 +6613,13 @@ find-up@^6.3.0:
|
||||
locate-path "^7.1.0"
|
||||
path-exists "^5.0.0"
|
||||
|
||||
flat-cache@^3.0.4:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz"
|
||||
integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==
|
||||
flat-cache@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c"
|
||||
integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==
|
||||
dependencies:
|
||||
flatted "^3.2.9"
|
||||
keyv "^4.5.3"
|
||||
rimraf "^3.0.2"
|
||||
keyv "^4.5.4"
|
||||
|
||||
flat@^5.0.2:
|
||||
version "5.0.2"
|
||||
@@ -6590,12 +6654,14 @@ form-data-encoder@^2.1.2:
|
||||
integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz"
|
||||
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.4.tgz#784cdcce0669a9d68e94d11ac4eea98088edd2c4"
|
||||
integrity sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
es-set-tostringtag "^2.1.0"
|
||||
hasown "^2.0.2"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
format@^0.2.0:
|
||||
@@ -6781,18 +6847,21 @@ globals@^11.1.0:
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
||||
|
||||
globals@^13.19.0:
|
||||
version "13.24.0"
|
||||
resolved "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz"
|
||||
integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==
|
||||
dependencies:
|
||||
type-fest "^0.20.2"
|
||||
globals@^14.0.0:
|
||||
version "14.0.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e"
|
||||
integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
|
||||
|
||||
globals@^15.14.0:
|
||||
version "15.15.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-15.15.0.tgz#7c4761299d41c32b075715a4ce1ede7897ff72a8"
|
||||
integrity sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==
|
||||
|
||||
globals@^16.3.0:
|
||||
version "16.3.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-16.3.0.tgz#66118e765ddaf9e2d880f7e17658543f93f1f667"
|
||||
integrity sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==
|
||||
|
||||
globalthis@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz"
|
||||
@@ -7285,6 +7354,11 @@ ignore@^5.2.0, ignore@^5.2.4:
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
|
||||
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
|
||||
|
||||
ignore@^7.0.0:
|
||||
version "7.0.5"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.5.tgz#4cb5f6cd7d4c7ab0365738c7aea888baa6d7efd9"
|
||||
integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==
|
||||
|
||||
image-size@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/image-size/-/image-size-2.0.2.tgz#84a7b43704db5736f364bf0d1b029821299b4bdc"
|
||||
@@ -7619,7 +7693,7 @@ is-obj@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
|
||||
integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
|
||||
|
||||
is-path-inside@^3.0.2, is-path-inside@^3.0.3:
|
||||
is-path-inside@^3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
|
||||
integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
|
||||
@@ -7921,7 +7995,7 @@ katex@^0.16.9:
|
||||
dependencies:
|
||||
commander "^8.3.0"
|
||||
|
||||
keyv@^4.5.3:
|
||||
keyv@^4.5.3, keyv@^4.5.4:
|
||||
version "4.5.4"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
|
||||
integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
|
||||
@@ -8949,7 +9023,7 @@ minimalistic-assert@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
|
||||
integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
|
||||
|
||||
minimatch@3.1.2, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||
minimatch@3.1.2, minimatch@^3.1.1, minimatch@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||
@@ -8963,6 +9037,13 @@ minimatch@^7.4.3:
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
minimatch@^9.0.4:
|
||||
version "9.0.5"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
|
||||
integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
minimist@^1.2.0:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
@@ -9011,11 +9092,6 @@ nanoid@^3.3.11, nanoid@^3.3.8:
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
|
||||
integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
|
||||
|
||||
natural-compare-lite@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz"
|
||||
integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==
|
||||
|
||||
natural-compare@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
|
||||
@@ -10165,10 +10241,10 @@ prettier-linter-helpers@^1.0.0:
|
||||
dependencies:
|
||||
fast-diff "^1.1.2"
|
||||
|
||||
prettier@^2.0.0:
|
||||
version "2.8.8"
|
||||
resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz"
|
||||
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
|
||||
prettier@^3.6.2:
|
||||
version "3.6.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.6.2.tgz#ccda02a1003ebbb2bfda6f83a074978f608b9393"
|
||||
integrity sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==
|
||||
|
||||
pretty-error@^4.0.0:
|
||||
version "4.0.0"
|
||||
@@ -11438,6 +11514,11 @@ semver@^7.3.5, semver@^7.3.7, semver@^7.5.4:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
|
||||
integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==
|
||||
|
||||
semver@^7.6.0:
|
||||
version "7.7.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
|
||||
integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
|
||||
|
||||
send@0.19.0:
|
||||
version "0.19.0"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
|
||||
@@ -12058,6 +12139,13 @@ swagger-ui-react@^5.26.0:
|
||||
xml-but-prettier "^1.0.1"
|
||||
zenscroll "^4.0.2"
|
||||
|
||||
synckit@^0.11.7:
|
||||
version "0.11.8"
|
||||
resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.8.tgz#b2aaae998a4ef47ded60773ad06e7cb821f55457"
|
||||
integrity sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==
|
||||
dependencies:
|
||||
"@pkgr/core" "^0.2.4"
|
||||
|
||||
tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
|
||||
@@ -12084,11 +12172,6 @@ terser@^5.10.0, terser@^5.15.1, terser@^5.31.1:
|
||||
commander "^2.20.0"
|
||||
source-map-support "~0.5.20"
|
||||
|
||||
text-table@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
|
||||
|
||||
throttle-debounce@^5.0.0, throttle-debounce@^5.0.2:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz"
|
||||
@@ -12180,6 +12263,11 @@ trough@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f"
|
||||
integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==
|
||||
|
||||
ts-api-utils@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91"
|
||||
integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==
|
||||
|
||||
ts-dedent@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5"
|
||||
@@ -12195,23 +12283,11 @@ ts-toolbelt@^9.6.0:
|
||||
resolved "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz"
|
||||
integrity sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==
|
||||
|
||||
tslib@^1.8.1:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
||||
tslib@^2.0.3, tslib@^2.3.0, tslib@^2.6.0:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
||||
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
||||
|
||||
tsutils@^3.21.0:
|
||||
version "3.21.0"
|
||||
resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz"
|
||||
integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
|
||||
dependencies:
|
||||
tslib "^1.8.1"
|
||||
|
||||
type-check@^0.4.0, type-check@~0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz"
|
||||
@@ -12306,6 +12382,16 @@ types-ramda@^0.30.0:
|
||||
dependencies:
|
||||
ts-toolbelt "^9.6.0"
|
||||
|
||||
typescript-eslint@^8.37.0:
|
||||
version "8.37.0"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.37.0.tgz#2235ddfa40cdbdadb1afb05f8bda688a2294b4c2"
|
||||
integrity sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin" "8.37.0"
|
||||
"@typescript-eslint/parser" "8.37.0"
|
||||
"@typescript-eslint/typescript-estree" "8.37.0"
|
||||
"@typescript-eslint/utils" "8.37.0"
|
||||
|
||||
typescript@~5.8.3:
|
||||
version "5.8.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e"
|
||||
|
||||
@@ -29,7 +29,7 @@ maintainers:
|
||||
- name: craig-rueda
|
||||
email: craig@craigrueda.com
|
||||
url: https://github.com/craig-rueda
|
||||
version: 0.14.2
|
||||
version: 0.14.3
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
version: 13.4.4
|
||||
|
||||
@@ -23,7 +23,7 @@ NOTE: This file is generated by helm-docs: https://github.com/norwoodj/helm-docs
|
||||
|
||||
# superset
|
||||
|
||||

|
||||

|
||||
|
||||
Apache Superset is a modern, enterprise-ready business intelligence web application
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ metadata:
|
||||
chart: {{ template "superset.chart" . }}
|
||||
release: {{ .Release.Name }}
|
||||
heritage: {{ .Release.Service }}
|
||||
{{- if .Values.extraLabels }}
|
||||
{{- toYaml .Values.extraLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.extraLabels }}
|
||||
{{- toYaml .Values.extraLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- if .Values.init.jobAnnotations }}
|
||||
annotations: {{- toYaml .Values.init.jobAnnotations | nindent 4 }}
|
||||
{{- end }}
|
||||
@@ -44,10 +44,10 @@ spec:
|
||||
{{- if or .Values.extraLabels .Values.init.podLabels }}
|
||||
labels:
|
||||
{{- if .Values.extraLabels }}
|
||||
{{- toYaml .Values.extraLabels | nindent 8 }}
|
||||
{{- toYaml .Values.extraLabels | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.init.podLabels }}
|
||||
{{- toYaml .Values.init.podLabels | nindent 8 }}
|
||||
{{- toYaml .Values.init.podLabels | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
spec:
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
{{- with .Values.supersetCeleryBeat.podDisruptionBudget }}
|
||||
{{- if .enabled -}}
|
||||
{{- if and .minAvailable .maxUnavailable }}
|
||||
{{- fail "Only one of minAvailable or maxUnavailable should be set" }}
|
||||
{{- fail "Only one of minAvailable or maxUnavailable should be set" }}
|
||||
{{- end}}
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
@@ -35,12 +35,12 @@ metadata:
|
||||
{{- toYaml $.Values.extraLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .minAvailable }}
|
||||
{{- if .minAvailable }}
|
||||
minAvailable: {{ .minAvailable }}
|
||||
{{- end }}
|
||||
{{- if .maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- if .maxUnavailable }}
|
||||
maxUnavailable: {{ .maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "supersetCeleryBeat.selectorLabels" $ | nindent 6 }}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
{{- with .Values.supersetCeleryFlower.podDisruptionBudget }}
|
||||
{{- if .enabled -}}
|
||||
{{- if and .minAvailable .maxUnavailable }}
|
||||
{{- fail "Only one of minAvailable or maxUnavailable should be set" }}
|
||||
{{- fail "Only one of minAvailable or maxUnavailable should be set" }}
|
||||
{{- end}}
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
@@ -35,12 +35,12 @@ metadata:
|
||||
{{- toYaml $.Values.extraLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .minAvailable }}
|
||||
{{- if .minAvailable }}
|
||||
minAvailable: {{ .minAvailable }}
|
||||
{{- end }}
|
||||
{{- if .maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- if .maxUnavailable }}
|
||||
maxUnavailable: {{ .maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "supersetCeleryFlower.selectorLabels" $ | nindent 6 }}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
{{- with .Values.supersetWorker.podDisruptionBudget }}
|
||||
{{- if .enabled -}}
|
||||
{{- if and .minAvailable .maxUnavailable }}
|
||||
{{- fail "Only one of minAvailable or maxUnavailable should be set" }}
|
||||
{{- fail "Only one of minAvailable or maxUnavailable should be set" }}
|
||||
{{- end}}
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
@@ -35,12 +35,12 @@ metadata:
|
||||
{{- toYaml $.Values.extraLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .minAvailable }}
|
||||
{{- if .minAvailable }}
|
||||
minAvailable: {{ .minAvailable }}
|
||||
{{- end }}
|
||||
{{- if .maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- if .maxUnavailable }}
|
||||
maxUnavailable: {{ .maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "supersetWorker.selectorLabels" $ | nindent 6 }}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
{{- with .Values.supersetWebsockets.podDisruptionBudget }}
|
||||
{{- if .enabled -}}
|
||||
{{- if and .minAvailable .maxUnavailable }}
|
||||
{{- fail "Only one of minAvailable or maxUnavailable should be set" }}
|
||||
{{- fail "Only one of minAvailable or maxUnavailable should be set" }}
|
||||
{{- end}}
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
@@ -35,12 +35,12 @@ metadata:
|
||||
{{- toYaml $.Values.extraLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .minAvailable }}
|
||||
{{- if .minAvailable }}
|
||||
minAvailable: {{ .minAvailable }}
|
||||
{{- end }}
|
||||
{{- if .maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- if .maxUnavailable }}
|
||||
maxUnavailable: {{ .maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "supersetWebsockets.selectorLabels" $ | nindent 6 }}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
{{- with .Values.supersetNode.podDisruptionBudget }}
|
||||
{{- if .enabled -}}
|
||||
{{- if and .minAvailable .maxUnavailable }}
|
||||
{{- fail "Only one of minAvailable or maxUnavailable should be set" }}
|
||||
{{- fail "Only one of minAvailable or maxUnavailable should be set" }}
|
||||
{{- end}}
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
@@ -35,12 +35,12 @@ metadata:
|
||||
{{- toYaml $.Values.extraLabels | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .minAvailable }}
|
||||
{{- if .minAvailable }}
|
||||
minAvailable: {{ .minAvailable }}
|
||||
{{- end }}
|
||||
{{- if .maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- if .maxUnavailable }}
|
||||
maxUnavailable: {{ .maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "supersetNode.selectorLabels" $ | nindent 6 }}
|
||||
|
||||
360
superset-embedded-sdk/package-lock.json
generated
360
superset-embedded-sdk/package-lock.json
generated
@@ -3251,20 +3251,6 @@
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios/node_modules/form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-jest": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
|
||||
@@ -3658,6 +3644,19 @@
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@@ -4064,6 +4063,20 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.19",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.19.tgz",
|
||||
@@ -4124,12 +4137,57 @@
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz",
|
||||
"integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-set-tostringtag": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
|
||||
@@ -4590,6 +4648,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/fs-readdir-recursive": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
|
||||
@@ -4617,10 +4691,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
@@ -4640,6 +4717,30 @@
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-package-type": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
|
||||
@@ -4649,6 +4750,19 @@
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/get-stream": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||
@@ -4711,6 +4825,18 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
@@ -4739,6 +4865,45 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-symbols": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/html-escaper": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
||||
@@ -6927,6 +7092,15 @@
|
||||
"tmpl": "1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
@@ -10565,19 +10739,6 @@
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"form-data": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
||||
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"babel-jest": {
|
||||
@@ -10862,6 +11023,16 @@
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"call-bind-apply-helpers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
||||
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
@@ -11140,6 +11311,17 @@
|
||||
"integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
|
||||
"dev": true
|
||||
},
|
||||
"dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind-apply-helpers": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.5.19",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.19.tgz",
|
||||
@@ -11183,12 +11365,45 @@
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
||||
"dev": true
|
||||
},
|
||||
"es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"dev": true
|
||||
},
|
||||
"es-module-lexer": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz",
|
||||
"integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==",
|
||||
"dev": true
|
||||
},
|
||||
"es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es-errors": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"es-set-tostringtag": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.6",
|
||||
"has-tostringtag": "^1.0.2",
|
||||
"hasown": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
|
||||
@@ -11503,6 +11718,19 @@
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||
"dev": true
|
||||
},
|
||||
"form-data": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"fs-readdir-recursive": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
|
||||
@@ -11523,9 +11751,9 @@
|
||||
"optional": true
|
||||
},
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"dev": true
|
||||
},
|
||||
"gensync": {
|
||||
@@ -11540,12 +11768,40 @@
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"dev": true
|
||||
},
|
||||
"get-intrinsic": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind-apply-helpers": "^1.0.2",
|
||||
"es-define-property": "^1.0.1",
|
||||
"es-errors": "^1.3.0",
|
||||
"es-object-atoms": "^1.1.1",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-proto": "^1.0.1",
|
||||
"gopd": "^1.2.0",
|
||||
"has-symbols": "^1.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"math-intrinsics": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"get-package-type": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
|
||||
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
|
||||
"dev": true
|
||||
},
|
||||
"get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"dunder-proto": "^1.0.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"get-stream": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
|
||||
@@ -11588,6 +11844,12 @@
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||
"dev": true
|
||||
},
|
||||
"gopd": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
||||
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
||||
"dev": true
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||
@@ -11609,6 +11871,30 @@
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"dev": true
|
||||
},
|
||||
"has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"has-tostringtag": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-symbols": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"html-escaper": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
|
||||
@@ -13219,6 +13505,12 @@
|
||||
"tmpl": "1.0.5"
|
||||
}
|
||||
},
|
||||
"math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
||||
"dev": true
|
||||
},
|
||||
"merge-stream": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
|
||||
|
||||
@@ -56,7 +56,7 @@ module.exports = {
|
||||
],
|
||||
coverageReporters: ['lcov', 'json-summary', 'html', 'text'],
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!d3-(interpolate|color|time)|remark-gfm|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|@rjsf/*.|sinon|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-serializer|entities|htmlparser2|rehype-sanitize|hast-util-sanitize|unified|unist-.*|hast-.*|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|geostyler|geostyler-.*|react-error-boundary|react-json-tree|react-base16-styling|lodash-es)',
|
||||
'node_modules/(?!d3-(interpolate|color|time|scale)|@mapbox/tiny-sdf|remark-gfm|(?!@ngrx|(?!deck.gl)|d3-scale)|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|@rjsf/*.|sinon|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-serializer|entities|htmlparser2|rehype-sanitize|hast-util-sanitize|unified|unist-.*|hast-.*|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|geostyler|geostyler-.*|react-error-boundary|react-json-tree|react-base16-styling|lodash-es)',
|
||||
],
|
||||
preset: 'ts-jest',
|
||||
transform: {
|
||||
|
||||
518
superset-frontend/package-lock.json
generated
518
superset-frontend/package-lock.json
generated
@@ -135,6 +135,7 @@
|
||||
"use-event-callback": "^0.1.0",
|
||||
"use-immer": "^0.9.0",
|
||||
"use-query-params": "^1.1.9",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -3969,20 +3970,38 @@
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@deck.gl/core": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/core/-/core-9.1.0.tgz",
|
||||
"integrity": "sha512-leocNGky9jZ0HUk8xm+HH4f+EL86PKiG8O4REpw4l/K1UbMssekR/L2moYwt7Bx98jSIibcUbhDWOBatKAUaTA==",
|
||||
"node_modules/@deck.gl/aggregation-layers": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/aggregation-layers/-/aggregation-layers-9.1.13.tgz",
|
||||
"integrity": "sha512-eDuT4S7GRx8LWdPuxGIiK8MfBynfvj3PgNB5mB1uiXcp1OR2eZ17wr3QBp1Rdk4LUsx1P1CkDyyIvi5mn4+aQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@luma.gl/constants": "^9.1.5",
|
||||
"@luma.gl/shadertools": "^9.1.5",
|
||||
"@math.gl/core": "^4.1.0",
|
||||
"@math.gl/web-mercator": "^4.1.0",
|
||||
"d3-hexbin": "^0.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deck.gl/core": "^9.1.0",
|
||||
"@deck.gl/layers": "^9.1.0",
|
||||
"@luma.gl/core": "^9.1.5",
|
||||
"@luma.gl/engine": "^9.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@deck.gl/core": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/core/-/core-9.1.13.tgz",
|
||||
"integrity": "sha512-c15DpwUEvDjmt3+/azSjcfhVQ5L5HiIj6LJob1KAwQOnB5zgVdKWukN/21ELQ7ekppEkfT0x4byRv5k4QVocqQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@loaders.gl/core": "^4.2.0",
|
||||
"@loaders.gl/images": "^4.2.0",
|
||||
"@luma.gl/constants": "^9.1.0",
|
||||
"@luma.gl/core": "^9.1.0",
|
||||
"@luma.gl/engine": "^9.1.0",
|
||||
"@luma.gl/shadertools": "^9.1.0",
|
||||
"@luma.gl/webgl": "^9.1.0",
|
||||
"@luma.gl/constants": "^9.1.5",
|
||||
"@luma.gl/core": "^9.1.5",
|
||||
"@luma.gl/engine": "^9.1.5",
|
||||
"@luma.gl/shadertools": "^9.1.5",
|
||||
"@luma.gl/webgl": "^9.1.5",
|
||||
"@math.gl/core": "^4.1.0",
|
||||
"@math.gl/sun": "^4.1.0",
|
||||
"@math.gl/types": "^4.1.0",
|
||||
@@ -3995,10 +4014,111 @@
|
||||
"mjolnir.js": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@deck.gl/extensions": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/extensions/-/extensions-9.1.13.tgz",
|
||||
"integrity": "sha512-Y6XCjXckcXyU+NhaDW4GA6nw9BAanFKNtltHcR+GUivGiK+QuBXlIggl+QLkWXD1EKp3os/DOM8kO0FmtAaC9A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@luma.gl/constants": "^9.1.5",
|
||||
"@luma.gl/shadertools": "^9.1.5",
|
||||
"@math.gl/core": "^4.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deck.gl/core": "^9.1.0",
|
||||
"@luma.gl/core": "^9.1.5",
|
||||
"@luma.gl/engine": "^9.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@deck.gl/geo-layers": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/geo-layers/-/geo-layers-9.1.13.tgz",
|
||||
"integrity": "sha512-+6GLQacUzQHcGraKCuDV6z1U44mJ08eg2/gaQGDJYUwh+YUCiyW1uWey5GvV9nRcaS47UApYxEDQZGpSamZT+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@loaders.gl/3d-tiles": "^4.2.0",
|
||||
"@loaders.gl/gis": "^4.2.0",
|
||||
"@loaders.gl/loader-utils": "^4.2.0",
|
||||
"@loaders.gl/mvt": "^4.2.0",
|
||||
"@loaders.gl/schema": "^4.2.0",
|
||||
"@loaders.gl/terrain": "^4.2.0",
|
||||
"@loaders.gl/tiles": "^4.2.0",
|
||||
"@loaders.gl/wms": "^4.2.0",
|
||||
"@luma.gl/gltf": "^9.1.5",
|
||||
"@luma.gl/shadertools": "^9.1.5",
|
||||
"@math.gl/core": "^4.1.0",
|
||||
"@math.gl/culling": "^4.1.0",
|
||||
"@math.gl/web-mercator": "^4.1.0",
|
||||
"@types/geojson": "^7946.0.8",
|
||||
"h3-js": "^4.1.0",
|
||||
"long": "^3.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deck.gl/core": "^9.1.0",
|
||||
"@deck.gl/extensions": "^9.1.0",
|
||||
"@deck.gl/layers": "^9.1.0",
|
||||
"@deck.gl/mesh-layers": "^9.1.0",
|
||||
"@loaders.gl/core": "^4.2.0",
|
||||
"@luma.gl/core": "^9.1.5",
|
||||
"@luma.gl/engine": "^9.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@deck.gl/layers": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/layers/-/layers-9.1.13.tgz",
|
||||
"integrity": "sha512-2eD2uARmObtCXrc1Q051fqy+LS2w6a700qPerqtqz+J/bOWTHSEZxAdIoHawDU7g+fi4/1lti0m8bdp2X/kZLA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@loaders.gl/images": "^4.2.0",
|
||||
"@loaders.gl/schema": "^4.2.0",
|
||||
"@luma.gl/shadertools": "^9.1.5",
|
||||
"@mapbox/tiny-sdf": "^2.0.5",
|
||||
"@math.gl/core": "^4.1.0",
|
||||
"@math.gl/polygon": "^4.1.0",
|
||||
"@math.gl/web-mercator": "^4.1.0",
|
||||
"earcut": "^2.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deck.gl/core": "^9.1.0",
|
||||
"@loaders.gl/core": "^4.2.0",
|
||||
"@luma.gl/core": "^9.1.5",
|
||||
"@luma.gl/engine": "^9.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@deck.gl/mesh-layers": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/mesh-layers/-/mesh-layers-9.1.13.tgz",
|
||||
"integrity": "sha512-ujhe9FtB4qRRCXH/hY5p+IQ5VO/AC+/dtern6CTzYzjGnUnAvsbIgBZ3jxSlb1B/D3wlVE778W2cmv7MIToJJg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@loaders.gl/gltf": "^4.2.0",
|
||||
"@luma.gl/gltf": "^9.1.5",
|
||||
"@luma.gl/shadertools": "^9.1.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deck.gl/core": "^9.1.0",
|
||||
"@luma.gl/core": "^9.1.5",
|
||||
"@luma.gl/engine": "^9.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@deck.gl/react": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/react/-/react-9.1.13.tgz",
|
||||
"integrity": "sha512-9tu5roGzvy4plLvUVUxDpEy3KHDxpAEBRdM/qC06Ijognfl/A9bECdQt4cqqGflkvg6VzQaw2Cy7eIRvzhXJbA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@deck.gl/core": "^9.1.0",
|
||||
"@deck.gl/widgets": "^9.1.0",
|
||||
"react": ">=16.3.0",
|
||||
"react-dom": ">=16.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@deck.gl/widgets": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/widgets/-/widgets-9.1.0.tgz",
|
||||
"integrity": "sha512-tsej/rTemNe3Xm/z4kO4wa+d89tMNLeMuVuv66vsT8LsNvLbz/SchdXYCRXqsvvYx/9wgurkSilPiv/j6epQoA==",
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/widgets/-/widgets-9.1.13.tgz",
|
||||
"integrity": "sha512-6nriLKNzXovWrm4Lj9MAdYf2W9/bSwJ1Rlq4jc8WvrOr1wtIJ7j6NdHlfGUs2Vv1PLt72M0jSqMwHQQevLvsqQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -8429,18 +8549,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@luma.gl/constants": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/constants/-/constants-9.1.0.tgz",
|
||||
"integrity": "sha512-BIkRHF36eE1FoghbEKzBjbs7+tX6RUH7gI7ZFKzVJEgXvT6xg12HM7uk+6L54fR/rUxEMjgL+uRzIxprCOGjOg==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"version": "9.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/constants/-/constants-9.1.9.tgz",
|
||||
"integrity": "sha512-yc9fml04OeTTcwK+7gmDMxoLQ67j4ZiAFXjmYvPomYyBVzS0NZxTDuwcCBmnxjLOiroOZW8FRRrVc/yOiFug2w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@luma.gl/core": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/core/-/core-9.1.0.tgz",
|
||||
"integrity": "sha512-HkcqDlxal6gOP7Y6KTRcEjnPuxSFMy+oJYfk623TGIxrEbN3x5uLqvbNgqLMXhV60WWq5Fj0LG1gHs1NyJHrLg==",
|
||||
"version": "9.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/core/-/core-9.1.9.tgz",
|
||||
"integrity": "sha512-1i9N7+I/UbFjx3axSMlc3/NufA+C2iBv/7mw51gRE/ypQPgvFmY/QqXBVZRe+nthF+OhlUMhO19TBndzYFTWhA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@math.gl/types": "^4.1.0",
|
||||
"@probe.gl/env": "^4.0.8",
|
||||
@@ -8450,11 +8568,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@luma.gl/engine": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/engine/-/engine-9.1.0.tgz",
|
||||
"integrity": "sha512-fKa4XqUqS/wmhAPlmkemjJ6YZM3QEzRWX1bZXtVCsydZOun8KCVZsSMpCj1W1+cpoAOBVIqvBqZFF8fZClj5XQ==",
|
||||
"version": "9.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/engine/-/engine-9.1.9.tgz",
|
||||
"integrity": "sha512-n1GLK1sUMFkWxdb+aZYn6ZBFltFEMi7X+6ZPxn2pBsNT6oeF4AyvH5AyqhOpvHvUnCLDt3Zsf1UIfx3MI//YSw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@math.gl/core": "^4.1.0",
|
||||
"@math.gl/types": "^4.1.0",
|
||||
@@ -8462,8 +8579,8 @@
|
||||
"@probe.gl/stats": "^4.0.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@luma.gl/core": "^9.1.0-beta.1",
|
||||
"@luma.gl/shadertools": "^9.1.0-beta.1"
|
||||
"@luma.gl/core": "^9.1.0",
|
||||
"@luma.gl/shadertools": "^9.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@luma.gl/gltf": {
|
||||
@@ -8483,33 +8600,31 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@luma.gl/shadertools": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/shadertools/-/shadertools-9.1.0.tgz",
|
||||
"integrity": "sha512-BRDKnf2g+Xq86f1OK00F2PA2QbmkcKiM8HJ/Iw8wZB3DvPu2jBKBaboHmEoo6gxq46P32vFGyvxso8umai5eJw==",
|
||||
"version": "9.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/shadertools/-/shadertools-9.1.9.tgz",
|
||||
"integrity": "sha512-Uqp2xfgIEunRMLXTeCJ4uEMlWcUGcYMZGJ8GAOrAeDzn4bMKVRKmZDC71vkuTctnaodM3UdrI9W6s1sJlrXsxw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@math.gl/core": "^4.1.0",
|
||||
"@math.gl/types": "^4.1.0",
|
||||
"wgsl_reflect": "^1.0.1"
|
||||
"wgsl_reflect": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@luma.gl/core": "^9.1.0-beta.1"
|
||||
"@luma.gl/core": "^9.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@luma.gl/webgl": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/webgl/-/webgl-9.1.0.tgz",
|
||||
"integrity": "sha512-dTftLUfOnW6F9vYOl1ZvO2I28OYFdiqHkN7BpPd+8GPzepFT8OtEZwbcb/JjF9TsVhaeLyl1oDckQg2ckre3sw==",
|
||||
"version": "9.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/webgl/-/webgl-9.1.9.tgz",
|
||||
"integrity": "sha512-jecHjhNSWkXH0v62rM6G5fIIkOmsrND27099iKgdutFvHIvd4QS4UzGWEEa9AEPlP0rTLqXkA6y6YL7f42ZkVg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@luma.gl/constants": "9.1.0",
|
||||
"@luma.gl/constants": "9.1.9",
|
||||
"@math.gl/types": "^4.1.0",
|
||||
"@probe.gl/env": "^4.0.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@luma.gl/core": "^9.1.0-alpha.1"
|
||||
"@luma.gl/core": "^9.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/extent": {
|
||||
@@ -14944,6 +15059,21 @@
|
||||
"@turf/invariant": "^5.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@turf/boolean-clockwise/node_modules/@turf/helpers": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz",
|
||||
"integrity": "sha512-/lF+JR+qNDHZ8bF9d+Cp58nxtZWJ3sqFe6n3u3Vpj+/0cqkjk4nXKYBSY0azm+GIYB5mWKxUXvuP/m0ZnKj1bw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@turf/boolean-clockwise/node_modules/@turf/invariant": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-5.2.0.tgz",
|
||||
"integrity": "sha512-28RCBGvCYsajVkw2EydpzLdcYyhSA77LovuOvgCJplJWaNVyJYH6BOR3HR9w50MEkPqb/Vc/jdo6I6ermlRtQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@turf/helpers": "^5.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@turf/clone": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@turf/clone/-/clone-5.1.5.tgz",
|
||||
@@ -14953,30 +15083,12 @@
|
||||
"@turf/helpers": "^5.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@turf/helpers": {
|
||||
"node_modules/@turf/clone/node_modules/@turf/helpers": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz",
|
||||
"integrity": "sha512-/lF+JR+qNDHZ8bF9d+Cp58nxtZWJ3sqFe6n3u3Vpj+/0cqkjk4nXKYBSY0azm+GIYB5mWKxUXvuP/m0ZnKj1bw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@turf/invariant": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-5.2.0.tgz",
|
||||
"integrity": "sha512-28RCBGvCYsajVkw2EydpzLdcYyhSA77LovuOvgCJplJWaNVyJYH6BOR3HR9w50MEkPqb/Vc/jdo6I6ermlRtQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@turf/helpers": "^5.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@turf/meta": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@turf/meta/-/meta-5.2.0.tgz",
|
||||
"integrity": "sha512-ZjQ3Ii62X9FjnK4hhdsbT+64AYRpaI8XMBMcyftEOGSmPMUVnkbvuv3C9geuElAXfQU7Zk1oWGOcrGOD9zr78Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@turf/helpers": "^5.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@turf/rewind": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@turf/rewind/-/rewind-5.1.5.tgz",
|
||||
@@ -14990,6 +15102,30 @@
|
||||
"@turf/meta": "^5.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@turf/rewind/node_modules/@turf/helpers": {
|
||||
"version": "5.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-5.1.5.tgz",
|
||||
"integrity": "sha512-/lF+JR+qNDHZ8bF9d+Cp58nxtZWJ3sqFe6n3u3Vpj+/0cqkjk4nXKYBSY0azm+GIYB5mWKxUXvuP/m0ZnKj1bw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@turf/rewind/node_modules/@turf/invariant": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-5.2.0.tgz",
|
||||
"integrity": "sha512-28RCBGvCYsajVkw2EydpzLdcYyhSA77LovuOvgCJplJWaNVyJYH6BOR3HR9w50MEkPqb/Vc/jdo6I6ermlRtQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@turf/helpers": "^5.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@turf/rewind/node_modules/@turf/meta": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@turf/meta/-/meta-5.2.0.tgz",
|
||||
"integrity": "sha512-ZjQ3Ii62X9FjnK4hhdsbT+64AYRpaI8XMBMcyftEOGSmPMUVnkbvuv3C9geuElAXfQU7Zk1oWGOcrGOD9zr78Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@turf/helpers": "^5.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@tybys/wasm-util": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz",
|
||||
@@ -21675,9 +21811,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/compression": {
|
||||
"version": "1.7.5",
|
||||
"resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz",
|
||||
"integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==",
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
|
||||
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -21685,7 +21821,7 @@
|
||||
"compressible": "~2.0.18",
|
||||
"debug": "2.6.9",
|
||||
"negotiator": "~0.6.4",
|
||||
"on-headers": "~1.0.2",
|
||||
"on-headers": "~1.1.0",
|
||||
"safe-buffer": "5.2.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
@@ -27452,14 +27588,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/form-data": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
|
||||
"integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
|
||||
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.8",
|
||||
"es-set-tostringtag": "^2.1.0",
|
||||
"hasown": "^2.0.2",
|
||||
"mime-types": "^2.1.12"
|
||||
},
|
||||
"engines": {
|
||||
@@ -37527,9 +37665,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz",
|
||||
"integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==",
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz",
|
||||
"integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -43988,9 +44126,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
|
||||
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -57374,11 +57512,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/wgsl_reflect": {
|
||||
"version": "1.0.16",
|
||||
"resolved": "https://registry.npmjs.org/wgsl_reflect/-/wgsl_reflect-1.0.16.tgz",
|
||||
"integrity": "sha512-OE3urfXXbHMD5lhKZwxOxC9SFYynEGEkWXQmvi7B1gzzr5jb9+drh9A8MeBvVqKqznCoBuh8WOzVuSGSZs4CkQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/wgsl_reflect/-/wgsl_reflect-1.2.1.tgz",
|
||||
"integrity": "sha512-PY6MdLqLW1NFj/V6f5/9/Nb4ultwlAF7lCLyjKOAkdnlf7LlrGXNFXzHHEV7Okg1zy4C4TpBIcc/G3PXW4py8g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/whatwg-encoding": {
|
||||
"version": "3.1.1",
|
||||
@@ -57722,6 +57859,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xlsx": {
|
||||
"version": "0.20.3",
|
||||
"resolved": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"integrity": "sha512-oLDq3jw7AcLqKWH2AhCpVTZl8mf6X2YReP+Neh0SJUzV/BdZYjth94tG5toiMB1PPrYtxOCfaoUCkvtuH+3AJA==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"xlsx": "bin/xlsx.njs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/xml-name-validator": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
|
||||
@@ -60875,6 +61024,7 @@
|
||||
"@luma.gl/shadertools": "^9.1.9",
|
||||
"@luma.gl/webgl": "^9.1.9",
|
||||
"@mapbox/geojson-extent": "^1.0.1",
|
||||
"@mapbox/tiny-sdf": "^2.0.6",
|
||||
"@math.gl/web-mercator": "^4.1.0",
|
||||
"@types/d3-array": "^2.0.0",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
@@ -60905,214 +61055,6 @@
|
||||
"react-map-gl": "^6.1.19"
|
||||
}
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/@deck.gl/aggregation-layers": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/aggregation-layers/-/aggregation-layers-9.1.13.tgz",
|
||||
"integrity": "sha512-eDuT4S7GRx8LWdPuxGIiK8MfBynfvj3PgNB5mB1uiXcp1OR2eZ17wr3QBp1Rdk4LUsx1P1CkDyyIvi5mn4+aQA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@luma.gl/constants": "^9.1.5",
|
||||
"@luma.gl/shadertools": "^9.1.5",
|
||||
"@math.gl/core": "^4.1.0",
|
||||
"@math.gl/web-mercator": "^4.1.0",
|
||||
"d3-hexbin": "^0.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deck.gl/core": "^9.1.0",
|
||||
"@deck.gl/layers": "^9.1.0",
|
||||
"@luma.gl/core": "^9.1.5",
|
||||
"@luma.gl/engine": "^9.1.5"
|
||||
}
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/@deck.gl/core": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/core/-/core-9.1.13.tgz",
|
||||
"integrity": "sha512-c15DpwUEvDjmt3+/azSjcfhVQ5L5HiIj6LJob1KAwQOnB5zgVdKWukN/21ELQ7ekppEkfT0x4byRv5k4QVocqQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@loaders.gl/core": "^4.2.0",
|
||||
"@loaders.gl/images": "^4.2.0",
|
||||
"@luma.gl/constants": "^9.1.5",
|
||||
"@luma.gl/core": "^9.1.5",
|
||||
"@luma.gl/engine": "^9.1.5",
|
||||
"@luma.gl/shadertools": "^9.1.5",
|
||||
"@luma.gl/webgl": "^9.1.5",
|
||||
"@math.gl/core": "^4.1.0",
|
||||
"@math.gl/sun": "^4.1.0",
|
||||
"@math.gl/types": "^4.1.0",
|
||||
"@math.gl/web-mercator": "^4.1.0",
|
||||
"@probe.gl/env": "^4.1.0",
|
||||
"@probe.gl/log": "^4.1.0",
|
||||
"@probe.gl/stats": "^4.1.0",
|
||||
"@types/offscreencanvas": "^2019.6.4",
|
||||
"gl-matrix": "^3.0.0",
|
||||
"mjolnir.js": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/@deck.gl/extensions": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/extensions/-/extensions-9.1.13.tgz",
|
||||
"integrity": "sha512-Y6XCjXckcXyU+NhaDW4GA6nw9BAanFKNtltHcR+GUivGiK+QuBXlIggl+QLkWXD1EKp3os/DOM8kO0FmtAaC9A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@luma.gl/constants": "^9.1.5",
|
||||
"@luma.gl/shadertools": "^9.1.5",
|
||||
"@math.gl/core": "^4.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deck.gl/core": "^9.1.0",
|
||||
"@luma.gl/core": "^9.1.5",
|
||||
"@luma.gl/engine": "^9.1.5"
|
||||
}
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/@deck.gl/geo-layers": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/geo-layers/-/geo-layers-9.1.13.tgz",
|
||||
"integrity": "sha512-+6GLQacUzQHcGraKCuDV6z1U44mJ08eg2/gaQGDJYUwh+YUCiyW1uWey5GvV9nRcaS47UApYxEDQZGpSamZT+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@loaders.gl/3d-tiles": "^4.2.0",
|
||||
"@loaders.gl/gis": "^4.2.0",
|
||||
"@loaders.gl/loader-utils": "^4.2.0",
|
||||
"@loaders.gl/mvt": "^4.2.0",
|
||||
"@loaders.gl/schema": "^4.2.0",
|
||||
"@loaders.gl/terrain": "^4.2.0",
|
||||
"@loaders.gl/tiles": "^4.2.0",
|
||||
"@loaders.gl/wms": "^4.2.0",
|
||||
"@luma.gl/gltf": "^9.1.5",
|
||||
"@luma.gl/shadertools": "^9.1.5",
|
||||
"@math.gl/core": "^4.1.0",
|
||||
"@math.gl/culling": "^4.1.0",
|
||||
"@math.gl/web-mercator": "^4.1.0",
|
||||
"@types/geojson": "^7946.0.8",
|
||||
"h3-js": "^4.1.0",
|
||||
"long": "^3.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deck.gl/core": "^9.1.0",
|
||||
"@deck.gl/extensions": "^9.1.0",
|
||||
"@deck.gl/layers": "^9.1.0",
|
||||
"@deck.gl/mesh-layers": "^9.1.0",
|
||||
"@loaders.gl/core": "^4.2.0",
|
||||
"@luma.gl/core": "^9.1.5",
|
||||
"@luma.gl/engine": "^9.1.5"
|
||||
}
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/@deck.gl/layers": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/layers/-/layers-9.1.13.tgz",
|
||||
"integrity": "sha512-2eD2uARmObtCXrc1Q051fqy+LS2w6a700qPerqtqz+J/bOWTHSEZxAdIoHawDU7g+fi4/1lti0m8bdp2X/kZLA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@loaders.gl/images": "^4.2.0",
|
||||
"@loaders.gl/schema": "^4.2.0",
|
||||
"@luma.gl/shadertools": "^9.1.5",
|
||||
"@mapbox/tiny-sdf": "^2.0.5",
|
||||
"@math.gl/core": "^4.1.0",
|
||||
"@math.gl/polygon": "^4.1.0",
|
||||
"@math.gl/web-mercator": "^4.1.0",
|
||||
"earcut": "^2.2.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deck.gl/core": "^9.1.0",
|
||||
"@loaders.gl/core": "^4.2.0",
|
||||
"@luma.gl/core": "^9.1.5",
|
||||
"@luma.gl/engine": "^9.1.5"
|
||||
}
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/@deck.gl/mesh-layers": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/mesh-layers/-/mesh-layers-9.1.13.tgz",
|
||||
"integrity": "sha512-ujhe9FtB4qRRCXH/hY5p+IQ5VO/AC+/dtern6CTzYzjGnUnAvsbIgBZ3jxSlb1B/D3wlVE778W2cmv7MIToJJg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@loaders.gl/gltf": "^4.2.0",
|
||||
"@luma.gl/gltf": "^9.1.5",
|
||||
"@luma.gl/shadertools": "^9.1.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@deck.gl/core": "^9.1.0",
|
||||
"@luma.gl/core": "^9.1.5",
|
||||
"@luma.gl/engine": "^9.1.5"
|
||||
}
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/@deck.gl/react": {
|
||||
"version": "9.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@deck.gl/react/-/react-9.1.13.tgz",
|
||||
"integrity": "sha512-9tu5roGzvy4plLvUVUxDpEy3KHDxpAEBRdM/qC06Ijognfl/A9bECdQt4cqqGflkvg6VzQaw2Cy7eIRvzhXJbA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@deck.gl/core": "^9.1.0",
|
||||
"@deck.gl/widgets": "^9.1.0",
|
||||
"react": ">=16.3.0",
|
||||
"react-dom": ">=16.3.0"
|
||||
}
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/@luma.gl/constants": {
|
||||
"version": "9.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/constants/-/constants-9.1.9.tgz",
|
||||
"integrity": "sha512-yc9fml04OeTTcwK+7gmDMxoLQ67j4ZiAFXjmYvPomYyBVzS0NZxTDuwcCBmnxjLOiroOZW8FRRrVc/yOiFug2w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/@luma.gl/core": {
|
||||
"version": "9.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/core/-/core-9.1.9.tgz",
|
||||
"integrity": "sha512-1i9N7+I/UbFjx3axSMlc3/NufA+C2iBv/7mw51gRE/ypQPgvFmY/QqXBVZRe+nthF+OhlUMhO19TBndzYFTWhA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@math.gl/types": "^4.1.0",
|
||||
"@probe.gl/env": "^4.0.8",
|
||||
"@probe.gl/log": "^4.0.8",
|
||||
"@probe.gl/stats": "^4.0.8",
|
||||
"@types/offscreencanvas": "^2019.6.4"
|
||||
}
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/@luma.gl/engine": {
|
||||
"version": "9.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/engine/-/engine-9.1.9.tgz",
|
||||
"integrity": "sha512-n1GLK1sUMFkWxdb+aZYn6ZBFltFEMi7X+6ZPxn2pBsNT6oeF4AyvH5AyqhOpvHvUnCLDt3Zsf1UIfx3MI//YSw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@math.gl/core": "^4.1.0",
|
||||
"@math.gl/types": "^4.1.0",
|
||||
"@probe.gl/log": "^4.0.8",
|
||||
"@probe.gl/stats": "^4.0.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@luma.gl/core": "^9.1.0",
|
||||
"@luma.gl/shadertools": "^9.1.0"
|
||||
}
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/@luma.gl/shadertools": {
|
||||
"version": "9.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/shadertools/-/shadertools-9.1.9.tgz",
|
||||
"integrity": "sha512-Uqp2xfgIEunRMLXTeCJ4uEMlWcUGcYMZGJ8GAOrAeDzn4bMKVRKmZDC71vkuTctnaodM3UdrI9W6s1sJlrXsxw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@math.gl/core": "^4.1.0",
|
||||
"@math.gl/types": "^4.1.0",
|
||||
"wgsl_reflect": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@luma.gl/core": "^9.1.0"
|
||||
}
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/@luma.gl/webgl": {
|
||||
"version": "9.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@luma.gl/webgl/-/webgl-9.1.9.tgz",
|
||||
"integrity": "sha512-jecHjhNSWkXH0v62rM6G5fIIkOmsrND27099iKgdutFvHIvd4QS4UzGWEEa9AEPlP0rTLqXkA6y6YL7f42ZkVg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@luma.gl/constants": "9.1.9",
|
||||
"@math.gl/types": "^4.1.0",
|
||||
"@probe.gl/env": "^4.0.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@luma.gl/core": "^9.1.0"
|
||||
}
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/d3-array": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
|
||||
@@ -61189,12 +61131,6 @@
|
||||
"integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"plugins/legacy-preset-chart-deckgl/node_modules/wgsl_reflect": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/wgsl_reflect/-/wgsl_reflect-1.2.1.tgz",
|
||||
"integrity": "sha512-PY6MdLqLW1NFj/V6f5/9/Nb4ultwlAF7lCLyjKOAkdnlf7LlrGXNFXzHHEV7Okg1zy4C4TpBIcc/G3PXW4py8g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"plugins/legacy-preset-chart-nvd3": {
|
||||
"name": "@superset-ui/legacy-preset-chart-nvd3",
|
||||
"version": "0.20.3",
|
||||
|
||||
@@ -104,12 +104,12 @@
|
||||
"@superset-ui/legacy-plugin-chart-world-map": "file:./plugins/legacy-plugin-chart-world-map",
|
||||
"@superset-ui/legacy-preset-chart-deckgl": "file:./plugins/legacy-preset-chart-deckgl",
|
||||
"@superset-ui/legacy-preset-chart-nvd3": "file:./plugins/legacy-preset-chart-nvd3",
|
||||
"@superset-ui/plugin-chart-ag-grid-table": "file:./plugins/plugin-chart-ag-grid-table",
|
||||
"@superset-ui/plugin-chart-cartodiagram": "file:./plugins/plugin-chart-cartodiagram",
|
||||
"@superset-ui/plugin-chart-echarts": "file:./plugins/plugin-chart-echarts",
|
||||
"@superset-ui/plugin-chart-handlebars": "file:./plugins/plugin-chart-handlebars",
|
||||
"@superset-ui/plugin-chart-pivot-table": "file:./plugins/plugin-chart-pivot-table",
|
||||
"@superset-ui/plugin-chart-table": "file:./plugins/plugin-chart-table",
|
||||
"@superset-ui/plugin-chart-ag-grid-table": "file:./plugins/plugin-chart-ag-grid-table",
|
||||
"@superset-ui/plugin-chart-word-cloud": "file:./plugins/plugin-chart-word-cloud",
|
||||
"@superset-ui/switchboard": "file:./packages/superset-ui-switchboard",
|
||||
"@types/d3-format": "^3.0.1",
|
||||
@@ -203,6 +203,7 @@
|
||||
"use-event-callback": "^0.1.0",
|
||||
"use-immer": "^0.9.0",
|
||||
"use-query-params": "^1.1.9",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -90,6 +90,7 @@ export interface Dataset {
|
||||
database?: Record<string, unknown>;
|
||||
normalize_columns?: boolean;
|
||||
always_filter_main_dttm?: boolean;
|
||||
extra?: object | string;
|
||||
}
|
||||
|
||||
export interface ControlPanelState {
|
||||
|
||||
@@ -202,7 +202,7 @@ export function AsyncAceEditor(
|
||||
|
||||
/* Basic editor styles with dark mode support */
|
||||
.ace_editor.ace-github,
|
||||
.ace_editor.ace-textmate {
|
||||
.ace_editor.ace-tm {
|
||||
background-color: ${token.colorBgContainer} !important;
|
||||
color: ${token.colorText} !important;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,6 @@ export function Button(props: ButtonProps) {
|
||||
if (!buttonStyle || buttonStyle === 'primary') {
|
||||
variant = 'solid';
|
||||
antdType = 'primary';
|
||||
color = 'primary';
|
||||
} else if (buttonStyle === 'secondary') {
|
||||
variant = 'filled';
|
||||
color = 'primary';
|
||||
@@ -78,7 +77,6 @@ export function Button(props: ButtonProps) {
|
||||
variant = 'outlined';
|
||||
color = 'default';
|
||||
} else if (buttonStyle === 'dashed') {
|
||||
color = 'primary';
|
||||
variant = 'dashed';
|
||||
antdType = 'dashed';
|
||||
} else if (buttonStyle === 'danger') {
|
||||
@@ -134,6 +132,11 @@ export function Button(props: ButtonProps) {
|
||||
'& > span > :first-of-type': {
|
||||
marginRight: firstChildMargin,
|
||||
},
|
||||
':not(:hover)': effectiveButtonStyle === 'secondary' && {
|
||||
// NOTE: This is the best we can do contrast wise for the secondary button using antd tokens
|
||||
// and abusing the semantics. Should be revisited when possible. https://github.com/apache/superset/pull/34253#issuecomment-3104834692
|
||||
color: `${theme.colorPrimaryTextHover} !important`,
|
||||
},
|
||||
}}
|
||||
icon={icon}
|
||||
{...restProps}
|
||||
|
||||
@@ -31,11 +31,6 @@ const StyledDiv = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const DescriptionContainer = styled.div`
|
||||
line-height: ${({ theme }) => theme.sizeUnit * 4}px;
|
||||
padding-top: 16px;
|
||||
`;
|
||||
|
||||
export function DeleteModal({
|
||||
description,
|
||||
onConfirm,
|
||||
@@ -81,12 +76,12 @@ export function DeleteModal({
|
||||
onHide={hide}
|
||||
onHandledPrimaryAction={confirm}
|
||||
primaryButtonName={t('Delete')}
|
||||
primaryButtonType="danger"
|
||||
primaryButtonStyle="danger"
|
||||
show={open}
|
||||
title={title}
|
||||
centered
|
||||
>
|
||||
<DescriptionContainer>{description}</DescriptionContainer>
|
||||
{description}
|
||||
<StyledDiv>
|
||||
<FormLabel htmlFor="delete">
|
||||
{t('Type "%s" to confirm', t('DELETE'))}
|
||||
|
||||
@@ -33,7 +33,7 @@ export const InteractiveModal = (props: ModalProps) => (
|
||||
InteractiveModal.args = {
|
||||
disablePrimaryButton: false,
|
||||
primaryButtonName: 'Danger',
|
||||
primaryButtonType: 'danger',
|
||||
primaryButtonStyle: 'danger',
|
||||
show: true,
|
||||
title: "I'm a modal!",
|
||||
resizable: false,
|
||||
|
||||
@@ -77,7 +77,7 @@ export const StyledModal = styled(BaseModal)<StyledModalProps>`
|
||||
.ant-modal-header {
|
||||
flex: 0 0 auto;
|
||||
border-radius: ${theme.borderRadius}px ${theme.borderRadius}px 0 0;
|
||||
padding: ${theme.sizeUnit * 4}px ${theme.sizeUnit * 6}px;
|
||||
padding: ${theme.sizeUnit * 4}px ${theme.sizeUnit * 4}px;
|
||||
|
||||
.ant-modal-title {
|
||||
font-weight: ${theme.fontWeightStrong};
|
||||
@@ -122,6 +122,7 @@ export const StyledModal = styled(BaseModal)<StyledModalProps>`
|
||||
.ant-modal-body {
|
||||
flex: 0 1 auto;
|
||||
padding: ${theme.sizeUnit * 4}px;
|
||||
padding-bottom: ${theme.sizeUnit * 2}px;
|
||||
overflow: auto;
|
||||
${!resizable && height && `height: ${height};`}
|
||||
}
|
||||
@@ -208,7 +209,7 @@ const CustomModal = ({
|
||||
onHide,
|
||||
onHandledPrimaryAction,
|
||||
primaryButtonName = t('OK'),
|
||||
primaryButtonType = 'primary',
|
||||
primaryButtonStyle = 'primary',
|
||||
show,
|
||||
name,
|
||||
title,
|
||||
@@ -261,7 +262,7 @@ const CustomModal = ({
|
||||
</Button>,
|
||||
<Button
|
||||
key="submit"
|
||||
buttonStyle={primaryButtonType}
|
||||
buttonStyle={primaryButtonStyle}
|
||||
disabled={disablePrimaryButton}
|
||||
tooltip={primaryTooltipMessage}
|
||||
loading={primaryButtonLoading}
|
||||
|
||||
@@ -20,6 +20,7 @@ import type { CSSProperties, ReactNode } from 'react';
|
||||
import type { ModalFuncProps } from 'antd';
|
||||
import type { ResizableProps } from 're-resizable';
|
||||
import type { DraggableProps } from 'react-draggable';
|
||||
import { ButtonStyle } from '../Button/types';
|
||||
|
||||
export interface ModalProps {
|
||||
className?: string;
|
||||
@@ -30,7 +31,7 @@ export interface ModalProps {
|
||||
onHide: () => void;
|
||||
onHandledPrimaryAction?: () => void;
|
||||
primaryButtonName?: string;
|
||||
primaryButtonType?: 'primary' | 'danger';
|
||||
primaryButtonStyle?: ButtonStyle;
|
||||
show: boolean;
|
||||
name?: string;
|
||||
title: ReactNode;
|
||||
|
||||
@@ -45,7 +45,7 @@ interface HandleSelectProps {
|
||||
}
|
||||
|
||||
const menuItemStyles = (theme: any) => css`
|
||||
&.antd5-menu-item {
|
||||
&.ant-menu-item {
|
||||
height: auto;
|
||||
line-height: 1.4;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
Radio as Antd5Radio,
|
||||
Radio as AntRadio,
|
||||
type CheckboxOptionType,
|
||||
type RadioGroupProps,
|
||||
} from 'antd';
|
||||
@@ -40,19 +40,19 @@ const RadioGroup = ({
|
||||
...props
|
||||
}: RadioGroupWrapperProps) => {
|
||||
const content = options.map((option: CheckboxOptionType) => (
|
||||
<Antd5Radio key={option.value} value={option.value}>
|
||||
<AntRadio key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Antd5Radio>
|
||||
</AntRadio>
|
||||
));
|
||||
return (
|
||||
<Antd5Radio.Group {...props}>
|
||||
<AntRadio.Group {...props}>
|
||||
{spaceConfig ? <Space {...spaceConfig}>{content}</Space> : content}
|
||||
</Antd5Radio.Group>
|
||||
</AntRadio.Group>
|
||||
);
|
||||
};
|
||||
export const Radio = Object.assign(Antd5Radio, {
|
||||
export const Radio = Object.assign(AntRadio, {
|
||||
GroupWrapper: RadioGroup,
|
||||
Button: Antd5Radio.Button,
|
||||
Button: AntRadio.Button,
|
||||
});
|
||||
export type {
|
||||
RadioChangeEvent,
|
||||
|
||||
@@ -19,28 +19,28 @@
|
||||
import { Tooltip } from 'antd';
|
||||
import { Dropdown, Icons } from '@superset-ui/core/components';
|
||||
import { t } from '@superset-ui/core';
|
||||
import { ThemeMode } from '../../theme/types';
|
||||
import { ThemeAlgorithm, ThemeMode } from '../../theme/types';
|
||||
|
||||
export interface ThemeSelectProps {
|
||||
changeThemeMode: (newMode: ThemeMode) => void;
|
||||
setThemeMode: (newMode: ThemeMode) => void;
|
||||
tooltipTitle?: string;
|
||||
themeMode: ThemeMode;
|
||||
}
|
||||
|
||||
const ThemeSelect: React.FC<ThemeSelectProps> = ({
|
||||
changeThemeMode,
|
||||
setThemeMode,
|
||||
tooltipTitle = 'Select theme',
|
||||
themeMode,
|
||||
}) => {
|
||||
const handleSelect = (mode: ThemeMode) => {
|
||||
changeThemeMode(mode);
|
||||
setThemeMode(mode);
|
||||
};
|
||||
|
||||
const themeIconMap: Record<ThemeMode, React.ReactNode> = {
|
||||
[ThemeMode.LIGHT]: <Icons.SunOutlined />,
|
||||
[ThemeMode.DARK]: <Icons.MoonOutlined />,
|
||||
const themeIconMap: Record<ThemeAlgorithm | ThemeMode, React.ReactNode> = {
|
||||
[ThemeAlgorithm.DEFAULT]: <Icons.SunOutlined />,
|
||||
[ThemeAlgorithm.DARK]: <Icons.MoonOutlined />,
|
||||
[ThemeMode.SYSTEM]: <Icons.FormatPainterOutlined />,
|
||||
[ThemeMode.COMPACT]: <Icons.CompressOutlined />,
|
||||
[ThemeAlgorithm.COMPACT]: <Icons.CompressOutlined />,
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -49,9 +49,9 @@ const ThemeSelect: React.FC<ThemeSelectProps> = ({
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
key: ThemeMode.LIGHT,
|
||||
key: ThemeMode.DEFAULT,
|
||||
label: t('Light'),
|
||||
onClick: () => handleSelect(ThemeMode.LIGHT),
|
||||
onClick: () => handleSelect(ThemeMode.DEFAULT),
|
||||
icon: <Icons.SunOutlined />,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
import { theme as antdThemeImport } from 'antd';
|
||||
import { Theme } from './Theme';
|
||||
import { AnyThemeConfig } from './types';
|
||||
import { AnyThemeConfig, ThemeAlgorithm } from './types';
|
||||
|
||||
// Mock emotion's cache to avoid actual DOM operations
|
||||
jest.mock('@emotion/cache', () => ({
|
||||
@@ -44,7 +44,7 @@ describe('Theme', () => {
|
||||
const parsedJson = JSON.parse(jsonString);
|
||||
|
||||
expect(parsedJson.token?.colorPrimary).toBe('#ff0000');
|
||||
expect(parsedJson.algorithm).toBe('dark');
|
||||
expect(parsedJson.algorithm).toBe(ThemeAlgorithm.DARK);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,7 +91,7 @@ describe('Theme', () => {
|
||||
|
||||
// Verify dark mode by using the serialized config from the public method
|
||||
const serialized = theme.toSerializedConfig();
|
||||
expect(serialized.algorithm).toBe('dark');
|
||||
expect(serialized.algorithm).toBe(ThemeAlgorithm.DARK);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -137,7 +137,7 @@ describe('Theme', () => {
|
||||
|
||||
// Verify the algorithm was updated
|
||||
const serialized = theme.toSerializedConfig();
|
||||
expect(serialized.algorithm).toBe('dark');
|
||||
expect(serialized.algorithm).toBe(ThemeAlgorithm.DARK);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -150,7 +150,7 @@ describe('Theme', () => {
|
||||
|
||||
// Verify dark algorithm is used
|
||||
const serialized = theme.toSerializedConfig();
|
||||
expect(serialized.algorithm).toBe('dark');
|
||||
expect(serialized.algorithm).toBe(ThemeAlgorithm.DARK);
|
||||
});
|
||||
|
||||
it('switches to default algorithm when toggling dark mode off', () => {
|
||||
@@ -164,7 +164,7 @@ describe('Theme', () => {
|
||||
|
||||
// Verify default algorithm is used
|
||||
const serialized = theme.toSerializedConfig();
|
||||
expect(serialized.algorithm).toBe('default');
|
||||
expect(serialized.algorithm).toBe(ThemeAlgorithm.DEFAULT);
|
||||
});
|
||||
|
||||
it('preserves other algorithms when toggling dark mode', () => {
|
||||
@@ -181,10 +181,11 @@ describe('Theme', () => {
|
||||
|
||||
// Verify default algorithm replaces dark but compact is preserved
|
||||
const serialized = theme.toSerializedConfig();
|
||||
|
||||
expect(Array.isArray(serialized.algorithm)).toBe(true);
|
||||
expect(serialized.algorithm).toContain('default');
|
||||
expect(serialized.algorithm).toContain('compact');
|
||||
expect(serialized.algorithm).not.toContain('dark');
|
||||
expect(serialized.algorithm).toContain(ThemeAlgorithm.DEFAULT);
|
||||
expect(serialized.algorithm).toContain(ThemeAlgorithm.COMPACT);
|
||||
expect(serialized.algorithm).not.toContain(ThemeAlgorithm.DARK);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -218,7 +219,7 @@ describe('Theme', () => {
|
||||
const serialized = theme.toSerializedConfig();
|
||||
|
||||
expect(serialized.token?.colorPrimary).toBe('#ff0000');
|
||||
expect(serialized.algorithm).toBe('dark');
|
||||
expect(serialized.algorithm).toBe(ThemeAlgorithm.DARK);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
/* eslint-disable theme-colors/no-literal-colors */
|
||||
import { SerializableThemeConfig } from './types';
|
||||
import { type SerializableThemeConfig, ThemeAlgorithm } from './types';
|
||||
|
||||
const exampleThemes: Record<string, SerializableThemeConfig> = {
|
||||
superset: {
|
||||
@@ -27,11 +27,11 @@ const exampleThemes: Record<string, SerializableThemeConfig> = {
|
||||
},
|
||||
supersetDark: {
|
||||
token: {},
|
||||
algorithm: 'dark',
|
||||
algorithm: ThemeAlgorithm.DARK,
|
||||
},
|
||||
supersetCompact: {
|
||||
token: {},
|
||||
algorithm: 'compact',
|
||||
algorithm: ThemeAlgorithm.COMPACT,
|
||||
},
|
||||
funky: {
|
||||
token: {
|
||||
@@ -43,7 +43,7 @@ const exampleThemes: Record<string, SerializableThemeConfig> = {
|
||||
borderRadius: 12,
|
||||
fontFamily: 'Comic Sans MS, cursive',
|
||||
},
|
||||
algorithm: 'default',
|
||||
algorithm: ThemeAlgorithm.DEFAULT,
|
||||
},
|
||||
funkyDark: {
|
||||
token: {
|
||||
@@ -55,7 +55,7 @@ const exampleThemes: Record<string, SerializableThemeConfig> = {
|
||||
borderRadius: 12,
|
||||
fontFamily: 'Comic Sans MS, cursive',
|
||||
},
|
||||
algorithm: 'dark',
|
||||
algorithm: ThemeAlgorithm.DARK,
|
||||
},
|
||||
};
|
||||
export default exampleThemes;
|
||||
|
||||
@@ -16,17 +16,17 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import emotionStyled from '@emotion/styled';
|
||||
import emotionStyled, { CreateStyled } from '@emotion/styled';
|
||||
import { useTheme as useThemeBasic } from '@emotion/react';
|
||||
// import { theme as antdThemeImport } from 'antd';
|
||||
import { Theme } from './Theme';
|
||||
import type {
|
||||
SupersetTheme,
|
||||
SerializableThemeConfig,
|
||||
AnyThemeConfig,
|
||||
ThemeStorage,
|
||||
ThemeControllerOptions,
|
||||
ThemeContextType,
|
||||
import {
|
||||
type SupersetTheme,
|
||||
type SerializableThemeConfig,
|
||||
type AnyThemeConfig,
|
||||
type ThemeStorage,
|
||||
type ThemeControllerOptions,
|
||||
type ThemeContextType,
|
||||
ThemeAlgorithm,
|
||||
} from './types';
|
||||
|
||||
export {
|
||||
@@ -56,10 +56,12 @@ export function useTheme() {
|
||||
return theme;
|
||||
}
|
||||
|
||||
const styled = emotionStyled;
|
||||
const styled: CreateStyled = emotionStyled;
|
||||
|
||||
// launching in in dark mode for now while iterating
|
||||
const themeObject = Theme.fromConfig({ algorithm: 'default' });
|
||||
const themeObject: Theme = Theme.fromConfig({
|
||||
algorithm: ThemeAlgorithm.DEFAULT,
|
||||
});
|
||||
|
||||
const { theme } = themeObject;
|
||||
const supersetTheme = theme;
|
||||
|
||||
@@ -33,6 +33,41 @@ import { Theme } from '.';
|
||||
export type AntdTokens = ReturnType<typeof antdThemeImport.getDesignToken>;
|
||||
export type AntdThemeConfig = ThemeConfig;
|
||||
|
||||
/**
|
||||
* Theme algorithms supported by Antd.
|
||||
* They can be used individually or in combination.
|
||||
* - DEFAULT: Default light theme
|
||||
* - DARK: Dark theme
|
||||
* - COMPACT: Compact theme (smaller spacing)
|
||||
*/
|
||||
export enum ThemeAlgorithm {
|
||||
DEFAULT = 'default',
|
||||
DARK = 'dark',
|
||||
COMPACT = 'compact',
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the current theme mode of the app.
|
||||
* It can be one of the following:
|
||||
* - DEFAULT: Light theme
|
||||
* - DARK: Dark theme
|
||||
* - SYSTEM: System theme (auto-detects based on system settings)
|
||||
*/
|
||||
export enum ThemeMode {
|
||||
DEFAULT = 'default',
|
||||
DARK = 'dark',
|
||||
SYSTEM = 'system',
|
||||
}
|
||||
|
||||
/**
|
||||
* All valid algorithm values that can be used in theme config.
|
||||
*/
|
||||
export type ThemeAlgorithmOption =
|
||||
| ThemeAlgorithm.DEFAULT
|
||||
| ThemeAlgorithm.DARK
|
||||
| ThemeAlgorithm.COMPACT
|
||||
| ThemeAlgorithm[];
|
||||
|
||||
/**
|
||||
* A serializable version of Ant Design's ThemeConfig
|
||||
* Compatible with theme editor exports
|
||||
@@ -40,11 +75,7 @@ export type AntdThemeConfig = ThemeConfig;
|
||||
export type SerializableThemeConfig = {
|
||||
token?: Record<string, any>;
|
||||
components?: Record<string, any>;
|
||||
algorithm?:
|
||||
| 'default'
|
||||
| 'dark'
|
||||
| 'compact'
|
||||
| ('default' | 'dark' | 'compact')[];
|
||||
algorithm?: ThemeAlgorithmOption;
|
||||
hashed?: boolean;
|
||||
inherit?: boolean;
|
||||
};
|
||||
@@ -358,13 +389,6 @@ export type AllowedAntdTokenKeys = Extract<
|
||||
keyof AntdTokens
|
||||
>;
|
||||
|
||||
export enum ThemeMode {
|
||||
LIGHT = 'light',
|
||||
DARK = 'dark',
|
||||
SYSTEM = 'system',
|
||||
COMPACT = 'compact',
|
||||
}
|
||||
|
||||
export type SharedAntdTokens = Pick<AntdTokens, AllowedAntdTokenKeys>;
|
||||
|
||||
/** The final shape for our custom theme object, combining old theme + shared antd + superset specifics. */
|
||||
@@ -379,7 +403,7 @@ export interface ThemeStorage {
|
||||
}
|
||||
|
||||
export interface ThemeControllerOptions {
|
||||
themeObject: Theme;
|
||||
themeObject?: Theme;
|
||||
storage?: ThemeStorage;
|
||||
storageKey?: string;
|
||||
modeStorageKey?: string;
|
||||
@@ -393,6 +417,6 @@ export interface ThemeContextType {
|
||||
theme: Theme;
|
||||
themeMode: ThemeMode;
|
||||
setTheme: (config: AnyThemeConfig) => void;
|
||||
changeThemeMode: (newMode: ThemeMode) => void;
|
||||
setThemeMode: (newMode: ThemeMode) => void;
|
||||
resetTheme: () => void;
|
||||
}
|
||||
|
||||
@@ -28,9 +28,10 @@ import {
|
||||
genDeprecatedColorVariations,
|
||||
} from './utils';
|
||||
import {
|
||||
AnyThemeConfig,
|
||||
SerializableThemeConfig,
|
||||
AntdThemeConfig,
|
||||
type AnyThemeConfig,
|
||||
type SerializableThemeConfig,
|
||||
type AntdThemeConfig,
|
||||
ThemeAlgorithm,
|
||||
} from './types';
|
||||
|
||||
// Mock tinycolor2 for consistent testing
|
||||
@@ -50,22 +51,25 @@ describe('Theme utilities', () => {
|
||||
const config: AnyThemeConfig = {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
};
|
||||
|
||||
expect(isSerializableConfig(config)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true when algorithm is a string', () => {
|
||||
const config: AnyThemeConfig = {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
algorithm: 'dark',
|
||||
algorithm: ThemeAlgorithm.DARK,
|
||||
};
|
||||
|
||||
expect(isSerializableConfig(config)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true when algorithm is an array of strings', () => {
|
||||
const config: AnyThemeConfig = {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
algorithm: ['dark', 'compact'],
|
||||
algorithm: [ThemeAlgorithm.DARK, ThemeAlgorithm.COMPACT],
|
||||
};
|
||||
|
||||
expect(isSerializableConfig(config)).toBe(true);
|
||||
});
|
||||
|
||||
@@ -74,15 +78,19 @@ describe('Theme utilities', () => {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
algorithm: antdThemeImport.darkAlgorithm,
|
||||
};
|
||||
|
||||
expect(isSerializableConfig(config)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false when algorithm is an array containing a function', () => {
|
||||
const config: AnyThemeConfig = {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
// @ts-ignore
|
||||
algorithm: [antdThemeImport.darkAlgorithm, 'compact'],
|
||||
algorithm: [
|
||||
antdThemeImport.darkAlgorithm,
|
||||
antdThemeImport.compactAlgorithm,
|
||||
],
|
||||
};
|
||||
|
||||
expect(isSerializableConfig(config)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -91,18 +99,22 @@ describe('Theme utilities', () => {
|
||||
it('converts string algorithm to function reference', () => {
|
||||
const config: SerializableThemeConfig = {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
algorithm: 'dark',
|
||||
algorithm: ThemeAlgorithm.DARK,
|
||||
};
|
||||
|
||||
const result = deserializeThemeConfig(config);
|
||||
|
||||
expect(result.algorithm).toBe(antdThemeImport.darkAlgorithm);
|
||||
});
|
||||
|
||||
it('converts array of string algorithms to function references', () => {
|
||||
const config: SerializableThemeConfig = {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
algorithm: ['dark', 'compact'],
|
||||
algorithm: [ThemeAlgorithm.DARK, ThemeAlgorithm.COMPACT],
|
||||
};
|
||||
|
||||
const result = deserializeThemeConfig(config);
|
||||
|
||||
expect(Array.isArray(result.algorithm)).toBe(true);
|
||||
expect(result.algorithm).toContain(antdThemeImport.darkAlgorithm);
|
||||
expect(result.algorithm).toContain(antdThemeImport.compactAlgorithm);
|
||||
@@ -111,10 +123,12 @@ describe('Theme utilities', () => {
|
||||
it('preserves other configuration properties', () => {
|
||||
const config: SerializableThemeConfig = {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
algorithm: 'dark',
|
||||
algorithm: ThemeAlgorithm.DARK,
|
||||
hashed: true,
|
||||
};
|
||||
|
||||
const result = deserializeThemeConfig(config);
|
||||
|
||||
expect(result.token).toEqual({ colorPrimary: '#ff0000' });
|
||||
expect(result.hashed).toBe(true);
|
||||
});
|
||||
@@ -123,25 +137,31 @@ describe('Theme utilities', () => {
|
||||
const config: SerializableThemeConfig = {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
};
|
||||
|
||||
const result = deserializeThemeConfig(config);
|
||||
expect(result.algorithm).toBeUndefined();
|
||||
|
||||
expect(result.algorithm).toBe(antdThemeImport.defaultAlgorithm);
|
||||
});
|
||||
|
||||
it('converts default algorithm string to function reference', () => {
|
||||
const config: SerializableThemeConfig = {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
algorithm: 'default',
|
||||
algorithm: ThemeAlgorithm.DEFAULT,
|
||||
};
|
||||
|
||||
const result = deserializeThemeConfig(config);
|
||||
|
||||
expect(result.algorithm).toBe(antdThemeImport.defaultAlgorithm);
|
||||
});
|
||||
|
||||
it('converts compact algorithm string to function reference', () => {
|
||||
const config: SerializableThemeConfig = {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
algorithm: 'compact',
|
||||
algorithm: ThemeAlgorithm.COMPACT,
|
||||
};
|
||||
|
||||
const result = deserializeThemeConfig(config);
|
||||
|
||||
expect(result.algorithm).toBe(antdThemeImport.compactAlgorithm);
|
||||
});
|
||||
});
|
||||
@@ -152,8 +172,10 @@ describe('Theme utilities', () => {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
algorithm: antdThemeImport.darkAlgorithm,
|
||||
};
|
||||
|
||||
const result = serializeThemeConfig(config);
|
||||
expect(result.algorithm).toBe('dark');
|
||||
|
||||
expect(result.algorithm).toBe(ThemeAlgorithm.DARK);
|
||||
});
|
||||
|
||||
it('converts array of function algorithms to strings', () => {
|
||||
@@ -164,10 +186,12 @@ describe('Theme utilities', () => {
|
||||
antdThemeImport.compactAlgorithm,
|
||||
],
|
||||
};
|
||||
|
||||
const result = serializeThemeConfig(config);
|
||||
|
||||
expect(Array.isArray(result.algorithm)).toBe(true);
|
||||
expect(result.algorithm).toContain('dark');
|
||||
expect(result.algorithm).toContain('compact');
|
||||
expect(result.algorithm).toContain(ThemeAlgorithm.DARK);
|
||||
expect(result.algorithm).toContain(ThemeAlgorithm.COMPACT);
|
||||
});
|
||||
|
||||
it('preserves other configuration properties', () => {
|
||||
@@ -176,7 +200,9 @@ describe('Theme utilities', () => {
|
||||
algorithm: antdThemeImport.darkAlgorithm,
|
||||
hashed: true,
|
||||
};
|
||||
|
||||
const result = serializeThemeConfig(config);
|
||||
|
||||
expect(result.token).toEqual({ colorPrimary: '#ff0000' });
|
||||
expect(result.hashed).toBe(true);
|
||||
});
|
||||
@@ -185,7 +211,9 @@ describe('Theme utilities', () => {
|
||||
const config: AntdThemeConfig = {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
};
|
||||
|
||||
const result = serializeThemeConfig(config);
|
||||
|
||||
expect(result.algorithm).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -196,8 +224,10 @@ describe('Theme utilities', () => {
|
||||
// @ts-ignore
|
||||
algorithm: unknownAlgorithm,
|
||||
};
|
||||
|
||||
const result = serializeThemeConfig(config);
|
||||
expect(result.algorithm).toBe('default');
|
||||
|
||||
expect(result.algorithm).toBe(ThemeAlgorithm.DEFAULT);
|
||||
});
|
||||
|
||||
it('converts default algorithm function to string', () => {
|
||||
@@ -205,8 +235,10 @@ describe('Theme utilities', () => {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
algorithm: antdThemeImport.defaultAlgorithm,
|
||||
};
|
||||
|
||||
const result = serializeThemeConfig(config);
|
||||
expect(result.algorithm).toBe('default');
|
||||
|
||||
expect(result.algorithm).toBe(ThemeAlgorithm.DEFAULT);
|
||||
});
|
||||
|
||||
it('converts compact algorithm function to string', () => {
|
||||
@@ -214,8 +246,10 @@ describe('Theme utilities', () => {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
algorithm: antdThemeImport.compactAlgorithm,
|
||||
};
|
||||
|
||||
const result = serializeThemeConfig(config);
|
||||
expect(result.algorithm).toBe('compact');
|
||||
|
||||
expect(result.algorithm).toBe(ThemeAlgorithm.COMPACT);
|
||||
});
|
||||
|
||||
it('defaults each unknown algorithm in array to "default"', () => {
|
||||
@@ -225,9 +259,14 @@ describe('Theme utilities', () => {
|
||||
// @ts-ignore
|
||||
algorithm: [antdThemeImport.darkAlgorithm, unknownAlgorithm],
|
||||
};
|
||||
|
||||
const result = serializeThemeConfig(config);
|
||||
|
||||
expect(Array.isArray(result.algorithm)).toBe(true);
|
||||
expect(result.algorithm).toEqual(['dark', 'default']);
|
||||
expect(result.algorithm).toEqual([
|
||||
ThemeAlgorithm.DARK,
|
||||
ThemeAlgorithm.DEFAULT,
|
||||
]);
|
||||
});
|
||||
|
||||
it('handles mixed known and unknown algorithms in array', () => {
|
||||
@@ -244,13 +283,15 @@ describe('Theme utilities', () => {
|
||||
unknownAlgorithm2,
|
||||
],
|
||||
};
|
||||
|
||||
const result = serializeThemeConfig(config);
|
||||
|
||||
expect(Array.isArray(result.algorithm)).toBe(true);
|
||||
expect(result.algorithm).toEqual([
|
||||
'dark',
|
||||
'default',
|
||||
'compact',
|
||||
'default',
|
||||
ThemeAlgorithm.DARK,
|
||||
ThemeAlgorithm.DEFAULT,
|
||||
ThemeAlgorithm.COMPACT,
|
||||
ThemeAlgorithm.DEFAULT,
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -261,16 +302,20 @@ describe('Theme utilities', () => {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
algorithm: antdThemeImport.darkAlgorithm,
|
||||
};
|
||||
|
||||
const result = normalizeThemeConfig(config);
|
||||
|
||||
expect(result).toBe(config);
|
||||
});
|
||||
|
||||
it('deserializes serializable configs', () => {
|
||||
const config: SerializableThemeConfig = {
|
||||
token: { colorPrimary: '#ff0000' },
|
||||
algorithm: 'dark',
|
||||
algorithm: ThemeAlgorithm.DARK,
|
||||
};
|
||||
|
||||
const result = normalizeThemeConfig(config);
|
||||
|
||||
expect(result.algorithm).toBe(antdThemeImport.darkAlgorithm);
|
||||
});
|
||||
});
|
||||
@@ -278,14 +323,18 @@ describe('Theme utilities', () => {
|
||||
describe('getAntdConfig', () => {
|
||||
it('returns config with default algorithm for light mode', () => {
|
||||
const seed = { colorPrimary: '#ff0000' };
|
||||
|
||||
const result = getAntdConfig(seed, false);
|
||||
|
||||
expect(result.token).toBe(seed);
|
||||
expect(result.algorithm).toBe(antdThemeImport.defaultAlgorithm);
|
||||
});
|
||||
|
||||
it('returns config with dark algorithm for dark mode', () => {
|
||||
const seed = { colorPrimary: '#ff0000' };
|
||||
|
||||
const result = getAntdConfig(seed, true);
|
||||
|
||||
expect(result.token).toBe(seed);
|
||||
expect(result.algorithm).toBe(antdThemeImport.darkAlgorithm);
|
||||
});
|
||||
@@ -301,7 +350,9 @@ describe('Theme utilities', () => {
|
||||
colorInfo: '#info',
|
||||
otherToken: 'ignore-me',
|
||||
};
|
||||
|
||||
const result = getSystemColors(tokens);
|
||||
|
||||
expect(result).toEqual({
|
||||
colorPrimary: '#primary',
|
||||
colorError: '#error',
|
||||
@@ -315,6 +366,7 @@ describe('Theme utilities', () => {
|
||||
describe('genDeprecatedColorVariations', () => {
|
||||
it('generates color variations for light mode', () => {
|
||||
const result = genDeprecatedColorVariations('#base-color', false);
|
||||
|
||||
expect(result.base).toBe('#base-color');
|
||||
expect(result.light1).toBe('#mixed-color');
|
||||
expect(result.dark1).toBe('#mixed-color');
|
||||
@@ -322,6 +374,7 @@ describe('Theme utilities', () => {
|
||||
|
||||
it('generates color variations for dark mode', () => {
|
||||
const result = genDeprecatedColorVariations('#base-color', true);
|
||||
|
||||
expect(result.base).toBe('#base-color');
|
||||
expect(result.light1).toBe('#mixed-color');
|
||||
expect(result.dark1).toBe('#mixed-color');
|
||||
@@ -337,7 +390,9 @@ describe('Theme utilities', () => {
|
||||
colorSuccess: '#success',
|
||||
colorInfo: '#info',
|
||||
};
|
||||
|
||||
const result = getDeprecatedColors(systemColors, false);
|
||||
|
||||
expect(result.primary.base).toBe('#primary');
|
||||
expect(result.error.base).toBe('#error');
|
||||
expect(result.warning.base).toBe('#warning');
|
||||
|
||||
@@ -19,13 +19,14 @@
|
||||
import { theme as antdThemeImport } from 'antd';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import {
|
||||
AntdThemeConfig,
|
||||
AnyThemeConfig,
|
||||
SerializableThemeConfig,
|
||||
DeprecatedColorVariations,
|
||||
DeprecatedThemeColors,
|
||||
SystemColors,
|
||||
SupersetTheme,
|
||||
type AntdThemeConfig,
|
||||
type AnyThemeConfig,
|
||||
type SerializableThemeConfig,
|
||||
type DeprecatedColorVariations,
|
||||
type DeprecatedThemeColors,
|
||||
type SystemColors,
|
||||
type SupersetTheme,
|
||||
ThemeAlgorithm,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
@@ -38,9 +39,8 @@ export function isSerializableConfig(
|
||||
|
||||
if (algorithm === undefined) return true;
|
||||
|
||||
if (Array.isArray(algorithm)) {
|
||||
if (Array.isArray(algorithm))
|
||||
return (algorithm as unknown[]).every(alg => typeof alg === 'string');
|
||||
}
|
||||
|
||||
return typeof algorithm === 'string';
|
||||
}
|
||||
@@ -60,9 +60,21 @@ export function deserializeThemeConfig(
|
||||
|
||||
let resolvedAlgorithm;
|
||||
if (Array.isArray(algorithm)) {
|
||||
resolvedAlgorithm = algorithm.map(alg => algorithmMap[alg]);
|
||||
} else if (algorithm) {
|
||||
const validAlgorithms = algorithm
|
||||
.map((alg: ThemeAlgorithm) => algorithmMap[alg])
|
||||
.filter(Boolean);
|
||||
|
||||
// If we have valid algorithms, use them; otherwise fallback to default
|
||||
if (validAlgorithms.length > 0) {
|
||||
resolvedAlgorithm =
|
||||
validAlgorithms.length === 1 ? validAlgorithms[0] : validAlgorithms;
|
||||
} else {
|
||||
resolvedAlgorithm = antdThemeImport.defaultAlgorithm;
|
||||
}
|
||||
} else if (algorithm && algorithmMap[algorithm]) {
|
||||
resolvedAlgorithm = algorithmMap[algorithm];
|
||||
} else {
|
||||
resolvedAlgorithm = antdThemeImport.defaultAlgorithm;
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -83,19 +95,21 @@ export function serializeThemeConfig(
|
||||
|
||||
if (Array.isArray(algorithm)) {
|
||||
serializedAlgorithm = algorithm.map(alg => {
|
||||
if (alg === antdThemeImport.defaultAlgorithm) return 'default';
|
||||
if (alg === antdThemeImport.darkAlgorithm) return 'dark';
|
||||
if (alg === antdThemeImport.compactAlgorithm) return 'compact';
|
||||
return 'default'; // Fallback
|
||||
}) as ('default' | 'dark' | 'compact')[];
|
||||
if (alg === antdThemeImport.defaultAlgorithm)
|
||||
return ThemeAlgorithm.DEFAULT;
|
||||
if (alg === antdThemeImport.darkAlgorithm) return ThemeAlgorithm.DARK;
|
||||
if (alg === antdThemeImport.compactAlgorithm)
|
||||
return ThemeAlgorithm.COMPACT;
|
||||
return ThemeAlgorithm.DEFAULT; // Fallback
|
||||
}) as ThemeAlgorithm[];
|
||||
} else if (algorithm) {
|
||||
if (algorithm === antdThemeImport.defaultAlgorithm)
|
||||
serializedAlgorithm = 'default';
|
||||
serializedAlgorithm = ThemeAlgorithm.DEFAULT;
|
||||
else if (algorithm === antdThemeImport.darkAlgorithm)
|
||||
serializedAlgorithm = 'dark';
|
||||
serializedAlgorithm = ThemeAlgorithm.DARK;
|
||||
else if (algorithm === antdThemeImport.compactAlgorithm)
|
||||
serializedAlgorithm = 'compact';
|
||||
else serializedAlgorithm = 'default'; // Fallback
|
||||
serializedAlgorithm = ThemeAlgorithm.COMPACT;
|
||||
else serializedAlgorithm = ThemeAlgorithm.DEFAULT; // Fallback
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -109,9 +123,8 @@ export function serializeThemeConfig(
|
||||
* This automatically detects and converts serializable configs
|
||||
*/
|
||||
export function normalizeThemeConfig(config: AnyThemeConfig): AntdThemeConfig {
|
||||
if (isSerializableConfig(config)) {
|
||||
return deserializeThemeConfig(config);
|
||||
}
|
||||
if (isSerializableConfig(config)) return deserializeThemeConfig(config);
|
||||
|
||||
return config as AntdThemeConfig;
|
||||
}
|
||||
|
||||
@@ -164,7 +177,7 @@ export function getDeprecatedColors(
|
||||
systemColors: SystemColors,
|
||||
isDark: boolean,
|
||||
): DeprecatedThemeColors {
|
||||
const sc = systemColors;
|
||||
const sc: SystemColors = systemColors;
|
||||
return {
|
||||
primary: genDeprecatedColorVariations(sc.colorPrimary, isDark),
|
||||
error: genDeprecatedColorVariations(sc.colorError, isDark),
|
||||
|
||||
@@ -23,7 +23,6 @@ const VALIDE_OSM_URLS = ['https://tile.osm', 'https://tile.openstreetmap'];
|
||||
|
||||
/**
|
||||
* Validate a [Mapbox styles URL](https://docs.mapbox.com/help/glossary/style-url/)
|
||||
* or a title server URL.
|
||||
* @param v
|
||||
*/
|
||||
export default function validateMapboxStylesUrl(v: unknown) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -42,4 +42,8 @@ export default styled(WorldMapComponent)`
|
||||
background-color: ${({ theme }) => theme.colors.grayscale.light5};
|
||||
}
|
||||
}
|
||||
.hoverinfo {
|
||||
background-color: ${({ theme }) => theme.colorBgElevated};
|
||||
color: ${({ theme }) => theme.colorTextSecondary};
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
getSequentialSchemeRegistry,
|
||||
CategoricalColorNamespace,
|
||||
} from '@superset-ui/core';
|
||||
import Datamap from 'datamaps/dist/datamaps.world.min';
|
||||
import Datamap from 'datamaps/dist/datamaps.all.min';
|
||||
import { ColorBy } from './utils';
|
||||
|
||||
const propTypes = {
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"@luma.gl/engine": "^9.1.9",
|
||||
"@luma.gl/shadertools": "^9.1.9",
|
||||
"@luma.gl/webgl": "^9.1.9",
|
||||
"@mapbox/tiny-sdf": "^2.0.6",
|
||||
"@mapbox/geojson-extent": "^1.0.1",
|
||||
"@math.gl/web-mercator": "^4.1.0",
|
||||
"@types/d3-array": "^2.0.0",
|
||||
|
||||
@@ -48,7 +48,7 @@ const sequentialSchemeRegistry = getSequentialSchemeRegistry();
|
||||
|
||||
export const DEFAULT_DECKGL_COLOR = { r: 158, g: 158, b: 158, a: 1 };
|
||||
|
||||
let deckglTiles: any;
|
||||
let deckglTiles: string[][];
|
||||
|
||||
export const DEFAULT_DECKGL_TILES = [
|
||||
['https://tile.openstreetmap.org/{z}/{x}/{y}.png', 'Streets (OSM)'],
|
||||
@@ -422,6 +422,7 @@ export const mapboxStyle = {
|
||||
choices: getDeckGLTiles(),
|
||||
default: getDeckGLTiles()[0][0],
|
||||
description: t(
|
||||
'Base layer map style. See Mapbox documentation: %s',
|
||||
'Mapbox base layer map style (see Mapbox documentation: %s) or tile server URL.',
|
||||
'https://docs.mapbox.com/help/glossary/style-url/',
|
||||
),
|
||||
|
||||
@@ -221,6 +221,7 @@ export function getColorBreakpointsBuckets(
|
||||
|
||||
return buckets;
|
||||
}
|
||||
|
||||
export function buildTileLayer(url: string, id: string) {
|
||||
interface TileLayerProps {
|
||||
id: string;
|
||||
|
||||
@@ -926,7 +926,7 @@ const SqlEditor: FC<Props> = ({
|
||||
css={css`
|
||||
margin-bottom: ${theme.sizeUnit * 2}px;
|
||||
padding-top: ${theme.sizeUnit * 4}px;
|
||||
.antd5-alert-action {
|
||||
.ant-alert-action {
|
||||
align-self: center;
|
||||
}
|
||||
`}
|
||||
|
||||
@@ -353,7 +353,7 @@ test('Sends the correct db when changing the database', async () => {
|
||||
await waitFor(() =>
|
||||
expect(props.onDbChange).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id: `mysql-test-mysql-2`,
|
||||
value: 2,
|
||||
database_name: 'test-mysql',
|
||||
backend: 'mysql',
|
||||
}),
|
||||
|
||||
@@ -319,11 +319,14 @@ export function DatabaseSelector({
|
||||
value: { label: string; value: number },
|
||||
database: DatabaseValue,
|
||||
) {
|
||||
setCurrentDb(database);
|
||||
// the database id is actually stored in the value property; the ID is used
|
||||
// for the DOM, so it can't be an integer
|
||||
const databaseWithId = { ...database, id: database.value };
|
||||
setCurrentDb(databaseWithId);
|
||||
setCurrentCatalog(undefined);
|
||||
setCurrentSchema(undefined);
|
||||
if (onDbChange) {
|
||||
onDbChange(database);
|
||||
onDbChange(databaseWithId);
|
||||
}
|
||||
if (onCatalogChange) {
|
||||
onCatalogChange(undefined);
|
||||
|
||||
@@ -54,7 +54,8 @@ export function ErrorMessageWithStackTrace({
|
||||
// Check if a custom error message component was registered for this message
|
||||
if (error) {
|
||||
const ErrorMessageComponent = getErrorMessageComponentRegistry().get(
|
||||
error.error_type,
|
||||
// @ts-ignore: plan to modify this part so that all errors in Superset 6.0 are standardized as Superset API error types
|
||||
error.errorType ?? error.error_type,
|
||||
);
|
||||
if (ErrorMessageComponent) {
|
||||
return (
|
||||
|
||||
@@ -362,7 +362,7 @@ export const ImportModal: FunctionComponent<ImportModelsModalProps> = ({
|
||||
onHandledPrimaryAction={onUpload}
|
||||
onHide={hide}
|
||||
primaryButtonName={needsOverwriteConfirm ? t('Overwrite') : t('Import')}
|
||||
primaryButtonType={needsOverwriteConfirm ? 'danger' : 'primary'}
|
||||
primaryButtonStyle={needsOverwriteConfirm ? 'danger' : 'primary'}
|
||||
width="750px"
|
||||
show={show}
|
||||
title={<h4>{t('Import %s', resourceLabel)}</h4>}
|
||||
|
||||
@@ -39,3 +39,10 @@ export interface TagType {
|
||||
css?: SerializedStyles;
|
||||
closable?: boolean;
|
||||
}
|
||||
|
||||
export enum TagTypeEnum {
|
||||
Custom = 1,
|
||||
Type = 2,
|
||||
Owner = 3,
|
||||
FavoritedBy = 4,
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { DEFAULT_D3_FORMAT, DEFAULT_D3_TIME_FORMAT } from '@superset-ui/core';
|
||||
|
||||
import { BootstrapData, CommonBootstrapData } from './types/bootstrapTypes';
|
||||
|
||||
export const DATETIME_WITH_TIME_ZONE = 'YYYY-MM-DD HH:mm:ssZ';
|
||||
@@ -141,7 +140,11 @@ export const DEFAULT_COMMON_BOOTSTRAP_DATA: CommonBootstrapData = {
|
||||
},
|
||||
extra_categorical_color_schemes: [],
|
||||
extra_sequential_color_schemes: [],
|
||||
theme: {},
|
||||
theme: {
|
||||
default: {},
|
||||
dark: {},
|
||||
settings: {},
|
||||
},
|
||||
menu_data: {
|
||||
menu: [],
|
||||
brand: {
|
||||
|
||||
@@ -53,6 +53,7 @@ import {
|
||||
DASHBOARD_POSITION_DATA_LIMIT,
|
||||
DASHBOARD_HEADER_ID,
|
||||
} from 'src/dashboard/util/constants';
|
||||
import { TagTypeEnum } from 'src/components/Tag/TagType';
|
||||
import setPeriodicRunner, {
|
||||
stopPeriodicRender,
|
||||
} from 'src/dashboard/util/setPeriodicRunner';
|
||||
@@ -415,7 +416,9 @@ const Header = () => {
|
||||
owners: dashboardInfo.owners,
|
||||
roles: dashboardInfo.roles,
|
||||
slug,
|
||||
tags: dashboardInfo.tags,
|
||||
tags: (dashboardInfo.tags || []).filter(
|
||||
item => item.type === TagTypeEnum.Custom || !item.type,
|
||||
),
|
||||
metadata: {
|
||||
...dashboardInfo?.metadata,
|
||||
color_namespace: currentColorNamespace,
|
||||
@@ -460,6 +463,7 @@ const Header = () => {
|
||||
dashboardInfo.metadata,
|
||||
dashboardInfo.owners,
|
||||
dashboardInfo.roles,
|
||||
dashboardInfo.tags,
|
||||
dashboardTitle,
|
||||
layout,
|
||||
refreshFrequency,
|
||||
|
||||
@@ -59,6 +59,7 @@ type SliceHeaderProps = SliceHeaderControlsProps & {
|
||||
formData: object;
|
||||
width: number;
|
||||
height: number;
|
||||
exportPivotExcel?: (arg0: string) => void;
|
||||
};
|
||||
|
||||
const annotationsLoading = t('Annotation layers are still loading.');
|
||||
@@ -167,6 +168,7 @@ const SliceHeader = forwardRef<HTMLDivElement, SliceHeaderProps>(
|
||||
formData,
|
||||
width,
|
||||
height,
|
||||
exportPivotExcel = () => ({}),
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
@@ -344,6 +346,7 @@ const SliceHeader = forwardRef<HTMLDivElement, SliceHeaderProps>(
|
||||
formData={formData}
|
||||
exploreUrl={exploreUrl}
|
||||
crossFiltersEnabled={isCrossFiltersEnabled}
|
||||
exportPivotExcel={exportPivotExcel}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -33,6 +33,7 @@ const createProps = (viz_type = VizType.Sunburst) =>
|
||||
exportFullCSV: jest.fn(),
|
||||
exportXLSX: jest.fn(),
|
||||
exportFullXLSX: jest.fn(),
|
||||
exportPivotExcel: jest.fn(),
|
||||
forceRefresh: jest.fn(),
|
||||
handleToggleFullSize: jest.fn(),
|
||||
toggleExpandSlice: jest.fn(),
|
||||
@@ -254,6 +255,20 @@ test('Should not show export full Excel if report is not table', async () => {
|
||||
expect(screen.queryByText('Export to full Excel')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('Should export to pivoted Excel if report is pivot table', async () => {
|
||||
const props = createProps(VizType.PivotTable);
|
||||
renderWrapper(props);
|
||||
openMenu();
|
||||
expect(props.exportPivotExcel).toHaveBeenCalledTimes(0);
|
||||
userEvent.hover(screen.getByText('Download'));
|
||||
userEvent.click(await screen.findByText('Export to Pivoted Excel'));
|
||||
expect(props.exportPivotExcel).toHaveBeenCalledTimes(1);
|
||||
expect(props.exportPivotExcel).toHaveBeenCalledWith(
|
||||
'.pvtTable',
|
||||
props.slice.slice_name,
|
||||
);
|
||||
});
|
||||
|
||||
test('Should "Show chart description"', () => {
|
||||
const props = createProps();
|
||||
renderWrapper(props);
|
||||
|
||||
@@ -128,6 +128,7 @@ export interface SliceHeaderControlsProps {
|
||||
exportXLSX?: (sliceId: number) => void;
|
||||
exportFullXLSX?: (sliceId: number) => void;
|
||||
handleToggleFullSize: () => void;
|
||||
exportPivotExcel?: (tableSelector: string, sliceName: string) => void;
|
||||
|
||||
addDangerToast: (message: string) => void;
|
||||
addSuccessToast: (message: string) => void;
|
||||
@@ -255,6 +256,10 @@ const SliceHeaderControls = (
|
||||
});
|
||||
break;
|
||||
}
|
||||
case MenuKeys.ExportPivotXlsx: {
|
||||
props.exportPivotExcel?.('.pvtTable', props.slice.slice_name);
|
||||
break;
|
||||
}
|
||||
case MenuKeys.CrossFilterScoping: {
|
||||
openScopingModal();
|
||||
break;
|
||||
@@ -468,6 +473,15 @@ const SliceHeaderControls = (
|
||||
{t('Export to Excel')}
|
||||
</Menu.Item>
|
||||
|
||||
{isPivotTable && (
|
||||
<Menu.Item
|
||||
key={MenuKeys.ExportPivotXlsx}
|
||||
icon={<Icons.FileOutlined css={dropdownIconsStyles} />}
|
||||
>
|
||||
{t('Export to Pivoted Excel')}
|
||||
</Menu.Item>
|
||||
)}
|
||||
|
||||
{isFeatureEnabled(FeatureFlag.AllowFullCsvExport) &&
|
||||
props.supersetCanCSV &&
|
||||
isTable && (
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
import { postFormData } from 'src/explore/exploreUtils/formData';
|
||||
import { URL_PARAMS } from 'src/constants';
|
||||
import { enforceSharedLabelsColorsArray } from 'src/utils/colorScheme';
|
||||
import exportPivotExcel from 'src/utils/downloadAsPivotExcel';
|
||||
|
||||
import SliceHeader from '../SliceHeader';
|
||||
import MissingChart from '../MissingChart';
|
||||
@@ -128,6 +129,9 @@ const Chart = props => {
|
||||
);
|
||||
|
||||
const chart = useSelector(state => state.charts[props.id] || EMPTY_OBJECT);
|
||||
const { queriesResponse, chartUpdateEndTime, chartStatus, annotationQuery } =
|
||||
chart;
|
||||
|
||||
const slice = useSelector(
|
||||
state => state.sliceEntities.slices[props.id] || EMPTY_OBJECT,
|
||||
);
|
||||
@@ -162,6 +166,12 @@ const Chart = props => {
|
||||
);
|
||||
const dashboardInfo = useSelector(state => state.dashboardInfo);
|
||||
|
||||
const isCached = useMemo(
|
||||
// eslint-disable-next-line camelcase
|
||||
() => queriesResponse?.map(({ is_cached }) => is_cached) || [],
|
||||
[queriesResponse],
|
||||
);
|
||||
|
||||
const [descriptionHeight, setDescriptionHeight] = useState(0);
|
||||
const [height, setHeight] = useState(props.height);
|
||||
const [width, setWidth] = useState(props.width);
|
||||
@@ -213,11 +223,15 @@ const Chart = props => {
|
||||
|
||||
const getHeaderHeight = useCallback(() => {
|
||||
if (headerRef.current) {
|
||||
const computedStyle = getComputedStyle(
|
||||
const computedMarginBottom = getComputedStyle(
|
||||
headerRef.current,
|
||||
).getPropertyValue('margin-bottom');
|
||||
const marginBottom = parseInt(computedStyle, 10) || 0;
|
||||
return headerRef.current.offsetHeight + marginBottom;
|
||||
const marginBottom = parseInt(computedMarginBottom, 10) || 0;
|
||||
const computedHeight = getComputedStyle(
|
||||
headerRef.current,
|
||||
).getPropertyValue('height');
|
||||
const height = parseInt(computedHeight, 10) || DEFAULT_HEADER_HEIGHT;
|
||||
return height + marginBottom;
|
||||
}
|
||||
return DEFAULT_HEADER_HEIGHT;
|
||||
}, [headerRef]);
|
||||
@@ -244,9 +258,9 @@ const Chart = props => {
|
||||
const logExploreChart = useCallback(() => {
|
||||
boundActionCreators.logEvent(LOG_ACTIONS_EXPLORE_DASHBOARD_CHART, {
|
||||
slice_id: slice.slice_id,
|
||||
is_cached: props.isCached,
|
||||
is_cached: isCached,
|
||||
});
|
||||
}, [boundActionCreators.logEvent, slice.slice_id, props.isCached]);
|
||||
}, [boundActionCreators.logEvent, slice.slice_id, isCached]);
|
||||
|
||||
const chartConfiguration = useSelector(
|
||||
state => state.dashboardInfo.metadata?.chart_configuration,
|
||||
@@ -360,22 +374,22 @@ const Chart = props => {
|
||||
: LOG_ACTIONS_EXPORT_XLSX_DASHBOARD_CHART;
|
||||
boundActionCreators.logEvent(logAction, {
|
||||
slice_id: slice.slice_id,
|
||||
is_cached: props.isCached,
|
||||
is_cached: isCached,
|
||||
});
|
||||
exportChart({
|
||||
formData: isFullCSV ? { ...formData, row_limit: maxRows } : formData,
|
||||
resultType: isPivot ? 'post_processed' : 'full',
|
||||
resultFormat: format,
|
||||
force: true,
|
||||
ownState: props.ownState,
|
||||
ownState: dataMask[props.id]?.ownState,
|
||||
});
|
||||
},
|
||||
[
|
||||
slice.slice_id,
|
||||
props.isCached,
|
||||
isCached,
|
||||
formData,
|
||||
props.maxRows,
|
||||
props.ownState,
|
||||
dataMask[props.id]?.ownState,
|
||||
boundActionCreators.logEvent,
|
||||
],
|
||||
);
|
||||
@@ -403,7 +417,7 @@ const Chart = props => {
|
||||
const forceRefresh = useCallback(() => {
|
||||
boundActionCreators.logEvent(LOG_ACTIONS_FORCE_REFRESH_CHART, {
|
||||
slice_id: slice.slice_id,
|
||||
is_cached: props.isCached,
|
||||
is_cached: isCached,
|
||||
});
|
||||
return boundActionCreators.refreshChart(chart.id, true, props.dashboardId);
|
||||
}, [
|
||||
@@ -411,7 +425,7 @@ const Chart = props => {
|
||||
chart.id,
|
||||
props.dashboardId,
|
||||
slice.slice_id,
|
||||
props.isCached,
|
||||
isCached,
|
||||
boundActionCreators.logEvent,
|
||||
]);
|
||||
|
||||
@@ -419,11 +433,7 @@ const Chart = props => {
|
||||
return <MissingChart height={getChartHeight()} />;
|
||||
}
|
||||
|
||||
const { queriesResponse, chartUpdateEndTime, chartStatus, annotationQuery } =
|
||||
chart;
|
||||
const isLoading = chartStatus === 'loading';
|
||||
// eslint-disable-next-line camelcase
|
||||
const isCached = queriesResponse?.map(({ is_cached }) => is_cached) || [];
|
||||
const cachedDttm =
|
||||
// eslint-disable-next-line camelcase
|
||||
queriesResponse?.map(({ cached_dttm }) => cached_dttm) || [];
|
||||
@@ -471,6 +481,7 @@ const Chart = props => {
|
||||
formData={formData}
|
||||
width={width}
|
||||
height={getHeaderHeight()}
|
||||
exportPivotExcel={exportPivotExcel}
|
||||
/>
|
||||
|
||||
{/*
|
||||
|
||||
@@ -27,6 +27,7 @@ export default function DownloadAsImage({
|
||||
text,
|
||||
logEvent,
|
||||
dashboardTitle,
|
||||
...props
|
||||
}: {
|
||||
text: string;
|
||||
dashboardTitle: string;
|
||||
@@ -50,6 +51,7 @@ export default function DownloadAsImage({
|
||||
onClick={e => {
|
||||
onDownloadImage(e.domEvent);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{text}
|
||||
</Menu.Item>
|
||||
|
||||
@@ -27,6 +27,7 @@ export default function DownloadAsPdf({
|
||||
text,
|
||||
logEvent,
|
||||
dashboardTitle,
|
||||
...props
|
||||
}: {
|
||||
text: string;
|
||||
dashboardTitle: string;
|
||||
@@ -50,6 +51,7 @@ export default function DownloadAsPdf({
|
||||
onClick={e => {
|
||||
onDownloadPdf(e.domEvent);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{text}
|
||||
</Menu.Item>
|
||||
|
||||
@@ -46,7 +46,7 @@ const containerStyle = (theme: SupersetTheme) => css`
|
||||
display: flex;
|
||||
|
||||
&& > .filter-clear-all-button {
|
||||
color: ${theme.colors.grayscale.base};
|
||||
color: ${theme.colorTextSecondary};
|
||||
margin-left: 0;
|
||||
&:hover {
|
||||
color: ${theme.colorPrimaryText};
|
||||
@@ -54,7 +54,7 @@ const containerStyle = (theme: SupersetTheme) => css`
|
||||
|
||||
&[disabled],
|
||||
&[disabled]:hover {
|
||||
color: ${theme.colors.grayscale.light1};
|
||||
color: ${theme.colorTextDisabled};
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -62,7 +62,6 @@ const containerStyle = (theme: SupersetTheme) => css`
|
||||
const verticalStyle = (theme: SupersetTheme, width: number) => css`
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
z-index: 100;
|
||||
|
||||
@@ -74,14 +73,10 @@ const verticalStyle = (theme: SupersetTheme, width: number) => css`
|
||||
padding-top: ${theme.sizeUnit * 6}px;
|
||||
|
||||
background: linear-gradient(
|
||||
${rgba(theme.colors.grayscale.light5, 0)},
|
||||
${theme.colors.grayscale.light5} 60%
|
||||
${rgba(theme.colorBgLayout, 0)},
|
||||
${theme.colorBgElevated} 20%
|
||||
);
|
||||
|
||||
& > button {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
& > .filter-apply-button {
|
||||
margin-bottom: ${theme.sizeUnit * 3}px;
|
||||
}
|
||||
@@ -94,13 +89,6 @@ const horizontalStyle = (theme: SupersetTheme) => css`
|
||||
text-transform: capitalize;
|
||||
font-weight: ${theme.fontWeightNormal};
|
||||
}
|
||||
& > .filter-apply-button {
|
||||
&[disabled],
|
||||
&[disabled]:hover {
|
||||
color: ${theme.colors.grayscale.light1};
|
||||
background: ${theme.colors.grayscale.light3};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ButtonsContainer = styled.div<{ isVertical: boolean; width: number }>`
|
||||
|
||||
@@ -289,4 +289,5 @@ export enum MenuKeys {
|
||||
ToggleFullscreen = 'toggle_fullscreen',
|
||||
ManageEmbedded = 'manage_embedded',
|
||||
ManageEmailReports = 'manage_email_reports',
|
||||
ExportPivotXlsx = 'export_pivot_xlsx',
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
|
||||
import { Icons } from '@superset-ui/core/components/Icons';
|
||||
import { Tooltip } from '@superset-ui/core/components/Tooltip';
|
||||
import { Typography } from '@superset-ui/core/components';
|
||||
import DatasourcePanelDragOption from './DatasourcePanelDragOption';
|
||||
import { DndItemType } from '../DndItemType';
|
||||
import { DndItemValue, FlattenedItem, Folder } from './types';
|
||||
@@ -94,7 +95,7 @@ const SectionHeaderTextContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const SectionHeader = styled.span`
|
||||
const SectionHeader = styled(Typography.Text)`
|
||||
${({ theme }) => css`
|
||||
font-size: ${theme.fontSize}px;
|
||||
font-weight: ${theme.fontWeightStrong};
|
||||
|
||||
@@ -46,6 +46,7 @@ import { Icons } from '@superset-ui/core/components/Icons';
|
||||
import Chart, { Slice } from 'src/types/Chart';
|
||||
import withToasts from 'src/components/MessageToasts/withToasts';
|
||||
import { type TagType } from 'src/components';
|
||||
import { TagTypeEnum } from 'src/components/Tag/TagType';
|
||||
import { loadTags } from 'src/components/Tag/utils';
|
||||
|
||||
export type PropertiesModalProps = {
|
||||
@@ -125,7 +126,7 @@ function PropertiesModal({
|
||||
);
|
||||
if (isFeatureEnabled(FeatureFlag.TaggingSystem)) {
|
||||
const customTags = chart.tags?.filter(
|
||||
(tag: TagType) => tag.type === 1,
|
||||
(tag: TagType) => tag.type === TagTypeEnum.Custom,
|
||||
);
|
||||
setTags(customTags);
|
||||
}
|
||||
|
||||
@@ -479,3 +479,30 @@ test('should show missing dataset state', () => {
|
||||
),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('should show forbidden dataset state', () => {
|
||||
// @ts-ignore
|
||||
delete window.location;
|
||||
// @ts-ignore
|
||||
window.location = { search: '?slice_id=152' };
|
||||
const error = {
|
||||
error_type: 'TABLE_SECURITY_ACCESS_ERROR',
|
||||
statusText: 'FORBIDDEN',
|
||||
message: 'You do not have access to the following tables: blocked_table',
|
||||
extra: {
|
||||
datasource: 152,
|
||||
datasource_name: 'forbidden dataset',
|
||||
},
|
||||
};
|
||||
const props = createProps({
|
||||
datasource: {
|
||||
...fallbackExploreInitialData.dataset,
|
||||
extra: {
|
||||
error,
|
||||
},
|
||||
},
|
||||
});
|
||||
render(<DatasourceControl {...props} />, { useRedux: true, useRouter: true });
|
||||
expect(screen.getByText(error.message)).toBeInTheDocument();
|
||||
expect(screen.getByText(error.statusText)).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -50,6 +50,7 @@ import {
|
||||
userHasPermission,
|
||||
isUserAdmin,
|
||||
} from 'src/dashboard/util/permissionUtils';
|
||||
import { ErrorMessageWithStackTrace } from 'src/components/ErrorMessage/ErrorMessageWithStackTrace';
|
||||
import ViewQueryModalFooter from 'src/explore/components/controls/ViewQueryModalFooter';
|
||||
import ViewQuery from 'src/explore/components/controls/ViewQuery';
|
||||
import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal';
|
||||
@@ -94,6 +95,7 @@ const Styles = styled.div`
|
||||
}
|
||||
.error-alert {
|
||||
margin: ${({ theme }) => 2 * theme.sizeUnit}px;
|
||||
min-height: 150px;
|
||||
}
|
||||
.ant-dropdown-trigger {
|
||||
margin-left: ${({ theme }) => 2 * theme.sizeUnit}px;
|
||||
@@ -281,7 +283,17 @@ class DatasourceControl extends PureComponent {
|
||||
showSaveDatasetModal,
|
||||
} = this.state;
|
||||
const { datasource, onChange, theme } = this.props;
|
||||
const isMissingDatasource = !datasource?.id;
|
||||
let extra;
|
||||
if (datasource?.extra) {
|
||||
if (typeof datasource.extra === 'string') {
|
||||
try {
|
||||
extra = JSON.parse(datasource.extra);
|
||||
} catch {} // eslint-disable-line no-empty
|
||||
} else {
|
||||
extra = datasource.extra; // eslint-disable-line prefer-destructuring
|
||||
}
|
||||
}
|
||||
const isMissingDatasource = !datasource?.id || Boolean(extra?.error);
|
||||
let isMissingParams = false;
|
||||
if (isMissingDatasource) {
|
||||
const datasourceId = getUrlParam(URL_PARAMS.datasourceId);
|
||||
@@ -386,20 +398,10 @@ class DatasourceControl extends PureComponent {
|
||||
|
||||
const { health_check_message: healthCheckMessage } = datasource;
|
||||
|
||||
let extra;
|
||||
if (datasource?.extra) {
|
||||
if (typeof datasource.extra === 'string') {
|
||||
try {
|
||||
extra = JSON.parse(datasource.extra);
|
||||
} catch {} // eslint-disable-line no-empty
|
||||
} else {
|
||||
extra = datasource.extra; // eslint-disable-line prefer-destructuring
|
||||
}
|
||||
}
|
||||
|
||||
const titleText = isMissingDatasource
|
||||
? t('Missing dataset')
|
||||
: getDatasourceTitle(datasource);
|
||||
const titleText =
|
||||
isMissingDatasource && !datasource.name
|
||||
? t('Missing dataset')
|
||||
: getDatasourceTitle(datasource);
|
||||
|
||||
const tooltip = titleText;
|
||||
|
||||
@@ -452,31 +454,42 @@ class DatasourceControl extends PureComponent {
|
||||
)}
|
||||
{isMissingDatasource && !isMissingParams && (
|
||||
<div className="error-alert">
|
||||
<ErrorAlert
|
||||
type="warning"
|
||||
errorType={t('Missing dataset')}
|
||||
descriptionPre={false}
|
||||
descriptionDetailsCollapsed={false}
|
||||
descriptionDetails={
|
||||
<>
|
||||
<p>
|
||||
{t(
|
||||
'The dataset linked to this chart may have been deleted.',
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
<Button
|
||||
buttonStyle="warning"
|
||||
onClick={() =>
|
||||
this.handleMenuItemClick({ key: CHANGE_DATASET })
|
||||
}
|
||||
>
|
||||
{t('Swap dataset')}
|
||||
</Button>
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
{extra?.error ? (
|
||||
<ErrorMessageWithStackTrace
|
||||
title={extra.error.statusText || extra.error.message}
|
||||
subtitle={
|
||||
extra.error.statusText ? extra.error.message : undefined
|
||||
}
|
||||
error={extra.error}
|
||||
source="explore"
|
||||
/>
|
||||
) : (
|
||||
<ErrorAlert
|
||||
type="warning"
|
||||
errorType={t('Missing dataset')}
|
||||
descriptionPre={false}
|
||||
descriptionDetailsCollapsed={false}
|
||||
descriptionDetails={
|
||||
<>
|
||||
<p>
|
||||
{t(
|
||||
'The dataset linked to this chart may have been deleted.',
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
<Button
|
||||
buttonStyle="warning"
|
||||
onClick={() =>
|
||||
this.handleMenuItemClick({ key: CHANGE_DATASET })
|
||||
}
|
||||
>
|
||||
{t('Swap dataset')}
|
||||
</Button>
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{showEditDatasourceModal && (
|
||||
|
||||
@@ -90,7 +90,10 @@ const DndFilterSelect = (props: DndFilterSelectProps) => {
|
||||
let extra = {};
|
||||
if (datasource?.extra) {
|
||||
try {
|
||||
extra = JSON.parse(datasource.extra);
|
||||
extra =
|
||||
typeof datasource.extra === 'string'
|
||||
? JSON.parse(datasource.extra)
|
||||
: datasource.extra;
|
||||
} catch {} // eslint-disable-line no-empty
|
||||
}
|
||||
return extra;
|
||||
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
LOG_ACTIONS_CHART_DOWNLOAD_AS_CSV_PIVOTED,
|
||||
LOG_ACTIONS_CHART_DOWNLOAD_AS_XLS,
|
||||
} from 'src/logger/LogUtils';
|
||||
import exportPivotExcel from 'src/utils/downloadAsPivotExcel';
|
||||
import ViewQueryModal from '../controls/ViewQueryModal';
|
||||
import EmbedCodeContent from '../EmbedCodeContent';
|
||||
import DashboardsSubMenu from './DashboardsSubMenu';
|
||||
@@ -67,6 +68,7 @@ const MENU_KEYS = {
|
||||
DELETE_REPORT: 'delete_report',
|
||||
VIEW_QUERY: 'view_query',
|
||||
RUN_IN_SQL_LAB: 'run_in_sql_lab',
|
||||
EXPORT_TO_PIVOT_XLSX: 'export_to_pivot_xlsx',
|
||||
};
|
||||
|
||||
const VIZ_TYPES_PIVOTABLE = [VizType.PivotTable];
|
||||
@@ -248,6 +250,16 @@ export const useExploreAdditionalActionsMenu = (
|
||||
}),
|
||||
);
|
||||
break;
|
||||
case MENU_KEYS.EXPORT_TO_PIVOT_XLSX:
|
||||
exportPivotExcel('.pvtTable', slice?.slice_name ?? t('pivoted_xlsx'));
|
||||
setIsDropdownVisible(false);
|
||||
dispatch(
|
||||
logEvent(LOG_ACTIONS_CHART_DOWNLOAD_AS_XLS, {
|
||||
chartId: slice?.slice_id,
|
||||
chartName: slice?.slice_name,
|
||||
}),
|
||||
);
|
||||
break;
|
||||
case MENU_KEYS.DOWNLOAD_AS_IMAGE:
|
||||
downloadAsImage(
|
||||
'.panel-body .chart-container',
|
||||
@@ -365,6 +377,13 @@ export const useExploreAdditionalActionsMenu = (
|
||||
>
|
||||
{t('Export to Excel')}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
key={MENU_KEYS.EXPORT_TO_PIVOT_XLSX}
|
||||
icon={<Icons.FileOutlined />}
|
||||
disabled={!canDownloadCSV}
|
||||
>
|
||||
{t('Export to Pivoted Excel')}
|
||||
</Menu.Item>
|
||||
</Menu.SubMenu>
|
||||
<Menu.SubMenu title={t('Share')} key={MENU_KEYS.SHARE_SUBMENU}>
|
||||
<Menu.Item key={MENU_KEYS.COPY_PERMALINK}>
|
||||
|
||||
@@ -159,8 +159,8 @@ export const fallbackExploreInitialData: ExplorePageInitialData = {
|
||||
verbose_map: {},
|
||||
main_dttm_col: '',
|
||||
owners: [],
|
||||
datasource_name: 'missing_datasource',
|
||||
name: 'missing_datasource',
|
||||
datasource_name: '',
|
||||
name: '',
|
||||
description: null,
|
||||
},
|
||||
slice: null,
|
||||
|
||||
@@ -69,7 +69,7 @@ export type Datasource = Dataset & {
|
||||
catalog?: string | null;
|
||||
schema?: string;
|
||||
is_sqllab_view?: boolean;
|
||||
extra?: string;
|
||||
extra?: string | object;
|
||||
};
|
||||
|
||||
export interface ExplorePageInitialData {
|
||||
|
||||
@@ -191,4 +191,32 @@ describe('ExtraOptions Component', () => {
|
||||
fireEvent.change(input, { target: { value: '1000' } });
|
||||
expect(onExtraInputChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders the collaps tab correctly and resets to default tab after closing', () => {
|
||||
const { rerender } = renderComponent();
|
||||
const sqlLabTab = screen.getByRole('tab', {
|
||||
name: /SQL Lab./i,
|
||||
});
|
||||
|
||||
expect(sqlLabTab).toHaveAttribute('aria-expanded', 'false');
|
||||
fireEvent.click(sqlLabTab);
|
||||
expect(sqlLabTab).toHaveAttribute('aria-expanded', 'true');
|
||||
const customDb = {
|
||||
...defaultDb,
|
||||
expose_in_sqllab: false,
|
||||
};
|
||||
|
||||
rerender(
|
||||
<ExtraOptions
|
||||
db={customDb as unknown as DatabaseObject}
|
||||
onInputChange={onInputChange}
|
||||
onTextChange={onTextChange}
|
||||
onEditorChange={onEditorChange}
|
||||
onExtraInputChange={onExtraInputChange}
|
||||
onExtraEditorChange={onExtraEditorChange}
|
||||
extraExtension={undefined}
|
||||
/>,
|
||||
);
|
||||
expect(sqlLabTab).toHaveAttribute('aria-expanded', 'false');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { ChangeEvent, EventHandler } from 'react';
|
||||
import { ChangeEvent, EventHandler, useState, useEffect } from 'react';
|
||||
import cx from 'classnames';
|
||||
import {
|
||||
t,
|
||||
@@ -88,12 +88,21 @@ const ExtraOptions = ({
|
||||
const isAllowRunAsyncDisabled = isFeatureEnabled(
|
||||
FeatureFlag.ForceSqlLabRunAsync,
|
||||
);
|
||||
const [activeKey, setActiveKey] = useState<string[] | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!expandableModalIsOpen && activeKey !== undefined) {
|
||||
setActiveKey(undefined);
|
||||
}
|
||||
}, [expandableModalIsOpen, activeKey]);
|
||||
|
||||
return (
|
||||
<Collapse
|
||||
expandIconPosition="end"
|
||||
accordion
|
||||
modalMode
|
||||
activeKey={activeKey}
|
||||
onChange={key => setActiveKey(key)}
|
||||
items={[
|
||||
{
|
||||
key: 'sql-lab',
|
||||
|
||||
@@ -102,9 +102,9 @@ const getCommonElements = () => ({
|
||||
cancelButton: screen.getByRole('button', { name: 'Cancel' }),
|
||||
uploadButton: screen.getByRole('button', { name: 'Upload' }),
|
||||
selectButton: screen.getByRole('button', { name: 'Select' }),
|
||||
panel1: screen.getByRole('heading', { name: /General information/i }),
|
||||
panel2: screen.getByRole('heading', { name: /file settings/i }),
|
||||
panel3: screen.getByRole('heading', { name: /columns/i }),
|
||||
panel1: screen.getByText(/General information/i, { selector: 'strong' }),
|
||||
panel2: screen.getByText(/file settings/i, { selector: 'strong' }),
|
||||
panel3: screen.getByText(/columns/i, { selector: 'strong' }),
|
||||
selectDatabase: screen.getByRole('combobox', { name: /select a database/i }),
|
||||
inputTableName: screen.getByRole('textbox', { name: /table name/i }),
|
||||
inputSchema: screen.getByRole('combobox', { name: /schema/i }),
|
||||
@@ -130,7 +130,7 @@ describe('UploadDataModal - General Information Elements', () => {
|
||||
|
||||
const common = getCommonElements();
|
||||
const title = screen.getByRole('heading', { name: /csv upload/i });
|
||||
const panel4 = screen.getByRole('heading', { name: /rows/i });
|
||||
const panel4 = screen.getByText(/rows/i);
|
||||
const selectDelimiter = screen.getByRole('combobox', {
|
||||
name: /choose a delimiter/i,
|
||||
});
|
||||
@@ -156,7 +156,7 @@ describe('UploadDataModal - General Information Elements', () => {
|
||||
|
||||
const common = getCommonElements();
|
||||
const title = screen.getByRole('heading', { name: /excel upload/i });
|
||||
const panel4 = screen.getByRole('heading', { name: /rows/i });
|
||||
const panel4 = screen.getByText(/rows/i);
|
||||
const selectSheetName = screen.getByRole('combobox', {
|
||||
name: /choose sheet name/i,
|
||||
});
|
||||
@@ -177,9 +177,7 @@ describe('UploadDataModal - General Information Elements', () => {
|
||||
]);
|
||||
|
||||
// Check elements that should NOT be visible
|
||||
expect(
|
||||
screen.queryByRole('heading', { name: /csv upload/i }),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByText(/csv upload/i)).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole('combobox', { name: /choose a delimiter/i }),
|
||||
).not.toBeInTheDocument();
|
||||
@@ -206,8 +204,8 @@ describe('UploadDataModal - General Information Elements', () => {
|
||||
|
||||
// Check elements that should NOT be visible
|
||||
expectElementsNotVisible([
|
||||
screen.queryByRole('heading', { name: /csv upload/i }),
|
||||
screen.queryByRole('heading', { name: /rows/i }),
|
||||
screen.queryByText(/csv upload/i),
|
||||
screen.queryByText(/rows/i),
|
||||
screen.queryByRole('combobox', { name: /choose a delimiter/i }),
|
||||
screen.queryByRole('combobox', { name: /choose sheet name/i }),
|
||||
]);
|
||||
@@ -216,7 +214,7 @@ describe('UploadDataModal - General Information Elements', () => {
|
||||
|
||||
describe('UploadDataModal - File Settings Elements', () => {
|
||||
const openFileSettings = async () => {
|
||||
const panelHeader = screen.getByRole('heading', { name: /file settings/i });
|
||||
const panelHeader = screen.getByText(/file settings/i);
|
||||
await userEvent.click(panelHeader);
|
||||
};
|
||||
|
||||
@@ -294,7 +292,7 @@ describe('UploadDataModal - File Settings Elements', () => {
|
||||
|
||||
describe('UploadDataModal - Columns Elements', () => {
|
||||
const openColumns = async () => {
|
||||
const panelHeader = screen.getByRole('heading', { name: /columns/i });
|
||||
const panelHeader = screen.getByText(/columns/i, { selector: 'strong' });
|
||||
await userEvent.click(panelHeader);
|
||||
};
|
||||
|
||||
@@ -365,7 +363,7 @@ describe('UploadDataModal - Rows Elements', () => {
|
||||
test('CSV/Excel rows render correctly', async () => {
|
||||
render(<UploadDataModal {...csvProps} />, { useRedux: true });
|
||||
|
||||
const panelHeader = screen.getByRole('heading', { name: /rows/i });
|
||||
const panelHeader = screen.getByText(/rows/i);
|
||||
await userEvent.click(panelHeader);
|
||||
|
||||
const elements = [
|
||||
@@ -380,7 +378,7 @@ describe('UploadDataModal - Rows Elements', () => {
|
||||
test('Columnar does not render rows', () => {
|
||||
render(<UploadDataModal {...columnarProps} />, { useRedux: true });
|
||||
|
||||
const panelHeader = screen.queryByRole('heading', { name: /rows/i });
|
||||
const panelHeader = screen.queryByText(/rows/i);
|
||||
expect(panelHeader).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -608,11 +606,11 @@ describe('UploadDataModal Collapse Tabs', () => {
|
||||
useRedux: true,
|
||||
});
|
||||
const generalInfoTab = screen.getByRole('tab', {
|
||||
name: /expanded General information Upload a file to a database./i,
|
||||
name: /expanded General information/i,
|
||||
});
|
||||
expect(generalInfoTab).toHaveAttribute('aria-expanded', 'true');
|
||||
const fileSettingsTab = screen.getByRole('tab', {
|
||||
name: /collapsed File settings Adjust how spaces, blank lines, null values are handled and other file wide settings./i,
|
||||
name: /collapsed File settings/i,
|
||||
});
|
||||
userEvent.click(fileSettingsTab);
|
||||
expect(fileSettingsTab).toHaveAttribute('aria-expanded', 'true');
|
||||
@@ -626,11 +624,11 @@ describe('UploadDataModal Collapse Tabs', () => {
|
||||
useRedux: true,
|
||||
});
|
||||
const generalInfoTab = screen.getByRole('tab', {
|
||||
name: /expanded General information Upload a file to a database./i,
|
||||
name: /expanded General information/i,
|
||||
});
|
||||
expect(generalInfoTab).toHaveAttribute('aria-expanded', 'true');
|
||||
const fileSettingsTab = screen.getByRole('tab', {
|
||||
name: /collapsed File settings Adjust how spaces, blank lines, null values are handled and other file wide settings./i,
|
||||
name: /collapsed File settings/i,
|
||||
});
|
||||
userEvent.click(fileSettingsTab);
|
||||
expect(fileSettingsTab).toHaveAttribute('aria-expanded', 'true');
|
||||
@@ -644,11 +642,11 @@ describe('UploadDataModal Collapse Tabs', () => {
|
||||
useRedux: true,
|
||||
});
|
||||
const generalInfoTab = screen.getByRole('tab', {
|
||||
name: /expanded General information Upload a file to a database./i,
|
||||
name: /expanded General information/i,
|
||||
});
|
||||
expect(generalInfoTab).toHaveAttribute('aria-expanded', 'true');
|
||||
const fileSettingsTab = screen.getByRole('tab', {
|
||||
name: /collapsed File settings Adjust how spaces, blank lines, null values are handled and other file wide settings./i,
|
||||
name: /collapsed File settings/i,
|
||||
});
|
||||
userEvent.click(fileSettingsTab);
|
||||
expect(fileSettingsTab).toHaveAttribute('aria-expanded', 'true');
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
} from 'react';
|
||||
|
||||
import {
|
||||
css,
|
||||
getClientErrorObject,
|
||||
SupersetClient,
|
||||
SupersetTheme,
|
||||
@@ -45,6 +46,7 @@ import {
|
||||
Upload,
|
||||
type UploadChangeParam,
|
||||
type UploadFile,
|
||||
Typography,
|
||||
} from '@superset-ui/core/components';
|
||||
import { Switch, SwitchProps } from '@superset-ui/core/components/Switch';
|
||||
import { Icons } from '@superset-ui/core/components/Icons';
|
||||
@@ -576,7 +578,17 @@ const UploadDataModal: FunctionComponent<UploadDataModalProps> = ({
|
||||
|
||||
const UploadTitle: FC = () => {
|
||||
const title = uploadTitles[type] || t('Upload');
|
||||
return <h4>{title}</h4>;
|
||||
return (
|
||||
<Typography.Title
|
||||
level={5}
|
||||
css={css`
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
`}
|
||||
>
|
||||
{title}
|
||||
</Typography.Title>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -592,7 +604,7 @@ const UploadDataModal: FunctionComponent<UploadDataModalProps> = ({
|
||||
onHandledPrimaryAction={form.submit}
|
||||
onHide={onClose}
|
||||
width="500px"
|
||||
primaryButtonName="Upload"
|
||||
primaryButtonName={t('Upload')}
|
||||
centered
|
||||
show={show}
|
||||
title={<UploadTitle />}
|
||||
@@ -615,10 +627,9 @@ const UploadDataModal: FunctionComponent<UploadDataModalProps> = ({
|
||||
{
|
||||
key: 'general',
|
||||
label: (
|
||||
<div>
|
||||
<h4>{t('General information')}</h4>
|
||||
<p className="helper">{t('Upload a file to a database.')}</p>
|
||||
</div>
|
||||
<Typography.Text strong>
|
||||
{t('General information')}
|
||||
</Typography.Text>
|
||||
),
|
||||
children: (
|
||||
<>
|
||||
@@ -772,14 +783,7 @@ const UploadDataModal: FunctionComponent<UploadDataModalProps> = ({
|
||||
{
|
||||
key: 'file-settings',
|
||||
label: (
|
||||
<div>
|
||||
<h4>{t('File settings')}</h4>
|
||||
<p className="helper">
|
||||
{t(
|
||||
'Adjust how spaces, blank lines, null values are handled and other file wide settings.',
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Typography.Text strong>{t('File settings')}</Typography.Text>
|
||||
),
|
||||
children: (
|
||||
<>
|
||||
@@ -901,16 +905,7 @@ const UploadDataModal: FunctionComponent<UploadDataModalProps> = ({
|
||||
},
|
||||
{
|
||||
key: 'columns',
|
||||
label: (
|
||||
<div>
|
||||
<h4>{t('Columns')}</h4>
|
||||
<p className="helper">
|
||||
{t(
|
||||
'Adjust column settings such as specifying the columns to read, how duplicates are handled, column data types, and more.',
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
),
|
||||
label: <Typography.Text strong>{t('Columns')}</Typography.Text>,
|
||||
children: (
|
||||
<>
|
||||
<Row>
|
||||
@@ -1010,14 +1005,7 @@ const UploadDataModal: FunctionComponent<UploadDataModalProps> = ({
|
||||
{
|
||||
key: 'rows',
|
||||
label: (
|
||||
<div>
|
||||
<h4>{t('Rows')}</h4>
|
||||
<p className="helper">
|
||||
{t(
|
||||
'Set header rows and the number of rows to read or skip.',
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<Typography.Text strong>{t('Rows')}</Typography.Text>
|
||||
),
|
||||
children: (
|
||||
<Row>
|
||||
|
||||
@@ -46,7 +46,7 @@ export const antDModalNoPaddingStyles = css`
|
||||
|
||||
export const formStyles = (theme: SupersetTheme) => css`
|
||||
.switch-label {
|
||||
color: ${theme.colors.grayscale.base};
|
||||
color: ${theme.colorTextSecondary};
|
||||
margin-left: ${theme.sizeUnit * 4}px;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -132,7 +132,7 @@ function GroupListModal({
|
||||
value: role.id,
|
||||
label: role.name,
|
||||
}))}
|
||||
getPopupContainer={trigger => trigger.closest('.antd5-modal-content')}
|
||||
getPopupContainer={trigger => trigger.closest('.ant-modal-content')}
|
||||
/>
|
||||
</FormItem>
|
||||
<FormItem name="users" label={t('Users')}>
|
||||
|
||||
@@ -187,7 +187,7 @@ const RightMenu = ({
|
||||
const {
|
||||
theme: themeEditorTheme,
|
||||
setTheme,
|
||||
changeThemeMode,
|
||||
setThemeMode,
|
||||
themeMode,
|
||||
} = useThemeContext();
|
||||
const dropdownItems: MenuObjectProps[] = [
|
||||
@@ -501,10 +501,7 @@ const RightMenu = ({
|
||||
)}
|
||||
{isFeatureEnabled(FeatureFlag.ThemeEnableDarkThemeSwitch) && (
|
||||
<span>
|
||||
<ThemeSelect
|
||||
changeThemeMode={changeThemeMode}
|
||||
themeMode={themeMode}
|
||||
/>
|
||||
<ThemeSelect setThemeMode={setThemeMode} themeMode={themeMode} />
|
||||
</span>
|
||||
)}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import { JsonObject, SupersetClient } from '@superset-ui/core';
|
||||
import rison from 'rison';
|
||||
import { TagType } from 'src/components';
|
||||
import { TagTypeEnum } from 'src/components/Tag/TagType';
|
||||
|
||||
export const OBJECT_TYPES_VALUES = Object.freeze([
|
||||
'dashboard',
|
||||
@@ -93,7 +94,11 @@ export function fetchTags(
|
||||
endpoint: `/api/v1/${objectType}/${objectId}`,
|
||||
})
|
||||
.then(({ json }) =>
|
||||
callback(json.result.tags.filter((tag: TagType) => tag.type === 1)),
|
||||
callback(
|
||||
json.result.tags.filter(
|
||||
(tag: TagType) => tag.type === TagTypeEnum.Custom,
|
||||
),
|
||||
),
|
||||
)
|
||||
.catch(response => error(response));
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ const Wrapper = styled.div`
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
||||
.antd5-input-number {
|
||||
.ant-input-number {
|
||||
min-width: 80px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -196,10 +196,10 @@ function ActionLogList() {
|
||||
}: any) => (
|
||||
<Typography.Text
|
||||
css={css`
|
||||
.antd5-typography-copy {
|
||||
.ant-typography-copy {
|
||||
visibility: hidden;
|
||||
}
|
||||
&:hover .antd5-typography-copy {
|
||||
&:hover .ant-typography-copy {
|
||||
visibility: visible;
|
||||
}
|
||||
`}
|
||||
@@ -224,10 +224,10 @@ function ActionLogList() {
|
||||
}: any) => (
|
||||
<Typography.Text
|
||||
css={css`
|
||||
.antd5-typography-copy {
|
||||
.ant-typography-copy {
|
||||
visibility: hidden;
|
||||
}
|
||||
&:hover .antd5-typography-copy {
|
||||
&:hover .ant-typography-copy {
|
||||
visibility: visible;
|
||||
}
|
||||
`}
|
||||
|
||||
@@ -31,6 +31,7 @@ import getFormDataWithExtraFilters from 'src/dashboard/util/charts/getFormDataWi
|
||||
import { URL_PARAMS } from 'src/constants';
|
||||
import { JsonObject, VizType } from '@superset-ui/core';
|
||||
import { useUnsavedChangesPrompt } from 'src/hooks/useUnsavedChangesPrompt';
|
||||
import { getParsedExploreURLParams } from 'src/explore/exploreUtils/getParsedExploreURLParams';
|
||||
import ChartPage from '.';
|
||||
|
||||
jest.mock('src/hooks/useUnsavedChangesPrompt', () => ({
|
||||
@@ -49,6 +50,9 @@ jest.mock(
|
||||
),
|
||||
);
|
||||
jest.mock('src/dashboard/util/charts/getFormDataWithExtraFilters');
|
||||
jest.mock('src/explore/exploreUtils/getParsedExploreURLParams', () => ({
|
||||
getParsedExploreURLParams: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('ChartPage', () => {
|
||||
beforeEach(() => {
|
||||
@@ -89,6 +93,91 @@ describe('ChartPage', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('displays the dataset name and error when it is prohibited', async () => {
|
||||
const chartApiRoute = `glob:*/api/v1/chart/*`;
|
||||
const exploreApiRoute = 'glob:*/api/v1/explore/*';
|
||||
const expectedDatasourceName = 'failed datasource name';
|
||||
(getParsedExploreURLParams as jest.Mock).mockReturnValue(
|
||||
new Map([['datasource_id', 1]]),
|
||||
);
|
||||
fetchMock.get(exploreApiRoute, () => {
|
||||
class Extra {
|
||||
datasource = 123;
|
||||
|
||||
datasource_name = expectedDatasourceName;
|
||||
}
|
||||
class SupersetSecurityError {
|
||||
message = 'You do not have a permission to the table';
|
||||
|
||||
extra = new Extra();
|
||||
}
|
||||
throw new SupersetSecurityError();
|
||||
});
|
||||
fetchMock.get(chartApiRoute, 200);
|
||||
const { getByTestId } = render(<ChartPage />, {
|
||||
useRouter: true,
|
||||
useRedux: true,
|
||||
useDnd: true,
|
||||
});
|
||||
await waitFor(
|
||||
() =>
|
||||
expect(getByTestId('mock-explore-chart-panel')).toHaveTextContent(
|
||||
JSON.stringify({ datasource_name: expectedDatasourceName }).slice(
|
||||
1,
|
||||
-1,
|
||||
),
|
||||
),
|
||||
{
|
||||
timeout: 5000,
|
||||
},
|
||||
);
|
||||
expect(fetchMock.calls(chartApiRoute).length).toEqual(0);
|
||||
expect(fetchMock.calls(exploreApiRoute).length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
test('fetches the chart api when explore metadata is prohibited and access from the chart link', async () => {
|
||||
const expectedChartId = 7;
|
||||
const expectedChartName = 'Unauthorized dataset owned chart name';
|
||||
(getParsedExploreURLParams as jest.Mock).mockReturnValue(
|
||||
new Map([['slice_id', expectedChartId]]),
|
||||
);
|
||||
const chartApiRoute = `glob:*/api/v1/chart/${expectedChartId}`;
|
||||
const exploreApiRoute = 'glob:*/api/v1/explore/*';
|
||||
|
||||
fetchMock.get(exploreApiRoute, () => {
|
||||
class Extra {
|
||||
datasource = 123;
|
||||
}
|
||||
class SupersetSecurityError {
|
||||
message = 'You do not have a permission to the table';
|
||||
|
||||
extra = new Extra();
|
||||
}
|
||||
throw new SupersetSecurityError();
|
||||
});
|
||||
fetchMock.get(chartApiRoute, {
|
||||
result: {
|
||||
id: expectedChartId,
|
||||
slice_name: expectedChartName,
|
||||
url: 'chartid',
|
||||
},
|
||||
});
|
||||
const { getByTestId, getByText } = render(<ChartPage />, {
|
||||
useRouter: true,
|
||||
useRedux: true,
|
||||
useDnd: true,
|
||||
});
|
||||
await waitFor(() => expect(fetchMock.calls(chartApiRoute).length).toBe(1), {
|
||||
timeout: 5000,
|
||||
});
|
||||
expect(fetchMock.calls(exploreApiRoute).length).toBeGreaterThanOrEqual(1);
|
||||
expect(getByTestId('mock-explore-chart-panel')).toBeInTheDocument();
|
||||
expect(getByTestId('mock-explore-chart-panel')).toHaveTextContent(
|
||||
JSON.stringify({ datasource: 123 }).slice(1, -1),
|
||||
);
|
||||
expect(getByText(expectedChartName)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('with dashboardContextFormData', () => {
|
||||
const dashboardPageId = 'mockPageId';
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import { ExploreResponsePayload, SaveActionType } from 'src/explore/types';
|
||||
import { fallbackExploreInitialData } from 'src/explore/fixtures';
|
||||
import { getItem, LocalStorageKeys } from 'src/utils/localStorageHelpers';
|
||||
import { getFormDataWithDashboardContext } from 'src/explore/controlUtils/getFormDataWithDashboardContext';
|
||||
import type Chart from 'src/types/Chart';
|
||||
|
||||
const isValidResult = (rv: JsonObject): boolean =>
|
||||
rv?.result?.form_data && rv?.result?.dataset;
|
||||
@@ -49,41 +50,31 @@ const hasDatasetId = (rv: JsonObject): boolean =>
|
||||
isDefined(rv?.result?.dataset?.id);
|
||||
|
||||
const fetchExploreData = async (exploreUrlParams: URLSearchParams) => {
|
||||
try {
|
||||
const rv = await makeApi<{}, ExploreResponsePayload>({
|
||||
method: 'GET',
|
||||
endpoint: 'api/v1/explore/',
|
||||
})(exploreUrlParams);
|
||||
if (isValidResult(rv)) {
|
||||
if (hasDatasetId(rv)) {
|
||||
return rv;
|
||||
}
|
||||
// Since there's no dataset id but the API responded with a valid payload,
|
||||
// we assume the dataset was deleted, so we preserve some values from previous
|
||||
// state so if the user decide to swap the datasource, the chart config remains
|
||||
fallbackExploreInitialData.form_data = {
|
||||
...rv.result.form_data,
|
||||
...fallbackExploreInitialData.form_data,
|
||||
};
|
||||
if (rv.result?.slice) {
|
||||
fallbackExploreInitialData.slice = rv.result.slice;
|
||||
}
|
||||
const rv = await makeApi<{}, ExploreResponsePayload>({
|
||||
method: 'GET',
|
||||
endpoint: 'api/v1/explore/',
|
||||
})(exploreUrlParams);
|
||||
if (isValidResult(rv)) {
|
||||
if (hasDatasetId(rv)) {
|
||||
return rv;
|
||||
}
|
||||
let message = t('Failed to load chart data');
|
||||
const responseError = rv?.result?.message;
|
||||
if (responseError) {
|
||||
message = `${message}:\n${responseError}`;
|
||||
// Since there's no dataset id but the API responded with a valid payload,
|
||||
// we assume the dataset was deleted, so we preserve some values from previous
|
||||
// state so if the user decide to swap the datasource, the chart config remains
|
||||
fallbackExploreInitialData.form_data = {
|
||||
...rv.result.form_data,
|
||||
...fallbackExploreInitialData.form_data,
|
||||
};
|
||||
if (rv.result?.slice) {
|
||||
fallbackExploreInitialData.slice = rv.result.slice;
|
||||
}
|
||||
throw new Error(message);
|
||||
} catch (err) {
|
||||
// todo: encapsulate the error handler
|
||||
const clientError = await getClientErrorObject(err);
|
||||
throw new Error(
|
||||
clientError.message ||
|
||||
clientError.error ||
|
||||
t('Failed to load chart data.'),
|
||||
);
|
||||
}
|
||||
let message = t('Failed to load chart data');
|
||||
const responseError = rv?.result?.message;
|
||||
if (responseError) {
|
||||
message = `${message}:\n${responseError}`;
|
||||
}
|
||||
throw new Error(message);
|
||||
};
|
||||
|
||||
const getDashboardPageContext = (pageId?: string | null) => {
|
||||
@@ -166,9 +157,62 @@ export default function ExplorePage() {
|
||||
}),
|
||||
);
|
||||
})
|
||||
.catch(err => {
|
||||
.catch(err => Promise.all([getClientErrorObject(err), err]))
|
||||
.then(resolved => {
|
||||
const [clientError, err] = resolved || [];
|
||||
if (!err) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
const errorMesage =
|
||||
clientError?.message ||
|
||||
clientError?.error ||
|
||||
t('Failed to load chart data.');
|
||||
dispatch(addDangerToast(errorMesage));
|
||||
|
||||
if (err.extra?.datasource) {
|
||||
const exploreData = {
|
||||
...fallbackExploreInitialData,
|
||||
dataset: {
|
||||
...fallbackExploreInitialData.dataset,
|
||||
id: err.extra?.datasource,
|
||||
name: err.extra?.datasource_name,
|
||||
extra: {
|
||||
error: err,
|
||||
},
|
||||
},
|
||||
};
|
||||
const chartId = exploreUrlParams.get('slice_id');
|
||||
return (
|
||||
chartId
|
||||
? makeApi<void, { result: Chart }>({
|
||||
method: 'GET',
|
||||
endpoint: `api/v1/chart/${chartId}`,
|
||||
})()
|
||||
: Promise.reject()
|
||||
)
|
||||
.then(
|
||||
({ result: { id, url, owners, form_data: _, ...data } }) => {
|
||||
const slice = {
|
||||
...data,
|
||||
datasource: err.extra?.datasource_name,
|
||||
slice_id: id,
|
||||
slice_url: url,
|
||||
owners: owners?.map(({ id }) => id),
|
||||
};
|
||||
dispatch(
|
||||
hydrateExplore({
|
||||
...exploreData,
|
||||
slice,
|
||||
}),
|
||||
);
|
||||
},
|
||||
)
|
||||
.catch(() => {
|
||||
dispatch(hydrateExplore(exploreData));
|
||||
});
|
||||
}
|
||||
dispatch(hydrateExplore(fallbackExploreInitialData));
|
||||
dispatch(addDangerToast(err.message));
|
||||
return Promise.resolve();
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoaded(true);
|
||||
|
||||
@@ -71,6 +71,7 @@ import PropertiesModal from 'src/explore/components/PropertiesModal';
|
||||
import Chart from 'src/types/Chart';
|
||||
import { Icons } from '@superset-ui/core/components/Icons';
|
||||
import { nativeFilterGate } from 'src/dashboard/components/nativeFilters/utils';
|
||||
import { TagTypeEnum } from 'src/components/Tag/TagType';
|
||||
import { loadTags } from 'src/components/Tag/utils';
|
||||
import ChartCard from 'src/features/charts/ChartCard';
|
||||
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
|
||||
@@ -417,7 +418,8 @@ function ChartList(props: ChartListProps) {
|
||||
<TagsList
|
||||
tags={tags.filter((tag: TagType) =>
|
||||
tag.type
|
||||
? tag.type === 1 || tag.type === 'TagTypes.custom'
|
||||
? tag.type === TagTypeEnum.Custom ||
|
||||
tag.type === 'TagTypes.custom'
|
||||
: true,
|
||||
)}
|
||||
maxTags={3}
|
||||
|
||||
@@ -67,6 +67,7 @@ import {
|
||||
Dashboard as CRUDDashboard,
|
||||
QueryObjectColumns,
|
||||
} from 'src/views/CRUD/types';
|
||||
import { TagTypeEnum } from 'src/components/Tag/TagType';
|
||||
import { loadTags } from 'src/components/Tag/utils';
|
||||
import DashboardCard from 'src/features/dashboards/DashboardCard';
|
||||
import { DashboardStatus } from 'src/features/dashboards/types';
|
||||
@@ -376,7 +377,8 @@ function DashboardList(props: DashboardListProps) {
|
||||
<TagsList
|
||||
tags={tags.filter(
|
||||
(tag: TagType) =>
|
||||
tag.type === 'TagTypes.custom' || tag.type === 1,
|
||||
tag.type === 'TagTypes.custom' ||
|
||||
tag.type === TagTypeEnum.Custom,
|
||||
)}
|
||||
maxTags={3}
|
||||
/>
|
||||
|
||||
@@ -62,7 +62,7 @@ const StyledCard = styled(Card)`
|
||||
margin-top: ${theme.marginXL}px;
|
||||
color: ${theme.colorBgContainer};
|
||||
background: ${theme.colorBgBase};
|
||||
.antd5-form-item-label label {
|
||||
.ant-form-item-label label {
|
||||
color: ${theme.colorPrimary};
|
||||
}
|
||||
`}
|
||||
|
||||
@@ -45,7 +45,7 @@ const StyledCard = styled(Card)`
|
||||
width: 50%;
|
||||
margin-top: ${theme.marginXL}px;
|
||||
background: ${theme.colorBgBase};
|
||||
.antd5-form-item-label label {
|
||||
.ant-form-item-label label {
|
||||
color: ${theme.colorPrimary};
|
||||
}
|
||||
`}
|
||||
|
||||
@@ -58,6 +58,7 @@ import handleResourceExport from 'src/utils/export';
|
||||
import SubMenu, { ButtonProps, SubMenuProps } from 'src/features/home/SubMenu';
|
||||
import { commonMenuData } from 'src/features/home/commonMenuData';
|
||||
import { QueryObjectColumns, SavedQueryObject } from 'src/views/CRUD/types';
|
||||
import { TagTypeEnum } from 'src/components/Tag/TagType';
|
||||
import { loadTags } from 'src/components/Tag/utils';
|
||||
import { Icons } from '@superset-ui/core/components/Icons';
|
||||
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
|
||||
@@ -393,7 +394,11 @@ function SavedQueryList({
|
||||
},
|
||||
}: any) => (
|
||||
// Only show custom type tags
|
||||
<TagsList tags={tags.filter((tag: TagType) => tag.type === 1)} />
|
||||
<TagsList
|
||||
tags={tags.filter(
|
||||
(tag: TagType) => tag.type === TagTypeEnum.Custom,
|
||||
)}
|
||||
/>
|
||||
),
|
||||
Header: t('Tags'),
|
||||
accessor: 'tags',
|
||||
|
||||
@@ -54,11 +54,11 @@ const DescriptionsContainer = styled.div`
|
||||
|
||||
const StyledLayout = styled.div`
|
||||
${({ theme }) => css`
|
||||
.antd5-row {
|
||||
.ant-row {
|
||||
margin: 0px ${theme.sizeUnit * 3}px ${theme.sizeUnit * 6}px
|
||||
${theme.sizeUnit * 3}px;
|
||||
}
|
||||
&& .menu > .antd5-menu {
|
||||
&& .menu > .ant-menu {
|
||||
padding: 0px;
|
||||
}
|
||||
&& .nav-right {
|
||||
|
||||
596
superset-frontend/src/theme/ThemeController.ts
Normal file
596
superset-frontend/src/theme/ThemeController.ts
Normal file
@@ -0,0 +1,596 @@
|
||||
/**
|
||||
* 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 {
|
||||
Theme,
|
||||
AnyThemeConfig,
|
||||
ThemeStorage,
|
||||
ThemeControllerOptions,
|
||||
themeObject as supersetThemeObject,
|
||||
} from '@superset-ui/core';
|
||||
import { SupersetTheme, ThemeMode } from '@superset-ui/core/theme/types';
|
||||
import {
|
||||
getAntdConfig,
|
||||
normalizeThemeConfig,
|
||||
} from '@superset-ui/core/theme/utils';
|
||||
import type {
|
||||
BootstrapThemeData,
|
||||
BootstrapThemeDataConfig,
|
||||
SerializableThemeSettings,
|
||||
} from 'src/types/bootstrapTypes';
|
||||
import getBootstrapData from 'src/utils/getBootstrapData';
|
||||
|
||||
const DEFAULT_THEME_SETTINGS = {
|
||||
enforced: false,
|
||||
allowSwitching: true,
|
||||
allowOSPreference: true,
|
||||
} as const;
|
||||
|
||||
const STORAGE_KEYS = {
|
||||
THEME_MODE: 'superset-theme-mode',
|
||||
} as const;
|
||||
|
||||
const MEDIA_QUERY_DARK_SCHEME = '(prefers-color-scheme: dark)';
|
||||
|
||||
export class LocalStorageAdapter implements ThemeStorage {
|
||||
getItem(key: string): string | null {
|
||||
try {
|
||||
return localStorage.getItem(key);
|
||||
} catch (error) {
|
||||
console.warn('Failed to read from localStorage:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
setItem(key: string, value: string): void {
|
||||
try {
|
||||
localStorage.setItem(key, value);
|
||||
} catch (error) {
|
||||
console.warn('Failed to write to localStorage:', error);
|
||||
}
|
||||
}
|
||||
|
||||
removeItem(key: string): void {
|
||||
try {
|
||||
localStorage.removeItem(key);
|
||||
} catch (error) {
|
||||
console.warn('Failed to remove from localStorage:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ThemeController {
|
||||
private themeObject: Theme;
|
||||
|
||||
private storage: ThemeStorage;
|
||||
|
||||
private modeStorageKey: string;
|
||||
|
||||
private defaultTheme: AnyThemeConfig;
|
||||
|
||||
private darkTheme: AnyThemeConfig | null;
|
||||
|
||||
private themeSettings: SerializableThemeSettings;
|
||||
|
||||
private systemMode: ThemeMode.DARK | ThemeMode.DEFAULT;
|
||||
|
||||
private currentMode: ThemeMode;
|
||||
|
||||
private readonly hasBootstrapThemes: boolean;
|
||||
|
||||
private onChangeCallbacks: Set<(theme: Theme) => void> = new Set();
|
||||
|
||||
private mediaQuery: MediaQueryList;
|
||||
|
||||
constructor(options: ThemeControllerOptions = {}) {
|
||||
const {
|
||||
storage = new LocalStorageAdapter(),
|
||||
modeStorageKey = STORAGE_KEYS.THEME_MODE,
|
||||
themeObject = supersetThemeObject,
|
||||
defaultTheme = (supersetThemeObject.theme as AnyThemeConfig) ?? {},
|
||||
onChange = null,
|
||||
} = options;
|
||||
|
||||
this.storage = storage;
|
||||
this.modeStorageKey = modeStorageKey;
|
||||
this.themeObject = themeObject;
|
||||
|
||||
// Initialize bootstrap data and themes
|
||||
const {
|
||||
bootstrapDefaultTheme,
|
||||
bootstrapDarkTheme,
|
||||
bootstrapThemeSettings,
|
||||
hasBootstrapThemes,
|
||||
}: BootstrapThemeData = this.loadBootstrapData();
|
||||
|
||||
this.hasBootstrapThemes = hasBootstrapThemes;
|
||||
this.themeSettings = bootstrapThemeSettings || {};
|
||||
|
||||
// Set themes based on bootstrap data availability
|
||||
if (this.hasBootstrapThemes) {
|
||||
this.darkTheme = bootstrapDarkTheme || bootstrapDefaultTheme || null;
|
||||
this.defaultTheme =
|
||||
bootstrapDefaultTheme || bootstrapDarkTheme || defaultTheme;
|
||||
} else {
|
||||
this.darkTheme = null;
|
||||
this.defaultTheme = defaultTheme;
|
||||
}
|
||||
|
||||
// Initialize system theme detection
|
||||
this.systemMode = ThemeController.getSystemPreferredMode();
|
||||
|
||||
// Only initialize media query listener if OS preference is allowed
|
||||
if (this.shouldInitializeMediaQueryListener())
|
||||
this.initializeMediaQueryListener();
|
||||
|
||||
// Initialize theme and mode
|
||||
this.currentMode = this.determineInitialMode();
|
||||
const initialTheme =
|
||||
this.getThemeForMode(this.currentMode) || this.defaultTheme;
|
||||
|
||||
// Setup change callback
|
||||
if (onChange) this.onChangeCallbacks.add(onChange);
|
||||
|
||||
// Apply initial theme and persist mode
|
||||
this.applyTheme(initialTheme);
|
||||
this.persistMode();
|
||||
}
|
||||
|
||||
// Public Methods
|
||||
|
||||
/**
|
||||
* Cleans up listeners and references. Should be called when the controller is no longer needed.
|
||||
*/
|
||||
public destroy(): void {
|
||||
if (this.mediaQuery)
|
||||
this.mediaQuery.removeEventListener(
|
||||
'change',
|
||||
this.handleSystemThemeChange,
|
||||
);
|
||||
|
||||
this.onChangeCallbacks.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user can update the theme.
|
||||
*/
|
||||
public canSetTheme(): boolean {
|
||||
return !this.themeSettings?.enforced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user can update the theme mode.
|
||||
*/
|
||||
public canSetMode(): boolean {
|
||||
return this.isModeUpdatable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current theme object.
|
||||
*/
|
||||
public getTheme(): Theme {
|
||||
return this.themeObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current theme mode.
|
||||
*/
|
||||
public getCurrentMode(): ThemeMode {
|
||||
return this.currentMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets new theme.
|
||||
* @param theme - The new theme to apply
|
||||
* @throws {Error} If the user does not have permission to update the theme
|
||||
*/
|
||||
public setTheme(theme: AnyThemeConfig): void {
|
||||
this.validateThemeUpdatePermission();
|
||||
|
||||
const normalizedTheme = this.normalizeTheme(theme);
|
||||
this.currentMode = this.determineInitialMode();
|
||||
|
||||
this.updateTheme(normalizedTheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the theme mode (light, dark, or system).
|
||||
* @param mode - The new theme mode to apply
|
||||
* @throws {Error} If the user does not have permission to update the theme mode
|
||||
*/
|
||||
public setThemeMode(mode: ThemeMode): void {
|
||||
this.validateModeUpdatePermission(mode);
|
||||
|
||||
if (this.currentMode === mode) return;
|
||||
|
||||
const theme: AnyThemeConfig | null = this.getThemeForMode(mode);
|
||||
if (!theme) {
|
||||
console.warn(`Theme for mode ${mode} not found, falling back to default`);
|
||||
this.fallbackToDefaultMode();
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentMode = mode;
|
||||
this.updateTheme(theme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the theme to the default theme.
|
||||
*/
|
||||
public resetTheme(): void {
|
||||
this.currentMode = ThemeMode.DEFAULT;
|
||||
const defaultTheme: AnyThemeConfig =
|
||||
this.getThemeForMode(ThemeMode.DEFAULT) || this.defaultTheme;
|
||||
|
||||
this.updateTheme(defaultTheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles system theme changes with error recovery.
|
||||
*/
|
||||
private handleSystemThemeChange = (): void => {
|
||||
try {
|
||||
const newSystemMode: ThemeMode.DEFAULT | ThemeMode.DARK =
|
||||
ThemeController.getSystemPreferredMode();
|
||||
|
||||
// Update systemMode regardless of current mode
|
||||
const oldSystemMode: ThemeMode.DEFAULT | ThemeMode.DARK = this.systemMode;
|
||||
this.systemMode = newSystemMode;
|
||||
|
||||
// Only update theme if currently in SYSTEM mode and the preference changed
|
||||
if (
|
||||
this.currentMode === ThemeMode.SYSTEM &&
|
||||
oldSystemMode !== newSystemMode
|
||||
) {
|
||||
const newTheme: AnyThemeConfig | null = this.getThemeForMode(
|
||||
ThemeMode.SYSTEM,
|
||||
);
|
||||
|
||||
if (newTheme) this.updateTheme(newTheme);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to handle system theme change:', error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the theme.
|
||||
* @param theme - The new theme to apply
|
||||
*/
|
||||
private updateTheme(theme?: AnyThemeConfig): void {
|
||||
try {
|
||||
// If no config provided, use current mode to get theme
|
||||
const config: AnyThemeConfig =
|
||||
theme || this.getThemeForMode(this.currentMode) || this.defaultTheme;
|
||||
|
||||
// Normalize the theme
|
||||
const normalizedTheme = this.normalizeTheme(config);
|
||||
|
||||
this.applyTheme(normalizedTheme);
|
||||
this.persistMode();
|
||||
this.notifyListeners();
|
||||
} catch (error) {
|
||||
console.error('Failed to update theme:', error);
|
||||
this.fallbackToDefaultMode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback to default mode with error recovery.
|
||||
*/
|
||||
private fallbackToDefaultMode(): void {
|
||||
this.currentMode = ThemeMode.DEFAULT;
|
||||
|
||||
// Get the default theme which will have the correct algorithm
|
||||
const defaultTheme: AnyThemeConfig =
|
||||
this.getThemeForMode(ThemeMode.DEFAULT) || this.defaultTheme;
|
||||
|
||||
this.applyTheme(defaultTheme);
|
||||
this.persistMode();
|
||||
this.notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback to be called when the theme changes.
|
||||
* @param callback - The callback to be called on theme change
|
||||
* @returns A function to unsubscribe from the theme change events
|
||||
*/
|
||||
public onChange(callback: (theme: Theme) => void): () => void {
|
||||
this.onChangeCallbacks.add(callback);
|
||||
return () => this.onChangeCallbacks.delete(callback);
|
||||
}
|
||||
|
||||
// Private Helper Methods
|
||||
|
||||
/**
|
||||
* Determines whether the MediaQueryList listener for system theme changes should be initialized.
|
||||
* This checks if OS preference detection is enabled in the theme settings.
|
||||
* @returns {boolean} True if the media query listener should be initialized, false otherwise
|
||||
*/
|
||||
private shouldInitializeMediaQueryListener(): boolean {
|
||||
const { allowOSPreference = DEFAULT_THEME_SETTINGS.allowOSPreference } =
|
||||
this.themeSettings || {};
|
||||
|
||||
return allowOSPreference === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes media query listeners if OS preference is allowed
|
||||
*/
|
||||
private initializeMediaQueryListener(): void {
|
||||
try {
|
||||
this.mediaQuery = window.matchMedia(MEDIA_QUERY_DARK_SCHEME);
|
||||
this.mediaQuery.addEventListener('change', this.handleSystemThemeChange);
|
||||
} catch (error) {
|
||||
console.warn('Failed to initialize media query listener:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads and validates bootstrap theme data.
|
||||
*/
|
||||
private loadBootstrapData(): BootstrapThemeData {
|
||||
const {
|
||||
common: { theme = {} as BootstrapThemeDataConfig },
|
||||
} = getBootstrapData();
|
||||
|
||||
const {
|
||||
default: defaultTheme,
|
||||
dark: darkTheme,
|
||||
settings: themeSettings,
|
||||
} = theme;
|
||||
|
||||
const hasValidDefault: boolean = this.isNonEmptyObject(defaultTheme);
|
||||
const hasValidDark: boolean = this.isNonEmptyObject(darkTheme);
|
||||
const hasValidSettings: boolean = this.isNonEmptyObject(themeSettings);
|
||||
|
||||
return {
|
||||
bootstrapDefaultTheme: hasValidDefault ? defaultTheme : null,
|
||||
bootstrapDarkTheme: hasValidDark ? darkTheme : null,
|
||||
bootstrapThemeSettings: hasValidSettings ? themeSettings : null,
|
||||
hasBootstrapThemes: hasValidDefault || hasValidDark,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an object is non-empty (has at least one property).
|
||||
*/
|
||||
private isNonEmptyObject(
|
||||
obj: Record<string, any> | undefined | null,
|
||||
): boolean {
|
||||
return Boolean(
|
||||
obj && typeof obj === 'object' && Object.keys(obj).length > 0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if mode updates are allowed.
|
||||
*/
|
||||
private isModeUpdatable(): boolean {
|
||||
const {
|
||||
enforced = DEFAULT_THEME_SETTINGS.enforced,
|
||||
allowSwitching = DEFAULT_THEME_SETTINGS.allowSwitching,
|
||||
} = this.themeSettings || {};
|
||||
|
||||
return !enforced && allowSwitching;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the theme configuration to ensure it has a valid algorithm.
|
||||
* @param theme - The theme configuration to normalize
|
||||
* @returns An object with normalized mode and algorithm.
|
||||
*/
|
||||
private normalizeTheme(theme: AnyThemeConfig): AnyThemeConfig {
|
||||
const normalizedTheme = normalizeThemeConfig(theme);
|
||||
return normalizedTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the appropriate theme configuration for a given mode.
|
||||
* @param mode - The theme mode to get the configuration for
|
||||
* @returns The theme configuration for the specified mode or null if not available
|
||||
*/
|
||||
private getThemeForMode(mode: ThemeMode): AnyThemeConfig | null {
|
||||
const { allowOSPreference = DEFAULT_THEME_SETTINGS.allowOSPreference } =
|
||||
this.themeSettings;
|
||||
|
||||
let resolvedMode: ThemeMode = mode;
|
||||
|
||||
if (mode === ThemeMode.SYSTEM) {
|
||||
if (!allowOSPreference) return null;
|
||||
resolvedMode = ThemeController.getSystemPreferredMode();
|
||||
}
|
||||
|
||||
if (!this.hasBootstrapThemes) {
|
||||
const baseTheme = this.defaultTheme.token as Partial<SupersetTheme>;
|
||||
return getAntdConfig(baseTheme, resolvedMode === ThemeMode.DARK);
|
||||
}
|
||||
|
||||
// Handle bootstrap themes using existing normalization
|
||||
const selectedTheme: AnyThemeConfig =
|
||||
resolvedMode === ThemeMode.DARK
|
||||
? this.darkTheme || this.defaultTheme
|
||||
: this.defaultTheme;
|
||||
|
||||
return selectedTheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the initial theme mode with error recovery.
|
||||
*/
|
||||
private determineInitialMode(): ThemeMode {
|
||||
const {
|
||||
enforced = DEFAULT_THEME_SETTINGS.enforced,
|
||||
allowOSPreference = DEFAULT_THEME_SETTINGS.allowOSPreference,
|
||||
allowSwitching = DEFAULT_THEME_SETTINGS.allowSwitching,
|
||||
} = this.themeSettings;
|
||||
|
||||
// Enforced mode always takes precedence
|
||||
if (enforced) {
|
||||
this.storage.removeItem(this.modeStorageKey);
|
||||
return ThemeMode.DEFAULT;
|
||||
}
|
||||
|
||||
// When OS preference is allowed but switching is not
|
||||
// This means the user MUST follow OS preference and cannot override it
|
||||
if (allowOSPreference && !allowSwitching) {
|
||||
// Clear any saved preference since switching is not allowed
|
||||
this.storage.removeItem(this.modeStorageKey);
|
||||
return ThemeMode.SYSTEM;
|
||||
}
|
||||
|
||||
// Try to restore saved mode
|
||||
const savedMode: ThemeMode | null = this.loadSavedMode();
|
||||
if (savedMode && this.isValidThemeMode(savedMode)) return savedMode;
|
||||
|
||||
// Fallback to system preference if allowed and available
|
||||
if (allowOSPreference && this.getThemeForMode(this.systemMode))
|
||||
return ThemeMode.SYSTEM;
|
||||
|
||||
return ThemeMode.DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely loads saved theme mode from storage.
|
||||
*/
|
||||
private loadSavedMode(): ThemeMode | null {
|
||||
try {
|
||||
const stored: string | null = this.storage.getItem(this.modeStorageKey);
|
||||
if (stored && Object.values(ThemeMode).includes(stored as ThemeMode))
|
||||
return stored as ThemeMode;
|
||||
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.warn('Failed to load saved theme mode:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if a theme mode is valid and supported.
|
||||
* This checks if the mode is one of the known ThemeMode values.
|
||||
* @param mode - The theme mode to validate
|
||||
* @returns {boolean} True if the mode is valid, false otherwise
|
||||
*/
|
||||
private isValidThemeMode(mode: ThemeMode): boolean {
|
||||
if (!Object.values(ThemeMode).includes(mode)) return false;
|
||||
|
||||
// Validate that we have the required theme data for the mode
|
||||
switch (mode) {
|
||||
case ThemeMode.DARK:
|
||||
return !!(this.darkTheme || this.defaultTheme);
|
||||
case ThemeMode.DEFAULT:
|
||||
return !!this.defaultTheme;
|
||||
case ThemeMode.SYSTEM:
|
||||
return this.themeSettings?.allowOSPreference !== false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates permission to update theme.
|
||||
*/
|
||||
private validateThemeUpdatePermission(): void {
|
||||
if (!this.canSetTheme())
|
||||
throw new Error('User does not have permission to update the theme');
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates permission to update mode.
|
||||
* @param newMode - The new mode to validate
|
||||
* @throws {Error} If the user does not have permission to update the theme mode
|
||||
* @throws {Error} If the new mode is SYSTEM and OS preference is not allowed
|
||||
*/
|
||||
private validateModeUpdatePermission(newMode: ThemeMode): void {
|
||||
const {
|
||||
allowOSPreference = DEFAULT_THEME_SETTINGS.allowOSPreference,
|
||||
allowSwitching = DEFAULT_THEME_SETTINGS.allowSwitching,
|
||||
} = this.themeSettings;
|
||||
|
||||
// If OS preference is allowed but switching is not,
|
||||
// don't allow any mode changes
|
||||
if (allowOSPreference && !allowSwitching)
|
||||
throw new Error(
|
||||
'Theme mode changes are not allowed when OS preference is enforced',
|
||||
);
|
||||
|
||||
// Check if user can set a new theme mode
|
||||
if (!this.canSetMode())
|
||||
throw new Error('User does not have permission to update the theme mode');
|
||||
|
||||
// Check if user has permissions to set OS preference as a theme mode
|
||||
if (newMode === ThemeMode.SYSTEM && !allowOSPreference)
|
||||
throw new Error('System theme mode is not allowed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the current theme configuration.
|
||||
* This method sets the theme on the themeObject and applies it to Theme.
|
||||
* It also handles any errors that may occur during the application of the theme.
|
||||
* @param theme - The theme configuration to apply
|
||||
*/
|
||||
private applyTheme(theme: AnyThemeConfig): void {
|
||||
try {
|
||||
const normalizedConfig = normalizeThemeConfig(theme);
|
||||
this.themeObject.setConfig(normalizedConfig);
|
||||
} catch (error) {
|
||||
console.error('Failed to apply theme:', error);
|
||||
this.fallbackToDefaultMode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the current theme mode to storage.
|
||||
*/
|
||||
private persistMode(): void {
|
||||
try {
|
||||
this.storage.setItem(this.modeStorageKey, this.currentMode);
|
||||
} catch (error) {
|
||||
console.warn('Failed to persist theme mode:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered listeners about theme changes.
|
||||
*/
|
||||
private notifyListeners(): void {
|
||||
this.onChangeCallbacks.forEach(callback => {
|
||||
try {
|
||||
callback(this.themeObject);
|
||||
} catch (error) {
|
||||
console.error('Error in theme change callback:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the system's preferred theme mode.
|
||||
* @returns {ThemeMode.DARK | ThemeMode.DEFAULT} The system's preferred theme mode
|
||||
*/
|
||||
static getSystemPreferredMode(): ThemeMode.DARK | ThemeMode.DEFAULT {
|
||||
try {
|
||||
return window.matchMedia(MEDIA_QUERY_DARK_SCHEME).matches
|
||||
? ThemeMode.DARK
|
||||
: ThemeMode.DEFAULT;
|
||||
} catch (error) {
|
||||
console.warn('Failed to detect system theme preference:', error);
|
||||
return ThemeMode.DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,246 +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 {
|
||||
Theme,
|
||||
AnyThemeConfig,
|
||||
ThemeStorage,
|
||||
ThemeControllerOptions,
|
||||
themeObject,
|
||||
} from '@superset-ui/core';
|
||||
import { ThemeMode } from '@superset-ui/core/theme/types';
|
||||
|
||||
export class LocalStorageAdapter implements ThemeStorage {
|
||||
getItem(key: string): string | null {
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
|
||||
setItem(key: string, value: string): void {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
removeItem(key: string): void {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
export class ThemeController {
|
||||
private themeObject: Theme;
|
||||
|
||||
private storage: ThemeStorage;
|
||||
|
||||
private storageKey: string;
|
||||
|
||||
private modeStorageKey: string;
|
||||
|
||||
private defaultTheme: AnyThemeConfig;
|
||||
|
||||
private systemMode: ThemeMode.DARK | ThemeMode.LIGHT;
|
||||
|
||||
private currentMode: ThemeMode;
|
||||
|
||||
private customizations: AnyThemeConfig = {};
|
||||
|
||||
private onChangeCallbacks: Set<(theme: Theme) => void> = new Set();
|
||||
|
||||
private canUpdateThemeFn: () => boolean;
|
||||
|
||||
private canUpdateModeFn: () => boolean;
|
||||
|
||||
private mediaQuery: MediaQueryList;
|
||||
|
||||
constructor(options: ThemeControllerOptions = { themeObject }) {
|
||||
this.storage = options.storage || new LocalStorageAdapter();
|
||||
this.storageKey = options.storageKey || 'superset-theme';
|
||||
this.modeStorageKey = options.modeStorageKey || `${this.storageKey}-mode`;
|
||||
this.defaultTheme = options.defaultTheme || {};
|
||||
this.themeObject = options.themeObject;
|
||||
|
||||
// Load customizations from storage
|
||||
const savedThemeJson = this.storage.getItem(this.storageKey);
|
||||
if (savedThemeJson) {
|
||||
try {
|
||||
this.customizations = JSON.parse(savedThemeJson);
|
||||
} catch (e) {
|
||||
console.error('Failed to parse saved theme:', e);
|
||||
this.storage.removeItem(this.storageKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine initial mode
|
||||
this.systemMode = ThemeController.getSystemMode();
|
||||
const savedMode = this.storage.getItem(this.modeStorageKey) as ThemeMode;
|
||||
this.currentMode = savedMode || ThemeMode.SYSTEM;
|
||||
|
||||
// Apply the initial theme and mode
|
||||
this.applyTheme();
|
||||
|
||||
if (options.onChange) {
|
||||
this.onChangeCallbacks.add(options.onChange);
|
||||
}
|
||||
this.canUpdateThemeFn = options.canUpdateTheme || (() => true);
|
||||
this.canUpdateModeFn = options.canUpdateMode || (() => true);
|
||||
|
||||
// Listen for system theme changes to enable dynamic `SYSTEM` mode
|
||||
this.mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
this.mediaQuery.addEventListener('change', this.handleSystemThemeChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up listeners. Should be called when the controller is no longer needed.
|
||||
*/
|
||||
public destroy(): void {
|
||||
this.mediaQuery.removeEventListener('change', this.handleSystemThemeChange);
|
||||
}
|
||||
|
||||
public canUpdateTheme(): boolean {
|
||||
return this.canUpdateThemeFn();
|
||||
}
|
||||
|
||||
public canUpdateMode(): boolean {
|
||||
return this.canUpdateModeFn();
|
||||
}
|
||||
|
||||
public getTheme(): Theme {
|
||||
return this.themeObject;
|
||||
}
|
||||
|
||||
public getCurrentMode(): ThemeMode {
|
||||
return this.currentMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets new theme customizations (e.g., from a JSON editor).
|
||||
* This method updates the theme's appearance but preserves the current mode.
|
||||
*/
|
||||
public setTheme(newCustomizations: AnyThemeConfig): void {
|
||||
if (!this.canUpdateTheme()) {
|
||||
throw new Error('User does not have permission to update the theme');
|
||||
}
|
||||
this.customizations = newCustomizations;
|
||||
|
||||
if (!newCustomizations.algorithm) {
|
||||
this.currentMode = ThemeMode.LIGHT;
|
||||
}
|
||||
|
||||
if (newCustomizations?.algorithm) {
|
||||
this.currentMode = newCustomizations.algorithm as ThemeMode;
|
||||
}
|
||||
|
||||
this.applyTheme();
|
||||
this.persist();
|
||||
this.notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the theme mode (light, dark, or system).
|
||||
* This is for the mode switch.
|
||||
*/
|
||||
public changeThemeMode(newMode: ThemeMode): void {
|
||||
if (!this.canUpdateMode()) {
|
||||
throw new Error('User does not have permission to update the theme mode');
|
||||
}
|
||||
if (this.currentMode === newMode) return;
|
||||
|
||||
this.currentMode = newMode;
|
||||
|
||||
this.applyTheme();
|
||||
this.persist();
|
||||
this.notifyListeners();
|
||||
}
|
||||
|
||||
public resetTheme(): void {
|
||||
this.customizations = this.defaultTheme;
|
||||
this.applyTheme();
|
||||
this.persist();
|
||||
this.notifyListeners();
|
||||
}
|
||||
|
||||
public onChange(callback: (theme: Theme) => void): () => void {
|
||||
this.onChangeCallbacks.add(callback);
|
||||
return () => {
|
||||
this.onChangeCallbacks.delete(callback);
|
||||
};
|
||||
}
|
||||
|
||||
private handleSystemThemeChange = (): void => {
|
||||
const newSystemMode = ThemeController.getSystemMode();
|
||||
if (this.systemMode === newSystemMode) return;
|
||||
|
||||
this.systemMode = newSystemMode;
|
||||
// If the current mode is SYSTEM, we need to re-apply the theme
|
||||
if (this.currentMode === ThemeMode.SYSTEM) {
|
||||
this.applyTheme();
|
||||
this.notifyListeners();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Centralized method to apply the current customizations and mode.
|
||||
*/
|
||||
private applyTheme(): void {
|
||||
const newConfig = { ...this.customizations };
|
||||
|
||||
switch (this.currentMode) {
|
||||
case ThemeMode.DARK:
|
||||
newConfig.algorithm = 'dark';
|
||||
break;
|
||||
case ThemeMode.LIGHT:
|
||||
newConfig.algorithm = 'default';
|
||||
break;
|
||||
case ThemeMode.SYSTEM:
|
||||
newConfig.algorithm =
|
||||
this.systemMode === ThemeMode.DARK ? 'dark' : 'default';
|
||||
break;
|
||||
case ThemeMode.COMPACT:
|
||||
newConfig.algorithm = 'compact';
|
||||
break;
|
||||
default:
|
||||
newConfig.algorithm = 'default';
|
||||
break;
|
||||
}
|
||||
|
||||
this.themeObject.setConfig(newConfig);
|
||||
}
|
||||
|
||||
private persist(): void {
|
||||
this.storage.setItem(this.modeStorageKey, this.currentMode);
|
||||
|
||||
const { algorithm, ...persistedCustomizations } = this.customizations;
|
||||
this.storage.setItem(
|
||||
this.storageKey,
|
||||
JSON.stringify(persistedCustomizations),
|
||||
);
|
||||
}
|
||||
|
||||
private notifyListeners(): void {
|
||||
this.onChangeCallbacks.forEach(callback => {
|
||||
try {
|
||||
callback(this.themeObject);
|
||||
} catch (e) {
|
||||
console.error('Error in theme change callback:', e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static getSystemMode(): ThemeMode.DARK | ThemeMode.LIGHT {
|
||||
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
return isDark ? ThemeMode.DARK : ThemeMode.LIGHT;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user