mirror of
https://github.com/apache/superset.git
synced 2026-08-29 19:41:17 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95b0ba8043 | ||
|
|
83990e0e64 | ||
|
|
2f15c32572 | ||
|
|
377116e5ab | ||
|
|
ad3c8b6dac |
@@ -3,7 +3,9 @@ codecov:
|
||||
after_n_builds: 4
|
||||
ignore:
|
||||
- "superset/migrations/versions/*.py"
|
||||
- "superset-frontend/packages/superset-ui-demo/**/*"
|
||||
- "**/*.stories.tsx"
|
||||
- "**/*.stories.jsx"
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Bug report
|
||||
description: Report a bug to improve Superset's stability
|
||||
labels: ["#bug"]
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name: Cosmetic Issue
|
||||
about: Describe a cosmetic issue with CSS, positioning, layout, labeling, or similar
|
||||
labels: "#bug:cosmetic"
|
||||
labels: "cosmetic-issue"
|
||||
---
|
||||
|
||||
## Screenshot
|
||||
|
||||
+8
-31
@@ -5,10 +5,6 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
groups:
|
||||
security:
|
||||
applies-to: "security-updates"
|
||||
patterns: ["*"]
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
@@ -24,6 +20,14 @@ updates:
|
||||
- dependency-name: "@types/react-dom"
|
||||
update-types: ["version-update:semver-major"]
|
||||
- dependency-name: "react-icons"
|
||||
# JSDOM v30 doesn't play well with Jest v30
|
||||
# Source: https://jestjs.io/blog#known-issues
|
||||
# GH thread: https://github.com/jsdom/jsdom/issues/3492
|
||||
- dependency-name: "jest-environment-jsdom"
|
||||
# `@swc/plugin-transform-imports` doesn't work with current Webpack-SWC hybrid setup
|
||||
# See https://github.com/apache/superset/pull/37384#issuecomment-3793991389
|
||||
# TODO: remove the plugin once Lodash usage has been migrated to a more readily tree-shakeable alternative
|
||||
- dependency-name: "@swc/plugin-transform-imports"
|
||||
# deck.gl and luma.gl share strict peer constraints across the root and
|
||||
# plugin workspaces, and root overrides pin their transitive versions.
|
||||
# Upgrade both families together in a manually validated change.
|
||||
@@ -57,9 +61,6 @@ updates:
|
||||
- npm
|
||||
- dependabot
|
||||
groups:
|
||||
security:
|
||||
applies-to: "security-updates"
|
||||
patterns: ["*"]
|
||||
rjsf:
|
||||
patterns:
|
||||
- "@rjsf/*"
|
||||
@@ -79,11 +80,6 @@ updates:
|
||||
patterns:
|
||||
- "ag-grid-react"
|
||||
- "ag-grid-community"
|
||||
swc:
|
||||
patterns:
|
||||
- "@swc/core"
|
||||
- "@swc/plugin-emotion"
|
||||
- "@swc/plugin-transform-imports"
|
||||
open-pull-requests-limit: 30
|
||||
versioning-strategy: increase
|
||||
cooldown:
|
||||
@@ -102,10 +98,6 @@ updates:
|
||||
labels:
|
||||
- pip
|
||||
- dependabot
|
||||
groups:
|
||||
security:
|
||||
applies-to: "security-updates"
|
||||
patterns: ["*"]
|
||||
cooldown:
|
||||
default-days: 7
|
||||
|
||||
@@ -113,10 +105,6 @@ updates:
|
||||
directory: ".github/actions"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
groups:
|
||||
security:
|
||||
applies-to: "security-updates"
|
||||
patterns: ["*"]
|
||||
open-pull-requests-limit: 10
|
||||
versioning-strategy: increase
|
||||
cooldown:
|
||||
@@ -127,9 +115,6 @@ updates:
|
||||
schedule:
|
||||
interval: "daily"
|
||||
groups:
|
||||
security:
|
||||
applies-to: "security-updates"
|
||||
patterns: ["*"]
|
||||
storybook:
|
||||
patterns:
|
||||
- "@storybook/*"
|
||||
@@ -157,10 +142,6 @@ updates:
|
||||
labels:
|
||||
- npm
|
||||
- dependabot
|
||||
groups:
|
||||
security:
|
||||
applies-to: "security-updates"
|
||||
patterns: ["*"]
|
||||
versioning-strategy: increase
|
||||
cooldown:
|
||||
default-days: 7
|
||||
@@ -172,10 +153,6 @@ updates:
|
||||
labels:
|
||||
- npm
|
||||
- dependabot
|
||||
groups:
|
||||
security:
|
||||
applies-to: "security-updates"
|
||||
patterns: ["*"]
|
||||
open-pull-requests-limit: 10
|
||||
versioning-strategy: increase
|
||||
cooldown:
|
||||
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
||||
uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0
|
||||
with:
|
||||
python-version: "3.11"
|
||||
enable-cache: true
|
||||
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
|
||||
uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -78,6 +78,6 @@ jobs:
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28 # v4.37.8
|
||||
uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -66,27 +66,6 @@ jobs:
|
||||
|
||||
- name: "Set up liccheck"
|
||||
run: |
|
||||
# liccheck (as of 0.9.2) still does a bare `import pkg_resources`
|
||||
# without declaring setuptools as a dependency, relying on it
|
||||
# having historically been bundled. setuptools 81+ (installed
|
||||
# above via requirements/base.txt) dropped the pkg_resources
|
||||
# subpackage entirely, so liccheck's own import breaks outright.
|
||||
#
|
||||
# Reinstalling an older setuptools would restore pkg_resources but
|
||||
# would also downgrade the *real* setuptools install, which then
|
||||
# trips liccheck's own working_set.resolve() -- it cross-checks
|
||||
# requirements/base.txt's declared `setuptools==84.0.0` against
|
||||
# what's actually installed, and a downgrade makes those disagree.
|
||||
#
|
||||
# Instead, vendor just the pkg_resources/ package files from an
|
||||
# old setuptools wheel into site-packages, leaving the real
|
||||
# setuptools install (and its dist-info metadata) untouched. This
|
||||
# gives liccheck an importable pkg_resources whose own working-set
|
||||
# scan still correctly reports the real installed setuptools
|
||||
# version, so no conflict is raised.
|
||||
pip download "setuptools<81" --no-deps -d /tmp/old-setuptools
|
||||
python -m zipfile -e /tmp/old-setuptools/setuptools-*.whl /tmp/old-setuptools-extracted/
|
||||
cp -r /tmp/old-setuptools-extracted/pkg_resources "$(python -c 'import site; print(site.getsitepackages()[0])')/"
|
||||
uv pip install --system liccheck
|
||||
- name: "Run liccheck"
|
||||
run: |
|
||||
|
||||
@@ -105,7 +105,6 @@ jobs:
|
||||
tool: customSmallerIsBetter
|
||||
output-file-path: bundle-size-summary.json
|
||||
external-data-json-path: bundle-size-history.json
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
fail-on-alert: false
|
||||
summary-always: true
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
||||
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
||||
|
||||
- name: Copy image to GHCR
|
||||
env:
|
||||
|
||||
@@ -141,7 +141,7 @@ jobs:
|
||||
yarn install --check-cache
|
||||
- name: Download database diagnostics (if triggered by integration tests)
|
||||
if: github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success'
|
||||
uses: dawidd6/action-download-artifact@d63b86af1b34672e53c440b1b83979861906bad7 # v24
|
||||
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21
|
||||
continue-on-error: true
|
||||
with:
|
||||
workflow: superset-python-integrationtest.yml
|
||||
@@ -150,7 +150,7 @@ jobs:
|
||||
path: docs/src/data/
|
||||
- name: Try to download latest diagnostics (for push/dispatch triggers)
|
||||
if: github.event_name != 'workflow_run'
|
||||
uses: dawidd6/action-download-artifact@d63b86af1b34672e53c440b1b83979861906bad7 # v24
|
||||
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21
|
||||
continue-on-error: true
|
||||
with:
|
||||
workflow: superset-python-integrationtest.yml
|
||||
|
||||
@@ -140,7 +140,7 @@ jobs:
|
||||
run: |
|
||||
yarn install --check-cache
|
||||
- name: Download database diagnostics from integration tests
|
||||
uses: dawidd6/action-download-artifact@d63b86af1b34672e53c440b1b83979861906bad7 # v24
|
||||
uses: dawidd6/action-download-artifact@b6e2e70617bc3265edd6dab6c906732b2f1ae151 # v21
|
||||
with:
|
||||
workflow: superset-python-integrationtest.yml
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
@@ -78,14 +78,14 @@ jobs:
|
||||
USE_DASHBOARD: ${{ github.event.inputs.use_dashboard == 'true' || 'false' }}
|
||||
services:
|
||||
postgres:
|
||||
image: ghcr.io/apache/superset/ci/postgres:17-alpine
|
||||
image: postgres:17-alpine
|
||||
env:
|
||||
POSTGRES_USER: superset
|
||||
POSTGRES_PASSWORD: superset
|
||||
ports:
|
||||
- 15432:5432
|
||||
redis:
|
||||
image: ghcr.io/apache/superset/ci/redis:7-alpine
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- 16379:6379
|
||||
steps:
|
||||
@@ -186,14 +186,14 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
services:
|
||||
postgres:
|
||||
image: ghcr.io/apache/superset/ci/postgres:17-alpine
|
||||
image: postgres:17-alpine
|
||||
env:
|
||||
POSTGRES_USER: superset
|
||||
POSTGRES_PASSWORD: superset
|
||||
ports:
|
||||
- 15432:5432
|
||||
redis:
|
||||
image: ghcr.io/apache/superset/ci/redis:7-alpine
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- 16379:6379
|
||||
steps:
|
||||
|
||||
@@ -53,7 +53,9 @@ jobs:
|
||||
mysql+mysqldb://superset:superset@127.0.0.1:13306/superset?charset=utf8mb4&binary_prefix=true
|
||||
services:
|
||||
mysql:
|
||||
image: ghcr.io/apache/superset/ci/mysql:8.0
|
||||
image: mysql:8.0
|
||||
# Authenticated pulls use our higher Docker Hub rate limit. Empty on
|
||||
# fork PRs (secrets unavailable) -> runner falls back to anonymous.
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
ports:
|
||||
@@ -64,7 +66,7 @@ jobs:
|
||||
--health-timeout=5s
|
||||
--health-retries=5
|
||||
redis:
|
||||
image: ghcr.io/apache/superset/ci/redis:7-alpine
|
||||
image: redis:7-alpine
|
||||
options: --entrypoint redis-server
|
||||
ports:
|
||||
- 16379:6379
|
||||
@@ -141,7 +143,7 @@ jobs:
|
||||
SUPERSET__SQLALCHEMY_DATABASE_URI: postgresql+psycopg2://superset:superset@127.0.0.1:15432/superset
|
||||
services:
|
||||
postgres:
|
||||
image: ghcr.io/apache/superset/ci/postgres:17-alpine
|
||||
image: postgres:17-alpine
|
||||
env:
|
||||
POSTGRES_USER: superset
|
||||
POSTGRES_PASSWORD: superset
|
||||
@@ -150,7 +152,7 @@ jobs:
|
||||
# GitHub action runner's default installations
|
||||
- 15432:5432
|
||||
redis:
|
||||
image: ghcr.io/apache/superset/ci/redis:7-alpine
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- 16379:6379
|
||||
steps:
|
||||
@@ -200,7 +202,7 @@ jobs:
|
||||
sqlite:///${{ github.workspace }}/.temp/examples.db?check_same_thread=true
|
||||
services:
|
||||
redis:
|
||||
image: ghcr.io/apache/superset/ci/redis:7-alpine
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- 16379:6379
|
||||
steps:
|
||||
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
SUPERSET__SQLALCHEMY_EXAMPLES_URI: presto://localhost:15433/memory/default
|
||||
services:
|
||||
postgres:
|
||||
image: ghcr.io/apache/superset/ci/postgres:17-alpine
|
||||
image: postgres:17-alpine
|
||||
env:
|
||||
POSTGRES_USER: superset
|
||||
POSTGRES_PASSWORD: superset
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
# GitHub action runner's default installations
|
||||
- 15432:5432
|
||||
presto:
|
||||
image: ghcr.io/apache/superset/ci/presto:350-e.6
|
||||
image: starburstdata/presto:350-e.6
|
||||
env:
|
||||
POSTGRES_USER: superset
|
||||
POSTGRES_PASSWORD: superset
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
# GitHub action runner's default installations
|
||||
- 15433:8080
|
||||
redis:
|
||||
image: ghcr.io/apache/superset/ci/redis:7-alpine
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- 16379:6379
|
||||
steps:
|
||||
@@ -116,7 +116,7 @@ jobs:
|
||||
UPLOAD_FOLDER: /tmp/.superset/uploads/
|
||||
services:
|
||||
postgres:
|
||||
image: ghcr.io/apache/superset/ci/postgres:17-alpine
|
||||
image: postgres:17-alpine
|
||||
env:
|
||||
POSTGRES_USER: superset
|
||||
POSTGRES_PASSWORD: superset
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
# GitHub action runner's default installations
|
||||
- 15432:5432
|
||||
redis:
|
||||
image: ghcr.io/apache/superset/ci/redis:7-alpine
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- 16379:6379
|
||||
steps:
|
||||
|
||||
@@ -53,6 +53,12 @@ jobs:
|
||||
python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["current", "next"]') }}
|
||||
env:
|
||||
PYTHONPATH: ${{ github.workspace }}
|
||||
# Promotes the SQLAlchemy 2.0 deprecation warnings already locked in as
|
||||
# errors via pytest.ini's `filterwarnings` to actually run in CI, so a
|
||||
# regression on those fails the build instead of relying on a
|
||||
# contributor remembering to set this locally. See the migration
|
||||
# battleplan: https://github.com/apache/superset/discussions/40273
|
||||
SQLALCHEMY_WARN_20: "1"
|
||||
steps:
|
||||
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
@@ -69,18 +75,13 @@ jobs:
|
||||
SUPERSET_SECRET_KEY: not-a-secret
|
||||
run: |
|
||||
pytest --durations-min=0.5 --cov-report= --cov=superset ./tests/common ./tests/unit_tests --cache-clear --maxfail=50 --junit-xml=test-results/junit-unit.xml
|
||||
# COVERAGE_FILE keeps these scoped gates off the default .coverage that
|
||||
# the step above wrote. pytest-cov starts a fresh data file per run, so
|
||||
# without it the last gate replaces the full-suite data and the report
|
||||
# uploaded below contains only that gate's subtree -- every other file
|
||||
# then reads as uncovered, and patch coverage on an unrelated PR is 0%.
|
||||
- name: Python 100% coverage unit tests
|
||||
env:
|
||||
SUPERSET_TESTENV: true
|
||||
SUPERSET_SECRET_KEY: not-a-secret
|
||||
run: |
|
||||
COVERAGE_FILE="${RUNNER_TEMP}/.coverage.sql" pytest --durations-min=0.5 --cov=superset/sql/ ./tests/unit_tests/sql/ --cache-clear --cov-fail-under=100 --junit-xml=test-results/junit-sql-coverage.xml
|
||||
COVERAGE_FILE="${RUNNER_TEMP}/.coverage.semantic_layers" pytest --durations-min=0.5 --cov=superset/semantic_layers/ ./tests/unit_tests/semantic_layers/ --cache-clear --cov-fail-under=100 --junit-xml=test-results/junit-semantic-layers-coverage.xml
|
||||
pytest --durations-min=0.5 --cov=superset/sql/ ./tests/unit_tests/sql/ --cache-clear --cov-fail-under=100 --junit-xml=test-results/junit-sql-coverage.xml
|
||||
pytest --durations-min=0.5 --cov=superset/semantic_layers/ ./tests/unit_tests/semantic_layers/ --cache-clear --cov-fail-under=100 --junit-xml=test-results/junit-semantic-layers-coverage.xml
|
||||
- name: Upload code coverage
|
||||
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
||||
with:
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
.stylelintignore
|
||||
.flake8
|
||||
.nvmrc
|
||||
.npmrc
|
||||
.rat-excludes
|
||||
.swcrc
|
||||
.*log
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
# [SIP] Proposal for a dashboard component Extensions contribution point
|
||||
|
||||
> **Companion SIP:** Pairs with [`SIP.md`](SIP.md) (first-class iframe component +
|
||||
> runtime CSP allowlist). That SIP is the **reference implementation** that proves
|
||||
> this contribution point: the iframe's UI becomes an extension-contributed
|
||||
> dashboard component, while its security-sensitive CSP backend stays in core.
|
||||
>
|
||||
> **Status:** Draft — POC tracked in `feat/csp-runtime-allowlist-iframe`.
|
||||
|
||||
## Motivation
|
||||
|
||||
Adding a new dashboard layout component to Superset today is a **core-only,
|
||||
high-friction** operation. The iframe component in the companion SIP had to touch
|
||||
~12 files: a type constant, the `componentLookup` map, the builder palette, and
|
||||
**seven hardcoded behavior maps** keyed by component-type string
|
||||
(`isValidChild`, `componentIsResizable`, `newComponentFactory`,
|
||||
`shouldWrapChildInRow`, `getDetailedComponentWidth`, `isDashboardEmpty`, plus the
|
||||
prop bundle injected by `DashboardComponent.tsx`). Component types are a **closed
|
||||
enum** baked into core.
|
||||
|
||||
There is a legacy escape hatch — the `DashboardComponentsRegistry` /
|
||||
`DYNAMIC_TYPE` path (`src/visualizations/dashboardComponents/`) — but it is an
|
||||
**antique that should be deprecated**:
|
||||
|
||||
- It is disconnected from the modern VS Code-style Extensions framework
|
||||
(`@apache-superset/core`, `ENABLE_EXTENSIONS`), which already has contribution
|
||||
points for `commands`, `menus`, `views`, `editors`, and `chat`.
|
||||
- Components registered through it are **second-class**: `DynamicComponent`
|
||||
renders them in a generic wrapper that only passes `dashboardData`. They do not
|
||||
receive the first-class layout lifecycle (edit mode, meta editing, resize, DnD)
|
||||
and cannot declare their own layout behavior.
|
||||
|
||||
We want a **single, modern way** to contribute a first-class dashboard layout
|
||||
component — via the Extensions framework — and to deprecate the legacy registry.
|
||||
The iframe component is the ideal pilot because it is self-contained.
|
||||
|
||||
## Proposed Change
|
||||
|
||||
### 1. A `dashboardComponents` contribution point
|
||||
|
||||
Add `dashboardComponents` to the Extensions `Contributions` interface
|
||||
(`packages/superset-core/src/contributions/index.ts`), alongside `views`,
|
||||
`commands`, etc., with a public registration API mirroring the existing ones
|
||||
(`registerDashboardComponent` returning a `Disposable`), exposed on
|
||||
`window.superset.dashboardComponents` and wired into `ExtensionsLoader`.
|
||||
|
||||
### 2. The Dashboard Component Contract (the heart of this SIP)
|
||||
|
||||
The contract has two halves. Getting this right is the real work — it becomes a
|
||||
**public API Superset must support indefinitely**.
|
||||
|
||||
**(a) Declarative behavior metadata** — replaces the seven hardcoded util maps:
|
||||
|
||||
```ts
|
||||
interface DashboardComponentContribution {
|
||||
id: string; // unique type key, namespaced, e.g. "my-org.iframe"
|
||||
name: string; // palette label
|
||||
description?: string;
|
||||
icon: string; // contributed icon id or known icon name
|
||||
resizable?: boolean; // -> componentIsResizable
|
||||
defaultMeta?: { // -> newComponentFactory
|
||||
width?: number;
|
||||
height?: number;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
nesting?: { // -> isValidChild / shouldWrapChildInRow
|
||||
validParents?: string[]; // e.g. [GRID, ROW, COLUMN, TAB]
|
||||
wrapInRow?: boolean;
|
||||
minWidth?: number; // -> getDetailedComponentWidth
|
||||
};
|
||||
isUserContent?: boolean; // -> isDashboardEmpty
|
||||
loadComponent: () => Promise<{ default: ComponentType<DashboardComponentProps> }>;
|
||||
}
|
||||
```
|
||||
|
||||
**(b) Runtime props contract** — a small, stable surface. Crucially, **the host
|
||||
owns the chrome** (the `Draggable` + `ResizableContainer` + `HoverMenu`/delete
|
||||
wrapper that every current `componentLookup` component re-implements today). The
|
||||
extension component renders only its *content* and, optionally, an *editor*:
|
||||
|
||||
```ts
|
||||
interface DashboardComponentProps {
|
||||
id: string;
|
||||
meta: Record<string, unknown>;
|
||||
editMode: boolean;
|
||||
updateMeta: (patch: Record<string, unknown>) => void; // wraps updateComponents
|
||||
// resize/drag/delete handled by the host wrapper, NOT the component
|
||||
}
|
||||
```
|
||||
|
||||
This is a strict improvement over the status quo: the iframe component in the
|
||||
companion PR hand-rolls the Draggable/Resizable/HoverMenu wrapper; under this
|
||||
contract that boilerplate moves into the host once, and contributed components
|
||||
shrink to "render content + edit meta."
|
||||
|
||||
### 3. Registry-driven core
|
||||
|
||||
Refactor `componentLookup` and the seven behavior maps to consult a registry,
|
||||
with the **built-in leaf components seeded into it** at startup. Structural
|
||||
container components (Chart, Tabs, Row, Column, Header) *are* the layout engine
|
||||
and stay bespoke; the contribution point targets **leaf/content components**
|
||||
(today: Markdown, Divider, Iframe; tomorrow: anything). `DashboardComponent.tsx`
|
||||
resolves contributed types through the registry and renders them inside the
|
||||
shared host chrome.
|
||||
|
||||
### 4. Deprecate `DashboardComponentsRegistry` / `DYNAMIC_TYPE`
|
||||
|
||||
Mark the legacy registry and `DYNAMIC_TYPE` deprecated. Provide a shim so existing
|
||||
dynamic components keep working, with a migration note pointing at the new
|
||||
contribution point. Removal happens in a later major per Superset's deprecation
|
||||
policy.
|
||||
|
||||
### 5. Graceful fallback for unknown types
|
||||
|
||||
A saved dashboard layout stores component **type strings** in its position JSON.
|
||||
If a dashboard references a type whose extension is disabled/uninstalled, the host
|
||||
must render a non-destructive placeholder ("This component requires the *X*
|
||||
extension") and **preserve the meta on save** so re-enabling the extension
|
||||
restores it. The layout engine already tolerates unknown types defensively
|
||||
(`componentLookup[type]` → null; `isValidChild` → false); this SIP makes that an
|
||||
intentional, user-visible contract rather than silent breakage.
|
||||
|
||||
### 6. Backend: APIs yes, security policy no
|
||||
|
||||
The Extensions framework **already** lets a component contribute a backend REST
|
||||
API: the `@api` decorator (`superset-core/.../rest_api/decorators.py`) detects
|
||||
extension context and registers the route via `appbuilder.add_api()` at entrypoint
|
||||
import, serving it under `/extensions/{publisher}/{name}/...` and auto-creating
|
||||
the endpoint's FAB permission. **No new work is required for an extension to ship
|
||||
an API.**
|
||||
|
||||
What an extension **cannot** do today, and what this SIP explicitly leaves to
|
||||
core:
|
||||
|
||||
- **Role policy for a permission.** Endpoint permissions are auto-created, but
|
||||
whether a permission is *Admin-only* (e.g. via
|
||||
`SupersetSecurityManager.ADMIN_ONLY_VIEW_MENUS`) is decided in core at
|
||||
`sync_role_definitions` time. The manifest's `permissions: list[str]` field is
|
||||
currently **dormant** (never read), and the `ContributionProcessorRegistry` that
|
||||
would process it is scaffolding that is not wired into the load pipeline.
|
||||
- **Security-sensitive request hooks** (e.g. rewriting CSP/Talisman headers).
|
||||
|
||||
This is exactly why the companion CSP feature keeps its backend in core: the
|
||||
component *UI* is extension-shaped, but punching holes in the CSP and gating it
|
||||
admin-only are core security responsibilities.
|
||||
|
||||
A **future, optional** extension of this SIP could finish wiring
|
||||
`ContributionProcessorRegistry` + a manifest permission-policy schema so
|
||||
extensions can declare role policy — but that is itself a security-review-worthy
|
||||
change and is out of scope here.
|
||||
|
||||
## New or Changed Public Interfaces
|
||||
|
||||
- **New contribution point** `dashboardComponents` on the `Contributions`
|
||||
interface; new `registerDashboardComponent(...) -> Disposable` API; new
|
||||
`window.superset.dashboardComponents` namespace.
|
||||
- **New public types** `DashboardComponentContribution` and
|
||||
`DashboardComponentProps` (the contract) — these become long-term public API.
|
||||
- **Changed (internal → registry-driven)** `componentLookup` and the seven
|
||||
behavior util maps; `DashboardComponent.tsx` resolution path; the host gains a
|
||||
shared component-chrome wrapper.
|
||||
- **Deprecated** `DashboardComponentsRegistry`, `DYNAMIC_TYPE`,
|
||||
`NewDynamicComponent`, `setupDashboardComponents`.
|
||||
|
||||
## New dependencies
|
||||
|
||||
None. Reuses the existing Extensions framework (module federation, manifest
|
||||
schema, `@api` decorator) and the existing functional-registry utilities.
|
||||
|
||||
## Migration Plan and Compatibility
|
||||
|
||||
- **No DB migration.** This is a frontend/framework change plus the (already
|
||||
supported) extension API path.
|
||||
- **Layout JSON is unchanged** — component types remain type strings. The new
|
||||
fallback behavior makes *unknown* types degrade gracefully instead of rendering
|
||||
nothing.
|
||||
- **Backwards compatible:** built-in components are seeded into the registry, so
|
||||
existing dashboards render identically. Legacy `DYNAMIC_TYPE` components keep
|
||||
working via a deprecation shim.
|
||||
- **Rollout:** the contribution point is only active under `ENABLE_EXTENSIONS`;
|
||||
with it off, behavior is identical to today.
|
||||
|
||||
## Rejected Alternatives
|
||||
|
||||
- **Keep / extend `DashboardComponentsRegistry`.** It is disconnected from the
|
||||
modern Extensions framework and produces second-class components. Deprecating it
|
||||
in favor of one contribution model is the goal, not a side effect.
|
||||
- **Require all built-in components to become extensions.** Chart/Tabs/Row/Column
|
||||
are the layout engine; extracting them is high-risk and low-value. The
|
||||
contribution point *adds* leaf components; it does not mandate extraction.
|
||||
- **Let the extension component own its own DnD/resize chrome** (as
|
||||
`componentLookup` components do today). Rejected: it bloats the contract,
|
||||
duplicates host logic, and makes the public API fragile. The host owns chrome.
|
||||
- **One combined SIP with the CSP feature.** Rejected: the framework change and
|
||||
the security-sensitive feature are distinct discussions with different
|
||||
reviewers and risk profiles, even though they share a POC branch.
|
||||
- **Move the CSP permission/role policy into the extension.** Not supported today
|
||||
(dormant manifest `permissions`, unwired contribution processor) and
|
||||
undesirable: admin-only gating and CSP-header rewriting are core security
|
||||
responsibilities.
|
||||
|
||||
## Implementation Status (POC)
|
||||
|
||||
Implemented on the POC branch (`@apache-superset/core` mirrors the `chat`
|
||||
contribution-point pattern from #41000/#41205):
|
||||
|
||||
- [x] `DashboardComponentDefinition` + `DashboardComponentProps` contract types
|
||||
(`packages/superset-core/src/dashboardComponents`), added to the
|
||||
`Contributions` interface and the package's subpath exports
|
||||
- [x] `dashboardComponents` contribution point: host `DashboardComponentsProvider`
|
||||
registry + public `registerDashboardComponent`/`getDashboardComponents` API
|
||||
(`src/core/dashboardComponents`), exposed on `window.superset` via
|
||||
`ExtensionsStartup` + `Namespaces`
|
||||
- [x] Shared host component-chrome wrapper `DashboardExtensionComponent`
|
||||
(owns Draggable/Resizable/HoverMenu/Delete; reads `resizable` from the
|
||||
definition) behind the new `EXTENSION_TYPE`
|
||||
- [x] `componentLookup` + builder palette resolve the registry; the seven
|
||||
behavior maps carry `EXTENSION_TYPE` leaf behavior
|
||||
- [x] Unknown-type graceful fallback (placeholder + meta preserved on save)
|
||||
- [x] Deprecation notices on `DashboardComponentsRegistry` / `DYNAMIC_TYPE`
|
||||
(legacy path still functions)
|
||||
- [x] Reference component: the built-in iframe is now delivered **through** the
|
||||
contribution point (`src/dashboard/extensions/iframe`), registered at
|
||||
startup exactly as a third-party extension would; its CSP backend remains
|
||||
in core per the companion SIP
|
||||
- [x] Tests: registry lifecycle (register/get/replace/dispose), host-wrapper
|
||||
resolution + fallback + `updateMeta`, iframe content + CSP UX
|
||||
|
||||
- [x] Per-component behavior policy honored by the layout engine: `resizable`,
|
||||
`minWidth`, `isUserContent`, `validParents`, and `wrapInRow` are seeded onto
|
||||
instance `meta` at creation and read by `componentIsResizable`,
|
||||
`getDetailedComponentWidth`, `isDashboardEmpty`, `isValidChild`, and
|
||||
`shouldWrapChildInRow` (the pure layout utils stay registry-free; behavior
|
||||
round-trips in the saved layout)
|
||||
- [x] Developer docs: `extension-points/dashboard-components.md` + a
|
||||
`contribution-types.md` section + sidebar entry, with an example extension
|
||||
|
||||
Remaining (follow-up, not POC-blocking):
|
||||
|
||||
- [ ] Manifest `contributions.dashboardComponents` declarative validation in the
|
||||
Python/TS manifest schema (runtime side-effect registration works today,
|
||||
matching how `chat` does it)
|
||||
- [ ] Remove the legacy `DashboardComponentsRegistry`/`DYNAMIC_TYPE` (major)
|
||||
@@ -0,0 +1,232 @@
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
# [SIP] Proposal for a first-class iframe dashboard component with a runtime CSP allowlist
|
||||
|
||||
> **Companion SIP:** This proposal pairs with
|
||||
> [`SIP-DASHBOARD-COMPONENT-CONTRIBUTION-POINT.md`](SIP-DASHBOARD-COMPONENT-CONTRIBUTION-POINT.md),
|
||||
> which proposes the Extensions contribution point that would let this iframe
|
||||
> component (and others) be shipped as an extension. The two are deliberately
|
||||
> separate discussions: **this** SIP covers the security-sensitive feature
|
||||
> (runtime CSP override + permissions); the companion covers the framework
|
||||
> change. They share one POC branch so the end-to-end story is demonstrable.
|
||||
|
||||
> **Status:** Draft — tracking the implementation in `feat/csp-runtime-allowlist-iframe`.
|
||||
> This document follows the SIP issue template and is kept in sync with the branch
|
||||
> as the implementation evolves. See SIP-0
|
||||
> (<https://github.com/apache/superset/issues/5602>) for the SIP process.
|
||||
|
||||
## Motivation
|
||||
|
||||
Superset ships a Talisman/Content-Security-Policy (CSP) configuration that, by
|
||||
design, prevents users from embedding arbitrary external content in a dashboard.
|
||||
The default policy declares `default-src 'self'` and **no** `frame-src`
|
||||
directive, so an `<iframe>` pointing at any third-party origin is blocked by the
|
||||
browser.
|
||||
|
||||
This is correct and secure default behavior, but it creates real friction:
|
||||
|
||||
- There is **no first-class "iframe" dashboard component**. Users historically
|
||||
smuggled iframes through Markdown, which is both a footgun and blocked by CSP.
|
||||
- When an embed *is* legitimately needed (an internal tool, a status page, a
|
||||
partner widget), the only way to allow it is to **edit `TALISMAN_CONFIG` and
|
||||
restart every Superset process**. That is a deploy-time, ops-team operation —
|
||||
far too heavyweight for "let me embed this one dashboard from our other
|
||||
internal app."
|
||||
- There is no in-product signal telling a user *why* their embed is blank, and
|
||||
no path to fix it.
|
||||
|
||||
We want to (a) make embedding a real, supported component, and (b) give trusted
|
||||
Admins a controlled, audited way to widen the CSP at runtime — without
|
||||
abandoning the secure-by-default posture that operators rely on.
|
||||
|
||||
## Proposed Change
|
||||
|
||||
The change has five parts.
|
||||
|
||||
### 1. A first-class `IFRAME` dashboard layout component
|
||||
|
||||
A new grid component (`IFRAME_TYPE`) modeled on the existing Markdown/Divider
|
||||
components. In edit mode the user pastes a URL; in view mode the component
|
||||
renders a sandboxed `<iframe>`. The component is registered through the same
|
||||
surface as every other layout element (type constant, `componentLookup`, drag
|
||||
palette, nesting/resize/width/wrap util maps).
|
||||
|
||||
The iframe is rendered with a restrictive `sandbox` attribute
|
||||
(`allow-scripts allow-same-origin allow-popups allow-forms`).
|
||||
|
||||
### 2. Domain flagging
|
||||
|
||||
When the runtime-allowlist feature is enabled, the component compares the
|
||||
embedded URL's **origin** against the current allowlist (fetched from the new
|
||||
API). If the origin is not yet allowed, it shows an inline warning explaining
|
||||
that the domain is blocked by the CSP.
|
||||
|
||||
### 3. "Enable domain in CSP" button
|
||||
|
||||
If the current user holds the new permission (Admins by default), the warning
|
||||
includes an **Enable domain in CSP** button. Clicking it `POST`s the origin to
|
||||
the allowlist API and re-checks. Users without the permission instead see "ask
|
||||
an administrator."
|
||||
|
||||
### 4. Permission gating
|
||||
|
||||
Mutating the allowlist requires `can write on CSPAllowlist`. The `CSPAllowlist`
|
||||
view-menu is registered in `SupersetSecurityManager.ADMIN_ONLY_VIEW_MENUS`, so
|
||||
the capability is reserved for Admins (or a custom role explicitly granted it),
|
||||
consistent with how other trusted, security-sensitive operations are scoped.
|
||||
|
||||
### 5. Runtime CSP override ("punched holes")
|
||||
|
||||
A new `csp_allowlist` metadata table stores allowlist entries. An `after_request`
|
||||
hook — registered **before** flask-talisman so that, because Flask runs
|
||||
`after_request` callbacks in reverse registration order, it runs **after**
|
||||
Talisman has set the header — merges the operator-curated entries into the
|
||||
response CSP header. Entries are cached in-process with a short TTL to avoid a DB
|
||||
hit per response; a write through the API invalidates the cache in the handling
|
||||
worker, and other workers converge when their cached copy expires.
|
||||
|
||||
The entire runtime-override path is inert unless the `CSP_RUNTIME_ALLOWLIST`
|
||||
feature flag is enabled, so the static, deploy-time policy remains the default
|
||||
and operators opt in explicitly.
|
||||
|
||||
```
|
||||
Browser ──> Flask request
|
||||
│
|
||||
Talisman after_request (sets "Content-Security-Policy: default-src 'self'; …")
|
||||
│
|
||||
merge_runtime_csp_allowlist (if flag on: appends allowlist origins to frame-src, …)
|
||||
│
|
||||
Response ──> Browser ("…; frame-src 'self' https://embed.example")
|
||||
```
|
||||
|
||||
#### Design decisions (resolved)
|
||||
|
||||
- **Scope: global.** Allowlist entries apply server-wide. CSP is a single
|
||||
per-response header; a global allowlist keeps the merge context-free and
|
||||
avoids per-dashboard request plumbing. (Per-dashboard scoping is a possible
|
||||
future extension.)
|
||||
- **Operator control: feature-flagged kill-switch.** The runtime override only
|
||||
functions when `CSP_RUNTIME_ALLOWLIST` is on (default **off**). Operators who
|
||||
want a purely static policy simply leave it off and the table is never
|
||||
consulted.
|
||||
|
||||
## New or Changed Public Interfaces
|
||||
|
||||
### REST API
|
||||
|
||||
- `GET /api/v1/csp_allowlist/` — list entries
|
||||
- `GET /api/v1/csp_allowlist/<id>` — get one
|
||||
- `POST /api/v1/csp_allowlist/` — create (validates origin + directive)
|
||||
- `PUT /api/v1/csp_allowlist/<id>` — update
|
||||
- `DELETE /api/v1/csp_allowlist/<id>` — delete
|
||||
- `DELETE /api/v1/csp_allowlist/?q=!(...)` — bulk delete
|
||||
|
||||
All write methods require `can write on CSPAllowlist` (Admin-only by default).
|
||||
Origins are validated server-side: bare `scheme://host[:port]` only — no
|
||||
wildcards, paths, query strings, fragments, or credentials. Only a fixed set of
|
||||
directives may be widened (`frame-src`, `child-src`, `img-src`, `connect-src`,
|
||||
`media-src`, `font-src`); notably **not** `script-src`.
|
||||
|
||||
### Model
|
||||
|
||||
- `CSPAllowlistEntry` (`superset/models/csp.py`, table `csp_allowlist`):
|
||||
`id`, `uuid`, `domain`, `directive` (default `frame-src`), `description`,
|
||||
audit columns. Unique on `(domain, directive)`.
|
||||
|
||||
### Feature flag
|
||||
|
||||
- `CSP_RUNTIME_ALLOWLIST` (default `False`) — gates the entire runtime-override
|
||||
path, backend and frontend.
|
||||
|
||||
### Config
|
||||
|
||||
- `CSP_RUNTIME_ALLOWLIST_CACHE_TTL` (default `30` seconds) — in-process cache TTL
|
||||
for the allowlist; also settable via env var.
|
||||
|
||||
### Frontend
|
||||
|
||||
- New `IFRAME` dashboard layout component and its registration across the
|
||||
dashboard util maps.
|
||||
- New `FeatureFlag.CspRuntimeAllowlist` enum member.
|
||||
|
||||
### Security model
|
||||
|
||||
- New `CSPAllowlist` view-menu added to `ADMIN_ONLY_VIEW_MENUS`.
|
||||
|
||||
## New dependencies
|
||||
|
||||
None. The implementation uses existing libraries (flask-talisman,
|
||||
Flask-AppBuilder, marshmallow, SQLAlchemy on the backend; existing
|
||||
`@superset-ui/core` components on the frontend).
|
||||
|
||||
## Migration Plan and Compatibility
|
||||
|
||||
- One Alembic migration adds the `csp_allowlist` table
|
||||
(`4a50792bd265`, down-revision `3a8e6f2c1b95`). The table is empty on creation.
|
||||
- Fully backward compatible: with the feature flag off (the default), behavior is
|
||||
identical to today — the static CSP is authoritative and the new table is never
|
||||
read. No existing dashboards, URLs, or policies change.
|
||||
- Rollback: dropping the table and disabling the flag fully reverts the feature.
|
||||
|
||||
### Security review notes
|
||||
|
||||
This feature deliberately relocates a *capability* (widening the CSP) from a
|
||||
purely deploy-time operator control into a runtime, permission-gated, audited
|
||||
operation. The mitigations that keep it within Superset's trust model:
|
||||
|
||||
- **Off by default** behind a feature flag the operator owns.
|
||||
- **Admin-only** write permission (a fully trusted principal per `SECURITY.md`).
|
||||
- **Strict origin validation** server-side — no wildcards, no `script-src`.
|
||||
- **Audit trail** via the audit mixin (`created_by` / `changed_by`).
|
||||
- The iframe is **sandboxed** and the merge can only *widen* a directive to a
|
||||
specific origin, never relax nonce/`strict-dynamic` protections on
|
||||
`script-src`.
|
||||
|
||||
## Rejected Alternatives
|
||||
|
||||
- **Dynamically reconfiguring flask-talisman at runtime.** Talisman is configured
|
||||
once at app init. Rather than mutate its internals, we add our own
|
||||
`after_request` hook that post-processes the header it already sets. This is
|
||||
simpler, avoids depending on Talisman internals, and rides the same per-request
|
||||
header machinery Talisman already uses for its nonce.
|
||||
- **Per-dashboard allowlist scoping.** More precise, but CSP is a per-response
|
||||
header; per-dashboard scoping adds request-context complexity for marginal
|
||||
benefit in the common case. Left as a possible future extension.
|
||||
- **"Always on" runtime override (no kill-switch).** Simpler, but moves a
|
||||
security control fully into the app with no operator opt-out. Rejected in favor
|
||||
of the feature-flag kill-switch.
|
||||
- **Shared/Redis-backed allowlist cache with cross-worker invalidation.**
|
||||
Correct but heavier. A short-TTL in-process cache is good enough: writes take
|
||||
effect immediately in the handling worker and within the TTL elsewhere, with no
|
||||
new infrastructure dependency.
|
||||
|
||||
## Implementation Status
|
||||
|
||||
- [x] Feature flag `CSP_RUNTIME_ALLOWLIST` + `CSP_RUNTIME_ALLOWLIST_CACHE_TTL`
|
||||
- [x] `CSPAllowlistEntry` model + Alembic migration
|
||||
- [x] DAO, marshmallow schemas (with origin/directive validation), REST API
|
||||
- [x] Admin-only permission (`CSPAllowlist` view-menu)
|
||||
- [x] `after_request` CSP merge hook + in-process TTL cache + invalidation
|
||||
- [x] `IFRAME` dashboard component + registration across util maps
|
||||
- [x] Domain flagging + permission-gated "Enable domain in CSP" button
|
||||
- [x] Tests: backend unit (validation + merge + hook), backend integration (API),
|
||||
frontend unit (util + component)
|
||||
- [ ] Docs (`docs/`) + `UPDATING.md` entry
|
||||
- [ ] Community/security review feedback
|
||||
+12
-60
@@ -25,30 +25,6 @@ assists people when migrating to a new version.
|
||||
## Next
|
||||
|
||||
- `SAMPLES_ROW_LIMIT` is now the default for `/datasource/samples` requests without a valid explicit `per_page`, rather than a hard per-request ceiling; explicit limits are honored up to the existing global row-limit ceiling, matching `/chart/data` SAMPLES requests.
|
||||
- The `cockroachdb` extra (`pip install apache-superset[cockroachdb]`) now installs `sqlalchemy-cockroachdb` instead of the abandoned `cockroachdb` package, whose SQLAlchemy dialect could not be imported under SQLAlchemy 2.0. Existing environments with the old package installed should `pip uninstall cockroachdb && pip install sqlalchemy-cockroachdb` (or simply reinstall the extra) to restore CockroachDB connectivity.
|
||||
|
||||
### MCP tool results preserve stored string values
|
||||
|
||||
Structured MCP tool results no longer add `<UNTRUSTED-CONTENT>` wrappers or
|
||||
rewrite delimiter-looking text inside string fields. Tool-result content remains
|
||||
user-controlled data, but clients must convey that trust boundary outside domain
|
||||
values instead of recognizing or removing marker strings.
|
||||
|
||||
Clients that handled the former delimiter convention should stop stripping marker
|
||||
text: the same text can be legitimate stored content. Response models and content
|
||||
types are unchanged, and no metadata-database migration is required. Automated
|
||||
read-modify-write workflows should be paused or pinned away from older instances
|
||||
until every serving instance is upgraded; a mixed-version response has no reliable
|
||||
signal that tells a client whether its text is decorated. Redis-backed MCP response
|
||||
caches use a new internal namespace after the upgrade, so upgraded instances do not
|
||||
reuse older cached results.
|
||||
|
||||
Values that a client already wrote back with presentation wrappers cannot be
|
||||
distinguished safely from intentional content. Operators should review possible
|
||||
`<UNTRUSTED-CONTENT>` / `</UNTRUSTED-CONTENT>` wrappers and
|
||||
`[ESCAPED-UNTRUSTED-CONTENT-OPEN]` /
|
||||
`[ESCAPED-UNTRUSTED-CONTENT-CLOSE]` substitutions rather than applying an automatic
|
||||
marker-removal migration.
|
||||
|
||||
### OAuth2 database callback metrics include their outcome
|
||||
|
||||
@@ -59,8 +35,6 @@ the old counter to use the outcome-specific replacements.
|
||||
|
||||
- [42930](https://github.com/apache/superset/pull/42930): Dataset import data-URI fetches no longer honor an HTTP(S) proxy when `DATASET_IMPORT_ALLOW_INTERNAL_DATA_URLS` is `False` (the default): the connection is now made directly to the destination so the peer-address check validates the real target instead of a proxy's. Deployments that require an egress proxy to reach legitimate external data URLs for dataset import should set `DATASET_IMPORT_ALLOW_INTERNAL_DATA_URLS = True` or otherwise ensure those URLs resolve without one.
|
||||
- [42935](https://github.com/apache/superset/pull/42935): The MCP service now refuses to start (`MCPAuthConfigError`) when `MCP_JWT_ISSUER` trusts more than one issuer and no `MCP_USER_RESOLVER` is configured, instead of only logging a warning. This was already a documented misconfiguration (the default resolver isn't issuer-scoped, so distinct trusted issuers minting the same username/email would resolve to the same Superset user); deployments trusting multiple issuers must configure an `MCP_USER_RESOLVER` that derives its identity from the token's `iss` claim before upgrading. Single-issuer deployments are unaffected.
|
||||
- [42429](https://github.com/apache/superset/pull/42429): The Country Map chart's Iran GeoJSON now gives Alborz province its own ISO 3166-2 code, `IR-32`, instead of `IR-30`. `ISO` is the join key used to color/filter provinces on this chart, so any existing dataset keyed on `IR-30` for Alborz will silently stop matching after upgrading; re-key that data to `IR-32`.
|
||||
- [43388](https://github.com/apache/superset/pull/43388): The MCP service now refuses to start (`MCPAuthConfigError`) if `MCP_DEV_USERNAME` and `MCP_AUTH_ENABLED = True` are both set, and separately if `MCP_AUTH_ENABLED = True` but no usable JWT key material is configured (RSA key/JWKS, or an explicit `MCP_JWT_SECRET` for HMAC) — both previously started with authentication silently weaker than configured. Deployments combining a dev-mode username with JWT auth enabled, or enabling JWT auth without key material, must pick one before upgrading: unset `MCP_DEV_USERNAME` for a real auth deployment, or unset `MCP_AUTH_ENABLED` (or configure the key material) for a dev-mode one. Response caching (`MCP_CACHE_CONFIG["enabled"] = True`) now also excludes every tool with a side effect by default, not only a partial list, so a previously-cached mutating tool call is no longer served from cache; no config change is needed to pick this up.
|
||||
- [42393](https://github.com/apache/superset/pull/42393): Exported dataset YAML now carries a `uuid` for each metric and column so that custom folder assignments (which reference metrics/columns by UUID) survive an import into another workspace. This affects any export bundle that contains datasets, not just a dataset export: chart, dashboard, database and full-asset exports all embed the same dataset YAML, so a dashboard exported from this release also fails to import into an older one even though no dataset was exported directly. As with `folders` and `currency_code_column`, the affected `datasets/` files fail schema validation (`Unknown field: uuid`) when imported into Superset releases that predate this change; regenerate or hand-edit exports for older targets in mixed-version fleets.
|
||||
- [42300](https://github.com/apache/superset/pull/42300): Timeseries charts (line/area/bar) with a Y-axis bound in effect — either an explicit `yAxisBounds` or one derived from `truncateYAxis` — now clamp out-of-range data points to that bound instead of letting ECharts drop the point (and the line segments around it) entirely. Any existing chart with a configured Y-axis bound and data outside it will look different after upgrading: a gap becomes a point pinned to the boundary. The clamp also rewrites the value ECharts reads for that point's tooltip and data label, so the displayed value is the bound rather than the true observation.
|
||||
- [42087](https://github.com/apache/superset/pull/42087): Stored calculated-column and metric expressions are validated when a query is built, under the same sub-query policy already applied to adhoc expressions. Previously only the dataset update path checked them on save, so expressions written by v1 import, by dataset duplication, or before that check existed were never validated. Since `ALLOW_ADHOC_SUBQUERY` defaults to `False` (see [19242](https://github.com/apache/superset/pull/19242)), a dataset whose stored expression contains a sub-query works before upgrading and afterwards fails at chart render with `Custom SQL fields cannot contain sub-queries.` There is no migration step, and the error does not name the offending dataset column, so audit stored expressions before upgrading: either rewrite them without the sub-query, or set `ALLOW_ADHOC_SUBQUERY = True` to keep the previous behaviour for both stored and adhoc expressions.
|
||||
@@ -128,23 +102,6 @@ dialect; each package's constraint in `pyproject.toml` documents why.
|
||||
No application-level configuration changes are required for deployments
|
||||
that don't touch SQLAlchemy directly.
|
||||
|
||||
### New metric aggregates: MEDIAN, Sample Standard Deviation, Sample Variance
|
||||
|
||||
`MEDIAN`, `STDDEV_SAMP`, and `VAR_SAMP` are now available anywhere a metric
|
||||
aggregate is chosen (every chart type, SQL Lab, MCP), not only in Pivot
|
||||
Table's controls. Support is opt-in per database engine *spec class*,
|
||||
verified against a live instance before being enabled: Postgres, MySQL
|
||||
(`STDDEV_SAMP`/`VAR_SAMP` only, no `MEDIAN`), DuckDB, and Redshift (inherits
|
||||
Postgres's support, not yet separately verified) ship enabled in this
|
||||
release. Engine specs that subclass one of those (e.g. MariaDB, Aurora
|
||||
MySQL/Postgres, TimescaleDB) inherit the same support, on the same
|
||||
not-yet-independently-verified basis. Picking one of these aggregates on a
|
||||
database that has not opted in returns a clear "not supported on this
|
||||
database" error rather than a failed query. See
|
||||
`docs/sip/median-stddev-variance-aggregates.md` for the full design
|
||||
rationale, including why this is safe to add without reintroducing the
|
||||
totals/subtotals correctness bug fixed by #41184 (SIP-216).
|
||||
|
||||
### Soft delete is on by default, and purging is live
|
||||
|
||||
`SOFT_DELETE` now ships **on** (`DEFAULT_FEATURE_FLAGS`), so deleting a
|
||||
@@ -186,12 +143,10 @@ misrepresents the entity as unchanged.
|
||||
- **Storage growth.** Capture writes shadow rows per save, so the metadata
|
||||
database grows with edit volume. The `version_history.prune_old_versions`
|
||||
beat task removes rows whose transaction is older than
|
||||
`SUPERSET_VERSION_HISTORY_RETENTION_DAYS` (default 30).
|
||||
- **Check a replaced `CELERY_CONFIG`.** Carry both the
|
||||
`superset.tasks.version_history_retention` import and the
|
||||
`version_history.prune_old_versions` beat entry; see
|
||||
[Version-history retention (pruning)](#version-history-retention-pruning) for
|
||||
the startup-warning behavior.
|
||||
`SUPERSET_VERSION_HISTORY_RETENTION_DAYS` (default 30). A deployment that
|
||||
replaces `CELERY_CONFIG` rather than inheriting it must carry both the
|
||||
`superset.tasks.version_history_retention` import and the beat entry; a
|
||||
startup warning names whichever is absent.
|
||||
- **`PUT` responses change shape.** Entity updates now return populated
|
||||
`old_version_uuid` / `new_version_uuid` fields and an `ETag` header, which
|
||||
were null or absent while capture was off.
|
||||
@@ -200,10 +155,7 @@ misrepresents the entity as unchanged.
|
||||
kill-switch — not removed with the rollout toggles. Setting it to a falsy value
|
||||
stops capture within a restart, without a revert-and-redeploy. Unlike the
|
||||
soft-delete toggle, turning it off is a clean stop: existing version rows remain
|
||||
readable and no entity state is altered. Restore is unavailable (404) while
|
||||
capture is off. A full rollback also sets
|
||||
`FEATURE_FLAGS = {"VERSION_HISTORY": False}` to hide the panel — capture off
|
||||
with the panel left on shows an empty or stale history.
|
||||
readable and no entity state is altered.
|
||||
|
||||
### Scheduled report execution now enforces one application deadline
|
||||
|
||||
@@ -665,9 +617,9 @@ ALTER TABLE tagged_object DROP CONSTRAINT <constraint_name>;
|
||||
ALTER TABLE tagged_object DROP FOREIGN KEY <constraint_name>;
|
||||
```
|
||||
|
||||
### Entity version-history infrastructure
|
||||
### Entity version-history infrastructure (gated off by default)
|
||||
|
||||
Introduces the schema and SQLAlchemy-Continuum wiring that captures version history for charts, dashboards, and datasets, plus read-only `GET /api/v1/{chart,dashboard,dataset}/<uuid>/versions/` endpoints. Capture is governed by the `ENABLE_VERSIONING_CAPTURE` config value — an operational kill-switch (a release toggle that became a permanent ops switch), not a feature flag; see "Version history is on by default" above for the shipped default. With capture off, no save writes version rows; the endpoints continue to serve already-captured rows read-only. The migration is additive; existing entity `PUT` responses gain `old_version_uuid` / `new_version_uuid` body fields and an `ETag` header (both null/absent when capture is off).
|
||||
Introduces the schema and SQLAlchemy-Continuum wiring that captures version history for charts, dashboards, and datasets, plus read-only `GET /api/v1/{chart,dashboard,dataset}/<uuid>/versions/` endpoints. This ships **inert**: a new config flag `ENABLE_VERSIONING_CAPTURE` defaults to `False`, so no save writes any version rows and the endpoints return empty. It is an operational kill-switch (a release toggle that becomes a permanent ops switch), not a feature flag — set it to `True` to enable capture once validated. The migration is additive; existing entity `PUT` responses gain `old_version_uuid` / `new_version_uuid` body fields and an `ETag` header (both null/absent when capture is off).
|
||||
|
||||
A few save- and import-path internals change **unconditionally** (independent of the flag), because the versioned mappers must behave correctly whether or not capture is enabled:
|
||||
|
||||
@@ -688,7 +640,7 @@ A read-only companion to the version-history endpoints: each entity type gains a
|
||||
| `q` | string | — | Case-insensitive search over the full history, applied before pagination (so `count` reflects matches) |
|
||||
| `page` / `page_size` | integer | `0` / `25` | Pagination (`page_size` clamped to 200) |
|
||||
|
||||
Authorization reuses the resource's `can_read` permission and per-object `raise_for_access`; related-entity rows are visibility-filtered to what the caller may see. The stream reflects captured history; with capture off it remains readable but stops accruing new entries.
|
||||
Authorization reuses the resource's `can_read` permission and per-object `raise_for_access`; related-entity rows are visibility-filtered to what the caller may see. The stream is empty unless version capture is on (`ENABLE_VERSIONING_CAPTURE`).
|
||||
|
||||
### Version-history retention (pruning)
|
||||
|
||||
@@ -708,7 +660,7 @@ Purging is **live by default** (`SOFT_DELETE_PURGE_DRY_RUN=False`), so the reten
|
||||
|
||||
Deployments that replace the default `CELERY_CONFIG` must ensure workers register `superset.tasks.deletion_retention` and schedule the `deletion_retention.purge_soft_deleted` task themselves. The shipped Docker development config uses `imports` and includes both entries. While `SOFT_DELETE` is statically enabled, a missing beat entry logs a startup warning; when the override explicitly defines `imports`, a missing purge module is also reported.
|
||||
|
||||
Operators can immediately erase a specific entity for compliance (GDPR) via `superset deletion-retention force-purge --uuid <uuid>`; this applies legacy hard-delete semantics — a live chart referencing a force-purged dataset is left without a datasource until re-pointed (the chart is not modified), and it purges the named entity even when it was never soft-deleted. Every scheduled evaluation writes a provisional, content-free record to the new `purge_audit_log` table before the cascade starts. Meaningful retained outcomes survive the entity they name. Blocked audit records carry a stable machine-readable `reason` code (`report_schedule`, `user_attribute`, or `cascade_integrity_failure` for an unexpected cascade failure caused by a database integrity constraint) so the audit table alone answers why an entity was not purged; records finalized before the column existed keep a NULL reason. Apply the migration before rolling out the new code: the audit model declares the column, so a worker on the new code with an un-migrated table fails its write-ahead write and the scheduled purge fails closed until the migration lands. During a rolling deploy, workers still on the old code write reason-less blocked rows and suppress on status alone; both effects are self-healing, since a NULL-reason record never matches a reason code and the next all-new-code run re-anchors the entity. Consecutive scheduled evaluations blocked with the same status **and reason** suppress only the redundant current provisional record — a reason change writes one new blocked record carrying the new code; completed outcomes, outcome transitions, and every force-purge attempt remain independent and immutable. Retained transition records are not automatically expired, so entities whose block reason changes repeatedly can accumulate multiple audit rows. The **scheduled** purge fails closed when its provisional record cannot be written, while **force-purge** proceeds even if the audit write fails — the operator is present and deletion outranks audit for a compliance erasure. Operators can monitor `deletion_retention.blocked_audit_suppressed` and `deletion_retention.blocked_audit_dedupe_fallback` to verify suppression and fail-safe fallback behavior without changing the existing blocked-workload gauge.
|
||||
Operators can immediately erase a specific entity for compliance (GDPR) via `superset deletion-retention force-purge --uuid <uuid>`; this applies legacy hard-delete semantics — a live chart referencing a force-purged dataset is left without a datasource until re-pointed (the chart is not modified), and it purges the named entity even when it was never soft-deleted. Every scheduled evaluation writes a provisional, content-free record to the new `purge_audit_log` table before the cascade starts. Meaningful retained outcomes survive the entity they name. Consecutive scheduled evaluations with the same blocked outcome suppress only the redundant current provisional record; completed outcomes, outcome transitions, and every force-purge attempt remain independent and immutable. The **scheduled** purge fails closed when its provisional record cannot be written, while **force-purge** proceeds even if the audit write fails — the operator is present and deletion outranks audit for a compliance erasure. Operators can monitor `deletion_retention.blocked_audit_suppressed` and `deletion_retention.blocked_audit_dedupe_fallback` to verify suppression and fail-safe fallback behavior without changing the existing blocked-workload gauge.
|
||||
|
||||
### Recently Archived view and permanent delete (purge) endpoints
|
||||
|
||||
@@ -904,7 +856,7 @@ The migration is transactional (all-or-nothing) and idempotent — it can be saf
|
||||
|
||||
### Soft delete and restore for datasets
|
||||
|
||||
**The soft-delete behavior in this section applies only when the `SOFT_DELETE` feature flag is enabled. The flag defaults to `True`** (`@lifecycle: testing`), so on a default deployment `DELETE /api/v1/dataset/<id>` uses the recoverable soft-delete behavior described below. Setting `SOFT_DELETE` to `False` restores legacy permanent hard-delete behavior for subsequent deletes.
|
||||
**The soft-delete behavior in this section applies only when the `SOFT_DELETE` feature flag is enabled. The flag defaults to `False`** (`@lifecycle: development`), so on a default deployment `DELETE /api/v1/dataset/<id>` continues to **hard-delete permanently** — nothing is recoverable. Enable `SOFT_DELETE` to get the behavior described below.
|
||||
|
||||
**Flag-toggle caveat:** the soft-delete visibility filter is evaluated per query while the flag is on. If datasets are soft-deleted during a flag-on window and the flag is later turned **off**, those rows reappear as live datasets in all lists, lookups, and relationship loads (including charts that reference them). The `POST /<uuid>/restore` endpoint and the `dataset_deleted_state` list filter remain functional regardless of the flag, deliberately, so rows soft-deleted during a flag-on window stay discoverable and restorable after a rollback of the flag.
|
||||
|
||||
@@ -934,7 +886,7 @@ With the flag enabled: `DELETE /api/v1/dataset/<id>` no longer hard-deletes the
|
||||
|
||||
### Soft delete and restore for charts
|
||||
|
||||
**Everything in this section applies only when the `SOFT_DELETE` feature flag is enabled. The flag defaults to `True`** (`@lifecycle: testing`), so on a default deployment `DELETE /api/v1/chart/<id>` uses the recoverable soft-delete behavior described below. Setting `SOFT_DELETE` to `False` restores legacy permanent hard-delete behavior for subsequent deletes.
|
||||
**Everything in this section applies only when the `SOFT_DELETE` feature flag is enabled. The flag defaults to `False`** (`@lifecycle: development`), so on a default deployment `DELETE /api/v1/chart/<id>` continues to **hard-delete permanently** — nothing is recoverable. Enable `SOFT_DELETE` to get the behavior described below.
|
||||
|
||||
**Flag-toggle caveat:** the soft-delete visibility filter is evaluated per query while the flag is on. If charts are soft-deleted during a flag-on window and the flag is later turned **off**, those rows reappear as live charts in all lists, lookups, and relationship loads (including dashboards that contained them). The `POST /<uuid>/restore` endpoint and the `chart_deleted_state` list filter remain functional regardless of the flag, deliberately, so rows soft-deleted during a flag-on window stay discoverable and restorable after a rollback of the flag.
|
||||
|
||||
@@ -958,7 +910,7 @@ With the flag enabled: `DELETE /api/v1/chart/<id>` no longer hard-deletes the ch
|
||||
|
||||
### Soft delete and restore for dashboards
|
||||
|
||||
**Everything in this section applies only when the `SOFT_DELETE` feature flag is enabled. The flag defaults to `True`** (`@lifecycle: testing`), so on a default deployment `DELETE /api/v1/dashboard/<id>` uses the recoverable soft-delete behavior described below. Setting `SOFT_DELETE` to `False` restores legacy permanent hard-delete behavior for subsequent deletes.
|
||||
**Everything in this section applies only when the `SOFT_DELETE` feature flag is enabled. The flag defaults to `False`** (`@lifecycle: development`), so on a default deployment `DELETE /api/v1/dashboard/<id>` continues to **hard-delete permanently** — nothing is recoverable. Enable `SOFT_DELETE` to get the behavior described below.
|
||||
|
||||
**Flag-toggle caveat:** the soft-delete visibility filter is evaluated per query while the flag is on. If dashboards are soft-deleted during a flag-on window and the flag is later turned **off**, those rows reappear as live dashboards in all lists and lookups (including slug lookups — if a soft-deleted dashboard's slug was reused while the flag was on, both rows become visible with the same slug). The `POST /<uuid>/restore` endpoint and the `dashboard_deleted_state` list filter remain functional regardless of the flag, deliberately, so rows soft-deleted during a flag-on window stay discoverable and restorable after a rollback of the flag.
|
||||
|
||||
|
||||
@@ -14,20 +14,6 @@ Users can configure automated alerts and reports to send dashboards or charts to
|
||||
|
||||
Alerts and reports are disabled by default. To turn them on, you'll need to change configuration settings and install a suitable headless browser in your environment.
|
||||
|
||||
## Content Format Options
|
||||
|
||||
When scheduling an alert or report, you can choose the format used to deliver the dashboard or chart:
|
||||
|
||||
- **PDF** – a full-page screenshot rendered as a PDF attachment. Available for both dashboards and charts.
|
||||
- **PNG** – a screenshot delivered as an attachment. Emails embed the image inline in the message body; Slack and webhook recipients receive it as an uploaded file. Available for both dashboards and charts.
|
||||
- **CSV** – chart data attached as a `.csv` file. Available for charts only.
|
||||
- **XLSX (Excel)** – chart data attached as a `.xlsx` file. Available for charts only. If the chart's data spans multiple server-paginated files, email delivery detects the bundle and renames the attachment to `.zip`; Slack and webhook deliveries always name the file with an `.xlsx` extension even when the contents are a multi-file ZIP archive.
|
||||
- **Text** – chart data embedded directly in the email or Slack message body. Available only for charts using a text-based visualization type (e.g. Table, Pivot Table, Paired t-test).
|
||||
|
||||
Dashboard reports and alerts are limited to the PDF and PNG formats; the CSV, XLSX, and Text options are only available when scheduling a report or alert for an individual chart.
|
||||
|
||||
For alerts (not scheduled reports), PNG/PDF screenshots and chart CSV/XLSX data are only generated when the `ALERTS_ATTACH_REPORTS` [feature flag](/admin-docs/configuration/configuring-superset#feature-flags) is enabled (the default); with it disabled, an alert notification is still sent, but without the attachment.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Commons
|
||||
|
||||
@@ -76,27 +76,6 @@ value defined in `DATA_CACHE_CONFIG`.
|
||||
Note, that by setting the cache timeout to `-1`, caching for charting data can be disabled, either
|
||||
per chart, dataset or database, or by default if set in `DATA_CACHE_CONFIG`.
|
||||
|
||||
Native filter option queries (the dropdown values for native filters) go through this same
|
||||
chart-data cache, but their freshness needs often differ from regular chart queries, especially for
|
||||
datasets whose visible values change frequently, including RLS-constrained datasets. Set
|
||||
`NATIVE_FILTER_OPTIONS_CACHE_TIMEOUT` in `superset_config.py` to give these queries a dedicated
|
||||
timeout, checked before the chart/dataset/database chain and the `DATA_CACHE_CONFIG` default above:
|
||||
|
||||
```python
|
||||
NATIVE_FILTER_OPTIONS_CACHE_TIMEOUT = 60 # seconds
|
||||
```
|
||||
|
||||
- `None` (default): native filter option queries fall through to the normal
|
||||
chart/dataset/database/`DATA_CACHE_CONFIG` resolution chain.
|
||||
- `-1`: disables caching for native filter option queries entirely.
|
||||
- `0`: passed directly to the cache backend; behavior is backend-specific, so use `-1` if the intent
|
||||
is to disable caching.
|
||||
- A positive integer: cache native filter option queries for that many seconds.
|
||||
|
||||
This setting only applies to requests detected as native filter option queries. It takes precedence
|
||||
over the per-chart/dataset/database timeouts, but not over an explicit per-request
|
||||
`custom_cache_timeout` override (e.g. "Force refresh").
|
||||
|
||||
## Limiting Cached Result Size
|
||||
|
||||
Very large chart or SQL query results can flood the cache backend (Redis/Memcached), evicting many
|
||||
|
||||
@@ -83,26 +83,6 @@ The optional username flag **-u** sets the user used for the datasource import.
|
||||
superset import_datasources -p <path / filename> -u 'admin'
|
||||
```
|
||||
|
||||
## Importing a Directory of Assets
|
||||
|
||||
The `import_directory` command imports a directory of exported assets (databases, datasets,
|
||||
dashboards, charts) in the same layout produced by the ZIP-based export. Saved queries and tags
|
||||
included in a full export are not imported by this command:
|
||||
|
||||
```bash
|
||||
superset import_directory <path / directory>
|
||||
```
|
||||
|
||||
As with `import_datasources`, the optional username flag **-u** sets the user assigned as the
|
||||
owner of the imported assets. The default is 'admin'. Example:
|
||||
|
||||
```bash
|
||||
superset import_directory <path / directory> -u 'admin'
|
||||
```
|
||||
|
||||
If the specified user does not exist, the command fails immediately with an error rather than
|
||||
importing the assets without an owner.
|
||||
|
||||
## Dashboard Import Overwrite Behavior
|
||||
|
||||
When importing a dashboard ZIP with the **overwrite** option enabled, any existing charts that are part of the dashboard are **replaced** rather than duplicated. This applies to:
|
||||
|
||||
@@ -505,8 +505,6 @@ All MCP settings go in `superset_config.py`. Defaults are defined in `superset/m
|
||||
| `MCP_DEV_USERNAME` | -- | Superset username for development mode (no auth) |
|
||||
| `MCP_RBAC_ENABLED` | `True` | Enforce Superset's role-based access control on MCP tool calls. When `True`, each tool checks that the authenticated user has the required FAB permission before executing. Disable only for testing or trusted-network deployments. |
|
||||
| `MCP_DISABLED_TOOLS` | `set()` | Set of tool names to remove from the MCP server at startup. Disabled tools are never advertised to AI clients during tool discovery. Useful when a custom extension tool should replace a built-in Superset tool. See [Disabling built-in tools](#disabling-built-in-tools). |
|
||||
| `MCP_DISABLED_CHART_PLUGINS` | `frozenset()` | Set of chart type plugin names (e.g. `"handlebars"`) to hide from `generate_chart`. Does not affect `get_chart_type_schema`. See [Disabling chart type plugins](#disabling-chart-type-plugins). |
|
||||
| `MCP_CHART_PLUGIN_ENABLED_FUNC` | `None` | Callable `(chart_type: str) -> bool` evaluated per registry lookup for dynamic enable/disable decisions. Takes precedence over `MCP_DISABLED_CHART_PLUGINS` when set. See [Disabling chart type plugins](#disabling-chart-type-plugins). |
|
||||
|
||||
### Authentication
|
||||
|
||||
@@ -578,7 +576,7 @@ MCP_CACHE_CONFIG = {
|
||||
| Key | Default | Description |
|
||||
| -------------------- | --------- | ----------------------------------------------------------- |
|
||||
| `enabled` | `False` | Enable response caching |
|
||||
| `CACHE_KEY_PREFIX` | `None` | Base prefix for shared Redis; Superset appends an internal response-contract namespace |
|
||||
| `CACHE_KEY_PREFIX` | `None` | Optional prefix for cache keys (useful for shared Redis) |
|
||||
| `list_tools_ttl` | `300` | Cache TTL in seconds for `tools/list` |
|
||||
| `list_resources_ttl` | `300` | Cache TTL for `resources/list` |
|
||||
| `list_prompts_ttl` | `300` | Cache TTL for `prompts/list` |
|
||||
@@ -720,34 +718,6 @@ Every MCP request passes through a middleware stack before reaching the tool fun
|
||||
|
||||
Additional middleware classes (`RateLimitMiddleware`, `FieldPermissionsMiddleware`, `PrivateToolMiddleware`) are implemented in `superset/mcp_service/middleware.py` but are not added to the default pipeline. They are available for operators who want to layer them in via a custom startup path.
|
||||
|
||||
### Tool Result Value Contract
|
||||
|
||||
Structured tool results preserve Superset domain values exactly. In particular,
|
||||
string fields are not wrapped in trust delimiters, and text that resembles a
|
||||
delimiter is returned as literal application data. This lets clients safely use a
|
||||
read result as the basis for an update without persisting presentation markup.
|
||||
|
||||
All tool-result content should still be treated as user-controlled data with no
|
||||
instruction authority. MCP clients should communicate that trust boundary through
|
||||
their model instructions or presentation layer, outside the returned field values;
|
||||
fixed or generated marker strings inside a value are ambiguous and must not be used
|
||||
as a trust signal.
|
||||
|
||||
For compatibility, clients that supported the former
|
||||
`<UNTRUSTED-CONTENT>` convention should stop recognizing or stripping those strings.
|
||||
The response schemas and content types have not changed. Because marker-looking text
|
||||
can be legitimate application data, a client cannot reliably distinguish a legacy
|
||||
decorated response from a clean one. Pause automated read-modify-write workflows, or
|
||||
route them only to upgraded instances, until every serving instance is upgraded.
|
||||
|
||||
Redis-backed MCP response caches include an internal response-contract namespace, so
|
||||
an upgraded instance does not reuse responses cached by an older release. Older
|
||||
instances can still return legacy values while they remain in service. After the
|
||||
upgrade, review previously written values for wrapper text and both
|
||||
`[ESCAPED-UNTRUSTED-CONTENT-OPEN]` and
|
||||
`[ESCAPED-UNTRUSTED-CONTENT-CLOSE]`; do not remove these strings automatically,
|
||||
because they may be intentional content.
|
||||
|
||||
### Error Sanitization
|
||||
|
||||
The `GlobalErrorHandlerMiddleware` automatically redacts sensitive information from all error messages before they reach the LLM client. The following are replaced with generic messages:
|
||||
@@ -782,20 +752,11 @@ For a 3-pod Kubernetes deployment with the defaults above, expect up to 3 × (5
|
||||
Enable response caching for read-heavy workloads (dashboards/datasets that don't change frequently). With the in-memory backend (default when `MCP_STORE_CONFIG` is disabled), caching is per-process. Use Redis-backed caching for consistent cache hits across multiple pods:
|
||||
|
||||
```python
|
||||
MCP_CACHE_CONFIG = {
|
||||
"enabled": True,
|
||||
# Cache keys don't include the requesting principal and hits are served
|
||||
# ahead of auth/RBAC, so a shared cache can return one caller's response
|
||||
# to another. Required for caching to actually start -- only appropriate
|
||||
# when every request is guaranteed to come from the same principal.
|
||||
"dangerously_share_cache_across_principals": True,
|
||||
"CACHE_KEY_PREFIX": "mcp_cache_",
|
||||
"call_tool_ttl": 3600,
|
||||
}
|
||||
MCP_CACHE_CONFIG = {"enabled": True, "call_tool_ttl": 3600}
|
||||
MCP_STORE_CONFIG = {"enabled": True, "CACHE_REDIS_URL": "redis://redis:6379/0"}
|
||||
```
|
||||
|
||||
Every tool with a side effect (create/update/delete/execute) is always excluded from caching regardless of this setting -- see the `excluded_tools` default in `superset/mcp_service/mcp_config.py` for the current list.
|
||||
Mutating tools (`generate_chart`, `update_chart`, `execute_sql`, `generate_dashboard`) are always excluded from caching regardless of this setting.
|
||||
|
||||
---
|
||||
|
||||
@@ -895,39 +856,6 @@ MCP_DISABLED_TOOLS = {"extensions.myorg.myextension.some_tool"}
|
||||
Specifying a tool name that does not exist logs a warning at startup and is otherwise ignored — it will not prevent the server from starting.
|
||||
:::
|
||||
|
||||
## Disabling chart type plugins
|
||||
|
||||
The `generate_chart` tool dispatches per chart type (`xy`, `table`, `pie`, `pivot_table`, `mixed_timeseries`, `handlebars`, `big_number`, `histogram`, `box_plot`, `waterfall`) to a registered chart type plugin. Two settings let operators enable or disable individual chart type plugins at runtime, without a code deploy.
|
||||
|
||||
### Static deny-list
|
||||
|
||||
Set `MCP_DISABLED_CHART_PLUGINS` in your `superset_config.py` to a set of chart type names:
|
||||
|
||||
```python
|
||||
# superset_config.py
|
||||
|
||||
# Emergency kill switch: hide "handlebars" from all callers
|
||||
MCP_DISABLED_CHART_PLUGINS = {"handlebars"}
|
||||
```
|
||||
|
||||
Disabled chart types stay registered but are filtered out at lookup time: they're never listed in `generate_chart`'s supported chart types, and `generate_chart` calls for them are rejected. `get_chart_type_schema` consults its own static schema/example map rather than the registry filter, so a disabled chart type's schema remains queryable through that tool even though `generate_chart` will reject it.
|
||||
|
||||
### Dynamic predicate
|
||||
|
||||
For per-request control (A/B tests, gradual rollout, entitlement checks), set `MCP_CHART_PLUGIN_ENABLED_FUNC` to a callable. It's evaluated as `enabled_func(chart_type: str) -> bool` on every registry lookup, and it takes precedence over `MCP_DISABLED_CHART_PLUGINS` when set:
|
||||
|
||||
```python
|
||||
# superset_config.py
|
||||
from flask import g
|
||||
|
||||
|
||||
def MCP_CHART_PLUGIN_ENABLED_FUNC(chart_type: str) -> bool:
|
||||
flags = getattr(g, "feature_flags", {})
|
||||
return flags.get(f"mcp_chart_{chart_type}", True)
|
||||
```
|
||||
|
||||
The callable must be cheap and in-process (consult already-loaded feature flags or request-local context) -- do not perform network I/O per call. If it raises, the registry fails closed (the plugin is hidden) and logs a warning.
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
- **Use TLS** for all production MCP endpoints -- place the server behind a reverse proxy with HTTPS
|
||||
|
||||
@@ -88,27 +88,6 @@ THEME_DARK = {
|
||||
# - OS preference detection is automatically enabled
|
||||
```
|
||||
|
||||
### Default Theme Mode
|
||||
|
||||
By default, Superset mimics the visitor's OS/browser preference (light or dark) for
|
||||
sessions that don't have a saved user preference. Use `THEME_DEFAULT_MODE` to override
|
||||
that starting point instance-wide for the standard application:
|
||||
|
||||
```python
|
||||
# Default theme mode for sessions without a saved user preference.
|
||||
# One of "default" (always light), "dark" (always dark), or "system" (mimic OS preference).
|
||||
THEME_DEFAULT_MODE = "dark"
|
||||
```
|
||||
|
||||
- `"system"` (the default) preserves the existing behavior of following the OS/browser
|
||||
preference, provided a dark theme is configured (`THEME_DARK` is not `None`). If no dark
|
||||
theme is available, Superset always starts in light mode regardless of this setting.
|
||||
- `"default"` or `"dark"` forces that starting mode for first-time visitors; users can still switch themes manually afterward if both `THEME_DEFAULT` and `THEME_DARK` are available.
|
||||
- A user's own saved preference, once they toggle the theme switcher, always takes precedence over `THEME_DEFAULT_MODE`.
|
||||
- `THEME_DEFAULT_MODE` has no effect on embedded dashboards: the embed SDK sets the
|
||||
starting mode via its own `themeMode` URL parameter, which takes precedence and falls
|
||||
back to light mode when the parameter is absent.
|
||||
|
||||
### App Branding
|
||||
|
||||
The application name shown in the browser title bar and navigation can be
|
||||
|
||||
@@ -243,42 +243,6 @@ Each subject in the response includes flat scalar ids (`user_id`, `role_id`, `gr
|
||||
than a nested object, so callers can match directly on whichever id they already have — only the
|
||||
id field matching the subject's `type` is populated; the others are `null`.
|
||||
|
||||
#### Extending Editorship with EXTRA_EDITORS_RESOLVER
|
||||
|
||||
Deployments that grant edit access to a dashboard or chart through a mechanism outside
|
||||
Superset's own Subject-based `editors` list — for example, a folder-permission system or an
|
||||
internal directory service — can plug that logic in with `EXTRA_EDITORS_RESOLVER`:
|
||||
|
||||
```python
|
||||
def extra_editors_resolver(resource):
|
||||
# `resource` is the Dashboard or Slice instance being checked.
|
||||
# Return Subject instances, raw subject ids, or dicts with an `id` key.
|
||||
return [...]
|
||||
|
||||
|
||||
EXTRA_EDITORS_RESOLVER = extra_editors_resolver
|
||||
```
|
||||
|
||||
The resolver's result is unioned with the resource's own `editors` for editorship checks: it
|
||||
feeds `is_editor`, `raise_for_editorship`, save-as, and soft-delete restore. When
|
||||
`EXTRA_EDITORS_RESOLVER` is configured, the usual lockout-prevention behavior — automatically
|
||||
re-adding a non-admin who removes themselves from a resource's editors — is skipped, since the
|
||||
deployment has its own way of keeping the resource editable. This skip is global to the setting,
|
||||
not per-resource: it still applies on a resource where the resolver currently returns no
|
||||
subjects, so a resolver that can't guarantee an alternate editor for every resource can let the
|
||||
last editor remove themselves and leave it uneditable by non-admins.
|
||||
|
||||
Resolved subject ids are also surfaced as `extra_editors` in the chart and dashboard `GET`
|
||||
responses, so API clients can distinguish resolver-granted editorship from the resource's own
|
||||
`editors` list. This field is attached after serialization and isn't part of the OpenAPI response
|
||||
schema, so generated API clients won't see it as a typed field.
|
||||
|
||||
Because the resolver is arbitrary per-deployment Python rather than a SQL-expressible condition,
|
||||
editorship it grants cannot be reflected in list-view filtering (for example, the soft-deleted
|
||||
archive is scoped to editors via a SQL query). It does still run once per row on chart and
|
||||
dashboard list responses to populate `extra_editors`, so a slow or unavailable external resolver
|
||||
affects ordinary list requests, not just direct per-object checks.
|
||||
|
||||
### Dashboard Access Control
|
||||
|
||||
Access to dashboards is managed via editors (subjects that have edit permissions to the dashboard).
|
||||
@@ -619,28 +583,6 @@ SELECT * FROM (
|
||||
queries run against tables that have associated datasets with RLS filters will then have
|
||||
the appropriate predicates injected automatically.
|
||||
|
||||
#### RLS Indicator in the Dataset List and Explore
|
||||
|
||||
When a dataset has one or more RLS filters that apply to it, Superset shows a lock
|
||||
icon badge next to the dataset name in the **Datasets** list and next to the dataset
|
||||
selector in **Explore**. Hovering over the badge shows a tooltip listing each
|
||||
applicable filter's name, filter type (Regular or Base), group key (if any), assigned
|
||||
subjects (labeled "Roles" in the tooltip, but may include users and groups too), and
|
||||
clause.
|
||||
|
||||
This badge also surfaces filters that are inherited from the physical tables
|
||||
referenced by a virtual (SQL-based) dataset, as described above. Inherited filters
|
||||
are marked "from underlying table" in the tooltip, and a summary note is shown
|
||||
whenever any of the listed filters are inherited rather than assigned directly to
|
||||
the dataset. Inherited-filter detection depends on Superset's SQL parser being able
|
||||
to identify the referenced tables and match them to a physical dataset by name,
|
||||
schema, and database, so it's best-effort: unparseable or unmatched references won't
|
||||
surface a filter on the badge even if one would apply at query time.
|
||||
|
||||
The badge is a visibility aid only — it does not change which filters are applied to
|
||||
a query. Use the RLS REST API described below if you need to confirm exactly which
|
||||
filters affect a dataset.
|
||||
|
||||
#### Checking RLS Filters via the API
|
||||
|
||||
You can use the RLS REST API to audit which filters are configured and which datasets
|
||||
|
||||
@@ -86,7 +86,6 @@
|
||||
"Israel",
|
||||
"Italy",
|
||||
"Italy (regions)",
|
||||
"Italy (regions and autonomous provinces)",
|
||||
"Ivory Coast",
|
||||
"Japan",
|
||||
"Jordan",
|
||||
|
||||
@@ -493,8 +493,8 @@ Frontend assets (TypeScript, JavaScript, CSS, and images) must be compiled in or
|
||||
|
||||
First, be sure you are using the following versions of Node.js and npm:
|
||||
|
||||
- `Node.js`: Version 24 (see `superset-frontend/.nvmrc` for the exact version)
|
||||
- `npm`: Version 11
|
||||
- `Node.js`: Version 22 (LTS)
|
||||
- `npm`: Version 10
|
||||
|
||||
We recommend using [nvm](https://github.com/nvm-sh/nvm) to manage your node environment:
|
||||
|
||||
@@ -507,8 +507,8 @@ export NVM_DIR="$HOME/.nvm"
|
||||
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
|
||||
|
||||
cd superset-frontend
|
||||
nvm install
|
||||
nvm use
|
||||
nvm install --lts
|
||||
nvm use --lts
|
||||
```
|
||||
|
||||
Or if you use the default macOS starting with Catalina shell `zsh`, try:
|
||||
|
||||
@@ -129,6 +129,27 @@ chat.registerChat(
|
||||
|
||||
See [Chat](./extension-points/chat.md) for implementation details.
|
||||
|
||||
### Dashboard Components
|
||||
|
||||
Extensions can add first-class layout components to the dashboard builder — elements that live in the grid alongside charts, Markdown, and tabs. The host owns the drag/resize/delete chrome, so the extension only provides the component that renders the element's content. The built-in iframe component is implemented through this contribution point.
|
||||
|
||||
```tsx
|
||||
import { dashboardComponents } from '@apache-superset/core';
|
||||
import WeatherWidget from './WeatherWidget';
|
||||
|
||||
dashboardComponents.registerDashboardComponent(
|
||||
{
|
||||
id: 'my-org.weather',
|
||||
name: 'Weather widget',
|
||||
icon: 'CloudOutlined',
|
||||
defaultMeta: { width: 4, height: 50 },
|
||||
},
|
||||
WeatherWidget,
|
||||
);
|
||||
```
|
||||
|
||||
See [Dashboard Components](./extension-points/dashboard-components.md) for implementation details.
|
||||
|
||||
## Backend
|
||||
|
||||
Backend contribution types allow extensions to extend Superset's server-side capabilities. Backend contributions are registered at startup via classes and functions imported from the auto-discovered `entrypoint.py` file.
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
---
|
||||
title: Dashboard Components
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
# Dashboard Component Contributions
|
||||
|
||||
Extensions can add first-class **layout components** to the dashboard builder —
|
||||
elements that sit in the grid alongside charts, Markdown, and tabs. The built-in
|
||||
iframe component is itself implemented through this contribution point.
|
||||
|
||||
The host owns the surrounding **chrome** (the drag handle, the resize container,
|
||||
and the delete affordance), so your component only renders its content and, in
|
||||
edit mode, its own editor affordances. This keeps the contract small and stable.
|
||||
|
||||
> This supersedes the legacy `DashboardComponentsRegistry` / `DYNAMIC_TYPE`
|
||||
> mechanism, which is deprecated.
|
||||
|
||||
## Overview
|
||||
|
||||
A dashboard component contribution is:
|
||||
|
||||
| Part | Role |
|
||||
|------|------|
|
||||
| **Definition** | A descriptor declaring the component's id, palette label, icon, and layout behavior (resizable, default size, nesting). |
|
||||
| **Component** | A React component that renders the element's content and receives the [`DashboardComponentProps`](#component-contract) contract. |
|
||||
|
||||
## The Component Contract
|
||||
|
||||
Your component receives a small, stable set of props. It never deals with drag,
|
||||
resize, or delete — the host renders it inside that chrome.
|
||||
|
||||
```ts
|
||||
interface DashboardComponentProps {
|
||||
/** The layout item id of this instance. */
|
||||
id: string;
|
||||
/** This instance's persisted meta (round-trips in the saved layout). */
|
||||
meta: Record<string, unknown>;
|
||||
/** Whether the dashboard is in edit mode. */
|
||||
editMode: boolean;
|
||||
/** Shallow-merge a patch into this instance's persisted meta. */
|
||||
updateMeta: (patch: Record<string, unknown>) => void;
|
||||
}
|
||||
```
|
||||
|
||||
Persist any per-instance state in `meta` via `updateMeta`. It is saved with the
|
||||
dashboard and rehydrated on load.
|
||||
|
||||
## Registering a Dashboard Component
|
||||
|
||||
Call `dashboardComponents.registerDashboardComponent` from your extension's entry
|
||||
point with a definition and your component:
|
||||
|
||||
```tsx
|
||||
import { dashboardComponents } from '@apache-superset/core';
|
||||
import WeatherWidget from './WeatherWidget';
|
||||
|
||||
dashboardComponents.registerDashboardComponent(
|
||||
{
|
||||
id: 'my-org.weather',
|
||||
name: 'Weather widget',
|
||||
description: 'Shows the current weather for a city',
|
||||
icon: 'CloudOutlined',
|
||||
resizable: true,
|
||||
defaultMeta: { width: 4, height: 50, city: 'Lisbon' },
|
||||
},
|
||||
WeatherWidget,
|
||||
);
|
||||
```
|
||||
|
||||
```tsx
|
||||
// WeatherWidget.tsx
|
||||
import type { dashboardComponents } from '@apache-superset/core';
|
||||
|
||||
type Props = dashboardComponents.DashboardComponentProps;
|
||||
|
||||
export default function WeatherWidget({ meta, editMode, updateMeta }: Props) {
|
||||
const city = (meta.city as string) ?? '';
|
||||
return editMode ? (
|
||||
<input
|
||||
value={city}
|
||||
onChange={e => updateMeta({ city: e.target.value })}
|
||||
placeholder="City"
|
||||
/>
|
||||
) : (
|
||||
<Forecast city={city} />
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
The component appears in the dashboard builder's **Layout elements** palette and
|
||||
can be dragged onto the grid like any built-in element.
|
||||
|
||||
## Definition Reference
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | `string` | Namespaced unique id, e.g. `my-org.weather`. Selects the component for each instance. |
|
||||
| `name` | `string` | Label shown in the builder palette. |
|
||||
| `description` | `string` | Optional longer description. |
|
||||
| `icon` | `string` | A known Superset icon name (e.g. `CloudOutlined`). Falls back to a generic icon. |
|
||||
| `resizable` | `boolean` | Whether instances can be resized. Defaults to `true`. |
|
||||
| `defaultMeta` | `object` | `meta` seeded onto a new instance (e.g. `width`, `height`, and your own keys). |
|
||||
| `isUserContent` | `boolean` | Whether an instance counts as content for "is this dashboard empty?" detection. Defaults to `true`. |
|
||||
| `minWidth` | `number` | Minimum width in grid columns. Defaults to `1`. |
|
||||
| `validParents` | `string[]` | Restrict which container types may hold the component (e.g. `['GRID', 'TAB']`). Defaults to standard content-leaf placement (grid, row, column, tab). |
|
||||
| `wrapInRow` | `boolean` | Whether a drop into the grid or a tab auto-wraps the component in a row. Defaults to `true`. |
|
||||
|
||||
The layout-relevant behavior fields are seeded onto each instance's `meta` at
|
||||
creation, so the dashboard honors them — and they round-trip in the saved layout
|
||||
even if the extension later becomes unavailable.
|
||||
|
||||
## Graceful Degradation
|
||||
|
||||
If a saved dashboard references a component whose extension is disabled or not
|
||||
yet loaded, the host renders a non-destructive placeholder in its place and
|
||||
preserves the instance's `meta` on save. Re-enabling the extension restores the
|
||||
component.
|
||||
|
||||
## Dashboard Components API Reference
|
||||
|
||||
All methods are available on the `dashboardComponents` namespace from
|
||||
`@apache-superset/core`:
|
||||
|
||||
| Method / Event | Description |
|
||||
|----------------|-------------|
|
||||
| `registerDashboardComponent(definition, component)` | Register a component. Returns a `Disposable` to unregister. Registering the same id again replaces the previous registration. |
|
||||
| `getDashboardComponent(id)` | Returns the registered component for `id`, or `undefined`. |
|
||||
| `getDashboardComponents()` | Returns all registered components. |
|
||||
| `onDidRegisterDashboardComponent(listener)` | Subscribe to registration events. Returns a `Disposable`. |
|
||||
| `onDidUnregisterDashboardComponent(listener)` | Subscribe to unregistration events. Returns a `Disposable`. |
|
||||
|
||||
## Next Steps
|
||||
|
||||
- **[Contribution Types](../contribution-types.md)** — Explore other contribution types
|
||||
- **[Development](../development.md)** — Set up your development environment
|
||||
@@ -49,6 +49,7 @@ module.exports = {
|
||||
'extensions/extension-points/sqllab',
|
||||
'extensions/extension-points/editors',
|
||||
'extensions/extension-points/chat',
|
||||
'extensions/extension-points/dashboard-components',
|
||||
],
|
||||
},
|
||||
'extensions/development',
|
||||
|
||||
@@ -406,18 +406,6 @@ ECharts option overrides bypass Superset's validation layer. Invalid option keys
|
||||
|
||||
When the **Search Box** is visible in a Table chart, the **Download** action exports only the rows currently visible after the search filter is applied — not the full underlying dataset. This matches the visual output and is intentional. To export the full dataset regardless of search state, use the **Download as CSV** option from the chart's three-dot menu in the dashboard or from the Explore chart toolbar before applying a search filter.
|
||||
|
||||
### Exporting a Chart as an Image or PDF
|
||||
|
||||
Alongside the raw-data export options (CSV, JSON, Excel), a chart's three-dot menu — in a dashboard or from the Explore chart toolbar — offers a few ways to export a visual snapshot of the chart:
|
||||
|
||||
- **Export screenshot (jpeg)** — a single-click JPEG screenshot of the chart.
|
||||
- **Export screenshot (png)** — opens a submenu with **Transparent background** and **Solid background** options. The solid option uses the current theme's background color. PNG produces a higher-quality image than the JPEG export.
|
||||
- **Export as PDF** — downloads the chart as a PDF file.
|
||||
|
||||
The dropdown menu is briefly hidden while the screenshot or PDF is being captured so it doesn't appear in the exported file. In Explore, these image and PDF options are available from the **Export All Data** submenu, and also from the **Export current view** submenu when the chart type supports current-view export.
|
||||
|
||||
These menu items respect your permissions: the dashboard export menu only appears if you can download, and the image/PDF options are disabled if you lack image-export permission.
|
||||
|
||||
### Sharing a Specific Tab
|
||||
|
||||
When a dashboard has tabs, each tab gets its own shareable URL. Navigate to the tab you want to share and copy the URL from your browser's address bar — the tab anchor is encoded in the URL so that anyone opening the link lands directly on that tab.
|
||||
|
||||
@@ -88,21 +88,6 @@ embedDashboard({
|
||||
|
||||
If the callback returns `null` or is not provided, Superset uses its own permalink URL as a fallback.
|
||||
|
||||
### Permalink origin rewriting
|
||||
|
||||
This rewrite only applies to the non-embedded permalink path — it has no effect on embedded dashboards. When Superset is not embedded, it rewrites the origin of any permalink URL it generates to `window.location.origin` before showing it to the user, which keeps a proxied or subdirectory-deployed Superset from handing out a permalink that points at an internal hostname the user's browser can't reach.
|
||||
|
||||
When Superset **is** embedded, this rewrite is skipped entirely regardless of the flag below: a `resolvePermalinkUrl` callback's return value is used as-is, and if no callback is provided (or it fails), the backend-supplied URL is also returned as-is.
|
||||
|
||||
If your reverse proxy correctly forwards `X-Forwarded-Host` and you'd rather non-embedded permalinks carry the backend's literal origin, opt out of the rewrite with `EMBEDDED_DISABLE_PERMALINK_ORIGIN_REWRITE`:
|
||||
|
||||
```python
|
||||
# superset_config.py
|
||||
EMBEDDED_DISABLE_PERMALINK_ORIGIN_REWRITE = True
|
||||
```
|
||||
|
||||
This defaults to `False` (rewrite enabled) and only affects non-embedded permalinks. Flipping the default would regress the common proxied/subdirectory deployment by exposing an unreachable internal host in copied permalinks.
|
||||
|
||||
---
|
||||
|
||||
## Feature Flags for Embedded Mode
|
||||
|
||||
@@ -11,11 +11,6 @@ When soft-delete is enabled, deleting a chart, dashboard, or dataset archives it
|
||||
instead of removing it permanently. The **Recently Archived** view lets owners
|
||||
and admins find archived objects and restore them.
|
||||
|
||||
A chart used by an alert or report cannot be archived while that dependency
|
||||
exists. In the chart list view, the archive confirmation lists the alerts and
|
||||
reports that use the chart; a blocked attempt names them and asks you to
|
||||
detach or delete them first.
|
||||
|
||||
:::note
|
||||
|
||||
This view is gated by the `SOFT_DELETE` feature flag. When the flag is off the
|
||||
@@ -26,8 +21,7 @@ page and its menu entry are hidden, and deletes are permanent as before.
|
||||
## Finding archived objects
|
||||
|
||||
Open **Recently Archived** and pick a type — **Chart**, **Dashboard**, or
|
||||
**Dataset** (shown as **Datasource** when semantic layers are enabled) — from
|
||||
the Type selector. The view shows one type at a time; each
|
||||
**Dataset** — from the Type selector. The view shows one type at a time; each
|
||||
type is read from its own list endpoint, so the same row-level access rules that
|
||||
govern the normal lists apply here.
|
||||
|
||||
|
||||
@@ -248,12 +248,11 @@ Ask your admin for the MCP server URL and any authentication tokens you need.
|
||||
|
||||
### Datasets
|
||||
|
||||
| Tool | Description |
|
||||
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `list_datasets` | List datasets with filtering and search |
|
||||
| `get_dataset_info` | Get dataset metadata (columns, metrics, schema details) |
|
||||
| `create_virtual_dataset` | Create a virtual dataset from a SQL query |
|
||||
| `update_dataset_metric` | Update a saved metric's expression, name, verbose_name, or format (affects every chart using it; requires dataset ownership) |
|
||||
| Tool | Description |
|
||||
| ------------------------ | ------------------------------------------------ |
|
||||
| `list_datasets` | List datasets with filtering and search |
|
||||
| `get_dataset_info` | Get dataset metadata (columns, metrics, filters) |
|
||||
| `create_virtual_dataset` | Create a virtual dataset from a SQL query |
|
||||
|
||||
### Charts
|
||||
|
||||
|
||||
@@ -15,29 +15,29 @@ description of what changed — "Chart renamed to Q3 Revenue", "Added filter on
|
||||
'Region'" — rather than a raw diff. You can search the history and filter it
|
||||
down to changes on the entity itself or on the things it depends on.
|
||||
|
||||
## Enabling and disabling it
|
||||
## Enabling it
|
||||
|
||||
Two switches are involved, and both matter.
|
||||
|
||||
| Setting | Type | Effect |
|
||||
| --- | --- | --- |
|
||||
| `VERSION_HISTORY` | Feature flag | Shows the version history UI |
|
||||
| `ENABLE_VERSIONING_CAPTURE` | Config value | Records versions as entities are saved |
|
||||
|
||||
Both default to on. To turn the feature off:
|
||||
|
||||
```python
|
||||
# superset_config.py
|
||||
FEATURE_FLAGS = {"VERSION_HISTORY": False}
|
||||
ENABLE_VERSIONING_CAPTURE = False
|
||||
FEATURE_FLAGS = {"VERSION_HISTORY": True}
|
||||
ENABLE_VERSIONING_CAPTURE = True
|
||||
```
|
||||
|
||||
Restart Superset and its workers for the capture change to take effect. Existing
|
||||
history remains readable while capture is off, but **Restore** is unavailable
|
||||
(404).
|
||||
Both default to off. They are separate because capture is the expensive half:
|
||||
an operator may want to start recording history before exposing the UI, so that
|
||||
there is something to show when they do.
|
||||
|
||||
Disable them together: capture off with the UI left on gives a panel that
|
||||
stops filling — an empty or stale history misrepresents the entity as
|
||||
unchanged. History only accrues while capture is on; edits made while it was
|
||||
off are not reconstructed.
|
||||
Turning the UI on without capture gives a panel that reports "No history yet"
|
||||
and never fills, so enable capture first — or at the same time. History only
|
||||
accrues from the moment capture is switched on; earlier edits are not
|
||||
reconstructed.
|
||||
|
||||
## Viewing history
|
||||
|
||||
|
||||
+10
-10
@@ -58,15 +58,15 @@
|
||||
"@fontsource/inter": "^5.3.0",
|
||||
"@mdx-js/react": "^3.1.1",
|
||||
"@saucelabs/theme-github-codeblock": "^0.3.0",
|
||||
"@storybook/addon-docs": "^10.5.10",
|
||||
"@storybook/addon-docs": "^10.5.7",
|
||||
"@superset-ui/core": "^0.20.4",
|
||||
"@swc/core": "^1.16.1",
|
||||
"antd": "^6.6.1",
|
||||
"baseline-browser-mapping": "^2.11.17",
|
||||
"@swc/core": "^1.15.47",
|
||||
"antd": "^6.6.0",
|
||||
"baseline-browser-mapping": "^2.11.13",
|
||||
"caniuse-lite": "^1.0.30001809",
|
||||
"docusaurus-plugin-openapi-docs": "^5.2.0",
|
||||
"docusaurus-theme-openapi-docs": "^5.2.0",
|
||||
"js-yaml": "^5.3.0",
|
||||
"js-yaml": "^5.2.3",
|
||||
"json-bigint": "^1.0.0",
|
||||
"prism-react-renderer": "^2.4.1",
|
||||
"react": "^18.3.1",
|
||||
@@ -76,9 +76,9 @@
|
||||
"react-svg-pan-zoom": "^3.13.1",
|
||||
"react-table": "^7.8.0",
|
||||
"remark-import-partial": "^0.0.2",
|
||||
"reselect": "^5.3.0",
|
||||
"storybook": "^10.5.10",
|
||||
"swagger-ui-react": "^5.32.14",
|
||||
"reselect": "^5.2.0",
|
||||
"storybook": "^10.5.7",
|
||||
"swagger-ui-react": "^5.32.13",
|
||||
"swc-loader": "^0.2.7",
|
||||
"tinycolor2": "^1.4.2",
|
||||
"unist-util-visit": "^5.1.0"
|
||||
@@ -93,8 +93,8 @@
|
||||
"@typescript-eslint/parser": "^8.67.0",
|
||||
"eslint": "^9.39.2",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"globals": "^17.11.0",
|
||||
"oxfmt": "^0.64.0",
|
||||
"globals": "^17.10.0",
|
||||
"oxfmt": "^0.63.0",
|
||||
"typescript": "~6.0.3",
|
||||
"typescript-eslint": "^8.67.0",
|
||||
"webpack": "^5.109.2"
|
||||
|
||||
@@ -1,236 +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.
|
||||
-->
|
||||
|
||||
# SIP: System-wide MEDIAN, Sample Standard Deviation, and Sample Variance metric aggregates
|
||||
|
||||
## [DRAFT — proposal for discussion]
|
||||
|
||||
This document now has an accompanying implementation in this PR, for the
|
||||
proposed mechanism plus a first, empirically-verified engine set (Postgres,
|
||||
MySQL, DuckDB, Redshift by inheritance). It is intended to seed discussion on
|
||||
whether this is the right shape and scope before it goes up for a formal SIP
|
||||
vote, not to pre-empt that discussion, the code exists so reviewers have a
|
||||
concrete design to react to rather than a description of one.
|
||||
|
||||
## Motivation
|
||||
|
||||
Before #41184 (SIP-216, the non-additive-totals fix), the Pivot Table chart
|
||||
exposed an "Aggregation function" control with 18 choices, including
|
||||
`Median`, `Sample Standard Deviation`, `Sample Variance`, `First`, `Last`,
|
||||
`Count Unique Values`, and `List Unique Values`. #41184 deleted that control
|
||||
wholesale, and deliberately so: it re-aggregated already-aggregated cell
|
||||
values to compute totals/subtotals, which is exactly the class of bug
|
||||
SIP-216 fixed (summing per-group averages, averaging per-group medians, etc.
|
||||
produces silently wrong totals). #42761 subsequently restored the one piece
|
||||
of that control's functionality that was cleanly separable from the
|
||||
correctness bug, the "show as % of row/column/total" display option,
|
||||
redesigned as a decoupled, post-hoc-only `showValuesAs` control.
|
||||
|
||||
A user has since noticed that several of the other pre-#41184 options never
|
||||
came back. Checking today's metric aggregate list (`AVG, COUNT,
|
||||
COUNT_DISTINCT, MAX, MIN, SUM`, see
|
||||
`superset-frontend/packages/superset-ui-core/src/query/types/Metric.ts`),
|
||||
most of these have a reasonable equivalent already: `Count Unique Values`
|
||||
maps to `COUNT_DISTINCT`; `Count`/`Average`/`Max`/`Min` are already standard
|
||||
aggregates; the two "fraction of" variants are already covered by
|
||||
`showValuesAs`. But `Median`, `Sample Standard Deviation`, and `Sample
|
||||
Variance` have no equivalent today anywhere in Superset, not just in Pivot
|
||||
Table, in any chart type, since the aggregate list is shared across the
|
||||
whole app.
|
||||
|
||||
This is a real, currently-live gap, not a hypothetical one:
|
||||
`superset/mcp_service/chart/chart_utils.py`, `schemas.py`, and
|
||||
`prompts/create_chart_guided.py` already treat `STDDEV`, `VAR`, `MEDIAN`,
|
||||
and `PERCENTILE` as valid aggregate values in their own validation and
|
||||
documentation, but those values are never recognized by
|
||||
`superset/connectors/sqla/models.py`'s `sqla_aggregations` dict (the actual
|
||||
mapping from aggregate name to SQL), so an AI agent using the MCP tool to
|
||||
build a chart with `"aggregate": "STDDEV"` today creates a chart that
|
||||
**errors at query time** with "Adhoc metric aggregate is invalid." This SIP
|
||||
proposes closing that gap for real, at the source, rather than patching
|
||||
around it in MCP.
|
||||
|
||||
## Proposed change
|
||||
|
||||
Add `MEDIAN`, `STDDEV_SAMP`, and `VAR_SAMP` as first-class, system-wide
|
||||
metric aggregates, available anywhere a metric aggregate is chosen (every
|
||||
chart type, SQL Lab metric picker, MCP), not as a Pivot-Table-specific
|
||||
control.
|
||||
|
||||
**Why this is safe with respect to SIP-216, and needs no Pivot-Table-specific
|
||||
code at all:** Pivot Table's non-additive-totals machinery
|
||||
(`superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/utilities.ts`)
|
||||
already classifies any metric aggregate not in `ADDITIVE_AGGREGATES = {SUM,
|
||||
COUNT, MIN, MAX}` as non-additive, which routes totals/subtotals through the
|
||||
correct DB-`GROUPING SETS`-rollup path rather than client-side
|
||||
re-aggregation (`AVG` and `COUNT_DISTINCT` already go through this path
|
||||
today). `MEDIAN`/`STDDEV_SAMP`/`VAR_SAMP` fall into that bucket
|
||||
automatically, with zero changes needed to the additivity logic. So once
|
||||
these are valid, buildable SQL aggregates, Pivot Table (and every other
|
||||
chart) gets correct behavior for free. This is the version of "restore the
|
||||
control" that does not reopen the bug that was just fixed.
|
||||
|
||||
**Where the actual change needs to land, and what this PR does:**
|
||||
|
||||
1. **Done.** `superset-frontend/packages/superset-ui-core/src/query/types/Metric.ts`,
|
||||
extended the `Aggregate` type.
|
||||
2. **Done.** `superset-frontend/src/explore/constants.ts`, added to `AGGREGATES`
|
||||
(drives `AGGREGATES_OPTIONS`, the dropdown in `AdhocMetricEditPopover`).
|
||||
3. **Done**, but not consolidated. `superset/connectors/sqla/models.py`
|
||||
(`sqla_aggregations`) and `superset/models/helpers.py`
|
||||
(`ExploreMixin.sqla_aggregations`) are both wired to consult the new
|
||||
`BaseEngineSpec.get_extended_aggregation_func`, in addition to their
|
||||
existing 6-aggregate dict, so neither's original, already-tested behavior
|
||||
changed. They remain two separate dicts, consolidating them into one
|
||||
source of truth is left as a follow-up (see Open questions).
|
||||
4. **Done**, and it surfaced a second, smaller bug on top of the one this SIP
|
||||
opened with: MCP's own aggregate names (`STDDEV`, `VAR`) never matched any
|
||||
real Superset aggregate, before or after this PR, they were always going
|
||||
to error regardless of what this SIP does. `superset/mcp_service/chart/*`
|
||||
now accepts the old shorthand as an alias, normalized to the real,
|
||||
unambiguous names (`STDDEV_SAMP`, `VAR_SAMP`) this PR ships, and the guided
|
||||
prompt text points at the correct names going forward. `MEDIAN`/
|
||||
`PERCENTILE` were already spelled correctly in MCP; `PERCENTILE` remains
|
||||
unimplemented (it needs a parameter this schema has no field for) and is
|
||||
unchanged by this PR, out of scope here.
|
||||
|
||||
**The part that needs real engineering care, this must not be a blind
|
||||
`sa.func.MEDIAN` / `sa.func.STDDEV_SAMP` / `sa.func.VAR_SAMP`:**
|
||||
|
||||
`sqla_aggregations` today is a flat, engine-unaware dict (`sa.func.AVG`,
|
||||
etc., SQLAlchemy emits whatever function name it is given, with zero
|
||||
validation that the target dialect actually has it). Superset already has
|
||||
precedent for exactly this class of per-engine capability difference:
|
||||
`BaseEngineSpec.supports_grouping_sets` and `_time_grain_expressions`, both
|
||||
introduced by #41184 itself. This SIP proposes the same shape, a new
|
||||
per-engine-overridable mechanism (for example
|
||||
`BaseEngineSpec.get_aggregate_sql(aggregate, column)` with a sensible
|
||||
default, overridden per engine spec where the default does not hold),
|
||||
rather than a single hardcoded dict.
|
||||
|
||||
Verified findings so far (via `sqlglot.transpile`, cross-checked against
|
||||
known engine docs; **not** exhaustively tested against live databases, that
|
||||
is necessary follow-up work this SIP alone cannot complete):
|
||||
|
||||
| Engine | `MEDIAN(x)` | `STDDEV_SAMP(x)` | `VAR_SAMP(x)` |
|
||||
|---|---|---|---|
|
||||
| Postgres | `PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY x)` | native | native |
|
||||
| MySQL | no native equivalent, needs explicit "unsupported" handling, not a blind emit | native | MySQL's `VARIANCE()` is an alias for `VAR_POP` (population), not `VAR_SAMP` (sample); a naive dialect-name substitution would silently compute the wrong statistic and needs an explicit, verified expression instead |
|
||||
| SQLite | only if the specific build was compiled with the (non-default) `SQLITE_ENABLE_PERCENTILE` extension (added in SQLite 3.43, 2023), cannot be assumed available | not available in core SQLite | not available in core SQLite |
|
||||
| BigQuery / Snowflake / DuckDB / Redshift / Oracle / T-SQL / Databricks / Spark | native `MEDIAN(x)` | native | native on BigQuery/Snowflake/Databricks/Spark, where `VARIANCE` is correctly sample variance; T-SQL has no function named `VARIANCE` at all and needs `VAR(x)` instead |
|
||||
| Trino / Presto / Hive | `PERCENTILE_CONT` / `approx_percentile` (dialect- and exactness-dependent) | native | `variance` is correctly sample variance per Trino/Presto docs |
|
||||
|
||||
This table is deliberately not exhaustive, Superset has roughly 75
|
||||
`db_engine_specs` files. The proposed default (`BaseEngineSpec`) should be
|
||||
the safe choice (mark unsupported, surface a clear user-facing error) rather
|
||||
than an optimistic one, with individual engine specs opting in once
|
||||
verified. Ship for the handful of engines above first, extend
|
||||
opportunistically.
|
||||
|
||||
**`Count Unique Values`, `First`, `Last`, `List Unique Values`, explicitly
|
||||
out of scope for this SIP:**
|
||||
|
||||
- `Count Unique Values` needs no work, it is already `COUNT_DISTINCT`.
|
||||
- `First`/`Last` have no well-defined, unambiguous meaning as a plain
|
||||
`GROUP BY` aggregate without an explicit ordering; most engines only
|
||||
support this via window functions (`FIRST_VALUE`/`LAST_VALUE` `OVER
|
||||
(ORDER BY ...)`) or do not support it as a simple aggregate at all
|
||||
(Postgres has neither built in). Restoring this properly would mean
|
||||
designing an "order by" sub-control on the metric, a real, separate
|
||||
feature, not a one-line aggregate addition. Proposed as a follow-up SIP if
|
||||
there is demand.
|
||||
- `List Unique Values` maps to the `STRING_AGG`/`GROUP_CONCAT`/`LISTAGG`/
|
||||
`ARRAY_AGG(DISTINCT ...)` family, real dialect differences, plus an open
|
||||
UX question (unbounded cell content for high-cardinality columns).
|
||||
Proposed as a follow-up SIP.
|
||||
|
||||
## New or changed public interfaces
|
||||
|
||||
- New `Aggregate` values (`MEDIAN`, `STDDEV_SAMP`, `VAR_SAMP`) selectable
|
||||
anywhere the standard metric control appears, every chart type, not just
|
||||
Pivot Table.
|
||||
- New `BaseEngineSpec` extensibility point for per-engine aggregate SQL
|
||||
generation (exact shape TBD in implementation, likely mirrors
|
||||
`_time_grain_expressions`).
|
||||
- No REST API surface changes beyond the existing metric aggregate field
|
||||
accepting new values.
|
||||
|
||||
## Migration plan and compatibility
|
||||
|
||||
No new tables/columns needed for the aggregate addition itself.
|
||||
|
||||
Restoring prior chart settings, the way #42761 restored `show_values_as` for
|
||||
charts that had it before #41184, is murkier here than it was for that PR
|
||||
and needs its own design pass: the old `aggregate_function` was a single
|
||||
Pivot-Table-level setting applied uniformly to every metric on the chart,
|
||||
not a per-metric property. A chart that had `aggregate_function: Median`
|
||||
before #41184, with a metric of `SUM(sales)`, was already silently wrong
|
||||
under the old architecture (that is the bug that was fixed); mechanically
|
||||
rewriting its metric to `MEDIAN(sales)` on upgrade would change what the
|
||||
chart's leaf cells display, not just its totals, which may not match user
|
||||
intent. This SIP proposes a best-effort, flagged-for-review migration
|
||||
(surface affected charts to admins rather than silently rewriting them)
|
||||
rather than a fully automatic one-to-one restoration.
|
||||
|
||||
## Rejected alternatives
|
||||
|
||||
- **Restoring the old `aggregateFunction` Pivot-Table control as-is.**
|
||||
Rejected: this is the literal mechanism SIP-216 removed because it
|
||||
reintroduces incorrect totals for non-additive metrics. Any fix has to go
|
||||
through the metric's own aggregate, not a separate pivot-level override.
|
||||
- **Routing all metric SQL generation through `sqlglot` expression-building
|
||||
instead of SQLAlchemy's `sa.func`.** More architecturally thorough (would
|
||||
give correct dialect syntax for free across more of the roughly 75 engine
|
||||
specs), but a much larger, more invasive change to a hot path used by
|
||||
every chart query. Noted as a possible future direction, not this SIP's
|
||||
scope; this SIP proposes the smaller, `supports_grouping_sets`-shaped
|
||||
extensibility point instead.
|
||||
|
||||
## Open questions
|
||||
|
||||
- **Resolved for this PR, worth confirming as the community's preferred
|
||||
shape:** implemented as `BaseEngineSpec._extended_aggregations` (a
|
||||
`{aggregate_name: sqla_column -> sqla_column}` dict) plus a
|
||||
`get_extended_aggregation_func` accessor, set on the concrete or shared
|
||||
base engine spec class per engine (e.g. on `PostgresBaseEngineSpec` so
|
||||
Redshift inherits it, but *not* on `PrestoBaseEngineSpec` so Hive/Spark/
|
||||
Databricks don't silently inherit unverified behavior, mirroring how
|
||||
`supports_grouping_sets` is opted into per-concrete-engine there today).
|
||||
Did not route through the `superset/sql/dialects/` sqlglot-based layer;
|
||||
that layer is for SQL Lab parsing, wiring it into chart-metric query
|
||||
building felt like a separate, larger change from this SIP's scope.
|
||||
- **Still open, not addressed in this PR:** how aggressively should
|
||||
`MEDIAN` degrade on engines without a native or exact equivalent?
|
||||
Trino/Presto/Hive were left unimplemented (unsupported) specifically to
|
||||
avoid silently answering this with an approximate function
|
||||
(`approx_percentile`) that changes the semantics of what a user asked
|
||||
for. If someone wants `MEDIAN` on those engines, this needs a real
|
||||
decision: require explicit opt-in, show a UI warning, or keep it
|
||||
disallowed.
|
||||
- **Resolved for this PR:** left the two `sqla_aggregations` dicts
|
||||
(`connectors/sqla/models.py` and `models/helpers.py`) unconsolidated,
|
||||
both now separately wired to the same new `get_extended_aggregation_func`
|
||||
hook. Consolidating them into one source of truth is real but unrelated
|
||||
cleanup, not bundled here to keep the diff reviewable.
|
||||
- **New, from implementation:** only Postgres, MySQL (partial), DuckDB, and
|
||||
Redshift (by inheritance, unverified) ship enabled. BigQuery, Snowflake,
|
||||
Trino, Presto, Hive, Spark, Databricks, Oracle, and T-SQL all have
|
||||
documented (not live-verified) support per the table above but are not
|
||||
yet wired up, each needs the same live-instance verification treatment
|
||||
before being enabled, this PR intentionally didn't guess.
|
||||
@@ -67,7 +67,7 @@ const communityLinks = [
|
||||
'Join our monthly virtual meetups and register for any upcoming events on Meetup',
|
||||
},
|
||||
{
|
||||
url: 'https://superset.apache.org/inTheWild/',
|
||||
url: 'https://github.com/apache/superset/blob/master/RESOURCES/INTHEWILD.md',
|
||||
title: 'Organizations',
|
||||
description:
|
||||
'A list of some of the organizations using Superset in production.',
|
||||
|
||||
Vendored
+18
-18
@@ -21,6 +21,12 @@
|
||||
"lifecycle": "development",
|
||||
"description": "Enables experimental chart plugins"
|
||||
},
|
||||
{
|
||||
"name": "CSP_RUNTIME_ALLOWLIST",
|
||||
"default": false,
|
||||
"lifecycle": "development",
|
||||
"description": "Allow users with the \"can write on CSPAllowlist\" permission (Admins by default) to punch holes in the Content Security Policy at runtime, e.g. to allow a new domain to be embedded in a dashboard iframe component. When disabled, the CSP is purely static/deploy-time and the allowlist is ignored."
|
||||
},
|
||||
{
|
||||
"name": "CSV_UPLOAD_PYARROW_ENGINE",
|
||||
"default": false,
|
||||
@@ -93,6 +99,12 @@
|
||||
"lifecycle": "development",
|
||||
"description": "Enable semantic layers and show semantic views alongside datasets"
|
||||
},
|
||||
{
|
||||
"name": "SOFT_DELETE",
|
||||
"default": true,
|
||||
"lifecycle": "development",
|
||||
"description": "Temporary rollout / kill-switch gate for soft delete (off = legacy hard delete). An emergency stop, not a clean rollback: flipping ON->OFF resurrects already-soft-deleted rows. Retained through this release as the move-back lever; removed (along with its two gate points \u2014 BaseDAO.delete routing and the do_orm_execute visibility listener) once post-flip confidence is established."
|
||||
},
|
||||
{
|
||||
"name": "TABLE_V2_TIME_COMPARISON_ENABLED",
|
||||
"default": false,
|
||||
@@ -104,6 +116,12 @@
|
||||
"default": false,
|
||||
"lifecycle": "development",
|
||||
"description": "Enables the tagging system for organizing assets"
|
||||
},
|
||||
{
|
||||
"name": "VERSION_HISTORY",
|
||||
"default": true,
|
||||
"lifecycle": "development",
|
||||
"description": "Enables the version history panel on Explore and Dashboard pages. History only accrues while ``ENABLE_VERSIONING_CAPTURE`` is also on; with capture off the panel renders but stays empty, so the two ship with matching defaults and should be changed together."
|
||||
}
|
||||
],
|
||||
"testing": [
|
||||
@@ -120,12 +138,6 @@
|
||||
"lifecycle": "testing",
|
||||
"description": "Enables filter functionality in Alerts and Reports"
|
||||
},
|
||||
{
|
||||
"name": "ALERT_REPORTS_RETRY",
|
||||
"default": false,
|
||||
"lifecycle": "testing",
|
||||
"description": "Enables automatic retry functionality for failed report executions"
|
||||
},
|
||||
{
|
||||
"name": "ALERT_REPORT_SLACK_V2",
|
||||
"default": true,
|
||||
@@ -227,12 +239,6 @@
|
||||
"lifecycle": "testing",
|
||||
"description": "Apply RLS rules to SQL Lab queries. Requires query parsing/manipulation. May break queries or allow RLS bypass. Use with care!"
|
||||
},
|
||||
{
|
||||
"name": "SOFT_DELETE",
|
||||
"default": true,
|
||||
"lifecycle": "testing",
|
||||
"description": "Temporary rollout / kill-switch gate for soft delete (off = legacy hard delete). An emergency stop, not a clean rollback: flipping ON->OFF resurrects already-soft-deleted rows. Retained through this release as the move-back lever; removed (along with its two gate points \u2014 BaseDAO.delete routing and the do_orm_execute visibility listener) once post-flip confidence is established."
|
||||
},
|
||||
{
|
||||
"name": "SSH_TUNNELING",
|
||||
"default": false,
|
||||
@@ -245,12 +251,6 @@
|
||||
"default": false,
|
||||
"lifecycle": "testing",
|
||||
"description": "Use analogous colors in charts"
|
||||
},
|
||||
{
|
||||
"name": "VERSION_HISTORY",
|
||||
"default": true,
|
||||
"lifecycle": "testing",
|
||||
"description": "Enables the version history panel on Explore and Dashboard pages. History only accrues while ``ENABLE_VERSIONING_CAPTURE`` is also on; with capture off the panel renders empty or stale history, so the two ship with matching defaults and should be changed together."
|
||||
}
|
||||
],
|
||||
"stable": [
|
||||
|
||||
+343
-350
@@ -2145,10 +2145,10 @@
|
||||
utility-types "^3.10.0"
|
||||
webpack "^5.88.1"
|
||||
|
||||
"@emnapi/core@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.11.0.tgz#8a655042dbbb10d0266670c9903c34a7001c705b"
|
||||
integrity sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==
|
||||
"@emnapi/core@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.11.1.tgz#b9e1064f3a6b1631e241e638eb48d736bfd372a6"
|
||||
integrity sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==
|
||||
dependencies:
|
||||
"@emnapi/wasi-threads" "1.2.2"
|
||||
tslib "^2.4.0"
|
||||
@@ -2169,10 +2169,10 @@
|
||||
"@emnapi/wasi-threads" "1.2.2"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@emnapi/runtime@1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.11.0.tgz#ce16b3674ff7266bbf50f9668bde8a04f3014d4e"
|
||||
integrity sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==
|
||||
"@emnapi/runtime@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.11.1.tgz#58f1f3d5d81a9b12f793ab688c96371901027c24"
|
||||
integrity sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
@@ -2934,20 +2934,13 @@
|
||||
"@emnapi/runtime" "^1.5.0"
|
||||
"@tybys/wasm-util" "^0.10.1"
|
||||
|
||||
"@napi-rs/wasm-runtime@^1.1.4":
|
||||
"@napi-rs/wasm-runtime@^1.1.4", "@napi-rs/wasm-runtime@^1.1.6":
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz#ed33806d0f9be98dc76d0c3d4fd872fda701b5d5"
|
||||
integrity sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==
|
||||
dependencies:
|
||||
"@tybys/wasm-util" "^0.10.3"
|
||||
|
||||
"@napi-rs/wasm-runtime@^1.1.5":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz#97e3d45d7424dc5da1d4e32f3bf3b292f6c1b44c"
|
||||
integrity sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==
|
||||
dependencies:
|
||||
"@tybys/wasm-util" "^0.10.3"
|
||||
|
||||
"@noble/hashes@1.4.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426"
|
||||
@@ -3083,199 +3076,199 @@
|
||||
resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.127.0.tgz#8374fcdfb4a641861218daa5700c447c00b66663"
|
||||
integrity sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==
|
||||
|
||||
"@oxc-resolver/binding-android-arm-eabi@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.2.tgz#ef832062e3a2f0c7604a8312c34eacf23a7bd7e3"
|
||||
integrity sha512-xQoCRv+gKax9KTdwdaQNnAFOai8neay7g3jExDIORzhbrejwGSJaZNTdOJHR5ziLg2joMxOCMOFMo4zuxza2uQ==
|
||||
"@oxc-resolver/binding-android-arm-eabi@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.23.0.tgz#31b3087c2c8a9d100ae22aced377c03befc87efa"
|
||||
integrity sha512-8IJyWRLVAyhTfe9/TIEbQqSQnl5rUqYJrUOS6Dkr+Mq9FGHMxDGeiEmwkBqCvDP5KckpPh/GYSgbag66O6JsCw==
|
||||
|
||||
"@oxc-resolver/binding-android-arm64@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.2.tgz#be6093b76621981c57833172a7fa9b5b0753daef"
|
||||
integrity sha512-HF5oiE2L05yInPYCFD/4uxSrEZW4SuIfn99Y6L1xnJnzl066JR+MJs2rIdstw8A2MPlAKH+13dpFPNycjqzvGg==
|
||||
"@oxc-resolver/binding-android-arm64@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.23.0.tgz#744d8c82d37189ddb76f911b6fbd9915d8090a9b"
|
||||
integrity sha512-pprVojnNhHxupwTT2gdeUlkxll6XEvWWBk3oVicOSNVWQC99OBnDhMQDoirqnzrE1bScQSMS2JgPpqdlrhz/Fg==
|
||||
|
||||
"@oxc-resolver/binding-darwin-arm64@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.2.tgz#b5ec0b6774ef9c60bb3e9237b39b603aac39f75f"
|
||||
integrity sha512-UX4u49CVCAD8QZNELaW8eMGgMAGwFWYEPbvNsh+3r/gs4NX3KfpiACMVwRQT0EuH3uat9hM5Zl+Ppm9pJD8tgg==
|
||||
"@oxc-resolver/binding-darwin-arm64@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.23.0.tgz#a57f71c22d3d39da3ce08fbd672a25673ad38364"
|
||||
integrity sha512-mbIrWIMAJeytyee36OyUP5XH92TP7FaKaQ2m5AjokKy7STgjrhRt7SMXqpqLjhGm6Xn721Xmsg6H3Rtd9YQETw==
|
||||
|
||||
"@oxc-resolver/binding-darwin-x64@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.2.tgz#79dbeeca048fda1de9c71169b08924535369a14d"
|
||||
integrity sha512-J9xPx7YBkrRmJ+xl561ztnMWEc1aOyjEIxBiGX1dVb3u7bGSnfObfcZk+Pd+uM0HZAPNsQ1xvD8j52A/uOSqNQ==
|
||||
"@oxc-resolver/binding-darwin-x64@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.23.0.tgz#ca28168b5aca8003c2a114731ffc556f04ac40e4"
|
||||
integrity sha512-UnIphmZ1LazUCr9DXWaKYWtKDefPMbgLsywaoYxRqVCNHhq4MM6d2q1Nz1i9Vzxt5i+cE2nRUYpAUHr/lijNYA==
|
||||
|
||||
"@oxc-resolver/binding-freebsd-x64@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.2.tgz#8d49e6cb2269597b6ef0bc90c5b23e68d946f8c3"
|
||||
integrity sha512-fRlt7OvSaQkWj6+EDTVxawVxOlqJB2QSnBfkeCyK4RTvsGctbw3BiH2Tb7DzMs7bikc4BRBpvWP5zF9K8b54Zg==
|
||||
"@oxc-resolver/binding-freebsd-x64@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.23.0.tgz#b956c5b3eda65e1302e291c185d7fca10fbfd7fb"
|
||||
integrity sha512-aaZ/cSEYFkSxgS2hOrobT6RQcsWNviOX8dW6CEkVx2/UYkAf9MeHbjl3W0usWV53rVV//ndBdn2nb1y7jsu4lw==
|
||||
|
||||
"@oxc-resolver/binding-linux-arm-gnueabihf@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.2.tgz#84413d558915ecf81da5914336b9fe9a70a84acd"
|
||||
integrity sha512-gK+vPUcPQITkGwBKpZGrcDHSlU6eDGl7AQacxS2CEKAZIBHWkOVFeJwLZ4tYnA1acJqRM5lt7yYwPCVqGHIJ7A==
|
||||
"@oxc-resolver/binding-linux-arm-gnueabihf@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.23.0.tgz#f59878badc556b023a12980f8fd4ca363f8065e2"
|
||||
integrity sha512-IoJLvO5SjLSVMaq83BNTrPCb1FppvoJc1IhZ5CoUVl3PykUBku7D+LK1j0GSurhJcIc6zfjghsvaZNpq5ev6Mg==
|
||||
|
||||
"@oxc-resolver/binding-linux-arm-musleabihf@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.2.tgz#af2554d8984592926ec92fd838bd01e3e14f2f53"
|
||||
integrity sha512-L/Rgas7SrOKy/z7IH+HxSFRqVO4PuLDKLEGKvnhoCBBq3UJ0YzGBou3qMzaAGgkJwsIvX2pBF+7ojzfUhLiZxQ==
|
||||
"@oxc-resolver/binding-linux-arm-musleabihf@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.23.0.tgz#19e4cd124f6fb0f17c2693525422be493edabf39"
|
||||
integrity sha512-vskFpwg44T/LFsfjSCnVZ5ygcuqzPC1yUzVEiKa8BgHAQz0+QLQQW3EGWLPVi8EXFghzjR4EtgPBtOhCjU4jdw==
|
||||
|
||||
"@oxc-resolver/binding-linux-arm64-gnu@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.2.tgz#453a2e5d1d858d7e5bff0ddde584ec46560a37b6"
|
||||
integrity sha512-CUEYvlX1Fk7E9kUMzuswru1J9HLxMwnpDeQGjQuI4ZH+iNCoa2X9T+pvyzrbsgl7WnIeTFTlNlHsRfVdf5g9/g==
|
||||
"@oxc-resolver/binding-linux-arm64-gnu@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.23.0.tgz#770c64d6f976c6374de3314942616b6c8f2afb05"
|
||||
integrity sha512-//TcHVhrChyw5RYtgts6WO7KcWq9387c1Z5Zvhqpk/ktAbyaRYgBZrpSY1GDCFq50ASt6B6jhh+JxB1rB45IAg==
|
||||
|
||||
"@oxc-resolver/binding-linux-arm64-musl@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.2.tgz#4e04fb8d43aa3baa037f88a292b0eee386dfd742"
|
||||
integrity sha512-ViN1ZibQyxwC67GpoP33oo+S9UyUnkog13vzQb9+v9bCNvrVzJuk0MRdacjtv/9xfRMVF/eqHFyl8YOeykI10Q==
|
||||
"@oxc-resolver/binding-linux-arm64-musl@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.23.0.tgz#88640f695e6ca9f71641b59f2996d5bb3eaaea42"
|
||||
integrity sha512-ZFqlwiTf7CXLLSGyAR9tYiO33LiaeIEXW+xm42d8mnUGpDgPltyrCGYtQezyMMEXvjhOgCz1X+i7sbDTJEx+bg==
|
||||
|
||||
"@oxc-resolver/binding-linux-ppc64-gnu@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.2.tgz#662de504b88a9d7c745808514bfdb2a4e9f53760"
|
||||
integrity sha512-CU1sCqWnhGqYD5I1HedHk5pujrn7ssDkNB/AEQd/pd3D/EojVSgJUlpbafwIbyhia3PgIfkvdFpRPWSALzVumA==
|
||||
"@oxc-resolver/binding-linux-ppc64-gnu@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.23.0.tgz#7c2a0c45ab65354b89f8777c806fcfebe09f12e8"
|
||||
integrity sha512-oZ5LeN5+H1R19dRjTAxKrxQguH+AsemHcnthEfFxf4OjmBSty2doHLeSmMunKy3zpTHJQ3lh3Af+dNS+W6dYeA==
|
||||
|
||||
"@oxc-resolver/binding-linux-riscv64-gnu@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.2.tgz#72e7d1edb906d42a2ad339a3a6be5b097c00a0a0"
|
||||
integrity sha512-jWVyZtIHca4Gb96x7dag+y69vlei7ffjrsveLkmf2ZhqEAz6ZSBnY1GWvgZUaZlwZP62A3xD092BW97Q5VGc+g==
|
||||
"@oxc-resolver/binding-linux-riscv64-gnu@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.23.0.tgz#fb502eb40ca7576d826672ba9f6c6ba9bb87094d"
|
||||
integrity sha512-O4ciFDyX5ebQd0qkb1bjAIg8IEfiLT03GbSeylwlwlUMK9KwBWaALwrxSbc0Msaz4U6iPj+T9eRXpD5mxBfmvA==
|
||||
|
||||
"@oxc-resolver/binding-linux-riscv64-musl@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.2.tgz#cf5788f4dd858ae6a27d1252b0b662b39a4bbf2d"
|
||||
integrity sha512-LF29obFqNgBUgDX7rmUK7M4D0JQG5LxhYzn3xXmECcHU9aQAdWG7NiY052qybtesEdwHQXKNTWYQ7mTsybNvWg==
|
||||
"@oxc-resolver/binding-linux-riscv64-musl@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.23.0.tgz#2159fe2807e1356d9149a151d06cd9a0c7253224"
|
||||
integrity sha512-P3o8Y9kISYjcxadmbO+94ThRwLhwGuDAbA7dcdd4+YLpfeF+mmobz8fXf4NmSdfSqjyRSkceJDBRZha9NVYkiQ==
|
||||
|
||||
"@oxc-resolver/binding-linux-s390x-gnu@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.2.tgz#ec3933ccb443b68eb9b879b7f25489fcde11cc53"
|
||||
integrity sha512-+NYcm+cCHBbtdQQ3A4phQTSuVRYnNHz7wrl9XRAPEovcdoqi0mb1K5ZOl+jN54ZD+q1zz3V0vltbFJmzecJKmw==
|
||||
"@oxc-resolver/binding-linux-s390x-gnu@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.23.0.tgz#1fb7c19af499a5701d53f2aa38e841f153d7cf86"
|
||||
integrity sha512-oj03m1E3RmTFczKhcKJDzHaEDKJnPIsDcQFVxBJsSdXGSuIPdt5TvcM332FfMQgzI6yDJqyl4InrnFfXrmUTKQ==
|
||||
|
||||
"@oxc-resolver/binding-linux-x64-gnu@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.2.tgz#9cb87e203ef40631d7ff2cd1a8a6c145a5595054"
|
||||
integrity sha512-UQqZDdG2r2HhAOsZEgufkIWHPQ886IUyuJQkoZByvzhW8j51R4UNzGBJFkTiTnLhQnggwJRdJgFWK4uY6ZVIMw==
|
||||
"@oxc-resolver/binding-linux-x64-gnu@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.23.0.tgz#580644712d78ebe1c1d4b7077db992ce149d46e8"
|
||||
integrity sha512-BqJxbSC8FdP7mSuSpRePTGHm0hXWV+dfz//f7SjsteZncLaBgWTBmi/OZNv7sX6CyG/Pt/eJkPorP+DkMOhMwQ==
|
||||
|
||||
"@oxc-resolver/binding-linux-x64-musl@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.2.tgz#a1ebb07307eccb2b150dc6bcac5a575cf3b647bc"
|
||||
integrity sha512-3Q9PMRjalWkT6NZ4jfujuqTCFwoWErg3y3BnOgb544B8IMw4PktiwWOigMfOHNRLMghZeJ7hpfpZf4CP7rV7Og==
|
||||
"@oxc-resolver/binding-linux-x64-musl@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.23.0.tgz#709b86ff4c0a43bfeb4d7e5cc7334ee77780c5ae"
|
||||
integrity sha512-utmw+VmUrW4K8LI5/6jhg4aGYKJHOIjQ9syYOOA6pF3w7haKu4r4enTe2U0C04/HbUvkq/Zif43xFsKW1Pnq9w==
|
||||
|
||||
"@oxc-resolver/binding-openharmony-arm64@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.2.tgz#aba2e14c823e3356acfb8bce06be97d5aa5fcab8"
|
||||
integrity sha512-Eljeq3ndtyKhM+Es8LITi4Zl2htzuRZcrPMF3kMCsrILztvU6AjZ3FuEhHHKobPUB9rMpzLpJP5bDqXI7r+iog==
|
||||
"@oxc-resolver/binding-openharmony-arm64@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.23.0.tgz#580331f88fbdbc7b518a93e2ab43a7692e24f7f4"
|
||||
integrity sha512-V6lbRrthHa4TbvsLjPtg+EkXT1tRY+s4I8rYLXUfiHlZzGx3sLv1EH9CEOOevjvUYHLsbe/gqCIc73XnQfPb9A==
|
||||
|
||||
"@oxc-resolver/binding-wasm32-wasi@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.2.tgz#8840346c1b8a3028b1b3575abb2e45fed529d3d6"
|
||||
integrity sha512-HGDbNsIywqc4LxU38+CTJNnB/6BF7rheWOJ259b4eE0aEnelYblC8x+1tEd63bp31fYne63RQz9Jb4yVnC7Yig==
|
||||
"@oxc-resolver/binding-wasm32-wasi@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.23.0.tgz#4d5f1300d1a4ef18b933329ad7c6b395bac28580"
|
||||
integrity sha512-gRoOxQPdnAmIAjxcuQNBxfihvx+wjTaQM/9/eP12xwnGNawOG/+Zz9RHN4WNSxT45b5CrscK4NB8aPh+oZQXAQ==
|
||||
dependencies:
|
||||
"@emnapi/core" "1.11.0"
|
||||
"@emnapi/runtime" "1.11.0"
|
||||
"@napi-rs/wasm-runtime" "^1.1.5"
|
||||
"@emnapi/core" "1.11.1"
|
||||
"@emnapi/runtime" "1.11.1"
|
||||
"@napi-rs/wasm-runtime" "^1.1.6"
|
||||
|
||||
"@oxc-resolver/binding-win32-arm64-msvc@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.2.tgz#c987ab635ffe553e6230255a07c0576686f5ab45"
|
||||
integrity sha512-iWx25CBEgH49iE9q5coEGI/jb1jl5kkCY9z6U5Og67xCkQ/WFMDc2J5U78+AE91SUxM2NqSLhJC8/PLfWnImww==
|
||||
"@oxc-resolver/binding-win32-arm64-msvc@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.23.0.tgz#87145652fdf60741d07b893150d61fa06e2a1d15"
|
||||
integrity sha512-CgTGMYsJVe1eUiCdJTpGw21svXw79ITsemN1h0hcNkiswasDbN5MoibSLY+gRMWP5syfEz5iffrjZnwEP8xeUA==
|
||||
|
||||
"@oxc-resolver/binding-win32-x64-msvc@11.21.2":
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.2.tgz#dbdf12721396ef4c31899d0963b002df32f57050"
|
||||
integrity sha512-VPoCAhKvCQTlG7vxqaBXcmuvbh77BfnGXj8g0pbvVXpm1F/R8rDVwqIWcEbMzrI1JvJlm8v7T9uMVQb6UctMRg==
|
||||
"@oxc-resolver/binding-win32-x64-msvc@11.23.0":
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.23.0.tgz#8b66dbfa7b796139e719063fc0e44084e80a1c15"
|
||||
integrity sha512-gUGJpr+Rn6zMxm5juApV0K3U845i8t47o8k+rbO0BHbi4PoJIfSPeQmrE2dgohQm2g5k6iviNFyXCGqvmaYUpw==
|
||||
|
||||
"@oxfmt/binding-android-arm-eabi@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.64.0.tgz#e14e25c032f6d8a6b025eb5ee7bb606c3cbdd10e"
|
||||
integrity sha512-o6uzh/jTOQeAY5TdkAeXdqv7MBRcPxiRA08zrcBtkKj5cSu/FMu0Hl7Q6Fi1KCKyCWZ6lJVjBzdsJvsKltUsGQ==
|
||||
"@oxfmt/binding-android-arm-eabi@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.63.0.tgz#136176dc94fdc41e21415cc770d86f5066282e0f"
|
||||
integrity sha512-YmRth4ZPGgEXcgmkhvANbC9uD67dxmSobW7DQuyt5tOBOKvPnIpk5SVHBj88E+7wMNRI2FhqaDbOhQFBix+b8A==
|
||||
|
||||
"@oxfmt/binding-android-arm64@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.64.0.tgz#294a15b8402eedde0e0a467748e3efadf61bf523"
|
||||
integrity sha512-jRGSUeeP7p3Gynw2YaCVtjBIA6ZxY6bEB/ES5i54OhqmRTyuVg7ZgstEtzgq6GOAJd+2QZ5pvf+bFfmW5Mp9cw==
|
||||
"@oxfmt/binding-android-arm64@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.63.0.tgz#10bc42457179210061c801122a64304619e3bdab"
|
||||
integrity sha512-icbahX8X2X3sRamOMecvdYeZXWjPDazRDIfvWfy7Ca1nc/ZDT2Y9k5Nt7s46EqFd7NQPdgk+CM3/SgIT5LPCaQ==
|
||||
|
||||
"@oxfmt/binding-darwin-arm64@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.64.0.tgz#d55b1a5d5d97d4ccde8e4be7b63e06e4e56f2d13"
|
||||
integrity sha512-JINwtU2lW7nOFSqi+H2qplipNUqah9Gc1jgGmB82kTD4UnZrZIVxCJ9qEmFiKfjNq27gYLFhrUb0to86aCwMjw==
|
||||
"@oxfmt/binding-darwin-arm64@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.63.0.tgz#5f9084d9a760a1836387f8970a7f9d614ec3d909"
|
||||
integrity sha512-WV+Ze5v5gI2qoj8jpAovt8KBTW8pjEz/AiMXXjeTQS+Bmf/MmZXTS40S8xNPDszX+W8WDv2Bbk6qKrMTtUGu1A==
|
||||
|
||||
"@oxfmt/binding-darwin-x64@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.64.0.tgz#1c9673270ed597ba9456d40fa0607d50e81158ea"
|
||||
integrity sha512-gCmuswrgrOSajV4HCRFkVCGIruPq8bjYuPYgSE2WQB3mD6XrdyZ3JMSRZCkQ8zCxOyGWriBo6QoZ5nmMHQ1BfA==
|
||||
"@oxfmt/binding-darwin-x64@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.63.0.tgz#badd4a02218a9a62319817d5c337b30159a54a21"
|
||||
integrity sha512-CJGSBdDxXOWIpoFXHpverimCvz084KA7L483rqJ44c3jDtzv6d4qOSoR/V9ywSHfV+Ks1lwIj2P49BFhunLNAA==
|
||||
|
||||
"@oxfmt/binding-freebsd-x64@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.64.0.tgz#9e8f8b3a5a558043c664d43d54e441756af30c56"
|
||||
integrity sha512-Ab8g7a38pT0MMImjh7anRSTve6buWBIlcXIFBYa5xl4s6UxEgKSc2xOOhbGtLwvXnEi2PsEDGoJh3oUU7xkehQ==
|
||||
"@oxfmt/binding-freebsd-x64@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.63.0.tgz#a17261e95c8ebef1f76d8aaac746a64fdb6ba51e"
|
||||
integrity sha512-BDfKY+KhL2078cgswBBFQPAYuxCy93bS/iC5frdSeSbTLcGrR6VC2hsuPTanoJmg84+wSyWl0wWC1eR+uTnkRg==
|
||||
|
||||
"@oxfmt/binding-linux-arm-gnueabihf@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.64.0.tgz#cfe552538c9e9402ca64d7b83b1ccf02457ef391"
|
||||
integrity sha512-BgvS3CoQ+Xy2deoZqEN8JVKabcCZi2RxA3yant8G9OAv9KuPJ9TCjHkqigzdHUVwErZxEP5d2bzLIEyKYyBDLg==
|
||||
"@oxfmt/binding-linux-arm-gnueabihf@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.63.0.tgz#baeee34bb08e0769af878623f442e83bc0aacd7a"
|
||||
integrity sha512-Ov1cQEXT4mj7cojAokWSS1eoxkoyvbDfAbxNsGIKY2o36kvdAaFzPxRN6NxFRk9fD72B8oCoTTX/NuYTUWlpsg==
|
||||
|
||||
"@oxfmt/binding-linux-arm-musleabihf@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.64.0.tgz#1944e367da59e8b1770c5ba96465d0c7e640053e"
|
||||
integrity sha512-QXpNxwoMj0YvnceCNZadNSden3bIcnvjn/sDp/rwZhRoZoZYGpHvtPyhGsdJz9uvT9GkaMW7SsLddurU56dt8w==
|
||||
"@oxfmt/binding-linux-arm-musleabihf@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.63.0.tgz#e70d5697ec4b6bb5f87a3f019e01b3f956b8e44b"
|
||||
integrity sha512-0LE7ro3+6L79jcMANycAZfRaC7zxr9YZ2+vEL5uMD9QlEep+rS/r1kSJsnuLl991NXJZD60euh0PC1GHrR20vw==
|
||||
|
||||
"@oxfmt/binding-linux-arm64-gnu@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.64.0.tgz#510386113bf6a128cf3106d612471dbd1a13b0f4"
|
||||
integrity sha512-BBgH3I1ppDsI5pZ4Pdhw0ceYxwVCfbU/bZEBCeZ6caRS9x0ZabErxubP7riGUn11PXZBhe8DYdjkDKP1FlVQ5w==
|
||||
"@oxfmt/binding-linux-arm64-gnu@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.63.0.tgz#638a8ed4f3d256c50aeb6d2c19cfc65792c902e1"
|
||||
integrity sha512-izPk+2Z4gjuZK32Fqh5qXoMpT/2NXzLh++ob57HiEiVSQZ1iYXu8EKMzb+K5AvWyIEXhdDIt7ADjGGtFhkT9Bw==
|
||||
|
||||
"@oxfmt/binding-linux-arm64-musl@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.64.0.tgz#7235405901cb0368b659eb42b362a817fc3330a3"
|
||||
integrity sha512-v19HSjC/BGXdt26qEvKZtwAHgGmQ2Agcap2kQP+KIqoRZqivVzYth3ui2dJA1i+6/fjpjga85lIOaJJjQ/bOOw==
|
||||
"@oxfmt/binding-linux-arm64-musl@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.63.0.tgz#af5a9b787f5233f27a3360ad56235fc1b011f760"
|
||||
integrity sha512-alPmbOuWXFXiSo+lOtv6X71C7SYMEDW2WVvywOvf9BwKgEhSNGhMTLeFVSjKUMCamcjbbgVdsWF8GN1uy8xshg==
|
||||
|
||||
"@oxfmt/binding-linux-ppc64-gnu@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.64.0.tgz#1f0563c530dfa634682ffa32d16830404b95a8c6"
|
||||
integrity sha512-PElLnOo4xFTBZrxPhgTIj0eHqZXwEBQoNWtb7facUV170T0B0FRET0iNbb3LUeLWTybkUW+vsdyv4ihOdyXGyw==
|
||||
"@oxfmt/binding-linux-ppc64-gnu@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.63.0.tgz#c1a211206134a5577e355a495989e0d733218d60"
|
||||
integrity sha512-BdzCPvolJc4AWZ+YMzgUDJcDzbQWrFjYuqBHoNHNqP1aCaluQRJNs4k3vNU5IG7vTpjf9zeD73D7MFM1TecZpg==
|
||||
|
||||
"@oxfmt/binding-linux-riscv64-gnu@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.64.0.tgz#36f55e955c5b38b587470f181146c9a11cf8bdb1"
|
||||
integrity sha512-Qzsg15n4F5CH+MorcRW4MkAEMiLzXmeG+DiDSbP/bBTqCmWOH3K9DHryNrve+JHlV0txS+B6Z9P5Xz+cmWeL+g==
|
||||
"@oxfmt/binding-linux-riscv64-gnu@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.63.0.tgz#4863f0311e5c1b88f75ef822959b3ca4fd938937"
|
||||
integrity sha512-7sIgfLzqtNKSkMGsGVyRpHwpjNezRg2XONvUOheFZs95TSZpM0JAuPpA8KrQFsWc4wPU95roX2O69JgH8igOgw==
|
||||
|
||||
"@oxfmt/binding-linux-riscv64-musl@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.64.0.tgz#bb9c6c3860c8832fe271623eb6131ea5f5e094cd"
|
||||
integrity sha512-/GZ358wnQ/Ez4UVnCcZIi56JkY0sOdZ+B108pqXKqZz3jLS59F4KEAB1Qv3fRlObrFEk+3L2vUQ/xoPx+3vjXw==
|
||||
"@oxfmt/binding-linux-riscv64-musl@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.63.0.tgz#ad05a017d12553e2f544743c4940adb552aa1d1c"
|
||||
integrity sha512-9Tcg0y0WcVa6Mm9AgcgFMseDS+VkFJZpKZ8We9SpDY4gg5jewSwln+0sO04QLcTS1BtfDl9MwR+NfID8L7PUTg==
|
||||
|
||||
"@oxfmt/binding-linux-s390x-gnu@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.64.0.tgz#7d736d923f3c7f88743f26479a49903c6dbaf818"
|
||||
integrity sha512-/C9We3DXegowfLXtVCYHeNiU9azwCDr5cQkEtCVlc74vyn+lLQSPApJ1CZmxAduqeq/Oi3gQ+IVptyhCaTMtkQ==
|
||||
"@oxfmt/binding-linux-s390x-gnu@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.63.0.tgz#2803f539db15bc66db115888fa8f84d6531ed2b9"
|
||||
integrity sha512-qWKC1pEOpx1qYhXaugPhHUeXwSfqEOk2wJH2LqVXGPV5iQYfdAZdt+d2XDiX4DTSWA2QDMUcFB+wEORh3Xn/sA==
|
||||
|
||||
"@oxfmt/binding-linux-x64-gnu@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.64.0.tgz#34dfe2bde9ed124324b45aae078618456e850452"
|
||||
integrity sha512-91KM2CeRWscIEHlj1NsW2WSnzGeq1Ehq+39bfDowTdkn+fcvK/x4Y1RcyqT7glyBjZio0ldkeCG6Usj3v7ASog==
|
||||
"@oxfmt/binding-linux-x64-gnu@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.63.0.tgz#c22a06a60ae2d6b3de522095e0c50a816040a033"
|
||||
integrity sha512-S9wXYOiGSqYGS4Fx/TFsY+xDd/7dE5s+rUgbA4TsHiVF9e8J3ZcKmP7dsP/7iqLI9Wz7Ic7TzEr3mdthRCTdrA==
|
||||
|
||||
"@oxfmt/binding-linux-x64-musl@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.64.0.tgz#b5edc644409aff9715279650767d34d2fb65d59a"
|
||||
integrity sha512-gw7uEk9I+7zoT1EYLra1eWArIzNcz8e3jkv+Noo2+o2T7wPvsNSQbfoa4DSfZlvn1i6mJ05RiZ4/omaXPDNhQg==
|
||||
"@oxfmt/binding-linux-x64-musl@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.63.0.tgz#48d3eeaf8e3757f638cf92de5ee4858befc9c0a3"
|
||||
integrity sha512-5eGyTJuMZNwBSHCivXt8Yuta6GeTYksOPXRk2MIhajiyFGQx7bjaHIwY+ZusAoFHhT157A9x6sktLjYo9D5oMQ==
|
||||
|
||||
"@oxfmt/binding-openharmony-arm64@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.64.0.tgz#6b1d9c662e08bf5fbc1e9ccdb45ed28c004b90c4"
|
||||
integrity sha512-HYHFf616FHSPSO07c09mjmXBfQ73wIVM3m0txOiooa5XZkGoxFd6B14PVj0LB0DXIqJ6wAO/dDR/NX/5UUaqnw==
|
||||
"@oxfmt/binding-openharmony-arm64@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.63.0.tgz#02be9e140ae35ba30f52bdce27612fece4a01ab3"
|
||||
integrity sha512-Rz7hx+Dv3DoW/S6pwVAyjfFXp7/trdQ1zg+vNmsdsdDNlUccugp4XNqambSuEAeP0DaG9k72AtNyfDXCEg0AGw==
|
||||
|
||||
"@oxfmt/binding-win32-arm64-msvc@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.64.0.tgz#bc5a005e159a8f9af4168eed2e61fe477f4029db"
|
||||
integrity sha512-uQjFp081IZSWD6VAofX2iO2z01awAdHmfC+NrieWIPKrT2hZKQDyq/U18M7ifC0sm0Wz8aHY/p6+FDYIzs/CrQ==
|
||||
"@oxfmt/binding-win32-arm64-msvc@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.63.0.tgz#2226eaf52b6345a2cb926499216b2486cf0dbec2"
|
||||
integrity sha512-T/IuizKN9mr4Xw6YYnptkXRNdLkyIlUZ7c8zfTOBpoytZyJ1BAsMUvsMDEx0X4YvSMpaivm+DR8112rQfzC25g==
|
||||
|
||||
"@oxfmt/binding-win32-ia32-msvc@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.64.0.tgz#88e90b96f7b39e4b6f75178c94c52d464fa58b53"
|
||||
integrity sha512-lNM6byTAQ881jugzFu8juJTbNRgsUTlswMA6pJmwi1XDvmIqnnb49lcUAs5gz94fCJLrVN+/X3s3jOKqx23WIQ==
|
||||
"@oxfmt/binding-win32-ia32-msvc@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.63.0.tgz#58d263bb5ecd7330c02f9dcd8cda10f66e42e74b"
|
||||
integrity sha512-XjrO5FJ5Wl9vsAxtCP1G/eaeT6y1K2s9CICUHGE42cEjou32/J6S+B1KnrOAboj6E7uhJnwPbRSvznWcxNdA0g==
|
||||
|
||||
"@oxfmt/binding-win32-x64-msvc@0.64.0":
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.64.0.tgz#788c7fe26f89e57269f79e8f8a34e9b1497bc674"
|
||||
integrity sha512-BtmbtL/QjMtF1a6C3CqoDluH2IfB6fJt62E+B9RFfUPtFk4Iz9PFS6+y/SzzOvSxc7aUk2Kphwg7Dh8lMbwu6g==
|
||||
"@oxfmt/binding-win32-x64-msvc@0.63.0":
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.63.0.tgz#02a166c8a8049c55d0096d1ba9d8e73f3a4d26a7"
|
||||
integrity sha512-sgsHCQy432OTQH4Ikk3tZptp3GqwnhwUDuY0loBH41zyHWfMZY9v8Dy78wsnSofHejvFozZGgJgBB1A0LQRwMQ==
|
||||
|
||||
"@parcel/watcher-android-arm64@2.5.6":
|
||||
version "2.5.6"
|
||||
@@ -3796,7 +3789,7 @@
|
||||
"@rc-component/util" "^1.3.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/select@~1.10.0", "@rc-component/select@~1.10.1":
|
||||
"@rc-component/select@~1.10.0":
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/select/-/select-1.10.1.tgz#323b2f458a637e8e752f8341094783741c613c34"
|
||||
integrity sha512-H+yQsl+qED9NilQ3g6zdpsMwUgwVjrcMTkNHAWRVU/MoNCYgTbDgU+MIMgZDK+rVdd2JUfI/MkysMcZZ0cyQKw==
|
||||
@@ -3831,7 +3824,7 @@
|
||||
"@rc-component/util" "^1.3.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/table@~1.11.1":
|
||||
"@rc-component/table@~1.11.0":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/table/-/table-1.11.1.tgz#7b5c2a7c26fd37b6a403082029b5a72fcb330a4d"
|
||||
integrity sha512-OWdS6DMmeWb7bJBGqPxYZpQbzBlBiXZUu2sqo6Ii7Sjs9GeK1IsrXrWk26SL2c6KEseabswdxrRj7WUm9LdECw==
|
||||
@@ -3873,7 +3866,7 @@
|
||||
"@rc-component/util" "^1.7.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
"@rc-component/tree-select@~1.16.1":
|
||||
"@rc-component/tree-select@~1.16.0":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@rc-component/tree-select/-/tree-select-1.16.1.tgz#dcaea96e396e98108cb29cc051840d4fbdda38cc"
|
||||
integrity sha512-a1Oi6EJhqAhdOxxupdJi6fP0RPHMKn5TcfkX2+llaQ4lF4nwfH7b6SCHcnsybaa2s+pk1yZYwVyeOYkDnEBRdg==
|
||||
@@ -4129,23 +4122,23 @@
|
||||
resolved "https://registry.yarnpkg.com/@standard-schema/utils/-/utils-0.3.0.tgz#3d5e608f16c2390c10528e98e59aef6bf73cae7b"
|
||||
integrity sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==
|
||||
|
||||
"@storybook/addon-docs@^10.5.10":
|
||||
version "10.5.10"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-10.5.10.tgz#c65d1d4a6d1e2de50f0decaf82ea57583f220b30"
|
||||
integrity sha512-06JoK3/a7FWI/6GzuidJP9iHp1/Vejboe6lzS1jW+d8ItpecriBt+oXh1VNmUM7i7PjI6pZnet+j51QnLyeOoQ==
|
||||
"@storybook/addon-docs@^10.5.7":
|
||||
version "10.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-10.5.7.tgz#6d599c94fc871c248ce06a5c081f57655c83f40a"
|
||||
integrity sha512-KNARJfjICaizinsR3INMEiipZm1ObYo+xw+E26gteu50Bcy2dIZUtk5uHY5XdtardU3AXX6yRXoBZ2HCY3lbHA==
|
||||
dependencies:
|
||||
"@mdx-js/react" "^3.0.0"
|
||||
"@storybook/csf-plugin" "10.5.10"
|
||||
"@storybook/csf-plugin" "10.5.7"
|
||||
"@storybook/icons" "^2.0.2"
|
||||
"@storybook/react-dom-shim" "10.5.10"
|
||||
"@storybook/react-dom-shim" "10.5.7"
|
||||
react "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
react-dom "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
ts-dedent "^2.0.0"
|
||||
|
||||
"@storybook/csf-plugin@10.5.10":
|
||||
version "10.5.10"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/csf-plugin/-/csf-plugin-10.5.10.tgz#c65da1cdfe0a11795a14c518e9f8f828b8d54b3b"
|
||||
integrity sha512-TaCLBrqVEr767+w58QDotDUiCTuE5cyRJuRcDlsKQyUIyGBv+lYD3lu8wBiVCYxgIjB/gu9HmqiC+0yx1rHzaw==
|
||||
"@storybook/csf-plugin@10.5.7":
|
||||
version "10.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/csf-plugin/-/csf-plugin-10.5.7.tgz#bc73f164d1b5f8e2931b2774f4b389a06453cf6e"
|
||||
integrity sha512-IaX8FlM0H36HNFhJ2+4L9bCldqfvHGqcLg841SJNyK/DhfMlM7JsvY/GDH2ZFuWrUf8FSOx96GRRnHq6XfRKag==
|
||||
dependencies:
|
||||
unplugin "^2.3.5"
|
||||
|
||||
@@ -4159,10 +4152,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@storybook/icons/-/icons-2.1.0.tgz#edfc2450a39c5e780f28c6cbc49acd7bff59b41a"
|
||||
integrity sha512-Fxh9vYpX9bQqFeHRiY8h2ApeRGDzRSMLwJwNZ/AIRqnyOKHxRKL+yFe+ctEkVJmuptRE9u1Hrn8ZZNHyfDKKNg==
|
||||
|
||||
"@storybook/react-dom-shim@10.5.10":
|
||||
version "10.5.10"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/react-dom-shim/-/react-dom-shim-10.5.10.tgz#8ddcad879d8804a9bbc09a5d0ea10f8356f01ed6"
|
||||
integrity sha512-rbu62ILo/VE3iXKmu+kWXFpD1H1Lwi0f19q/x7JnDsD2dxKS9w5znLEqPIq2qxpzi/wjjIb2iUP1cRG1d/9W5A==
|
||||
"@storybook/react-dom-shim@10.5.7":
|
||||
version "10.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/react-dom-shim/-/react-dom-shim-10.5.7.tgz#9a5aa0e0f89c09e71c6cbfc6bb1abeb537e5aabf"
|
||||
integrity sha512-lxOkyh+wu/MiBXvYQHjZfD+DRKOa4bHBzbuGuiHXnHXmdOcTRdcrQTsoeN2FPtfugmmOG66cZUEgDwNX+k5eRA==
|
||||
|
||||
"@superset-ui/core@^0.20.4":
|
||||
version "0.20.4"
|
||||
@@ -4862,86 +4855,86 @@
|
||||
dependencies:
|
||||
apg-lite "^1.0.4"
|
||||
|
||||
"@swc/core-darwin-arm64@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.16.1.tgz#f6f6983e2268888558cdbe043001d82449445def"
|
||||
integrity sha512-zlJblJ8ncErD43lKdxjbUaUskJQf+LxiPXYcWXD8/8ZMV+7uuAT+CwjciLXpyZBd5Pq/S726bMpeeAwSeL1hhg==
|
||||
"@swc/core-darwin-arm64@1.15.47":
|
||||
version "1.15.47"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.47.tgz#345ce6a1bf4033da189c2e3eff1244190195d15b"
|
||||
integrity sha512-GsoMtan3ojGGMGFbl31mmRu5ctZ56re8grGE8mO/OHJ8O+JRkzod02fe7X6ZQ8JvamA3imkEkx/h3u+vsOgPgA==
|
||||
|
||||
"@swc/core-darwin-x64@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.16.1.tgz#98b61e8c7ffe9f6263a08677353ba5606f6992de"
|
||||
integrity sha512-IN0BmPWb0YAh/17mmlWB/HDBtTw2MfuW4hulf/tQAgTQBRH17l+z499bNJLK6LizSjqs0P7V+jU38Zj+vJC1DA==
|
||||
"@swc/core-darwin-x64@1.15.47":
|
||||
version "1.15.47"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.15.47.tgz#f3debf50b5c1602bf392acb412bd33fd6d7e4f98"
|
||||
integrity sha512-leTi7Rx3KF4zcC637iqWgk9SoV8VXAD8ppQYXsep63px5A/UftOcxLN1pmr8Z1si/YvX90ompP/rHgpYkgwXWg==
|
||||
|
||||
"@swc/core-linux-arm-gnueabihf@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.16.1.tgz#afc245521cd43a65a87cdd87fe99fb9e4f4eaa58"
|
||||
integrity sha512-EYgrx2YOCQ2Twz2S793kqNjPkpvYVUPzzR95bIb7by+VQcyaai4lZZ2iz/tZvcFVKSNcN3/JTKwx+aBn2ZL52A==
|
||||
"@swc/core-linux-arm-gnueabihf@1.15.47":
|
||||
version "1.15.47"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.47.tgz#14a247a12c6d3de1ee63fa4fdbf5a4302936b5d6"
|
||||
integrity sha512-hBqHuoWKKIsKmDBn9qVeWqj5GWZhtlcczVaqQmNRXsDfq+voR5CxKRfamA367QjJXtceYuliLFfEL8QsskRM2g==
|
||||
|
||||
"@swc/core-linux-arm64-gnu@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.16.1.tgz#c44ca749af555ef8127795de141094cd28da9714"
|
||||
integrity sha512-moyKm0YZlHdHohzm1YwgAyesqnE853rO0REMfJLFAova51wF9BNi+3ZW2PeS7Vqvn6HeJuepLpAHbBdZctxpHA==
|
||||
"@swc/core-linux-arm64-gnu@1.15.47":
|
||||
version "1.15.47"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.47.tgz#3b8d09c481ae51c7b72d98fb6ce98f7b90065a1a"
|
||||
integrity sha512-TBxvRz+B4K205TWHHZxWVxkC2RFNP/Mz3PNcECBos5PsKwxjg3QSJzdoebr0VCf0Bfh8HOPldKxAP/8XkFe9gA==
|
||||
|
||||
"@swc/core-linux-arm64-musl@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.16.1.tgz#a1a3d15d5fb074c474c9a60a14488ec16124253f"
|
||||
integrity sha512-kKGBO9wdapiSzuf5ZzZ2fYtlu1BNSYtIIUxvH1ir/gcelTOREEHGDCLTDFx/2Knf878nU11A40z7LxwasEFxqA==
|
||||
"@swc/core-linux-arm64-musl@1.15.47":
|
||||
version "1.15.47"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.47.tgz#7ff2baa16e67b29017fdf7c6b69e40de7920ce1a"
|
||||
integrity sha512-3Yu3Uq/VgytqsPjTMbkPU1ExADytbdWbruJYhA584E9jrpE2Ki+R6VVPoZCeAVk1Cb7QxcRTgblw6bSa6a/R+w==
|
||||
|
||||
"@swc/core-linux-ppc64-gnu@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.16.1.tgz#7eb33976ece5e45e63f9c9c1ab0da9405df76f7f"
|
||||
integrity sha512-nZ6qahtLxC3PM54cWOQZHxt4lTCF/3J4LIoWWzz6v7A+rLs8Dx54anYQf7mH3eIi8KlNpgKci/ie8ZSqFN8O7A==
|
||||
"@swc/core-linux-ppc64-gnu@1.15.47":
|
||||
version "1.15.47"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.47.tgz#a3841982fe2eb2d889648c8e212b6d821db316d6"
|
||||
integrity sha512-wfdMi5IaOaNtmh2/6geRoxIdNfqylUZFdtzTKS655y1axWfIWyx7As74vv0wVdjeCIZ3WmCI9odDd4rUttXOSQ==
|
||||
|
||||
"@swc/core-linux-s390x-gnu@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.16.1.tgz#f02f2687d2ee1c8f59430ef638c63714862c9389"
|
||||
integrity sha512-4ji5PNzhYq193Z4/4xUaSoNJza6iCkDJSzhetrbB6KOYxsr+kxtQr8ePWhMJUiMt6JUWtXaZ1PYT8FhtED+nGA==
|
||||
"@swc/core-linux-s390x-gnu@1.15.47":
|
||||
version "1.15.47"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.47.tgz#edbfd705d6285f7dce48915871478bc9603904c3"
|
||||
integrity sha512-3hHYBY0yx8Ez7GMRrkhXHQzMdR5IZA6Wq5Ee4svlgwvSECLpnAJ9+0AimEGUFDvuLwE7nV/2+PYe8+Nm4rvNcQ==
|
||||
|
||||
"@swc/core-linux-x64-gnu@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.16.1.tgz#af4c571bbe07044ee0bec49ade1e53c1022d4979"
|
||||
integrity sha512-VJQxqrisHV+B394IgrOu8YsIIXZgffnf5tO+yc9Z/hoUpuZEvuQTjWwlnpZdpyD+0nx6LTD1/3k646JYm43yJA==
|
||||
"@swc/core-linux-x64-gnu@1.15.47":
|
||||
version "1.15.47"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.47.tgz#e7f61a7771d6a9b5b274521ba61809b3d7644325"
|
||||
integrity sha512-TjfhjgP/jGCfFHYC3JQPhJA1HwErbIJ9JfREDc1KNkvY6P0LodCgKVIlQ5deeTbkG7ih3bF5PHJLuLpaZjdRyQ==
|
||||
|
||||
"@swc/core-linux-x64-musl@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.16.1.tgz#113eb36a1d3bd21bbf4a48a22fad97dc1c7cc91c"
|
||||
integrity sha512-r9oV1mwxxsIGcLV1IQ/tw76MW3doatKze1QFWuC+a7QqJUkhY/bKTSVk6NpKKUGm2LDsE33Va8VqSClfA7vSiQ==
|
||||
"@swc/core-linux-x64-musl@1.15.47":
|
||||
version "1.15.47"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.47.tgz#7c1ef8305444bcc7894de177fe225f2d8f3be609"
|
||||
integrity sha512-CQpS8Ge/avfjZd0UEwG/sds83Uu32deQXcV1Jo3jD0mmvQQqtYAjpsDZXugmheeAwmt+YIuoVtVHro8LMYHqsQ==
|
||||
|
||||
"@swc/core-win32-arm64-msvc@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.16.1.tgz#7cc6cfde26ad7e15fe93de98033e7c1892bcf127"
|
||||
integrity sha512-6huNRessoBLxWEqBm5zJXyCQ27TO7anvkdiuQ5MDO4CJni0nOXEqKtV9RllQ2TdyENKKsUMXVnIfW2hIXx/R5Q==
|
||||
"@swc/core-win32-arm64-msvc@1.15.47":
|
||||
version "1.15.47"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.47.tgz#953856d26b28956d1a18ef10e5f221202b2cb8f1"
|
||||
integrity sha512-0W8IKHsUTYiT7G2RqtOoVWk+89yzZikIiDUb/sCK6BmQDBhN91hQSfyUtW12jhEWLzYgcfmisfsZrmZE+84U1A==
|
||||
|
||||
"@swc/core-win32-ia32-msvc@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.16.1.tgz#2330c734f4129c2064b8848fb956501788aab9a3"
|
||||
integrity sha512-OVKJFUzphrGmsh+BGtcZDesx0YryV7/Yvy5XGgTqnrZfjnyfcr5uaqYQugCckdIlupc5Vs3XtDjRAj12z4ZPlw==
|
||||
"@swc/core-win32-ia32-msvc@1.15.47":
|
||||
version "1.15.47"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.47.tgz#2743a5bccc49f252c23bad3135193640cbdcef3a"
|
||||
integrity sha512-ZIp49d2Z4/ka2jO9otOg4hDvTdPmp86kVOgS2M5FCPI7eKKZ1W0boxWn+8XeZrfERtFGW0AlMRm4JhlJa7l3NA==
|
||||
|
||||
"@swc/core-win32-x64-msvc@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.16.1.tgz#04825a3f9e6fbe390825ff02708a5ebdd3a9841b"
|
||||
integrity sha512-Bt+VIhWYCGk4urklnkkteLUOeLv1VxigwTCeB/xC6rBZxY6IIKdDwCJf6on3E3SUGsIqmQS6QqtuJQc1VxF4Aw==
|
||||
"@swc/core-win32-x64-msvc@1.15.47":
|
||||
version "1.15.47"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.47.tgz#9674ad0c9187b7cbe5cc3080b31b960d3ee688b9"
|
||||
integrity sha512-2h8Iek95vnixkBRCo+H8p09+Q5ll2NgSMFrWTy0iKt7+/t+8/T5mBpiT6c0ZxSS7wcWjwZ9sGZkK70tTSYHdDw==
|
||||
|
||||
"@swc/core@^1.15.40", "@swc/core@^1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.16.1.tgz#5ea7ff32f3b352c871aa47195efd4932f709a569"
|
||||
integrity sha512-nUaeu91O5QZKrQdaDCHd402ogUIoNOOjpkZNq0UomWK0G6gDaGmLhvddF1/3BXf5O8aLyo6ZPY/aMDWvaJQ/hg==
|
||||
"@swc/core@^1.15.40", "@swc/core@^1.15.47":
|
||||
version "1.15.47"
|
||||
resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.15.47.tgz#6226e842160e247eb79a9aeac1095ebddb56639f"
|
||||
integrity sha512-FbsO5JcfOjfH38W/rohBRBweJeERsAuIP4f377lmkmxTcq9exjtx4SkRuZY5CdfhR2CBVwDIJegBpJDffwNsOg==
|
||||
dependencies:
|
||||
"@swc/counter" "^0.1.3"
|
||||
"@swc/types" "^0.1.28"
|
||||
"@swc/types" "^0.1.27"
|
||||
optionalDependencies:
|
||||
"@swc/core-darwin-arm64" "1.16.1"
|
||||
"@swc/core-darwin-x64" "1.16.1"
|
||||
"@swc/core-linux-arm-gnueabihf" "1.16.1"
|
||||
"@swc/core-linux-arm64-gnu" "1.16.1"
|
||||
"@swc/core-linux-arm64-musl" "1.16.1"
|
||||
"@swc/core-linux-ppc64-gnu" "1.16.1"
|
||||
"@swc/core-linux-s390x-gnu" "1.16.1"
|
||||
"@swc/core-linux-x64-gnu" "1.16.1"
|
||||
"@swc/core-linux-x64-musl" "1.16.1"
|
||||
"@swc/core-win32-arm64-msvc" "1.16.1"
|
||||
"@swc/core-win32-ia32-msvc" "1.16.1"
|
||||
"@swc/core-win32-x64-msvc" "1.16.1"
|
||||
"@swc/core-darwin-arm64" "1.15.47"
|
||||
"@swc/core-darwin-x64" "1.15.47"
|
||||
"@swc/core-linux-arm-gnueabihf" "1.15.47"
|
||||
"@swc/core-linux-arm64-gnu" "1.15.47"
|
||||
"@swc/core-linux-arm64-musl" "1.15.47"
|
||||
"@swc/core-linux-ppc64-gnu" "1.15.47"
|
||||
"@swc/core-linux-s390x-gnu" "1.15.47"
|
||||
"@swc/core-linux-x64-gnu" "1.15.47"
|
||||
"@swc/core-linux-x64-musl" "1.15.47"
|
||||
"@swc/core-win32-arm64-msvc" "1.15.47"
|
||||
"@swc/core-win32-ia32-msvc" "1.15.47"
|
||||
"@swc/core-win32-x64-msvc" "1.15.47"
|
||||
|
||||
"@swc/counter@^0.1.3":
|
||||
version "0.1.3"
|
||||
@@ -5028,10 +5021,10 @@
|
||||
"@swc/html-win32-ia32-msvc" "1.15.43"
|
||||
"@swc/html-win32-x64-msvc" "1.15.43"
|
||||
|
||||
"@swc/types@^0.1.28":
|
||||
version "0.1.28"
|
||||
resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.28.tgz#e3cd892383fba3b8904c40518bbe1265a50753f2"
|
||||
integrity sha512-V6Mnml8v09QALx6K0elJ7o9K/MkVDtW3t6L+7Ou/JcWtb3xwId2AH4FeOceySd2JaO87IMw4+6vSZxLm34LPbw==
|
||||
"@swc/types@^0.1.27":
|
||||
version "0.1.27"
|
||||
resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.27.tgz#12080b0c426dea450634f202d9a3c82ac396e793"
|
||||
integrity sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==
|
||||
dependencies:
|
||||
"@swc/counter" "^0.1.3"
|
||||
|
||||
@@ -6188,10 +6181,10 @@ ansis@^3.2.0:
|
||||
resolved "https://registry.yarnpkg.com/ansis/-/ansis-3.17.0.tgz#fa8d9c2a93fe7d1177e0c17f9eeb562a58a832d7"
|
||||
integrity sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==
|
||||
|
||||
antd@^6.6.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/antd/-/antd-6.6.1.tgz#3235d76413b525b1f3287b87bdaf6ba0e7148521"
|
||||
integrity sha512-QHIHYoUk9N9nJy1T9fyxWKjY0qApdTEDd/6lzqYng8Uryv9FejNmbhKvYF7obGqB+TuLXQsPVF7fOVgyzM1KrQ==
|
||||
antd@^6.6.0:
|
||||
version "6.6.0"
|
||||
resolved "https://registry.yarnpkg.com/antd/-/antd-6.6.0.tgz#8acb84c54b36594b5c1a9084c8acb6a03b79961b"
|
||||
integrity sha512-UDwWIbpmrCHB9ZQ+bPh4vQfB6DTI2ulIyoQ0Tc9xxalFblttiNGHl3ySBD9SyV/8+gUjFzfSx1+iU1Fog2i46w==
|
||||
dependencies:
|
||||
"@ant-design/colors" "^8.0.1"
|
||||
"@ant-design/cssinjs" "^2.1.2"
|
||||
@@ -6224,16 +6217,16 @@ antd@^6.6.1:
|
||||
"@rc-component/rate" "~1.0.1"
|
||||
"@rc-component/resize-observer" "^1.1.2"
|
||||
"@rc-component/segmented" "~1.3.0"
|
||||
"@rc-component/select" "~1.10.1"
|
||||
"@rc-component/select" "~1.10.0"
|
||||
"@rc-component/slider" "~1.1.1"
|
||||
"@rc-component/steps" "~1.2.2"
|
||||
"@rc-component/switch" "~1.0.3"
|
||||
"@rc-component/table" "~1.11.1"
|
||||
"@rc-component/table" "~1.11.0"
|
||||
"@rc-component/tabs" "~1.12.0"
|
||||
"@rc-component/tooltip" "~1.5.0"
|
||||
"@rc-component/tour" "~2.4.0"
|
||||
"@rc-component/tree" "~1.4.0"
|
||||
"@rc-component/tree-select" "~1.16.1"
|
||||
"@rc-component/tree-select" "~1.16.0"
|
||||
"@rc-component/trigger" "^3.10.1"
|
||||
"@rc-component/upload" "~1.1.1"
|
||||
"@rc-component/util" "^1.12.0"
|
||||
@@ -6529,10 +6522,10 @@ base64-js@^1.3.1, base64-js@^1.5.1:
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
baseline-browser-mapping@^2.10.38, baseline-browser-mapping@^2.11.17, baseline-browser-mapping@^2.9.19:
|
||||
version "2.11.17"
|
||||
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.17.tgz#a168205490077c5d7c542f1610016af4ade8f875"
|
||||
integrity sha512-KAUDn1OSS0fmPlGO+NOUMRcOQ/b/shUBH3OgkG73mPgdf+JD/BQ6fHboGxNOxnUmlwcq+lLq3dTkayRPuSfXwg==
|
||||
baseline-browser-mapping@^2.10.38, baseline-browser-mapping@^2.11.13, baseline-browser-mapping@^2.9.19:
|
||||
version "2.11.13"
|
||||
resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.13.tgz#660073103c1bee93e54df55f117b7528adf6af19"
|
||||
integrity sha512-k9HNuUVMlqVjQ9UHzfPjIqiDbWw7WqT1AoT7GL8VwvF3r0ZfArtgiSPAlmupyNquNgOJHTuH4CKYf8ttMTWBTQ==
|
||||
|
||||
batch@0.6.1:
|
||||
version "0.6.1"
|
||||
@@ -6614,9 +6607,9 @@ boxen@^7.0.0:
|
||||
wrap-ansi "^8.1.0"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.18"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.18.tgz#3ce74d89885136be1535341f8c3d4425c29a5cab"
|
||||
integrity sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==
|
||||
version "1.1.15"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.15.tgz#a6d90d54067236e5f42570a3b7378d594d9b7738"
|
||||
integrity sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
@@ -9181,10 +9174,10 @@ globals@^14.0.0:
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e"
|
||||
integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
|
||||
|
||||
globals@^17.11.0:
|
||||
version "17.11.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-17.11.0.tgz#d643485bb30220d7751e511cf4f68c73d3870d87"
|
||||
integrity sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==
|
||||
globals@^17.10.0:
|
||||
version "17.10.0"
|
||||
resolved "https://registry.yarnpkg.com/globals/-/globals-17.10.0.tgz#f9dbd847ae99e236f98b13095e2426ac3b25a45c"
|
||||
integrity sha512-V0kztuWST2k8A/VbxAY8+L+7+Rgo3fyA24IHRLrZp7HOzJjV0gHSaZUjK9lpP/IrBSNite2tZ1prhRkinRu1CA==
|
||||
|
||||
globalthis@^1.0.4:
|
||||
version "1.0.4"
|
||||
@@ -10298,10 +10291,10 @@ js-yaml@4.1.0, js-yaml@=4.3.1, js-yaml@^4.1.0, js-yaml@^4.1.1, js-yaml@^4.2.0, j
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
||||
js-yaml@^5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-5.3.0.tgz#526430a6da31065127528ae695ce168cfc5f91f0"
|
||||
integrity sha512-muutsYr+e2+d3rTgUGslq5rxbBlUy3cJ61IsHag2QNDQV+7zXWjkUpmALIajhrlLlrgRUiymj6U3zUr/TMK84Q==
|
||||
js-yaml@^5.2.3:
|
||||
version "5.2.3"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-5.2.3.tgz#0942ae8f507e22eb0e54624871789cd477106e54"
|
||||
integrity sha512-n+mUVyUX5bVv7G/G2zyIHOhdxfuU1dY2NOFzTQUWiMUbFss8b57NFlgCCaggU78wSw5KVS9cllzeLyzyR+n5nw==
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
||||
@@ -11856,10 +11849,10 @@ neotraverse@0.6.15:
|
||||
resolved "https://registry.yarnpkg.com/neotraverse/-/neotraverse-0.6.15.tgz#dc4abb64700c52440f13bc53635b559862420360"
|
||||
integrity sha512-HZpdkco+JeXq0G+WWpMJ4NsX3pqb5O7eR9uGz3FfoFt+LYzU8iRWp49nJtud6hsDoywM8tIrDo3gjgmOqJA8LA==
|
||||
|
||||
neotraverse@=1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/neotraverse/-/neotraverse-1.0.1.tgz#7c89b43f6504ef85928c718f578c68621576d194"
|
||||
integrity sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==
|
||||
neotraverse@=0.6.18:
|
||||
version "0.6.18"
|
||||
resolved "https://registry.yarnpkg.com/neotraverse/-/neotraverse-0.6.18.tgz#abcb33dda2e8e713cf6321b29405e822230cdb30"
|
||||
integrity sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==
|
||||
|
||||
no-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
@@ -12244,57 +12237,57 @@ oxc-parser@^0.127.0:
|
||||
"@oxc-parser/binding-win32-ia32-msvc" "0.127.0"
|
||||
"@oxc-parser/binding-win32-x64-msvc" "0.127.0"
|
||||
|
||||
oxc-resolver@11.21.2:
|
||||
version "11.21.2"
|
||||
resolved "https://registry.yarnpkg.com/oxc-resolver/-/oxc-resolver-11.21.2.tgz#06f49557c98adb97133d85362797c460ef028057"
|
||||
integrity sha512-w5tLwYN3Zo24w5EeWJjJWZOwhYqTtC8PS2B1tIt7BZUuqTIcU07sQValbDw+rq7+AuAGzOHklgK+ifsy4lpXfw==
|
||||
oxc-resolver@^11.19.1:
|
||||
version "11.23.0"
|
||||
resolved "https://registry.yarnpkg.com/oxc-resolver/-/oxc-resolver-11.23.0.tgz#bb9e32fa028dbde0584b5964dbc59de2878ac171"
|
||||
integrity sha512-f0+l598CJMOLnYPXsXxttJALH0ljtivdRMKtvHhxRuWa5FYmw5+qODARl8oYjMC/brpzKcrpdORsOBrTqhBZ9A==
|
||||
optionalDependencies:
|
||||
"@oxc-resolver/binding-android-arm-eabi" "11.21.2"
|
||||
"@oxc-resolver/binding-android-arm64" "11.21.2"
|
||||
"@oxc-resolver/binding-darwin-arm64" "11.21.2"
|
||||
"@oxc-resolver/binding-darwin-x64" "11.21.2"
|
||||
"@oxc-resolver/binding-freebsd-x64" "11.21.2"
|
||||
"@oxc-resolver/binding-linux-arm-gnueabihf" "11.21.2"
|
||||
"@oxc-resolver/binding-linux-arm-musleabihf" "11.21.2"
|
||||
"@oxc-resolver/binding-linux-arm64-gnu" "11.21.2"
|
||||
"@oxc-resolver/binding-linux-arm64-musl" "11.21.2"
|
||||
"@oxc-resolver/binding-linux-ppc64-gnu" "11.21.2"
|
||||
"@oxc-resolver/binding-linux-riscv64-gnu" "11.21.2"
|
||||
"@oxc-resolver/binding-linux-riscv64-musl" "11.21.2"
|
||||
"@oxc-resolver/binding-linux-s390x-gnu" "11.21.2"
|
||||
"@oxc-resolver/binding-linux-x64-gnu" "11.21.2"
|
||||
"@oxc-resolver/binding-linux-x64-musl" "11.21.2"
|
||||
"@oxc-resolver/binding-openharmony-arm64" "11.21.2"
|
||||
"@oxc-resolver/binding-wasm32-wasi" "11.21.2"
|
||||
"@oxc-resolver/binding-win32-arm64-msvc" "11.21.2"
|
||||
"@oxc-resolver/binding-win32-x64-msvc" "11.21.2"
|
||||
"@oxc-resolver/binding-android-arm-eabi" "11.23.0"
|
||||
"@oxc-resolver/binding-android-arm64" "11.23.0"
|
||||
"@oxc-resolver/binding-darwin-arm64" "11.23.0"
|
||||
"@oxc-resolver/binding-darwin-x64" "11.23.0"
|
||||
"@oxc-resolver/binding-freebsd-x64" "11.23.0"
|
||||
"@oxc-resolver/binding-linux-arm-gnueabihf" "11.23.0"
|
||||
"@oxc-resolver/binding-linux-arm-musleabihf" "11.23.0"
|
||||
"@oxc-resolver/binding-linux-arm64-gnu" "11.23.0"
|
||||
"@oxc-resolver/binding-linux-arm64-musl" "11.23.0"
|
||||
"@oxc-resolver/binding-linux-ppc64-gnu" "11.23.0"
|
||||
"@oxc-resolver/binding-linux-riscv64-gnu" "11.23.0"
|
||||
"@oxc-resolver/binding-linux-riscv64-musl" "11.23.0"
|
||||
"@oxc-resolver/binding-linux-s390x-gnu" "11.23.0"
|
||||
"@oxc-resolver/binding-linux-x64-gnu" "11.23.0"
|
||||
"@oxc-resolver/binding-linux-x64-musl" "11.23.0"
|
||||
"@oxc-resolver/binding-openharmony-arm64" "11.23.0"
|
||||
"@oxc-resolver/binding-wasm32-wasi" "11.23.0"
|
||||
"@oxc-resolver/binding-win32-arm64-msvc" "11.23.0"
|
||||
"@oxc-resolver/binding-win32-x64-msvc" "11.23.0"
|
||||
|
||||
oxfmt@^0.64.0:
|
||||
version "0.64.0"
|
||||
resolved "https://registry.yarnpkg.com/oxfmt/-/oxfmt-0.64.0.tgz#666a5148cdf7385007cd46e35e8ff8f94ecfd96b"
|
||||
integrity sha512-XZ4GFBN/PLbXKq+0zrgpQfPKYuJlUuj+nzZJY7UpIbFMNyefNLCdN9EwViycNqnYcv0wrn0jXcQLlqJp8RCKBg==
|
||||
oxfmt@^0.63.0:
|
||||
version "0.63.0"
|
||||
resolved "https://registry.yarnpkg.com/oxfmt/-/oxfmt-0.63.0.tgz#c7338e6c43a68d5cf8dc61c08b617d77cb54e323"
|
||||
integrity sha512-kgdDwv35wvVf6554U2Ab8Jnd0zTM+TsEQWwaB70RAjK3gICFAFGO+2Hd3Be27GMoXj3XRL9IKSNRVl7KBQL6iw==
|
||||
dependencies:
|
||||
tinypool "2.1.0"
|
||||
optionalDependencies:
|
||||
"@oxfmt/binding-android-arm-eabi" "0.64.0"
|
||||
"@oxfmt/binding-android-arm64" "0.64.0"
|
||||
"@oxfmt/binding-darwin-arm64" "0.64.0"
|
||||
"@oxfmt/binding-darwin-x64" "0.64.0"
|
||||
"@oxfmt/binding-freebsd-x64" "0.64.0"
|
||||
"@oxfmt/binding-linux-arm-gnueabihf" "0.64.0"
|
||||
"@oxfmt/binding-linux-arm-musleabihf" "0.64.0"
|
||||
"@oxfmt/binding-linux-arm64-gnu" "0.64.0"
|
||||
"@oxfmt/binding-linux-arm64-musl" "0.64.0"
|
||||
"@oxfmt/binding-linux-ppc64-gnu" "0.64.0"
|
||||
"@oxfmt/binding-linux-riscv64-gnu" "0.64.0"
|
||||
"@oxfmt/binding-linux-riscv64-musl" "0.64.0"
|
||||
"@oxfmt/binding-linux-s390x-gnu" "0.64.0"
|
||||
"@oxfmt/binding-linux-x64-gnu" "0.64.0"
|
||||
"@oxfmt/binding-linux-x64-musl" "0.64.0"
|
||||
"@oxfmt/binding-openharmony-arm64" "0.64.0"
|
||||
"@oxfmt/binding-win32-arm64-msvc" "0.64.0"
|
||||
"@oxfmt/binding-win32-ia32-msvc" "0.64.0"
|
||||
"@oxfmt/binding-win32-x64-msvc" "0.64.0"
|
||||
"@oxfmt/binding-android-arm-eabi" "0.63.0"
|
||||
"@oxfmt/binding-android-arm64" "0.63.0"
|
||||
"@oxfmt/binding-darwin-arm64" "0.63.0"
|
||||
"@oxfmt/binding-darwin-x64" "0.63.0"
|
||||
"@oxfmt/binding-freebsd-x64" "0.63.0"
|
||||
"@oxfmt/binding-linux-arm-gnueabihf" "0.63.0"
|
||||
"@oxfmt/binding-linux-arm-musleabihf" "0.63.0"
|
||||
"@oxfmt/binding-linux-arm64-gnu" "0.63.0"
|
||||
"@oxfmt/binding-linux-arm64-musl" "0.63.0"
|
||||
"@oxfmt/binding-linux-ppc64-gnu" "0.63.0"
|
||||
"@oxfmt/binding-linux-riscv64-gnu" "0.63.0"
|
||||
"@oxfmt/binding-linux-riscv64-musl" "0.63.0"
|
||||
"@oxfmt/binding-linux-s390x-gnu" "0.63.0"
|
||||
"@oxfmt/binding-linux-x64-gnu" "0.63.0"
|
||||
"@oxfmt/binding-linux-x64-musl" "0.63.0"
|
||||
"@oxfmt/binding-openharmony-arm64" "0.63.0"
|
||||
"@oxfmt/binding-win32-arm64-msvc" "0.63.0"
|
||||
"@oxfmt/binding-win32-ia32-msvc" "0.63.0"
|
||||
"@oxfmt/binding-win32-x64-msvc" "0.63.0"
|
||||
|
||||
p-cancelable@^3.0.0:
|
||||
version "3.0.0"
|
||||
@@ -13590,7 +13583,7 @@ react-modal@^3.16.3:
|
||||
react-lifecycles-compat "^3.0.0"
|
||||
warning "^4.0.3"
|
||||
|
||||
react-redux@^9.2.0, react-redux@^9.3.0:
|
||||
react-redux@^9.2.0:
|
||||
version "9.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.3.0.tgz#a30113bb6d95c0a715d54dda4308d450fca6ce09"
|
||||
integrity sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==
|
||||
@@ -14106,10 +14099,10 @@ reselect@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524"
|
||||
integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==
|
||||
|
||||
reselect@^5.1.0, reselect@^5.1.1, reselect@^5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.3.0.tgz#0a3e3ed4436bdf2ab7c5e0f392dab2c062595d61"
|
||||
integrity sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==
|
||||
reselect@^5.1.0, reselect@^5.1.1, reselect@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.2.0.tgz#f380ef7664332d26ea06c1cba04bdbbdcaa955f1"
|
||||
integrity sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==
|
||||
|
||||
resize-observer-polyfill@1.5.1:
|
||||
version "1.5.1"
|
||||
@@ -14790,10 +14783,10 @@ stop-iteration-iterator@^1.1.0:
|
||||
es-errors "^1.3.0"
|
||||
internal-slot "^1.1.0"
|
||||
|
||||
storybook@^10.5.10:
|
||||
version "10.5.10"
|
||||
resolved "https://registry.yarnpkg.com/storybook/-/storybook-10.5.10.tgz#97e9b4a71b4df7732e82d64edffe07f9e8d70083"
|
||||
integrity sha512-Rz8k9ejFHsi7lbtJTaxZlhCUz4GkbJIKEoKDjXeLfr/ZhXip73E6keKxW0KH8iGeKiCqHAbJCV4YIQrxTOLiig==
|
||||
storybook@^10.5.7:
|
||||
version "10.5.7"
|
||||
resolved "https://registry.yarnpkg.com/storybook/-/storybook-10.5.7.tgz#adfc465e51f337291c095278c23f1b8024ef2da7"
|
||||
integrity sha512-oiKvWIwIoOhFP1i6dASYyMXwPHKEtVZMshqSB7EvIVYjWRh0l9H7gHEt1z4Gh2rLGFMekWdsm4s94rvwpR7gkg==
|
||||
dependencies:
|
||||
"@storybook/global" "^5.0.0"
|
||||
"@storybook/icons" "^2.0.2"
|
||||
@@ -14807,7 +14800,7 @@ storybook@^10.5.10:
|
||||
jsonc-parser "^3.3.1"
|
||||
open "^10.2.0"
|
||||
oxc-parser "^0.127.0"
|
||||
oxc-resolver "11.21.2"
|
||||
oxc-resolver "^11.19.1"
|
||||
recast "^0.23.5"
|
||||
semver "^7.7.3"
|
||||
use-sync-external-store "^1.5.0"
|
||||
@@ -15064,10 +15057,10 @@ svgo@^3.0.2, svgo@^3.2.0:
|
||||
picocolors "^1.0.0"
|
||||
sax "^1.5.0"
|
||||
|
||||
swagger-client@^3.38.0:
|
||||
version "3.38.0"
|
||||
resolved "https://registry.yarnpkg.com/swagger-client/-/swagger-client-3.38.0.tgz#542431f02d809b49115272ff8b9e48d545b9f53c"
|
||||
integrity sha512-n7aykm1BEdQ3fKePJJx63UGjYe8/5fuxFMi3qZP4OJGZvzljKvmhxNwIF/MB71sF/lop9NeWZReKvPib9CY+2g==
|
||||
swagger-client@^3.37.8:
|
||||
version "3.37.8"
|
||||
resolved "https://registry.yarnpkg.com/swagger-client/-/swagger-client-3.37.8.tgz#26c24c89cbfda7459f6afb53bdfcb6d8dbe9ac82"
|
||||
integrity sha512-uoKwfq+8DvWVDhoALDrEtex9f26Yi2VkvEFjsrMHd8Gl+TcApJkVXtNiE35p5JQjMsvwkvr1eLVlOFNF4GL1bQ==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.22.15"
|
||||
"@scarf/scarf" "=1.4.0"
|
||||
@@ -15081,7 +15074,7 @@ swagger-client@^3.38.0:
|
||||
deepmerge "~4.3.0"
|
||||
fast-json-patch "^3.0.0-1"
|
||||
js-yaml "^4.2.0"
|
||||
neotraverse "=1.0.1"
|
||||
neotraverse "=0.6.18"
|
||||
node-abort-controller "^3.1.1"
|
||||
openapi-path-templating "^2.2.1"
|
||||
openapi-server-url-templating "^1.3.0"
|
||||
@@ -15110,10 +15103,10 @@ swagger-client@^3.38.0:
|
||||
"@swagger-api/apidom-parser-adapter-openapi-yaml-3-2" "^1.12.0"
|
||||
"@swagger-api/apidom-parser-adapter-yaml-1-2" "^1.12.0"
|
||||
|
||||
swagger-ui-react@^5.32.14:
|
||||
version "5.32.14"
|
||||
resolved "https://registry.yarnpkg.com/swagger-ui-react/-/swagger-ui-react-5.32.14.tgz#31b69b0f6910e87dbcc81886208061ca1f72e034"
|
||||
integrity sha512-6LAVBeC78DplbJ7kutm/YeBYo22nPzGOca4bIZAvQG4w2eSetnYDdazaUfY0qzQUlg/H90HnYZX3rg67EmENOw==
|
||||
swagger-ui-react@^5.32.13:
|
||||
version "5.32.13"
|
||||
resolved "https://registry.yarnpkg.com/swagger-ui-react/-/swagger-ui-react-5.32.13.tgz#04c96140b0a2d4ea01ebec4d4cfc655d5ed9a500"
|
||||
integrity sha512-XIDl+Ny6kE1N8wpSPiOFrjPfAevs4GR4XmV6BT6NLMikkMFIbIVocWbA8pnKYyYXQe8Rccfli5o2zDfySw0FnQ==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.27.1"
|
||||
"@scarf/scarf" "=1.4.0"
|
||||
@@ -15136,7 +15129,7 @@ swagger-ui-react@^5.32.14:
|
||||
react-immutable-proptypes "2.2.0"
|
||||
react-immutable-pure-component "^2.2.0"
|
||||
react-inspector "^6.0.1"
|
||||
react-redux "^9.3.0"
|
||||
react-redux "^9.2.0"
|
||||
react-syntax-highlighter "^16.0.0"
|
||||
redux "^5.0.1"
|
||||
redux-immutable "^4.0.0"
|
||||
@@ -15144,7 +15137,7 @@ swagger-ui-react@^5.32.14:
|
||||
reselect "^5.1.1"
|
||||
serialize-error "^8.1.0"
|
||||
sha.js "^2.4.12"
|
||||
swagger-client "^3.38.0"
|
||||
swagger-client "^3.37.8"
|
||||
url-parse "^1.5.10"
|
||||
xml "=1.0.1"
|
||||
xml-but-prettier "^1.0.1"
|
||||
|
||||
+19
-34
@@ -16,7 +16,7 @@
|
||||
# under the License.
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=84.0.0", "wheel"]
|
||||
requires = ["setuptools>=40.9.0", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
@@ -67,7 +67,7 @@ dependencies = [
|
||||
"flask-sqlalchemy>=3.1.1, <4.0",
|
||||
"flask-wtf>=1.3.0, <2.0",
|
||||
"geopy",
|
||||
"greenlet<=3.5.5, >=3.5.5",
|
||||
"greenlet<=3.5.4, >=3.5.4",
|
||||
"gunicorn>=26.0.0, <27; sys_platform != 'win32'",
|
||||
"hashids>=1.3.1, <2",
|
||||
# holidays>=0.45 required for security fix
|
||||
@@ -94,16 +94,16 @@ dependencies = [
|
||||
"parsedatetime",
|
||||
"paramiko>=3.4.0, <4.0", # 4.0 removed DSSKey, still referenced by sshtunnel
|
||||
"pgsanity",
|
||||
"Pillow>=12.3.0, <13", # raise floor to match resolved pin; closes SCA false-positive on 11.x-range CVEs already fixed in 12.3.0
|
||||
"Pillow>=11.0.0, <13",
|
||||
"polyline>=2.0.4, <3.0",
|
||||
"pydantic>=2.8.0",
|
||||
"pyparsing>=3.3.2, <4",
|
||||
"python-dateutil",
|
||||
"python-dotenv", # optional dependencies for Flask but required for Superset, see https://flask.palletsprojects.com/en/stable/installation/#optional-dependencies
|
||||
"pygeohash",
|
||||
"pyarrow>=25.0.1, <26", # before upgrading pyarrow, check that all db dependencies support this, see e.g. https://github.com/apache/superset/pull/34693
|
||||
"pyarrow>=24.0.0, <26", # before upgrading pyarrow, check that all db dependencies support this, see e.g. https://github.com/apache/superset/pull/34693
|
||||
"pyyaml>=6.0.3, <7.0.0",
|
||||
"PyJWT>=2.13.0, <3.0", # raise floor to match resolved pin; closes SCA false-positive on 2.4.x-range CVEs already fixed in 2.13.0
|
||||
"PyJWT>=2.4.0, <3.0",
|
||||
"redis>=5.0.0, <9.0",
|
||||
"rison>=2.0.1, <3.0",
|
||||
|
||||
@@ -111,10 +111,10 @@ dependencies = [
|
||||
"sshtunnel>=0.4.0, <0.5",
|
||||
"simplejson>=4.1.1",
|
||||
"slack_sdk>=3.43.0, <4",
|
||||
"sqlalchemy>=2.0.52, <2.1",
|
||||
"sqlalchemy>=2.0.0, <2.1",
|
||||
"sqlalchemy-continuum>=1.6.0, <2.0.0",
|
||||
"sqlalchemy-utils>=0.42.1, <0.43", # expanding lowerbound to work with pydoris
|
||||
"sqlglot>=30.17.0, <31", # 30.16.0 adds Trino inline UDF IF/CASE routine statement parsing
|
||||
"sqlglot>=30.16.0, <31", # 30.16.0 adds Trino inline UDF IF/CASE routine statement parsing
|
||||
# newer pandas needs 0.9+
|
||||
"tabulate>=0.10.0, <1.0",
|
||||
"typing-extensions>=4.16.0, <5",
|
||||
@@ -141,18 +141,8 @@ bigquery = [
|
||||
"sqlalchemy-bigquery>=1.17.2",
|
||||
"google-cloud-bigquery>=3.42.3",
|
||||
]
|
||||
clickhouse = ["clickhouse-connect>=1.7.2, <2.0"]
|
||||
# The `cockroachdb` PyPI package (last released 2021) is abandoned and its
|
||||
# SQLAlchemy dialect cannot even import under SQLAlchemy 2.0 (it references
|
||||
# sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2, removed in
|
||||
# 2.0). sqlalchemy-cockroachdb is the actively maintained replacement,
|
||||
# already linked from CockroachDbEngineSpec.metadata's docs_url, and
|
||||
# registers the same `cockroachdb` SQLAlchemy dialect entry point.
|
||||
# sqlalchemy-cockroachdb depends only on SQLAlchemy itself, not on a DBAPI
|
||||
# driver, so psycopg2-binary is pinned alongside it (matching the `postgres`
|
||||
# extra) to keep this extra self-contained -- CockroachDB speaks the
|
||||
# PostgreSQL wire protocol, so psycopg2 is what actually opens connections.
|
||||
cockroachdb = ["sqlalchemy-cockroachdb>=2.0.0, <3", "psycopg2-binary==2.9.12"]
|
||||
clickhouse = ["clickhouse-connect>=1.6.0, <2.0"]
|
||||
cockroachdb = ["cockroachdb>=0.3.5, <0.4"]
|
||||
crate = ["sqlalchemy-cratedb>=0.43.1, <1"]
|
||||
# sqlalchemy-d1's only release (0.1.0, Nov 2025) pins sqlalchemy<2,>=1.4,
|
||||
# explicitly excluding SQLAlchemy 2.0. See superset/db_engine_specs/d1.py's
|
||||
@@ -195,12 +185,11 @@ excel = ["xlrd>=2.0.2, <2.1"]
|
||||
# installing this extra is only required to actually run exports.
|
||||
excel-export = ["boto3"]
|
||||
fastmcp = [
|
||||
"fastmcp>=3.4.7,<4.0",
|
||||
"mcp>=1.29.1,<3.0",
|
||||
"fastmcp>=3.4.6,<4.0",
|
||||
# tiktoken backs the response-size-guard token estimator. Without
|
||||
# it, the middleware falls back to a coarser character-based
|
||||
# heuristic that under-counts JSON-heavy MCP responses.
|
||||
"tiktoken>=0.14.0,<1.0",
|
||||
"tiktoken>=0.13.0,<1.0",
|
||||
]
|
||||
# sqlalchemy-firebird >=2.0.0 unconditionally requires SQLAlchemy 2.0 on
|
||||
# Python >=3.8 (which covers Superset's >=3.11 floor), with no dual-compat
|
||||
@@ -208,7 +197,7 @@ fastmcp = [
|
||||
# landed (discussion #40273).
|
||||
firebird = ["sqlalchemy-firebird>=2.2.0"]
|
||||
firebolt = ["firebolt-sqlalchemy>=1.1.2, <2"]
|
||||
gevent = ["gevent>=26.8.0"]
|
||||
gevent = ["gevent>=26.7.0"]
|
||||
gsheets = ["shillelagh[gsheetsapi]>=1.4.5, <2"]
|
||||
hana = ["hdbcli==2.29.25", "sqlalchemy_hana==3.0.3"]
|
||||
hive = [
|
||||
@@ -229,12 +218,8 @@ motherduck = ["apache-superset[duckdb]"]
|
||||
mysql = ["mysqlclient>=2.2.8, <3"]
|
||||
ocient = [
|
||||
# Closed-source vendor package with no public changelog; permissive
|
||||
# unpinned sqlalchemy>=1.4 declared. Verified compatible with SQLAlchemy
|
||||
# 2.0 against pyocient>=3.9.0 (discussion #40273): dialect construction,
|
||||
# error extraction, and GIS-type sanitization all pass under 2.0.52. Note
|
||||
# pyocient 3.9.0 relocated its geo-type classes from private top-level
|
||||
# names (pyocient._STPoint) to public ones under pyocient.api
|
||||
# (pyocient.api.STPoint), which is unrelated to the SQLAlchemy bump.
|
||||
# unpinned sqlalchemy>=1.4 declared, but SQLAlchemy 2.0 support is
|
||||
# unverified. Lower confidence than the other bumps in this PR.
|
||||
"sqlalchemy-ocient>=3.0.0, <4",
|
||||
"pyocient>=3.9.0, <4",
|
||||
"shapely",
|
||||
@@ -246,8 +231,8 @@ pinot = ["pinotdb>=5.0.0, <10.0.0"]
|
||||
playwright = ["playwright>=1.62.0, <2"]
|
||||
postgres = ["psycopg2-binary==2.9.12"]
|
||||
presto = ["pyhive[presto]>=0.6.5"]
|
||||
trino = ["trino>=0.339.0"]
|
||||
prophet = ["prophet>=1.4.0, <2"]
|
||||
trino = ["trino>=0.338.0"]
|
||||
prophet = ["prophet>=1.3.0, <2"]
|
||||
# sqlalchemy-redshift cuts hard from SQLAlchemy 1.4-only (0.8.x) to 2.0-only
|
||||
# (>=1.0.0) with no dual-compat release. Bumped now that Superset's own
|
||||
# SQLAlchemy 2.0 core bump has landed (discussion #40273).
|
||||
@@ -260,7 +245,7 @@ risingwave = ["sqlalchemy-risingwave>=2.0.0"]
|
||||
shillelagh = ["shillelagh[all]>=1.4.5, <2"]
|
||||
singlestore = ["sqlalchemy-singlestoredb>=1.2.1, <2"]
|
||||
snowflake = ["snowflake-sqlalchemy>=1.11.0, <2"]
|
||||
sqlite = ["syntaqlite>=0.9.0,<0.10.0"]
|
||||
sqlite = ["syntaqlite>=0.7.0,<0.8.0"]
|
||||
spark = [
|
||||
"pyhive[hive_pure_sasl]>=0.7",
|
||||
"tableschema",
|
||||
@@ -270,7 +255,7 @@ tdengine = [
|
||||
"taospy>=2.8.10",
|
||||
"taos-ws-py>=0.7.0"
|
||||
]
|
||||
teradata = ["teradatasql>=20.0.0.66"]
|
||||
teradata = ["teradatasql>=20.0.0.64"]
|
||||
thumbnails = [] # deprecated, will be removed in 7.0
|
||||
vertica = ["sqlalchemy-vertica-python>= 0.6.3, < 0.7"]
|
||||
netezza = ["nzalchemy>= 11.1.2, < 11.2"]
|
||||
@@ -304,7 +289,7 @@ development = [
|
||||
"ruff",
|
||||
"sqloxide",
|
||||
"statsd",
|
||||
"syntaqlite>=0.9.0,<0.10.0",
|
||||
"syntaqlite>=0.7.0,<0.8.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
|
||||
@@ -30,7 +30,7 @@ cryptography>=50.0.0,<51.0.0
|
||||
# Security: Snyk - XSS vulnerability in Mako templates
|
||||
mako>=1.4.1,<2.0.0
|
||||
# Security: CVE-2024-52338 (CRITICAL) - Deserialization of untrusted data in IPC/Parquet readers
|
||||
pyarrow>=25.0.1,<26.0.0
|
||||
pyarrow>=24.0.0,<26.0.0
|
||||
# Security: CVE-2026-27459 - pyopenssl certificate validation
|
||||
pyopenssl>=26.0.0,<27.0.0
|
||||
# Security: CVE-2026-25645 (MEDIUM) - Insecure Temporary File
|
||||
@@ -52,11 +52,11 @@ marshmallow-sqlalchemy>=1.5.0
|
||||
# needed for python 3.12 support
|
||||
openapi-schema-validator>=0.6.3
|
||||
|
||||
# Pin setuptools <85 until all dependencies migrate from pkg_resources to importlib.metadata
|
||||
# Pin setuptools <81 until all dependencies migrate from pkg_resources to importlib.metadata
|
||||
# pkg_resources is deprecated and will be removed in setuptools 81+ (around 2025-11-30)
|
||||
# Known affected packages: Preset's 'clients' package
|
||||
# See docs/docs/contributing/pkg-resources-migration.md for details
|
||||
setuptools<85
|
||||
setuptools<81
|
||||
|
||||
# google-auth 2.53+ dropped its transitive dependency on cachetools, which is
|
||||
# imported directly by superset.db_engine_specs.aws_iam. We declare cachetools
|
||||
|
||||
@@ -163,16 +163,16 @@ google-auth==2.53.0
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# shillelagh
|
||||
greenlet==3.5.5
|
||||
greenlet==3.5.4
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# shillelagh
|
||||
# sqlalchemy
|
||||
gunicorn==26.2.0
|
||||
gunicorn==26.0.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
hashids==1.3.1
|
||||
# via apache-superset (pyproject.toml)
|
||||
holidays==0.103
|
||||
holidays==0.102
|
||||
# via apache-superset (pyproject.toml)
|
||||
humanize==4.12.3
|
||||
# via apache-superset (pyproject.toml)
|
||||
@@ -222,7 +222,7 @@ markupsafe==3.0.2
|
||||
# mako
|
||||
# werkzeug
|
||||
# wtforms
|
||||
marshmallow==4.3.1
|
||||
marshmallow==4.3.0
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
@@ -263,6 +263,7 @@ packaging==25.0
|
||||
# apache-superset (pyproject.toml)
|
||||
# apispec
|
||||
# deprecation
|
||||
# gunicorn
|
||||
# kombu
|
||||
# limits
|
||||
# shillelagh
|
||||
@@ -286,7 +287,7 @@ prison==0.2.1
|
||||
# via flask-appbuilder
|
||||
prompt-toolkit==3.0.51
|
||||
# via click-repl
|
||||
pyarrow==25.0.1
|
||||
pyarrow==25.0.0
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# apache-superset (pyproject.toml)
|
||||
@@ -366,7 +367,7 @@ rpds-py==0.25.0
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
setuptools==84.0.0
|
||||
setuptools==80.9.0
|
||||
# via -r requirements/base.in
|
||||
shillelagh==1.4.5
|
||||
# via apache-superset (pyproject.toml)
|
||||
@@ -380,7 +381,7 @@ six==1.17.0
|
||||
# wtforms-json
|
||||
slack-sdk==3.43.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
sqlalchemy==2.0.52
|
||||
sqlalchemy==2.0.51
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# alembic
|
||||
@@ -398,7 +399,7 @@ sqlalchemy-utils==0.42.1
|
||||
# apache-superset (pyproject.toml)
|
||||
# apache-superset-core
|
||||
# flask-appbuilder
|
||||
sqlglot==30.17.0
|
||||
sqlglot==30.16.0
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# apache-superset-core
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
-e .[development,bigquery,cockroachdb,druid,duckdb,fastmcp,gevent,gsheets,mysql,postgres,presto,prophet,trino,thumbnails]
|
||||
-e .[development,bigquery,druid,duckdb,fastmcp,gevent,gsheets,mysql,postgres,presto,prophet,trino,thumbnails]
|
||||
-e ./superset-extensions-cli[test]
|
||||
|
||||
@@ -337,7 +337,7 @@ geopy==2.4.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
gevent==26.8.0
|
||||
gevent==26.7.0
|
||||
# via apache-superset
|
||||
google-api-core==2.33.0
|
||||
# via
|
||||
@@ -375,7 +375,7 @@ googleapis-common-protos==1.66.0
|
||||
# via
|
||||
# google-api-core
|
||||
# grpcio-status
|
||||
greenlet==3.5.5
|
||||
greenlet==3.5.4
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -391,7 +391,7 @@ grpcio==1.83.0
|
||||
# grpcio-status
|
||||
grpcio-status==1.60.1
|
||||
# via google-api-core
|
||||
gunicorn==26.2.0
|
||||
gunicorn==26.0.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -403,7 +403,7 @@ hashids==1.3.1
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
holidays==0.103
|
||||
holidays==0.102
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -434,6 +434,8 @@ importlib-metadata==8.7.0
|
||||
# via
|
||||
# keyring
|
||||
# opentelemetry-api
|
||||
importlib-resources==6.5.2
|
||||
# via prophet
|
||||
iniconfig==2.0.0
|
||||
# via pytest
|
||||
isodate==0.7.2
|
||||
@@ -528,7 +530,7 @@ markupsafe==3.0.2
|
||||
# mako
|
||||
# werkzeug
|
||||
# wtforms
|
||||
marshmallow==4.3.1
|
||||
marshmallow==4.3.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -547,10 +549,8 @@ matplotlib==3.9.0
|
||||
# via prophet
|
||||
mccabe==0.7.0
|
||||
# via pylint
|
||||
mcp==1.29.1
|
||||
# via
|
||||
# apache-superset
|
||||
# fastmcp-slim
|
||||
mcp==1.24.0
|
||||
# via fastmcp-slim
|
||||
mdurl==0.1.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
@@ -624,6 +624,7 @@ packaging==25.0
|
||||
# duckdb-engine
|
||||
# fastmcp-slim
|
||||
# google-cloud-bigquery
|
||||
# gunicorn
|
||||
# kombu
|
||||
# limits
|
||||
# matplotlib
|
||||
@@ -663,7 +664,7 @@ pillow==12.3.0
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
# matplotlib
|
||||
pip==26.2.1
|
||||
pip==25.1.1
|
||||
# via apache-superset
|
||||
platformdirs==4.3.8
|
||||
# via
|
||||
@@ -692,7 +693,7 @@ prompt-toolkit==3.0.51
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# click-repl
|
||||
prophet==1.4.0
|
||||
prophet==1.3.0
|
||||
# via apache-superset
|
||||
proto-plus==1.25.0
|
||||
# via google-api-core
|
||||
@@ -710,7 +711,7 @@ psycopg2-binary==2.9.12
|
||||
# via apache-superset
|
||||
py-key-value-aio==0.4.4
|
||||
# via fastmcp-slim
|
||||
pyarrow==25.0.1
|
||||
pyarrow==25.0.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -920,7 +921,7 @@ secretstorage==3.5.0
|
||||
# via keyring
|
||||
semver==3.0.4
|
||||
# via apache-superset-extensions-cli
|
||||
setuptools==84.0.0
|
||||
setuptools==80.9.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# nodeenv
|
||||
@@ -949,7 +950,7 @@ slack-sdk==3.43.0
|
||||
# apache-superset
|
||||
sniffio==1.3.1
|
||||
# via anyio
|
||||
sqlalchemy==2.0.52
|
||||
sqlalchemy==2.0.51
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# alembic
|
||||
@@ -961,13 +962,10 @@ sqlalchemy==2.0.52
|
||||
# marshmallow-sqlalchemy
|
||||
# shillelagh
|
||||
# sqlalchemy-bigquery
|
||||
# sqlalchemy-cockroachdb
|
||||
# sqlalchemy-continuum
|
||||
# sqlalchemy-utils
|
||||
sqlalchemy-bigquery==1.17.2
|
||||
# via apache-superset
|
||||
sqlalchemy-cockroachdb==2.0.4
|
||||
# via apache-superset
|
||||
sqlalchemy-continuum==1.7.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
@@ -978,7 +976,7 @@ sqlalchemy-utils==0.42.1
|
||||
# apache-superset
|
||||
# apache-superset-core
|
||||
# flask-appbuilder
|
||||
sqlglot==30.17.0
|
||||
sqlglot==30.16.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -997,13 +995,13 @@ starlette==1.3.1
|
||||
# mcp
|
||||
statsd==4.0.1
|
||||
# via apache-superset
|
||||
syntaqlite==0.9.0
|
||||
syntaqlite==0.7.1
|
||||
# via apache-superset
|
||||
tabulate==0.10.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
tiktoken==0.14.0
|
||||
tiktoken==0.13.0
|
||||
# via apache-superset
|
||||
tomli-w==1.2.0
|
||||
# via apache-superset-extensions-cli
|
||||
@@ -1013,7 +1011,7 @@ tqdm==4.67.1
|
||||
# via
|
||||
# cmdstanpy
|
||||
# prophet
|
||||
trino==0.339.0
|
||||
trino==0.338.0
|
||||
# via apache-superset
|
||||
typing-extensions==4.16.0
|
||||
# via
|
||||
|
||||
@@ -93,7 +93,7 @@ def find_models(module: ModuleType) -> list[type[Model]]: # noqa: C901
|
||||
# where the current model is out-of-sync with the existing table after a
|
||||
# downgrade
|
||||
sqlalchemy_uri = current_app.config["SQLALCHEMY_DATABASE_URI"]
|
||||
engine = create_engine(sqlalchemy_uri)
|
||||
engine = create_engine(sqlalchemy_uri, future=True)
|
||||
Base = automap_base() # noqa: N806
|
||||
Base.prepare(engine, reflect=True)
|
||||
seen = set()
|
||||
|
||||
@@ -42,7 +42,6 @@ RETRYABLE_STATUS_CODES: frozenset[int] = frozenset({429})
|
||||
PATTERNS = {
|
||||
"python": [
|
||||
r"^\.github/workflows/.*python",
|
||||
r"^\.github/workflows/frontend-bundle-size-nightly\.yml$",
|
||||
r"^\.github/workflows/scheduled-docker-image-refresh\.yml$",
|
||||
r"^docker-compose-image-tag\.yml$",
|
||||
r"^tests/",
|
||||
|
||||
@@ -390,3 +390,19 @@ def get_session() -> scoped_session:
|
||||
:returns: The SQLAlchemy scoped session instance.
|
||||
"""
|
||||
raise NotImplementedError("Function will be replaced during initialization")
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Dataset",
|
||||
"Database",
|
||||
"Chart",
|
||||
"Dashboard",
|
||||
"User",
|
||||
"Role",
|
||||
"Group",
|
||||
"Tag",
|
||||
"KeyValue",
|
||||
"Subject",
|
||||
"CoreModel",
|
||||
"get_session",
|
||||
]
|
||||
|
||||
@@ -183,3 +183,10 @@ def prompt(
|
||||
"MCP prompt decorator not initialized. "
|
||||
"This decorator should be replaced during Superset startup."
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"tool",
|
||||
"prompt",
|
||||
"ToolAnnotations",
|
||||
]
|
||||
|
||||
@@ -55,3 +55,9 @@ class SavedQueryDAO(BaseDAO[SavedQuery]):
|
||||
model_cls = None
|
||||
base_filter = None
|
||||
id_column_name = "id"
|
||||
|
||||
|
||||
__all__ = [
|
||||
"QueryDAO",
|
||||
"SavedQueryDAO",
|
||||
]
|
||||
|
||||
@@ -71,3 +71,9 @@ class SavedQuery(CoreModel):
|
||||
database_id: int | None
|
||||
description: str | None
|
||||
user_id: int | None
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Query",
|
||||
"SavedQuery",
|
||||
]
|
||||
|
||||
@@ -46,3 +46,6 @@ def get_sqlglot_dialect(database: "Database") -> Dialects:
|
||||
:returns: The SQLGlot dialect enum corresponding to the database.
|
||||
"""
|
||||
raise NotImplementedError("Function will be replaced during initialization")
|
||||
|
||||
|
||||
__all__ = ["get_sqlglot_dialect"]
|
||||
|
||||
@@ -165,3 +165,13 @@ class AsyncQueryHandle:
|
||||
:returns: True if cancellation was successful
|
||||
"""
|
||||
raise NotImplementedError("Method will be replaced during initialization")
|
||||
|
||||
|
||||
__all__ = [
|
||||
"QueryStatus",
|
||||
"QueryOptions",
|
||||
"QueryResult",
|
||||
"StatementResult",
|
||||
"AsyncQueryHandle",
|
||||
"CacheOptions",
|
||||
]
|
||||
|
||||
@@ -27,3 +27,6 @@ class RestApi(BaseApi):
|
||||
"""
|
||||
|
||||
allow_browser_login = True
|
||||
|
||||
|
||||
__all__ = ["RestApi"]
|
||||
|
||||
@@ -98,3 +98,6 @@ def api(
|
||||
"API decorator not initialized. "
|
||||
"This decorator should be replaced during Superset startup."
|
||||
)
|
||||
|
||||
|
||||
__all__ = ["api"]
|
||||
|
||||
@@ -164,3 +164,6 @@ class AbstractSemanticViewDAO(BaseDAO[SemanticViewModel]):
|
||||
:return: SemanticViewModel instance or None
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
__all__ = ["AbstractSemanticLayerDAO", "AbstractSemanticViewDAO"]
|
||||
|
||||
@@ -97,3 +97,6 @@ def semantic_layer(
|
||||
"Semantic layer decorator not initialized. "
|
||||
"This decorator should be replaced during Superset startup."
|
||||
)
|
||||
|
||||
|
||||
__all__ = ["semantic_layer"]
|
||||
|
||||
@@ -21,7 +21,6 @@ from abc import ABC, abstractmethod
|
||||
from typing import Any, Generic, TypeVar
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from superset_core.semantic_layers.view import SemanticView
|
||||
|
||||
ConfigT = TypeVar("ConfigT", bound=BaseModel)
|
||||
|
||||
@@ -80,3 +80,6 @@ class SemanticViewModel(CoreModel):
|
||||
semantic_layer_uuid: UUID
|
||||
created_on: datetime | None
|
||||
changed_on: datetime | None
|
||||
|
||||
|
||||
__all__ = ["SemanticLayerModel", "SemanticViewModel"]
|
||||
|
||||
@@ -71,3 +71,6 @@ class TaskDAO(BaseDAO[Task]):
|
||||
:returns: Task instance or None if not found or not active
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
__all__ = ["TaskDAO"]
|
||||
|
||||
@@ -144,3 +144,9 @@ def get_context() -> TaskContext:
|
||||
)
|
||||
"""
|
||||
raise NotImplementedError("Function will be replaced during initialization")
|
||||
|
||||
|
||||
__all__ = [
|
||||
"task",
|
||||
"get_context",
|
||||
]
|
||||
|
||||
@@ -161,3 +161,9 @@ class TaskSubscriber(CoreModel):
|
||||
changed_on: datetime | None
|
||||
created_by_fk: int | None
|
||||
changed_by_fk: int | None
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Task",
|
||||
"TaskSubscriber",
|
||||
]
|
||||
|
||||
@@ -226,3 +226,12 @@ class TaskContext(ABC):
|
||||
cleanup_partial_work()
|
||||
"""
|
||||
...
|
||||
|
||||
|
||||
__all__ = [
|
||||
"TaskStatus",
|
||||
"TaskScope",
|
||||
"TaskProperties",
|
||||
"TaskContext",
|
||||
"TaskOptions",
|
||||
]
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../superset-frontend/.npmrc
|
||||
@@ -1 +0,0 @@
|
||||
min-release-age=3
|
||||
+103
-54
@@ -2086,6 +2086,14 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"dependencies": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
@@ -2099,6 +2107,18 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
|
||||
"version": "3.15.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz",
|
||||
"integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==",
|
||||
"dependencies": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
@@ -3195,9 +3215,9 @@
|
||||
"integrity": "sha512-Fc8Ne62jJlKHiG/ajlonC4Sd66Pq68fFwK4ihJGNZpGqboc324SQk+lRvMzpPRuJOmfrJefdG8/7JdWX4bzJ2Q=="
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
|
||||
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz",
|
||||
"integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
@@ -3814,13 +3834,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz",
|
||||
"integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q=="
|
||||
},
|
||||
"node_modules/d3-format": {
|
||||
"version": "1.4.5",
|
||||
@@ -4301,6 +4317,18 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
|
||||
"bin": {
|
||||
"esparse": "bin/esparse.js",
|
||||
"esvalidate": "bin/esvalidate.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/esquery": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
|
||||
@@ -4826,9 +4854,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/glob/node_modules/brace-expansion": {
|
||||
"version": "1.1.18",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
|
||||
"integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
|
||||
"version": "1.1.16",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
|
||||
"integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
@@ -5558,19 +5586,9 @@
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
|
||||
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/puzrin"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/nodeca"
|
||||
}
|
||||
],
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
@@ -7830,6 +7848,11 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
|
||||
},
|
||||
"node_modules/sshpk": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
|
||||
@@ -7992,9 +8015,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/test-exclude/node_modules/brace-expansion": {
|
||||
"version": "1.1.18",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
|
||||
"integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
|
||||
"version": "1.1.16",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
|
||||
"integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
@@ -9819,7 +9842,7 @@
|
||||
"debug": "4.4.0",
|
||||
"execa": "4.1.0",
|
||||
"istanbul-lib-coverage": "^3.0.0",
|
||||
"js-yaml": "4.3.1",
|
||||
"js-yaml": "4.1.1",
|
||||
"nyc": "15.1.0",
|
||||
"tinyglobby": "^0.2.14"
|
||||
},
|
||||
@@ -10098,7 +10121,7 @@
|
||||
"requires": {
|
||||
"@eslint/object-schema": "^3.0.5",
|
||||
"debug": "^4.3.1",
|
||||
"minimatch": ">=10"
|
||||
"minimatch": "^10.2.4"
|
||||
}
|
||||
},
|
||||
"@eslint/config-helpers": {
|
||||
@@ -10190,10 +10213,18 @@
|
||||
"camelcase": "^5.3.1",
|
||||
"find-up": "^4.1.0",
|
||||
"get-package-type": "^0.1.0",
|
||||
"js-yaml": "4.3.1",
|
||||
"js-yaml": "4.1.1",
|
||||
"resolve-from": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
},
|
||||
"find-up": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
@@ -10203,6 +10234,14 @@
|
||||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz",
|
||||
"integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==",
|
||||
"requires": {
|
||||
"argparse": "^1.0.7",
|
||||
"esprima": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
@@ -11146,9 +11185,9 @@
|
||||
"integrity": "sha512-Fc8Ne62jJlKHiG/ajlonC4Sd66Pq68fFwK4ihJGNZpGqboc324SQk+lRvMzpPRuJOmfrJefdG8/7JdWX4bzJ2Q=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "5.0.9",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz",
|
||||
"integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==",
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz",
|
||||
"integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
@@ -11591,9 +11630,9 @@
|
||||
}
|
||||
},
|
||||
"d3-color": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
|
||||
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz",
|
||||
"integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q=="
|
||||
},
|
||||
"d3-format": {
|
||||
"version": "1.4.5",
|
||||
@@ -11605,7 +11644,7 @@
|
||||
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz",
|
||||
"integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==",
|
||||
"requires": {
|
||||
"d3-color": "3.1.0"
|
||||
"d3-color": "1"
|
||||
}
|
||||
},
|
||||
"d3-scale": {
|
||||
@@ -11853,7 +11892,7 @@
|
||||
"imurmurhash": "^0.1.4",
|
||||
"is-glob": "^4.0.0",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"minimatch": ">=10",
|
||||
"minimatch": "^10.2.4",
|
||||
"natural-compare": "^1.4.0",
|
||||
"optionator": "^0.9.3"
|
||||
},
|
||||
@@ -11942,6 +11981,11 @@
|
||||
"eslint-visitor-keys": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"esprima": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||
},
|
||||
"esquery": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
|
||||
@@ -12290,7 +12334,7 @@
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "<10",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
@@ -12301,9 +12345,9 @@
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.18",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
|
||||
"integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
|
||||
"version": "1.1.16",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
|
||||
"integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -12314,7 +12358,7 @@
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.18"
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12808,9 +12852,9 @@
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
|
||||
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"requires": {
|
||||
"argparse": "^2.0.1"
|
||||
}
|
||||
@@ -13391,7 +13435,7 @@
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"brace-expansion": ">=5.0.9"
|
||||
"brace-expansion": "^5.0.5"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
@@ -14339,6 +14383,11 @@
|
||||
"which": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
|
||||
@@ -14445,7 +14494,7 @@
|
||||
"requires": {
|
||||
"@istanbuljs/schema": "^0.1.2",
|
||||
"glob": "^7.1.4",
|
||||
"minimatch": "<10"
|
||||
"minimatch": "^3.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
@@ -14454,9 +14503,9 @@
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.18",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
|
||||
"integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
|
||||
"version": "1.1.16",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
|
||||
"integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -14467,7 +14516,7 @@
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
|
||||
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.18"
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,20 +30,14 @@
|
||||
"overrides": {
|
||||
"@babel/core": "^7.29.6",
|
||||
"@cypress/code-coverage": {
|
||||
"js-yaml": "4.3.1"
|
||||
"js-yaml": "4.1.1"
|
||||
},
|
||||
"@cypress/request": "^3.0.0",
|
||||
"cypress": {
|
||||
"form-data": "^2.3.4"
|
||||
},
|
||||
"d3-interpolate": {
|
||||
"d3-color": "3.1.0"
|
||||
},
|
||||
"minimatch@<10": {
|
||||
"brace-expansion": "1.1.18"
|
||||
},
|
||||
"minimatch@>=10": {
|
||||
"brace-expansion": ">=5.0.9"
|
||||
"brace-expansion": ">=5.0.8"
|
||||
},
|
||||
"qs": "^6.14.2",
|
||||
"uuid": "^11.1.1"
|
||||
|
||||
@@ -77,7 +77,7 @@ module.exports = {
|
||||
// @ant-design/colors and @ant-design/fast-color are allowed through because
|
||||
// @ant-design/icons >= 6.3 deep-imports the ESM build of @ant-design/colors
|
||||
// from its CJS output, so babel-jest must transform those files.
|
||||
'node_modules/(?!@ant-design/(colors|fast-color)|@formatjs/.*|d3-(array|interpolate|color|time|scale|time-format|format|selection)|internmap|@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|uuid|@rjsf/*.|@x0k/.*|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-.*|hastscript|refractor|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|character-reference-invalid|is-alphanumerical|is-alphabetical|is-decimal|is-hexadecimal|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|geostyler|geostyler-.*|(?!geostyler)lodash|react-error-boundary|react-json-tree|react-base16-styling|lodash-es|rbush|quickselect|react-diff-viewer-continued|storybook/*.|json-stringify-pretty-compact|@x0k/json-schema-merge|content-disposition)',
|
||||
'node_modules/(?!@ant-design/(colors|fast-color)|@formatjs/.*|d3-(array|interpolate|color|time|scale|time-format|format|selection)|internmap|@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|uuid|@rjsf/*.|@x0k/.*|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-.*|hastscript|refractor|rehype-.*|remark-.*|mdast-.*|micromark-.*|parse-entities|character-reference-invalid|is-alphanumerical|is-alphabetical|is-decimal|is-hexadecimal|property-information|space-separated-tokens|comma-separated-tokens|bail|devlop|zwitch|longest-streak|geostyler|geostyler-.*|(?!geostyler)lodash|react-error-boundary|react-json-tree|react-base16-styling|lodash-es|rbush|quickselect|react-diff-viewer-continued|storybook/*.|json-stringify-pretty-compact|@x0k/json-schema-merge)',
|
||||
],
|
||||
preset: 'ts-jest',
|
||||
transform: {
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
// === Import plugin rules ===
|
||||
"import/named": "error",
|
||||
"import/export": "error",
|
||||
"import/no-named-as-default": "warn",
|
||||
"import/no-named-as-default": "error",
|
||||
"import/no-named-as-default-member": "error",
|
||||
"import/no-mutable-exports": "error",
|
||||
"import/no-amd": "error",
|
||||
|
||||
Generated
+723
-557
File diff suppressed because it is too large
Load Diff
@@ -122,9 +122,9 @@
|
||||
"@luma.gl/shadertools": "~9.2.5",
|
||||
"@luma.gl/webgl": "~9.2.5",
|
||||
"@reduxjs/toolkit": "^1.9.3",
|
||||
"@rjsf/core": "^6.8.0",
|
||||
"@rjsf/core": "^6.7.1",
|
||||
"@rjsf/utils": "^6.6.2",
|
||||
"@rjsf/validator-ajv8": "^6.8.0",
|
||||
"@rjsf/validator-ajv8": "^6.7.1",
|
||||
"@scarf/scarf": "^1.4.0",
|
||||
"@superset-ui/chart-controls": "file:./packages/superset-ui-chart-controls",
|
||||
"@superset-ui/core": "file:./packages/superset-ui-core",
|
||||
@@ -158,12 +158,12 @@
|
||||
"@visx/xychart": "^4.0.0",
|
||||
"ag-grid-community": "36.1.0",
|
||||
"ag-grid-react": "36.1.0",
|
||||
"antd": "^6.6.1",
|
||||
"antd": "^6.6.0",
|
||||
"chrono-node": "^2.10.1",
|
||||
"classnames": "^2.2.5",
|
||||
"content-disposition": "^3.0.0",
|
||||
"content-disposition": "^2.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"dayjs": "^1.11.23",
|
||||
"dayjs": "^1.11.21",
|
||||
"dom-to-image-more": "^3.10.2",
|
||||
"dom-to-pdf": "^0.3.2",
|
||||
"echarts": "^6.1.0",
|
||||
@@ -176,8 +176,8 @@
|
||||
"geostyler-openlayers-parser": "^5.7.1",
|
||||
"geostyler-style": "11.0.2",
|
||||
"geostyler-wfs-parser": "^3.0.1",
|
||||
"google-auth-library": "^11.0.2",
|
||||
"immer": "^11.1.18",
|
||||
"google-auth-library": "^11.0.1",
|
||||
"immer": "^11.1.16",
|
||||
"interweave": "^13.1.1",
|
||||
"jquery": "^4.0.0",
|
||||
"js-levenshtein": "^1.1.6",
|
||||
@@ -185,7 +185,7 @@
|
||||
"json-stringify-pretty-compact": "^4.0.0",
|
||||
"lodash": "^4.18.1",
|
||||
"lodash-es": "^4.18.1",
|
||||
"mapbox-gl": "^3.29.0",
|
||||
"mapbox-gl": "^3.28.1",
|
||||
"markdown-to-jsx": "^9.10.2",
|
||||
"match-sorter": "^8.3.0",
|
||||
"memoize-one": "^6.0.0",
|
||||
@@ -230,7 +230,7 @@
|
||||
"use-event-callback": "^0.1.0",
|
||||
"use-immer": "^0.11.0",
|
||||
"use-query-params": "^2.2.2",
|
||||
"uuid": "^14.0.2",
|
||||
"uuid": "^14.0.1",
|
||||
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
|
||||
"yargs": "^18.1.0"
|
||||
},
|
||||
@@ -257,14 +257,14 @@
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.1",
|
||||
"@playwright/test": "^1.62.1",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.6.2",
|
||||
"@storybook/addon-docs": "10.5.10",
|
||||
"@storybook/addon-links": "10.5.10",
|
||||
"@storybook/react-webpack5": "10.5.10",
|
||||
"@storybook/addon-docs": "10.5.7",
|
||||
"@storybook/addon-links": "10.5.7",
|
||||
"@storybook/react-webpack5": "10.5.7",
|
||||
"@storybook/test-runner": "0.24.4",
|
||||
"@svgr/webpack": "^8.1.0",
|
||||
"@swc/core": "^1.16.1",
|
||||
"@swc/plugin-emotion": "^15.0.0",
|
||||
"@swc/plugin-transform-imports": "^13.0.0",
|
||||
"@swc/core": "^1.15.47",
|
||||
"@swc/plugin-emotion": "^14.19.0",
|
||||
"@swc/plugin-transform-imports": "^12.5.0",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/jest-dom": "^7.0.1",
|
||||
"@testing-library/react": "^15.0.0",
|
||||
@@ -295,13 +295,13 @@
|
||||
"babel-loader": "^10.1.1",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||
"baseline-browser-mapping": "^2.11.17",
|
||||
"baseline-browser-mapping": "^2.11.13",
|
||||
"cheerio": "1.2.0",
|
||||
"concurrently": "^10.0.5",
|
||||
"concurrently": "^10.0.4",
|
||||
"copy-webpack-plugin": "^14.0.0",
|
||||
"cross-env": "^10.1.0",
|
||||
"css-loader": "^7.1.4",
|
||||
"eslint": "^10.9.0",
|
||||
"eslint": "^10.8.1",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-import-resolver-typescript": "^4.4.5",
|
||||
"eslint-plugin-i18n-strings": "file:eslint-rules/eslint-plugin-i18n-strings",
|
||||
@@ -311,8 +311,8 @@
|
||||
"eslint-plugin-lodash": "^8.0.0",
|
||||
"eslint-plugin-no-only-tests": "^3.4.0",
|
||||
"eslint-plugin-react-prefer-function-component": "^5.0.0",
|
||||
"eslint-plugin-react-you-might-not-need-an-effect": "^1.0.2",
|
||||
"eslint-plugin-storybook": "10.5.10",
|
||||
"eslint-plugin-react-you-might-not-need-an-effect": "^1.0.1",
|
||||
"eslint-plugin-storybook": "10.5.7",
|
||||
"eslint-plugin-testing-library": "^7.16.2",
|
||||
"eslint-plugin-theme-colors": "file:eslint-rules/eslint-plugin-theme-colors",
|
||||
"fetch-mock": "^12.6.0",
|
||||
@@ -326,13 +326,13 @@
|
||||
"jest-websocket-mock": "^2.5.0",
|
||||
"js-yaml-loader": "^1.2.2",
|
||||
"jsdom": "^30.0.1",
|
||||
"lerna": "^10.0.1",
|
||||
"lerna": "^10.0.0",
|
||||
"lightningcss": "^1.33.0",
|
||||
"mini-css-extract-plugin": "^2.10.2",
|
||||
"minimizer-webpack-plugin": "^5.6.1",
|
||||
"open-cli": "^9.0.0",
|
||||
"oxfmt": "^0.64.0",
|
||||
"oxlint": "^1.79.0",
|
||||
"oxfmt": "^0.63.0",
|
||||
"oxlint": "^1.78.0",
|
||||
"po2json": "^0.4.5",
|
||||
"postcss-styled-syntax": "^0.7.2",
|
||||
"process": "^0.11.10",
|
||||
@@ -343,7 +343,7 @@
|
||||
"source-map": "^0.8.0",
|
||||
"source-map-support": "^0.5.21",
|
||||
"speed-measure-webpack-plugin": "^1.6.0",
|
||||
"storybook": "10.5.10",
|
||||
"storybook": "10.5.7",
|
||||
"style-loader": "^4.0.0",
|
||||
"stylelint": "^17.14.1",
|
||||
"swc-loader": "^0.2.7",
|
||||
@@ -383,9 +383,6 @@
|
||||
"@great-expectations/jsonforms-antd-renderers": {
|
||||
"antd": "$antd"
|
||||
},
|
||||
"@istanbuljs/load-nyc-config": {
|
||||
"js-yaml": "^3.15.1"
|
||||
},
|
||||
"@jest/globals": "^30.4.0",
|
||||
"@jest/types": "^30.4.0",
|
||||
"@luma.gl/constants": "~9.2.5",
|
||||
@@ -395,10 +392,7 @@
|
||||
"@luma.gl/shadertools": "~9.2.5",
|
||||
"@luma.gl/webgl": "~9.2.5",
|
||||
"core-js": "^3.38.1",
|
||||
"cosmiconfig": {
|
||||
"js-yaml": "^4.3.1"
|
||||
},
|
||||
"dompurify": "^3.4.13",
|
||||
"dompurify": "^3.4.11",
|
||||
"esbuild": "^0.28.1",
|
||||
"eslint-plugin-import": {
|
||||
"eslint": "$eslint"
|
||||
@@ -414,22 +408,16 @@
|
||||
"jest-mock": "^30.4.0",
|
||||
"jest-runtime": "^30.4.0",
|
||||
"jest-util": "^30.4.0",
|
||||
"js-yaml-loader": {
|
||||
"js-yaml": "^3.15.1"
|
||||
},
|
||||
"jspdf": "^4.2.0",
|
||||
"lerna": {
|
||||
"js-yaml": "^4.3.1"
|
||||
"js-yaml": "^4.3.0"
|
||||
},
|
||||
"minimatch@>=10": {
|
||||
"brace-expansion": ">=5.0.8"
|
||||
},
|
||||
"nanoid@>=3 <4": "3.3.18",
|
||||
"nwsapi": "^2.2.24",
|
||||
"nwsapi": "^2.2.13",
|
||||
"puppeteer": "^22.4.1",
|
||||
"react-diff-viewer-continued": {
|
||||
"js-yaml": "^4.3.1"
|
||||
},
|
||||
"tar": "^7.5.16",
|
||||
"typescript-json-schema": "^0.68.0",
|
||||
"underscore": "^1.13.7",
|
||||
|
||||
@@ -35,6 +35,10 @@
|
||||
"types": "./lib/commands/index.d.ts",
|
||||
"default": "./lib/commands/index.js"
|
||||
},
|
||||
"./dashboardComponents": {
|
||||
"types": "./lib/dashboardComponents/index.d.ts",
|
||||
"default": "./lib/dashboardComponents/index.js"
|
||||
},
|
||||
"./editors": {
|
||||
"types": "./lib/editors/index.d.ts",
|
||||
"default": "./lib/editors/index.js"
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
import { Chat } from '../chat';
|
||||
import { Command } from '../commands';
|
||||
import { DashboardComponentDefinition } from '../dashboardComponents';
|
||||
import { View } from '../views';
|
||||
import { Menu } from '../menus';
|
||||
import { Editor } from '../editors';
|
||||
@@ -90,4 +91,9 @@ export interface Contributions {
|
||||
* chat at a time.
|
||||
*/
|
||||
chat?: Chat;
|
||||
/**
|
||||
* Dashboard layout components contributed by the extension. Each becomes a
|
||||
* first-class, draggable element in the dashboard builder palette.
|
||||
*/
|
||||
dashboardComponents?: DashboardComponentDefinition[];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Dashboard component contribution API for Superset extensions.
|
||||
*
|
||||
* A dashboard component is a first-class dashboard layout element (like the
|
||||
* built-in Markdown or iframe) contributed by an extension. The extension
|
||||
* provides a single React component that renders the element's *content*; the
|
||||
* host owns the surrounding chrome (drag handle, resize, delete) so the
|
||||
* contributed component stays small and the contract stable.
|
||||
*
|
||||
* This replaces the legacy `DashboardComponentsRegistry` / `DYNAMIC_TYPE`
|
||||
* mechanism, which is deprecated.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { dashboardComponents } from '@apache-superset/core';
|
||||
*
|
||||
* dashboardComponents.registerDashboardComponent(
|
||||
* {
|
||||
* id: 'acme.weather',
|
||||
* name: 'Weather widget',
|
||||
* icon: 'CloudOutlined',
|
||||
* resizable: true,
|
||||
* defaultMeta: { width: 4, height: 50 },
|
||||
* },
|
||||
* WeatherWidget,
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { ComponentType } from 'react';
|
||||
import type { Disposable, Event } from '../common';
|
||||
|
||||
/**
|
||||
* Props passed by the host to a contributed dashboard component. The host
|
||||
* renders this component inside its own drag/resize/delete chrome, so the
|
||||
* component only needs to render content (and, in edit mode, its own editor
|
||||
* affordances). Persisted state lives in `meta`; mutate it via `updateMeta`.
|
||||
*/
|
||||
export interface DashboardComponentProps {
|
||||
/** The layout item id of this component instance. */
|
||||
id: string;
|
||||
/** The component instance's persisted meta (round-trips in the layout). */
|
||||
meta: Record<string, unknown>;
|
||||
/** Whether the dashboard is in edit mode. */
|
||||
editMode: boolean;
|
||||
/** Shallow-merge a patch into this component's persisted meta. */
|
||||
updateMeta: (patch: Record<string, unknown>) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declarative descriptor for a contributed dashboard component. The behavior
|
||||
* fields replace what was historically hardcoded in the dashboard util maps
|
||||
* (resizability, default sizing, nesting, etc.).
|
||||
*/
|
||||
export interface DashboardComponentDefinition {
|
||||
/** Namespaced unique id, e.g. "acme.weather" or "superset.iframe". */
|
||||
id: string;
|
||||
/** Human-readable label shown in the builder palette. */
|
||||
name: string;
|
||||
/** Optional longer description. */
|
||||
description?: string;
|
||||
/** Icon id (a known Superset icon name) shown in the palette. */
|
||||
icon?: string;
|
||||
/** Whether instances can be resized. Defaults to true. */
|
||||
resizable?: boolean;
|
||||
/** Default `meta` seeded onto a newly created instance (e.g. width/height). */
|
||||
defaultMeta?: Record<string, unknown>;
|
||||
/**
|
||||
* Whether an instance counts as user content for "is this dashboard empty?"
|
||||
* detection. Defaults to true.
|
||||
*/
|
||||
isUserContent?: boolean;
|
||||
/** Minimum width in grid columns. Defaults to 1. */
|
||||
minWidth?: number;
|
||||
/**
|
||||
* Restrict which container types may hold this component (e.g.
|
||||
* `['GRID', 'TAB']`). When omitted, the component is allowed wherever a
|
||||
* standard content leaf is allowed (grid, row, column, tab).
|
||||
*/
|
||||
validParents?: string[];
|
||||
/**
|
||||
* Whether a drop into the grid or a tab auto-wraps the component in a row.
|
||||
* Defaults to true (matching built-in content components).
|
||||
*/
|
||||
wrapInRow?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The subset of a definition's behavior that is seeded onto each instance's
|
||||
* `meta` at creation, so the dashboard layout engine can honor it (and so it
|
||||
* round-trips in the saved layout even if the extension later becomes
|
||||
* unavailable). Read by the dashboard util maps; not part of the rendered
|
||||
* component's concern.
|
||||
*/
|
||||
export interface DashboardComponentBehaviorMeta {
|
||||
extensionComponentId: string;
|
||||
resizable?: boolean;
|
||||
isUserContent?: boolean;
|
||||
minWidth?: number;
|
||||
validParents?: string[];
|
||||
wrapInRow?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* A registered dashboard component: its definition plus the React component
|
||||
* the host renders.
|
||||
*/
|
||||
export interface RegisteredDashboardComponent {
|
||||
definition: DashboardComponentDefinition;
|
||||
Component: ComponentType<DashboardComponentProps>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a dashboard component. Disposing the returned Disposable
|
||||
* unregisters it. Registering a second component with the same id replaces the
|
||||
* first.
|
||||
*
|
||||
* @param definition The component descriptor (id, name, behavior).
|
||||
* @param component The React component rendering the element's content.
|
||||
* @returns A Disposable that unregisters the component when disposed.
|
||||
*/
|
||||
export declare function registerDashboardComponent(
|
||||
definition: DashboardComponentDefinition,
|
||||
component: ComponentType<DashboardComponentProps>,
|
||||
): Disposable;
|
||||
|
||||
/** Returns the registered component for `id`, or undefined. */
|
||||
export declare function getDashboardComponent(
|
||||
id: string,
|
||||
): RegisteredDashboardComponent | undefined;
|
||||
|
||||
/** Returns all registered dashboard components. */
|
||||
export declare function getDashboardComponents(): RegisteredDashboardComponent[];
|
||||
|
||||
/** Event fired when a dashboard component is registered. */
|
||||
export declare const onDidRegisterDashboardComponent: Event<DashboardComponentDefinition>;
|
||||
|
||||
/** Event fired when a dashboard component is unregistered. */
|
||||
export declare const onDidUnregisterDashboardComponent: Event<DashboardComponentDefinition>;
|
||||
@@ -20,6 +20,7 @@ export * as common from './common';
|
||||
export * as authentication from './authentication';
|
||||
export * as chat from './chat';
|
||||
export * as commands from './commands';
|
||||
export * as dashboardComponents from './dashboardComponents';
|
||||
export * as editors from './editors';
|
||||
export * as extensions from './extensions';
|
||||
export * as menus from './menus';
|
||||
|
||||
+1
-15
@@ -51,20 +51,6 @@ export interface MetricOptionProps {
|
||||
shouldShowTooltip?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* `url` is an arbitrary caller-supplied string rendered as an href. Only
|
||||
* http(s) and relative URLs become links; other schemes degrade to plain
|
||||
* text.
|
||||
*/
|
||||
function isSafeHref(url: string): boolean {
|
||||
try {
|
||||
const { protocol } = new URL(url, window.location.origin);
|
||||
return protocol === 'http:' || protocol === 'https:';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function MetricOption({
|
||||
metric,
|
||||
labelRef,
|
||||
@@ -84,7 +70,7 @@ export function MetricOption({
|
||||
`}
|
||||
ref={labelRef}
|
||||
>
|
||||
{url && isSafeHref(url) ? (
|
||||
{url ? (
|
||||
<Typography.Link
|
||||
href={url}
|
||||
target={openInNewWindow ? '_blank' : ''}
|
||||
|
||||
@@ -122,12 +122,6 @@ export const timeComparisonControls: ({
|
||||
}
|
||||
return newState;
|
||||
},
|
||||
// Re-run this control's validation whenever `time_compare` changes so
|
||||
// the "date required" error clears once a non-custom shift is picked.
|
||||
// Without it the stale error survives in Redux (see the
|
||||
// dependantControls path in exploreReducer's SET_FIELD_VALUE handler)
|
||||
// and blocks further chart updates until a page refresh.
|
||||
validationDependencies: ['time_compare'],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
+1
-28
@@ -17,7 +17,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { QueryFormMetric } from '@superset-ui/core';
|
||||
import { getTotalsMetrics, toTotalsAggregate } from './getTotalsMetrics';
|
||||
import { getTotalsMetrics } from './getTotalsMetrics';
|
||||
|
||||
const simpleMetric = (aggregate: string): QueryFormMetric =>
|
||||
({
|
||||
@@ -76,31 +76,4 @@ describe('getTotalsMetrics', () => {
|
||||
test('returns an empty array when given no metrics', () => {
|
||||
expect(getTotalsMetrics([], 'AVG')).toEqual([]);
|
||||
});
|
||||
|
||||
test("ORIGINAL keeps each metric's own aggregate", () => {
|
||||
const metrics = [
|
||||
simpleMetric('COUNT_DISTINCT'),
|
||||
sqlMetric(),
|
||||
savedMetric(),
|
||||
];
|
||||
const result = getTotalsMetrics(metrics, 'ORIGINAL');
|
||||
|
||||
expect(result).toBe(metrics);
|
||||
expect(result[0]).toEqual(
|
||||
expect.objectContaining({ aggregate: 'COUNT_DISTINCT' }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toTotalsAggregate', () => {
|
||||
test.each(['SUM', 'AVG'] as const)('passes %s through', value => {
|
||||
expect(toTotalsAggregate(value)).toBe(value);
|
||||
});
|
||||
|
||||
test.each([undefined, null, '', 'MEDIAN', 'sum'])(
|
||||
'falls back to ORIGINAL for %p',
|
||||
value => {
|
||||
expect(toTotalsAggregate(value)).toBe('ORIGINAL');
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
+11
-31
@@ -18,46 +18,26 @@
|
||||
*/
|
||||
import { isAdhocMetricSimple, QueryFormMetric } from '@superset-ui/core';
|
||||
|
||||
/**
|
||||
* How the "Show summary" totals row aggregates each metric.
|
||||
*
|
||||
* ``ORIGINAL`` keeps every metric's own aggregation. It is the default because
|
||||
* overriding is not universally valid: ``SUM`` over a ``COUNT_DISTINCT`` of a
|
||||
* non-numeric column (a uuid, say) is rejected outright by the database, and
|
||||
* over a numeric id column it silently produces a meaningless number.
|
||||
*/
|
||||
export type TotalsAggregate = 'ORIGINAL' | 'SUM' | 'AVG';
|
||||
export type TotalsAggregate = 'SUM' | 'AVG';
|
||||
|
||||
/**
|
||||
* Build the metrics for a chart's "Show summary" totals query.
|
||||
* Build the metrics for a chart's "Show summary" totals query, overriding
|
||||
* each Simple (adhoc) metric's aggregate function with the user-chosen
|
||||
* totals aggregate. The totals query has no GROUP BY, so the database
|
||||
* evaluates each metric fresh over all rows -- swapping the aggregate here
|
||||
* is a correct, independent computation, not a re-aggregation of
|
||||
* already-aggregated per-row values.
|
||||
*
|
||||
* With SUM or AVG, each Simple (adhoc) metric is cloned with its aggregate
|
||||
* replaced. The totals query has no GROUP BY, so the database evaluates each
|
||||
* metric fresh over all rows -- that swap is an independent computation, not a
|
||||
* re-aggregation of already-aggregated per-row values.
|
||||
*
|
||||
* Custom-SQL and saved (string) metrics always pass through unchanged: there is
|
||||
* no safe way to rewrite an arbitrary SQL expression's aggregate without
|
||||
* parsing it, so the totals row keeps their own native aggregate.
|
||||
* Custom-SQL metrics and saved (string) metrics pass through unchanged:
|
||||
* there is no safe way to rewrite an arbitrary SQL expression's aggregate
|
||||
* function without parsing it, so the totals row keeps their own native
|
||||
* aggregate for those.
|
||||
*/
|
||||
export function getTotalsMetrics(
|
||||
metrics: QueryFormMetric[],
|
||||
aggregate: TotalsAggregate,
|
||||
): QueryFormMetric[] {
|
||||
if (aggregate === 'ORIGINAL') {
|
||||
return metrics;
|
||||
}
|
||||
return metrics.map(metric =>
|
||||
isAdhocMetricSimple(metric) ? { ...metric, aggregate } : metric,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Narrow a raw ``totals_aggregate`` form-data value to a TotalsAggregate.
|
||||
*
|
||||
* Anything other than an explicit SUM/AVG — including charts saved before the
|
||||
* control existed — keeps each metric's own aggregation.
|
||||
*/
|
||||
export function toTotalsAggregate(value: unknown): TotalsAggregate {
|
||||
return value === 'SUM' || value === 'AVG' ? value : 'ORIGINAL';
|
||||
}
|
||||
|
||||
-23
@@ -112,26 +112,3 @@ test('shows a Tooltip for the verbose metric name', () => {
|
||||
const { getByTestId } = setup();
|
||||
expect(getByTestId('mock-tooltip')).toBeInTheDocument();
|
||||
});
|
||||
test('does not render javascript: URLs as links', () => {
|
||||
// Regression test: the url prop can be creator-authored and must
|
||||
// never become a script-bearing href for other viewers.
|
||||
const { queryByRole, getByText } = setup({
|
||||
url: 'javascript:alert(document.domain)', // eslint-disable-line no-script-url
|
||||
});
|
||||
expect(queryByRole('link')).not.toBeInTheDocument();
|
||||
expect(getByText(defaultProps.metric.verbose_name)).toBeInTheDocument();
|
||||
});
|
||||
test('does not render data: URLs as links', () => {
|
||||
const { queryByRole } = setup({
|
||||
url: 'data:text/html,<script>alert(1)</script>',
|
||||
});
|
||||
expect(queryByRole('link')).not.toBeInTheDocument();
|
||||
});
|
||||
test('renders relative URLs as links', () => {
|
||||
const { getByRole } = setup({
|
||||
url: '/superset/dashboard/1/',
|
||||
});
|
||||
expect(
|
||||
getByRole('link', { name: defaultProps.metric.verbose_name }),
|
||||
).toHaveAttribute('href', '/superset/dashboard/1/');
|
||||
});
|
||||
|
||||
@@ -67,8 +67,8 @@
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-time": "^3.1.0",
|
||||
"d3-time-format": "^4.1.0",
|
||||
"dayjs": "^1.11.23",
|
||||
"dompurify": "^3.4.14",
|
||||
"dayjs": "^1.11.21",
|
||||
"dompurify": "^3.4.13",
|
||||
"fetch-retry": "^6.0.0",
|
||||
"handlebars": "^4.7.9",
|
||||
"jed": "^1.1.1",
|
||||
@@ -79,7 +79,7 @@
|
||||
"re-resizable": "^6.11.2",
|
||||
"react-ace": "^14.0.1",
|
||||
"react-draggable": "^4.7.1",
|
||||
"react-error-boundary": "^6.1.3",
|
||||
"react-error-boundary": "^6.1.2",
|
||||
"react-js-cron": "^6.0.2",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-resize-detector": "^7.1.2",
|
||||
@@ -89,7 +89,7 @@
|
||||
"rehype-raw": "^7.0.0",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"reselect": "^5.3.0",
|
||||
"reselect": "^5.2.0",
|
||||
"rison": "^0.1.1",
|
||||
"seedrandom": "^3.0.5",
|
||||
"xss": "^1.0.15"
|
||||
|
||||
@@ -25,7 +25,6 @@ export enum VizType {
|
||||
BoxPlot = 'box_plot',
|
||||
Bubble = 'bubble_v2',
|
||||
Bullet = 'bullet',
|
||||
Butterfly = 'butterfly',
|
||||
Calendar = 'cal_heatmap',
|
||||
Cartodiagram = 'cartodiagram',
|
||||
Chord = 'chord',
|
||||
|
||||
@@ -1,153 +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 { useState } from 'react';
|
||||
import { fireEvent, render, screen } from '@superset-ui/core/spec';
|
||||
import { Input } from '../Input';
|
||||
import { Modal } from './Modal';
|
||||
|
||||
const drag = (
|
||||
target: Element,
|
||||
from: [number, number],
|
||||
to: [number, number],
|
||||
) => {
|
||||
fireEvent.mouseDown(target, { clientX: from[0], clientY: from[1] });
|
||||
fireEvent.mouseMove(document, { clientX: to[0], clientY: to[1] });
|
||||
fireEvent.mouseUp(document);
|
||||
};
|
||||
|
||||
const isDragged = () => !!document.querySelector('.react-draggable-dragged');
|
||||
|
||||
describe('Modal draggable', () => {
|
||||
test('dragging from the title bar moves the modal', () => {
|
||||
render(
|
||||
<Modal show onHide={() => {}} title="Edit Dataset" draggable name="test">
|
||||
<Input data-test="field" defaultValue="value" />
|
||||
</Modal>,
|
||||
);
|
||||
|
||||
const trigger = document.querySelector('.draggable-trigger') as HTMLElement;
|
||||
drag(trigger, [100, 50], [150, 90]);
|
||||
|
||||
expect(isDragged()).toBe(true);
|
||||
});
|
||||
|
||||
test('dragging inside modal content does not move the modal', () => {
|
||||
render(
|
||||
<Modal show onHide={() => {}} title="Edit Dataset" draggable name="test">
|
||||
<Input data-test="field" defaultValue="first_view_event" />
|
||||
</Modal>,
|
||||
);
|
||||
|
||||
const input = screen.getByTestId('field');
|
||||
drag(input, [200, 400], [260, 430]);
|
||||
|
||||
expect(isDragged()).toBe(false);
|
||||
});
|
||||
|
||||
test('dragging inside modal content does not move the modal, even after an unrelated re-render while the title was hovered', () => {
|
||||
// Regression test: the title bar used to gate dragging with a
|
||||
// hover-tracked boolean (mouseover/mouseout on `.draggable-trigger`)
|
||||
// instead of react-draggable's own `handle` prop. Because the title
|
||||
// element was defined as an inline component recreated on every
|
||||
// render, any unrelated state change while the cursor was over the
|
||||
// title (e.g. typing in any field) force-remounted it without a real
|
||||
// mouseout ever firing, leaving dragging permanently enabled -- so
|
||||
// selecting text anywhere in the modal dragged the whole modal
|
||||
// instead.
|
||||
function Harness() {
|
||||
const [tick, setTick] = useState(0);
|
||||
return (
|
||||
<Modal
|
||||
show
|
||||
onHide={() => {}}
|
||||
title="Edit Dataset"
|
||||
draggable
|
||||
name="test"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
data-test="rerender"
|
||||
onClick={() => setTick(tick + 1)}
|
||||
>
|
||||
rerender
|
||||
</button>
|
||||
<Input data-test="field" defaultValue="first_view_event" />
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
render(<Harness />);
|
||||
|
||||
const trigger = document.querySelector('.draggable-trigger') as HTMLElement;
|
||||
fireEvent.mouseOver(trigger);
|
||||
fireEvent.click(screen.getByTestId('rerender'));
|
||||
|
||||
const input = screen.getByTestId('field');
|
||||
drag(input, [200, 400], [260, 430]);
|
||||
|
||||
expect(isDragged()).toBe(false);
|
||||
});
|
||||
|
||||
test('dragging is disabled entirely when draggable is not set', () => {
|
||||
render(
|
||||
<Modal show onHide={() => {}} title="Edit Dataset" name="test">
|
||||
<Input data-test="field" defaultValue="value" />
|
||||
</Modal>,
|
||||
);
|
||||
|
||||
expect(document.querySelector('.draggable-trigger')).toBeNull();
|
||||
});
|
||||
|
||||
test('draggableConfig cannot re-enable dragging on a non-draggable modal', () => {
|
||||
render(
|
||||
<Modal
|
||||
show
|
||||
onHide={() => {}}
|
||||
title="Edit Dataset"
|
||||
name="test"
|
||||
draggableConfig={{ disabled: false }}
|
||||
>
|
||||
<Input data-test="field" defaultValue="value" />
|
||||
</Modal>,
|
||||
);
|
||||
|
||||
expect(document.querySelector('.draggable-trigger')).toBeNull();
|
||||
});
|
||||
|
||||
test('draggableConfig can still opt a draggable modal out of dragging', () => {
|
||||
render(
|
||||
<Modal
|
||||
show
|
||||
onHide={() => {}}
|
||||
title="Edit Dataset"
|
||||
draggable
|
||||
name="test"
|
||||
draggableConfig={{ disabled: true }}
|
||||
>
|
||||
<Input data-test="field" defaultValue="value" />
|
||||
</Modal>,
|
||||
);
|
||||
|
||||
const trigger = document.querySelector('.draggable-trigger') as HTMLElement;
|
||||
drag(trigger, [100, 50], [150, 90]);
|
||||
|
||||
expect(isDragged()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -269,6 +269,7 @@ const CustomModal = ({
|
||||
);
|
||||
const draggableRef = useRef<HTMLDivElement>(null);
|
||||
const [bounds, setBounds] = useState<DraggableBounds>({});
|
||||
const [dragDisabled, setDragDisabled] = useState<boolean>(true);
|
||||
const theme = useTheme();
|
||||
|
||||
const handleOnHide = () => {
|
||||
@@ -338,7 +339,19 @@ const CustomModal = ({
|
||||
}, [hideFooter, resizableConfig]);
|
||||
|
||||
const ModalTitle = () =>
|
||||
draggable ? <div className="draggable-trigger">{title}</div> : <>{title}</>;
|
||||
draggable ? (
|
||||
<div
|
||||
className="draggable-trigger"
|
||||
onMouseOver={() => dragDisabled && setDragDisabled(false)}
|
||||
onMouseOut={() => !dragDisabled && setDragDisabled(true)}
|
||||
onFocus={() => dragDisabled && setDragDisabled(false)}
|
||||
onBlur={() => !dragDisabled && setDragDisabled(true)}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
) : (
|
||||
<>{title}</>
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledModal
|
||||
@@ -365,19 +378,13 @@ const CustomModal = ({
|
||||
modalRender={modal =>
|
||||
resizable || draggable ? (
|
||||
<Draggable
|
||||
disabled={!draggable || dragDisabled}
|
||||
bounds={bounds ?? false}
|
||||
onStart={(event, uiData) => onDragStart(event, uiData)}
|
||||
{...draggableConfig}
|
||||
// `disabled` and `handle` are applied after the spread so callers
|
||||
// can't use `draggableConfig` to re-enable dragging on a
|
||||
// non-draggable modal or move the drag handle off the title bar.
|
||||
// A caller opting a draggable modal out via
|
||||
// `draggableConfig.disabled` is still honored.
|
||||
disabled={!draggable || !!draggableConfig?.disabled}
|
||||
handle={draggable ? '.draggable-trigger' : undefined}
|
||||
// Pass nodeRef so react-draggable does not fall back to
|
||||
// ReactDOM.findDOMNode (deprecated in React 18+ Strict Mode).
|
||||
nodeRef={draggableRef}
|
||||
{...draggableConfig}
|
||||
>
|
||||
{resizable ? (
|
||||
<Resizable className="resizable" {...getResizableConfig}>
|
||||
|
||||
@@ -65,16 +65,12 @@ export type AntdExposedProps = Pick<
|
||||
| 'onOpenChange'
|
||||
| 'optionRender'
|
||||
| 'placeholder'
|
||||
| 'prefix'
|
||||
| 'showArrow'
|
||||
| 'showSearch'
|
||||
| 'tokenSeparators'
|
||||
| 'virtual'
|
||||
| 'getPopupContainer'
|
||||
| 'menuItemSelectedIcon'
|
||||
// lets a caller with long option labels stop the popup inheriting the
|
||||
// trigger's width, which otherwise truncates every option
|
||||
| 'popupMatchSelectWidth'
|
||||
>;
|
||||
|
||||
export type SelectOptionsType = Exclude<AntdProps['options'], undefined>;
|
||||
|
||||
-28
@@ -246,34 +246,6 @@ test('wraps component with proper container div', () => {
|
||||
expect(wrapper).toHaveAttribute('data-themed-ag-grid', 'true');
|
||||
});
|
||||
|
||||
test('applies non-transparent backgrounds to native menus, tooltips and overlays', () => {
|
||||
const customTheme = {
|
||||
...supersetTheme,
|
||||
colorBgElevated: '#f2f2f2',
|
||||
};
|
||||
|
||||
render(
|
||||
<ThemeProvider theme={customTheme}>
|
||||
<ThemedAgGridReact rowData={mockRowData} columnDefs={mockColumnDefs} />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
|
||||
const agGrid = screen.getByTestId('ag-grid-react');
|
||||
const theme = JSON.parse(agGrid.getAttribute('data-theme') || '{}');
|
||||
|
||||
// ag-grid's own context/column menus, side bar, tooltips and overlays are
|
||||
// rendered against these params rather than `backgroundColor` (which is
|
||||
// intentionally 'transparent' so the surrounding app shows through the
|
||||
// grid body). Without explicit values they inherit transparency too,
|
||||
// making native menus/popups unreadable.
|
||||
expect(theme.chromeBackgroundColor).toBe('#f2f2f2');
|
||||
expect(theme.menuBackgroundColor).toBe('#f2f2f2');
|
||||
expect(theme.menuBorder).toBe(true);
|
||||
expect(theme.sideBarBackgroundColor).toBe('#f2f2f2');
|
||||
expect(theme.tooltipBackgroundColor).toBe('#f2f2f2');
|
||||
expect(theme.modalOverlayBackgroundColor).toBe('#f2f2f2');
|
||||
});
|
||||
|
||||
test('handles missing theme gracefully', () => {
|
||||
const incompleteTheme = {
|
||||
...supersetTheme,
|
||||
|
||||
-11
@@ -104,17 +104,6 @@ export const ThemedAgGridReact = forwardRef<
|
||||
foregroundColor: theme.colorText,
|
||||
browserColorScheme: isDarkMode ? 'dark' : 'light',
|
||||
|
||||
// Native menus, popups, side bar, tooltips and loading/no-rows overlays
|
||||
// are rendered against these params rather than `backgroundColor`
|
||||
// (which is intentionally transparent). Without explicit values they
|
||||
// inherit transparency too, making them unreadable.
|
||||
chromeBackgroundColor: theme.colorBgElevated,
|
||||
menuBackgroundColor: theme.colorBgElevated,
|
||||
menuBorder: true,
|
||||
sideBarBackgroundColor: theme.colorBgElevated,
|
||||
tooltipBackgroundColor: theme.colorBgElevated,
|
||||
modalOverlayBackgroundColor: theme.colorBgElevated,
|
||||
|
||||
// Header styling
|
||||
headerBackgroundColor: theme.colorFillTertiary,
|
||||
headerTextColor: theme.colorTextHeading,
|
||||
|
||||
+4
-14
@@ -38,21 +38,11 @@ function formatMemory(
|
||||
: ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'RB', 'QB'];
|
||||
const base = binary ? 1024 : 1000;
|
||||
|
||||
let i = Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
suffixes.length - 1,
|
||||
Math.floor(Math.log(absValue) / Math.log(base)),
|
||||
),
|
||||
const i = Math.min(
|
||||
suffixes.length - 1,
|
||||
Math.floor(Math.log(absValue) / Math.log(base)),
|
||||
);
|
||||
let scaled = parseFloat((absValue / Math.pow(base, i)).toFixed(decimals));
|
||||
|
||||
if (scaled >= base && i < suffixes.length - 1) {
|
||||
i += 1;
|
||||
scaled = parseFloat((absValue / Math.pow(base, i)).toFixed(decimals));
|
||||
}
|
||||
|
||||
formatted = `${sign}${scaled}${suffixes[i]}`;
|
||||
formatted = `${sign}${parseFloat((absValue / Math.pow(base, i)).toFixed(decimals))}${suffixes[i]}`;
|
||||
}
|
||||
|
||||
if (transfer) {
|
||||
|
||||
+3
-2
@@ -64,8 +64,9 @@ export default function createSmartNumberFormatter(
|
||||
description,
|
||||
formatFunc: value => `${getSign(value)}${formatValue(value)}`,
|
||||
id:
|
||||
id ??
|
||||
(signed ? NumberFormats.SMART_NUMBER_SIGNED : NumberFormats.SMART_NUMBER),
|
||||
id || signed
|
||||
? NumberFormats.SMART_NUMBER_SIGNED
|
||||
: NumberFormats.SMART_NUMBER,
|
||||
label: label ?? 'Adaptive formatter',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -119,20 +119,6 @@ export function retrieveErrorMessage(
|
||||
return statusError || parseStringResponse(str);
|
||||
}
|
||||
|
||||
function getFirstValidationError(message: JsonObject): string | undefined {
|
||||
const [firstError] = Object.values(message);
|
||||
|
||||
if (typeof firstError === 'string') {
|
||||
return firstError;
|
||||
}
|
||||
|
||||
if (Array.isArray(firstError)) {
|
||||
return firstError.find((item): item is string => typeof item === 'string');
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function parseErrorJson(responseJson: JsonObject): ClientErrorObject {
|
||||
let error = { ...responseJson };
|
||||
// Backwards compatibility for old error renderers with the new error object
|
||||
@@ -140,12 +126,13 @@ export function parseErrorJson(responseJson: JsonObject): ClientErrorObject {
|
||||
error.error = error.description = error.errors[0].message;
|
||||
error.link = error.errors[0]?.extra?.link;
|
||||
}
|
||||
// Marshmallow field validation returns arrays for string messages, but
|
||||
// serializes lazy translation messages as strings instead.
|
||||
// Marshmallow field validation returns the error message in the format
|
||||
// of { message: { field1: [msg1, msg2], field2: [msg], } }
|
||||
if (!error.error && error.message) {
|
||||
if (typeof error.message === 'object') {
|
||||
error.error =
|
||||
getFirstValidationError(error.message) || t('Invalid input');
|
||||
Object.values(error.message as Record<string, string[]>)[0]?.[0] ||
|
||||
t('Invalid input');
|
||||
}
|
||||
if (typeof error.message === 'string') {
|
||||
if (checkForHtml(error.message)) {
|
||||
|
||||
@@ -25,11 +25,8 @@ export type Aggregate =
|
||||
| 'COUNT'
|
||||
| 'COUNT_DISTINCT'
|
||||
| 'MAX'
|
||||
| 'MEDIAN'
|
||||
| 'MIN'
|
||||
| 'STDDEV_SAMP'
|
||||
| 'SUM'
|
||||
| 'VAR_SAMP';
|
||||
| 'SUM';
|
||||
|
||||
export interface AdhocMetricBase {
|
||||
hasCustomLabel?: boolean;
|
||||
|
||||
@@ -49,6 +49,5 @@ export { default as finestTemporalGrainFormatter } from './formatters/finestTemp
|
||||
|
||||
export { default as normalizeTimestamp } from './utils/normalizeTimestamp';
|
||||
export { default as denormalizeTimestamp } from './utils/denormalizeTimestamp';
|
||||
export { default as createTimeRangeFromGranularity } from './utils/createTimeRangeFromGranularity';
|
||||
|
||||
export * from './types';
|
||||
|
||||
@@ -30,7 +30,6 @@ import type {
|
||||
QueryFormData,
|
||||
} from '../query';
|
||||
import type { JsonResponse } from '../connection';
|
||||
import type { MenuItem } from '../components/Menu';
|
||||
|
||||
/**
|
||||
* A function which returns text (or marked-up text)
|
||||
@@ -165,13 +164,6 @@ export interface SliceHeaderExtension {
|
||||
dashboardId: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for extensions to the Slice Header more-options menu
|
||||
*/
|
||||
export interface SliceHeaderMenuExtension extends SliceHeaderExtension {
|
||||
sliceName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for extensions to Embed Modal
|
||||
*/
|
||||
@@ -270,9 +262,6 @@ export type Extensions = Partial<{
|
||||
'sqleditor.extension.form': ComponentType<SQLFormExtensionProps>;
|
||||
'sqleditor.extension.resultTable': ComponentType<SQLResultTableExtensionProps>;
|
||||
'dashboard.slice.header': ComponentType<SliceHeaderExtension>;
|
||||
'dashboard.slice.header.menu': (
|
||||
context: SliceHeaderMenuExtension,
|
||||
) => MenuItem[];
|
||||
'sqleditor.extension.customAutocomplete': (
|
||||
args: CustomAutoCompleteArgs,
|
||||
) => CustomAutocomplete[] | undefined;
|
||||
|
||||
@@ -28,10 +28,10 @@ export enum FeatureFlag {
|
||||
AlertReportSlackV2 = 'ALERT_REPORT_SLACK_V2',
|
||||
AlertReportWebhook = 'ALERT_REPORT_WEBHOOK',
|
||||
AlertReportsFilter = 'ALERT_REPORTS_FILTER',
|
||||
AlertReportsRetry = 'ALERT_REPORTS_RETRY',
|
||||
AllowFullCsvExport = 'ALLOW_FULL_CSV_EXPORT',
|
||||
ChartPluginsExperimental = 'CHART_PLUGINS_EXPERIMENTAL',
|
||||
ConfirmDashboardDiff = 'CONFIRM_DASHBOARD_DIFF',
|
||||
CspRuntimeAllowlist = 'CSP_RUNTIME_ALLOWLIST',
|
||||
CssTemplates = 'CSS_TEMPLATES',
|
||||
DashboardVirtualization = 'DASHBOARD_VIRTUALIZATION',
|
||||
DashboardVirtualizationDeferData = 'DASHBOARD_VIRTUALIZATION_DEFER_DATA',
|
||||
|
||||
@@ -123,25 +123,6 @@ describe('isProbablyHTML', () => {
|
||||
expect(isProbablyHTML('<canvas></canvas>')).toBe(true);
|
||||
expect(isProbablyHTML('<iframe src="page.html"></iframe>')).toBe(true);
|
||||
});
|
||||
|
||||
test('should return true for script-capable and foreign-content tags', () => {
|
||||
expect(isProbablyHTML('<svg onload="alert(1)"></svg>')).toBe(true);
|
||||
expect(isProbablyHTML('<math><mi>x</mi></math>')).toBe(true);
|
||||
expect(
|
||||
isProbablyHTML('<details open ontoggle="alert(1)">x</details>'),
|
||||
).toBe(true);
|
||||
expect(isProbablyHTML('<summary>x</summary>')).toBe(true);
|
||||
expect(isProbablyHTML('<object data="x"></object>')).toBe(true);
|
||||
expect(isProbablyHTML('<embed src="x">')).toBe(true);
|
||||
expect(isProbablyHTML('<marquee>x</marquee>')).toBe(true);
|
||||
expect(isProbablyHTML('<template>x</template>')).toBe(true);
|
||||
expect(isProbablyHTML('<dialog open>x</dialog>')).toBe(true);
|
||||
});
|
||||
|
||||
test('should return true for elements that parse into document.head', () => {
|
||||
expect(isProbablyHTML('<style>body { display: none; }</style>')).toBe(true);
|
||||
expect(isProbablyHTML('<title>injected</title>')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sanitizeHtmlIfNeeded', () => {
|
||||
@@ -156,24 +137,6 @@ describe('sanitizeHtmlIfNeeded', () => {
|
||||
const sanitizedString = sanitizeHtmlIfNeeded(plainText);
|
||||
expect(sanitizedString).toEqual(plainText);
|
||||
});
|
||||
|
||||
test('should sanitize svg/details/style payloads instead of passing them through', () => {
|
||||
const svgPayload = '<svg onload="alert(document.cookie)"></svg>';
|
||||
const sanitizedSvg = sanitizeHtmlIfNeeded(svgPayload);
|
||||
expect(sanitizedSvg).not.toContain('<svg');
|
||||
expect(sanitizedSvg).not.toContain('onload');
|
||||
|
||||
// `details` (with its `open` attribute) is in js-xss's default
|
||||
// whitelist, so the tag itself survives sanitization; the fix is that
|
||||
// the payload is now routed through FilterXSS at all, which strips the
|
||||
// non-whitelisted `ontoggle` handler instead of returning it verbatim.
|
||||
const detailsPayload = '<details open ontoggle="alert(1)">x</details>';
|
||||
const sanitizedDetails = sanitizeHtmlIfNeeded(detailsPayload);
|
||||
expect(sanitizedDetails).toEqual('<details open>x</details>');
|
||||
|
||||
const stylePayload = '<style>body { display: none; }</style>';
|
||||
expect(sanitizeHtmlIfNeeded(stylePayload)).not.toContain('<style');
|
||||
});
|
||||
});
|
||||
|
||||
describe('safeHtmlSpan', () => {
|
||||
|
||||
@@ -154,20 +154,6 @@ const KNOWN_HTML_TAGS = new Set([
|
||||
'html',
|
||||
'head',
|
||||
'body',
|
||||
// Script-capable elements and foreign-content roots (SVG/MathML). These
|
||||
// must be classified as HTML so that downstream sanitization is applied;
|
||||
// omitting them makes the heuristic fail open — payloads such as
|
||||
// `<svg onload=...>` or `<details open ontoggle=...>` would be classified
|
||||
// "not HTML" and returned verbatim by sanitizeHtmlIfNeeded.
|
||||
'svg',
|
||||
'math',
|
||||
'details',
|
||||
'summary',
|
||||
'object',
|
||||
'embed',
|
||||
'marquee',
|
||||
'template',
|
||||
'dialog',
|
||||
]);
|
||||
|
||||
const HTML_TAG_PATTERN = new RegExp(
|
||||
@@ -197,15 +183,10 @@ export function isProbablyHTML(text: string) {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(cleanedStr, 'text/html');
|
||||
|
||||
// Check if parsing created actual HTML elements (not just text nodes).
|
||||
// Some elements (e.g. <style>, <title>, <meta>, <link>) parse into
|
||||
// document.head rather than document.body, so both must be inspected —
|
||||
// otherwise a bare <style> payload is classified "not HTML" and skips
|
||||
// sanitization.
|
||||
const elements = [
|
||||
...Array.from(doc.head.childNodes),
|
||||
...Array.from(doc.body.childNodes),
|
||||
].filter(node => node.nodeType === 1) as Element[];
|
||||
// Check if parsing created actual HTML elements (not just text nodes)
|
||||
const elements = Array.from(doc.body.childNodes).filter(
|
||||
node => node.nodeType === 1,
|
||||
) as Element[];
|
||||
|
||||
// If no elements were created, it's not HTML
|
||||
if (elements.length === 0) {
|
||||
|
||||
-25
@@ -60,31 +60,6 @@ test('formats float bytes in human readable format with default options', () =>
|
||||
expect(formatter(1200.666)).toBe('1.2kB');
|
||||
});
|
||||
|
||||
test('formats values below one byte without dropping the unit', () => {
|
||||
const formatter = createMemoryFormatter();
|
||||
expect(formatter(0.5)).toBe('0.5B');
|
||||
expect(formatter(0.004)).toBe('0B');
|
||||
expect(formatter(-0.25)).toBe('-0.25B');
|
||||
|
||||
const binaryFormatter = createMemoryFormatter({ binary: true });
|
||||
expect(binaryFormatter(0.5)).toBe('0.5B');
|
||||
});
|
||||
|
||||
test('rolls over to the next unit when rounding reaches the base', () => {
|
||||
const formatter = createMemoryFormatter();
|
||||
expect(formatter(999999)).toBe('1MB');
|
||||
expect(formatter(999995)).toBe('1MB');
|
||||
expect(formatter(999994)).toBe('999.99kB');
|
||||
expect(formatter(-999999)).toBe('-1MB');
|
||||
|
||||
const binaryFormatter = createMemoryFormatter({ binary: true });
|
||||
expect(binaryFormatter(1024 * 1024 - 1)).toBe('1MiB');
|
||||
|
||||
// the largest unit has nothing to roll over into
|
||||
const largest = createMemoryFormatter();
|
||||
expect(largest(Math.pow(1000, 11))).toBe('1000QB');
|
||||
});
|
||||
|
||||
test('formats bytes in human readable format with additional binary option', () => {
|
||||
const formatter = createMemoryFormatter({ binary: true });
|
||||
expect(formatter(0)).toBe('0B');
|
||||
|
||||
-6
@@ -24,12 +24,6 @@ describe('createSmartNumberFormatter(options)', () => {
|
||||
const formatter = createSmartNumberFormatter();
|
||||
expect(formatter).toBeInstanceOf(NumberFormatter);
|
||||
});
|
||||
test('uses the supplied formatter id regardless of signed option', () => {
|
||||
expect(createSmartNumberFormatter({ id: 'custom' }).id).toBe('custom');
|
||||
expect(
|
||||
createSmartNumberFormatter({ id: 'custom-signed', signed: true }).id,
|
||||
).toBe('custom-signed');
|
||||
});
|
||||
describe('using default options', () => {
|
||||
const formatter = createSmartNumberFormatter();
|
||||
test('formats 0 correctly', () => {
|
||||
|
||||
@@ -244,24 +244,6 @@ test('parseErrorJson with message', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('parseErrorJson preserves string-valued validation messages', () => {
|
||||
const calculatedColumnError =
|
||||
'Custom SQL fields cannot be parsed as a single SQL statement.';
|
||||
|
||||
expect(
|
||||
parseErrorJson({
|
||||
message: {
|
||||
'columns.0.expression': calculatedColumnError,
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
message: {
|
||||
'columns.0.expression': calculatedColumnError,
|
||||
},
|
||||
error: calculatedColumnError,
|
||||
});
|
||||
});
|
||||
|
||||
test('parseErrorJson with HTML message', () => {
|
||||
expect(
|
||||
parseErrorJson({
|
||||
|
||||
@@ -96,57 +96,16 @@ export class Menu {
|
||||
itemText: string,
|
||||
options?: { timeout?: number },
|
||||
): Promise<void> {
|
||||
const popup = await this.openSubmenu(submenuText, {
|
||||
timeout: options?.timeout,
|
||||
itemText,
|
||||
});
|
||||
|
||||
// Use dispatchEvent instead of click to bypass viewport and pointer interception
|
||||
// issues. Ant Design renders submenu popups in a portal that can be positioned
|
||||
// outside the viewport or behind chart content (e.g., large tables with z-index).
|
||||
await popup.getByText(itemText, { exact: true }).dispatchEvent('click');
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a submenu and returns its popup locator, without selecting an item.
|
||||
* Useful when the caller needs to read the popup's contents (e.g. the set of
|
||||
* offered items) rather than clicking a known item.
|
||||
*
|
||||
* Uses hover as primary approach, falls back to keyboard then dispatchEvent -
|
||||
* same fallback chain as {@link selectSubmenuItem}.
|
||||
*
|
||||
* @param submenuText - The text of the submenu to open (e.g., "Download")
|
||||
* @param options - Optional timeout, an `itemText` to scope the popup lookup
|
||||
* to (useful when multiple submenu popups could otherwise match), and a
|
||||
* `popupSelector` override for submenus that render with an additional,
|
||||
* more specific class than the generic Ant Design popup class.
|
||||
*/
|
||||
async openSubmenu(
|
||||
submenuText: string,
|
||||
options?: { timeout?: number; itemText?: string; popupSelector?: string },
|
||||
): Promise<Locator> {
|
||||
const timeout = options?.timeout ?? TIMEOUT.FORM_LOAD;
|
||||
const matchPopup = (): Locator => {
|
||||
const base = this.page.locator(
|
||||
options?.popupSelector ?? Menu.SELECTORS.SUBMENU_POPUP,
|
||||
);
|
||||
return options?.itemText
|
||||
? base.filter({ hasText: options.itemText })
|
||||
: base;
|
||||
};
|
||||
|
||||
// Try hover first (most natural user interaction)
|
||||
let popup = await this.openSubmenuWithHover(
|
||||
submenuText,
|
||||
matchPopup,
|
||||
timeout,
|
||||
);
|
||||
let popup = await this.openSubmenuWithHover(submenuText, itemText, timeout);
|
||||
|
||||
// Fallback to keyboard navigation
|
||||
if (!popup) {
|
||||
popup = await this.openSubmenuWithKeyboard(
|
||||
submenuText,
|
||||
matchPopup,
|
||||
itemText,
|
||||
timeout,
|
||||
);
|
||||
}
|
||||
@@ -155,7 +114,7 @@ export class Menu {
|
||||
if (!popup) {
|
||||
popup = await this.openSubmenuWithDispatchEvent(
|
||||
submenuText,
|
||||
matchPopup,
|
||||
itemText,
|
||||
timeout,
|
||||
);
|
||||
}
|
||||
@@ -166,7 +125,10 @@ export class Menu {
|
||||
);
|
||||
}
|
||||
|
||||
return popup;
|
||||
// Use dispatchEvent instead of click to bypass viewport and pointer interception
|
||||
// issues. Ant Design renders submenu popups in a portal that can be positioned
|
||||
// outside the viewport or behind chart content (e.g., large tables with z-index).
|
||||
await popup.getByText(itemText, { exact: true }).dispatchEvent('click');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,14 +137,17 @@ export class Menu {
|
||||
*/
|
||||
private async openSubmenuWithHover(
|
||||
submenuText: string,
|
||||
matchPopup: () => Locator,
|
||||
itemText: string,
|
||||
timeout: number,
|
||||
): Promise<Locator | null> {
|
||||
try {
|
||||
const submenuTitle = this.getSubmenuTitle(submenuText);
|
||||
await submenuTitle.hover();
|
||||
|
||||
const popup = matchPopup();
|
||||
// Find the popup that contains the expected item (scopes to correct popup)
|
||||
const popup = this.page
|
||||
.locator(Menu.SELECTORS.SUBMENU_POPUP)
|
||||
.filter({ hasText: itemText });
|
||||
await popup.waitFor({ state: 'visible', timeout });
|
||||
|
||||
// Allow Ant Design's slide-in animation to complete before clicking.
|
||||
@@ -201,7 +166,7 @@ export class Menu {
|
||||
*/
|
||||
private async openSubmenuWithKeyboard(
|
||||
submenuText: string,
|
||||
matchPopup: () => Locator,
|
||||
itemText: string,
|
||||
timeout: number,
|
||||
): Promise<Locator | null> {
|
||||
try {
|
||||
@@ -209,7 +174,9 @@ export class Menu {
|
||||
await submenuTitle.focus();
|
||||
await this.page.keyboard.press('ArrowRight');
|
||||
|
||||
const popup = matchPopup();
|
||||
const popup = this.page
|
||||
.locator(Menu.SELECTORS.SUBMENU_POPUP)
|
||||
.filter({ hasText: itemText });
|
||||
await popup.waitFor({ state: 'visible', timeout });
|
||||
|
||||
return popup;
|
||||
@@ -224,7 +191,7 @@ export class Menu {
|
||||
*/
|
||||
private async openSubmenuWithDispatchEvent(
|
||||
submenuText: string,
|
||||
matchPopup: () => Locator,
|
||||
itemText: string,
|
||||
timeout: number,
|
||||
): Promise<Locator | null> {
|
||||
try {
|
||||
@@ -247,7 +214,9 @@ export class Menu {
|
||||
);
|
||||
});
|
||||
|
||||
const popup = matchPopup();
|
||||
const popup = this.page
|
||||
.locator(Menu.SELECTORS.SUBMENU_POPUP)
|
||||
.filter({ hasText: itemText });
|
||||
await popup.waitFor({ state: 'visible', timeout });
|
||||
|
||||
return popup;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user