mirror of
https://github.com/apache/superset.git
synced 2026-09-09 08:44:32 +00:00
Compare commits
77
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c76e4d98f2 | ||
|
|
fc110d8428 | ||
|
|
4f26627a6a | ||
|
|
613871ed36 | ||
|
|
c503f92642 | ||
|
|
50c1504cac | ||
|
|
5d771e9f46 | ||
|
|
c9965c7bf4 | ||
|
|
9669e41471 | ||
|
|
daf41bdf04 | ||
|
|
986b648f1c | ||
|
|
acb29937ae | ||
|
|
2875c76d55 | ||
|
|
d0f139c210 | ||
|
|
9de50b85e5 | ||
|
|
c510946df5 | ||
|
|
91f4d01ca2 | ||
|
|
3828dcb922 | ||
|
|
362d3509cb | ||
|
|
fa31e9c1c3 | ||
|
|
335a2a78ea | ||
|
|
3564baabf6 | ||
|
|
380675662d | ||
|
|
0e6170aaa3 | ||
|
|
9d89dbddc7 | ||
|
|
02ea5775c1 | ||
|
|
83d5d1ccda | ||
|
|
7b65d22205 | ||
|
|
1339bdc6ce | ||
|
|
10866337c5 | ||
|
|
e6d088d6b1 | ||
|
|
5986fe92c2 | ||
|
|
04ab089962 | ||
|
|
bc35151350 | ||
|
|
7029c468c4 | ||
|
|
60553b4326 | ||
|
|
85ea6e900a | ||
|
|
a114b4cc46 | ||
|
|
3ccab2ea7a | ||
|
|
067a356b6f | ||
|
|
57eafd75e0 | ||
|
|
c45fc6a31a | ||
|
|
33c6df77c9 | ||
|
|
204ccc7ce7 | ||
|
|
08694d64cb | ||
|
|
056fd218d4 | ||
|
|
ad319bd8a3 | ||
|
|
7382ee0bfe | ||
|
|
7d0cf1acfc | ||
|
|
c4e145b071 | ||
|
|
cdb6c8e0c9 | ||
|
|
89786160f9 | ||
|
|
bd3ab30907 | ||
|
|
1b3758d2a4 | ||
|
|
1fb6b98941 | ||
|
|
8d6044a147 | ||
|
|
574d121cd8 | ||
|
|
d22069a7af | ||
|
|
233f6379ae | ||
|
|
4440887dc2 | ||
|
|
53070fa4c0 | ||
|
|
92bd2ba057 | ||
|
|
4eff13b81e | ||
|
|
06913244de | ||
|
|
a5377541c9 | ||
|
|
e5e6d74819 | ||
|
|
b8fb5a268c | ||
|
|
07052752b6 | ||
|
|
7c9c32651c | ||
|
|
0db9ca7401 | ||
|
|
7d09490bb8 | ||
|
|
df3abe474c | ||
|
|
87f138cc80 | ||
|
|
c03f3441bd | ||
|
|
f3896ccf20 | ||
|
|
18d277c096 | ||
|
|
fe1b368bcd |
@@ -1,23 +0,0 @@
|
||||
name: Label Draft PRs
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- converted_to_draft
|
||||
jobs:
|
||||
label-draft:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check if the PR is a draft
|
||||
id: check-draft
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const isDraft = context.payload.pull_request.draft;
|
||||
core.setOutput('isDraft', isDraft);
|
||||
- name: Add `review:draft` Label
|
||||
if: steps.check-draft.outputs.isDraft == 'true'
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
labels: "review:draft"
|
||||
@@ -26,7 +26,7 @@ runs:
|
||||
|
||||
- name: Set up QEMU
|
||||
if: ${{ inputs.build == 'true' }}
|
||||
uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
|
||||
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
|
||||
with:
|
||||
# Pin the binfmt image to a specific QEMU release. The default
|
||||
# (`tonistiigi/binfmt:latest`) is a moving target, and drift across
|
||||
@@ -39,12 +39,12 @@ runs:
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
if: ${{ inputs.build == 'true' }}
|
||||
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
|
||||
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
||||
|
||||
- name: Try to login to DockerHub
|
||||
if: ${{ inputs.login-to-dockerhub == 'true' }}
|
||||
continue-on-error: true
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
|
||||
with:
|
||||
username: ${{ inputs.dockerhub-user }}
|
||||
password: ${{ inputs.dockerhub-token }}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# Verifies that every `uses:` ref under .github/ is on the ASF Infrastructure
|
||||
# GitHub Actions allowlist (apache/infrastructure-actions). An action that is
|
||||
# not allowlisted fails at "Set up job" with no logs and no notification, so
|
||||
# this check surfaces the problem at PR time instead. It also warns (without
|
||||
# failing) when a pinned SHA's allowlist entry is about to expire.
|
||||
name: ASF Allowlist Check
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
paths:
|
||||
- ".github/**"
|
||||
push:
|
||||
branches:
|
||||
- "master"
|
||||
- "[0-9].[0-9]*"
|
||||
paths:
|
||||
- ".github/**"
|
||||
schedule:
|
||||
# Weekly, so allowlist expirations are surfaced even when nothing under
|
||||
# .github/ has changed.
|
||||
- cron: "0 6 * * 1"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# cancel previous workflow jobs for PRs
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
asf-allowlist-check:
|
||||
runs-on: ubuntu-26.04
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Check action refs against the ASF allowlist
|
||||
uses: apache/infrastructure-actions/allowlist-check@df54e48ff76152790f317934c691cfa7fd7a1a46 # allowlist-check/v1.0.1
|
||||
with:
|
||||
# Default scan-glob is .github/**/*.yml, which misses .yaml files.
|
||||
scan-glob: ".github/**/*.y*ml"
|
||||
@@ -49,4 +49,4 @@ jobs:
|
||||
run: bash .github/workflows/scripts/check-docs-deploy-freshness.test.sh
|
||||
|
||||
- name: Check for security issues on GHA workflows
|
||||
uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2
|
||||
uses: zizmorcore/zizmor-action@70fb788f84895a7701f5643d103d587e460b5c99 # v0.6.3
|
||||
|
||||
@@ -168,7 +168,10 @@ jobs:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.python == 'true' || needs.changes.outputs.frontend == 'true'
|
||||
runs-on: ubuntu-26.04
|
||||
timeout-minutes: 30
|
||||
# Embedded Tests below adds its own gunicorn boot + spec run on top of
|
||||
# Required and Soft-delete; 30m was tight even for the two-step shadow
|
||||
# job this replaced.
|
||||
timeout-minutes: 40
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
@@ -176,7 +179,10 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
browser: ["chromium"]
|
||||
app_root: ${{ github.event_name == 'push' && fromJSON('["", "/app/prefix"]') || fromJSON('[""]') }}
|
||||
# Subdirectory deployment (APPLICATION_ROOT) is a required-to-pass
|
||||
# dimension, not an optional one, so it runs on every event —
|
||||
# unlike cypress-matrix above, which only widens on push.
|
||||
app_root: ["", "/app/prefix"]
|
||||
env:
|
||||
SUPERSET_ENV: development
|
||||
SUPERSET_CONFIG: tests.integration_tests.superset_test_config
|
||||
@@ -266,14 +272,25 @@ jobs:
|
||||
# Scoped to this step: each playwright-run boots its own gunicorn
|
||||
# with the step's env, so the Required Tests server above keeps
|
||||
# master's Flask configuration while this one runs with SOFT_DELETE
|
||||
# on — the same isolation pattern as the Embedded step in
|
||||
# superset-playwright.yml. Without a flag-on server the
|
||||
# recently-archived specs skip themselves everywhere and ship zero
|
||||
# executed E2E coverage; in the Required run above they are
|
||||
# collected and skipped, which is expected.
|
||||
# on — the same isolation pattern as the Embedded Tests step below.
|
||||
# Without a flag-on server the recently-archived specs skip
|
||||
# themselves everywhere and ship zero executed E2E coverage; in the
|
||||
# Required run above they are collected and skipped, which is
|
||||
# expected.
|
||||
SUPERSET_FEATURE_SOFT_DELETE: "true"
|
||||
with:
|
||||
run: playwright-run "${{ matrix.app_root }}" recently-archived/
|
||||
- name: Run Playwright (Embedded Tests)
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||
# Scoped to this step for the same reason as Soft-delete above:
|
||||
# embedding is a real, required feature, so its Playwright coverage
|
||||
# now gates merges instead of running only in shadow mode.
|
||||
SUPERSET_FEATURE_EMBEDDED_SUPERSET: "true"
|
||||
INCLUDE_EMBEDDED: "true"
|
||||
with:
|
||||
run: playwright-run "${{ matrix.app_root }}" embedded
|
||||
- name: Set safe app root
|
||||
if: failure()
|
||||
id: set-safe-app-root
|
||||
|
||||
@@ -46,8 +46,10 @@ jobs:
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# NOTE: Required Playwright tests are in superset-e2e.yml (E2E / playwright-tests)
|
||||
# This workflow contains only experimental tests that run in shadow mode
|
||||
# NOTE: Required Playwright tests are in superset-e2e.yml (E2E / playwright-tests),
|
||||
# including Embedded — it moved out of this workflow because embedding is a
|
||||
# required feature, not an experimental one. This workflow now contains
|
||||
# only experimental and mobile tests, which run in shadow mode.
|
||||
playwright-tests-experimental:
|
||||
needs: changes
|
||||
if: needs.changes.outputs.python == 'true' || needs.changes.outputs.frontend == 'true'
|
||||
@@ -130,10 +132,6 @@ jobs:
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
run: build-instrumented-assets
|
||||
- name: Build embedded SDK
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
run: build-embedded-sdk
|
||||
- name: Install Playwright
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
@@ -144,27 +142,13 @@ jobs:
|
||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||
with:
|
||||
run: playwright-run "${{ matrix.app_root }}" experimental/
|
||||
- name: Run Playwright (Embedded Tests)
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||
# Scope embedded-only env vars to this step. Setting them at the job
|
||||
# level enabled the EMBEDDED_SUPERSET feature flag inside Flask for
|
||||
# the preceding "Required Tests" and "Experimental Tests" steps too,
|
||||
# which loads extra handlers and destabilizes the werkzeug dev
|
||||
# server under the 2-worker Playwright load. Required Tests should
|
||||
# match master's Flask configuration.
|
||||
SUPERSET_FEATURE_EMBEDDED_SUPERSET: "true"
|
||||
INCLUDE_EMBEDDED: "true"
|
||||
with:
|
||||
run: playwright-run "${{ matrix.app_root }}" embedded
|
||||
- name: Run Playwright (Mobile Tests)
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=4096"
|
||||
# Scoped to this step for the same reason as the embedded flags
|
||||
# above: the mobile consumption mode should not alter Flask's
|
||||
# configuration for the required desktop test steps.
|
||||
# Scoped to this step: setting feature flags at the job level would
|
||||
# alter Flask's configuration for the preceding Experimental step
|
||||
# too — the mobile consumption mode should not do that.
|
||||
SUPERSET_FEATURE_MOBILE_CONSUMPTION_MODE: "true"
|
||||
INCLUDE_MOBILE: "true"
|
||||
with:
|
||||
|
||||
@@ -210,9 +210,15 @@ jobs:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-backend/
|
||||
uses: $/.github/actions/setup-backend/
|
||||
- name: Install dependencies
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
# cached-dependencies is a git submodule (not a plain directory), and
|
||||
# the $/ self-repository syntax resolves action files directly from
|
||||
# the repository without performing a real (submodule-aware)
|
||||
# checkout, so it can't see into a submodule's gitlink. Keep this one
|
||||
# on the workspace-relative ./ form, consistent with every other
|
||||
# workflow in the repo that references this action.
|
||||
uses: ./.github/actions/cached-dependencies # zizmor: ignore[self-repository] - $/ cannot resolve an action that lives in a submodule; ./ is required here
|
||||
with:
|
||||
run: |
|
||||
# sqlite needs this working directory
|
||||
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
id: check
|
||||
uses: ./.github/actions/change-detector/
|
||||
uses: $/.github/actions/change-detector/
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -80,9 +80,15 @@ jobs:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-backend/
|
||||
uses: $/.github/actions/setup-backend/
|
||||
- name: Setup Postgres
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
# cached-dependencies is a submodule (not a plain directory), and
|
||||
# the $/ self-repository syntax resolves action files directly from
|
||||
# the repository without performing a real (submodule-aware)
|
||||
# checkout, so it can't see into a submodule's link. Keep this one
|
||||
# on the workspace-relative ./ form, consistent with every other
|
||||
# workflow in the repo that references this action.
|
||||
uses: ./.github/actions/cached-dependencies # zizmor: ignore[self-repository] - $/ cannot resolve an action that lives in a submodule; ./ is required here
|
||||
with:
|
||||
run: setup-postgres
|
||||
- name: Start Celery worker
|
||||
@@ -141,13 +147,19 @@ jobs:
|
||||
- name: Start hadoop and hive
|
||||
run: docker compose -f scripts/databases/hive/docker-compose.yml up -d
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-backend/
|
||||
uses: $/.github/actions/setup-backend/
|
||||
- name: Setup Postgres
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
with:
|
||||
run: setup-postgres
|
||||
- name: Start Celery worker
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
# cached-dependencies is a submodule (not a plain directory), and
|
||||
# the $/ self-repository syntax resolves action files directly from
|
||||
# the repository without performing a real (submodule-aware)
|
||||
# checkout, so it can't see into a submodule's link. Keep this one
|
||||
# on the workspace-relative ./ form, consistent with every other
|
||||
# workflow in the repo that references this action.
|
||||
uses: ./.github/actions/cached-dependencies # zizmor: ignore[self-repository] - $/ cannot resolve an action that lives in a submodule; ./ is required here
|
||||
with:
|
||||
run: celery-worker
|
||||
- name: Python unit tests (PostgreSQL)
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
- name: Check for file changes
|
||||
id: check
|
||||
uses: ./.github/actions/change-detector/
|
||||
uses: $/.github/actions/change-detector/
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -60,7 +60,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Setup Python
|
||||
uses: ./.github/actions/setup-backend/
|
||||
uses: $/.github/actions/setup-backend/
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Python unit tests
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: check
|
||||
uses: ./.github/actions/change-detector/
|
||||
uses: $/.github/actions/change-detector/
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -45,7 +45,13 @@ jobs:
|
||||
cache-dependency-path: "superset-frontend/package-lock.json"
|
||||
- name: Install dependencies
|
||||
if: steps.check.outputs.frontend
|
||||
uses: ./.github/actions/cached-dependencies
|
||||
# cached-dependencies is a git submodule (not a plain directory), and
|
||||
# the $/ self-repository syntax resolves action files directly from
|
||||
# the repository without performing a real (submodule-aware)
|
||||
# checkout, so it can't see into a submodule's gitlink. Keep this one
|
||||
# on the workspace-relative ./ form, consistent with every other
|
||||
# workflow in the repo that references this action.
|
||||
uses: ./.github/actions/cached-dependencies # zizmor: ignore[self-repository] - $/ cannot resolve an action that lives in a submodule; ./ is required here
|
||||
with:
|
||||
run: npm-install
|
||||
- name: lint
|
||||
@@ -68,13 +74,13 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: check
|
||||
uses: ./.github/actions/change-detector/
|
||||
uses: $/.github/actions/change-detector/
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Python
|
||||
if: steps.check.outputs.python == 'true' || steps.check.outputs.frontend == 'true'
|
||||
uses: ./.github/actions/setup-backend/
|
||||
uses: $/.github/actions/setup-backend/
|
||||
|
||||
- name: Install gettext tools
|
||||
if: steps.check.outputs.python == 'true' || steps.check.outputs.frontend == 'true'
|
||||
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup supersetbot
|
||||
uses: ./.github/actions/setup-supersetbot/
|
||||
uses: $/.github/actions/setup-supersetbot/
|
||||
|
||||
- name: Execute custom Node.js script
|
||||
env:
|
||||
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Docker Environment
|
||||
uses: ./.github/actions/setup-docker
|
||||
uses: $/.github/actions/setup-docker
|
||||
with:
|
||||
dockerhub-user: ${{ secrets.DOCKERHUB_USER }}
|
||||
dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Setup supersetbot
|
||||
uses: ./.github/actions/setup-supersetbot/
|
||||
uses: $/.github/actions/setup-supersetbot
|
||||
|
||||
- name: Execute custom Node.js script
|
||||
env:
|
||||
@@ -139,7 +139,7 @@ jobs:
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Setup supersetbot
|
||||
uses: ./.github/actions/setup-supersetbot/
|
||||
uses: $/.github/actions/setup-supersetbot/
|
||||
|
||||
- name: Label the PRs with the right release-related labels
|
||||
env:
|
||||
|
||||
@@ -178,7 +178,7 @@ repos:
|
||||
name: zizmor (GHA security audit)
|
||||
entry: zizmor
|
||||
language: python
|
||||
additional_dependencies: [zizmor==1.25.2]
|
||||
additional_dependencies: [zizmor==1.30.0]
|
||||
files: ^\.github/
|
||||
types: [yaml]
|
||||
pass_filenames: false
|
||||
|
||||
+29
@@ -24,6 +24,34 @@ assists people when migrating to a new version.
|
||||
|
||||
## Next
|
||||
|
||||
### Tagging is on by default
|
||||
|
||||
`TAGGING_SYSTEM` now ships **on**. The Tags menu entry, the tag columns and
|
||||
filters on the chart, dashboard and saved-query lists, and the Tags field in the
|
||||
chart and dashboard property modals are all visible without configuration, and
|
||||
tags are included in asset export and import.
|
||||
|
||||
**What operators should expect:**
|
||||
|
||||
- **Implicit tags accrue.** Saving a chart, dashboard, dataset or saved query,
|
||||
and favoriting an asset, write rows to `tag` and `tagged_object` (`type:chart`,
|
||||
`editor:<user id>`, `favorited_by:<user id>`). These have always been created
|
||||
when the flag was on; they are simply no longer opt-in.
|
||||
- **Exports gain a `tags` key and a `tags.yaml` file.** Chart and dashboard
|
||||
export bundles carry custom tags. Importers on 6.0 and later understand both;
|
||||
older importers skip the unrecognized `tags.yaml` file but reject chart and
|
||||
dashboard YAML that contains a `tags` key, so strip that key before importing
|
||||
a bundle into Superset 5.x or earlier.
|
||||
- **The flag is honored at write time.** The tagging SQLA event listeners are
|
||||
always attached at startup; the ones that create tags check `TAGGING_SYSTEM`
|
||||
when they fire, so the flag, including a runtime override through
|
||||
`GET_FEATURE_FLAGS_FUNC` or `IS_FEATURE_ENABLED_FUNC`, takes effect without a
|
||||
restart. The cleanup listeners run regardless of the flag, so deleting an
|
||||
asset never leaves orphaned `tagged_object` rows behind.
|
||||
|
||||
Set `FEATURE_FLAGS = {"TAGGING_SYSTEM": False}` to restore the previous
|
||||
behavior. Existing tag rows are left untouched.
|
||||
|
||||
### Global Async Queries re-platformed onto the Global Task Framework (breaking)
|
||||
|
||||
Global Async Queries (GAQ) no longer runs on its own bespoke async-events
|
||||
@@ -202,6 +230,7 @@ before retrying. Preview or recheck failures fail closed rather than treating
|
||||
unknown impact as zero. Chart and dashboard purge endpoints are unchanged.
|
||||
|
||||
- The dashboard datasource-based visibility fallback now fails closed: a dashboard whose member charts’ datasources cannot be resolved (deleted datasource rows, missing `datasource_id`, or unsupported datasource types) is no longer accessible to users without explicit editor/viewer rights, and a dashboard composed of semantic-view charts now requires `datasource_access` on (at least one of) its semantic views or their parent semantic layer — previously any authenticated user could open such a dashboard’s shell. Because the fallback now considers every member chart rather than only table-backed ones, a user holding `datasource_access` on any single member datasource — including a semantic view or its parent layer — can open a mixed dashboard that previously denied them. Dashboards with no charts remain accessible, and dashboards with explicit viewers are unaffected. Conversely, holders of `all_datasource_access` now see every published no-viewer dashboard in the dashboard list — including chart-less ones previously hidden by the inner joins — matching what the object-level gate already allowed them to open.
|
||||
- Version restore (`POST /api/v1/{chart,dashboard,dataset}/<uuid>/versions/<version_uuid>/restore`) now refuses an **externally managed** entity (`is_managed_externally = True`) with HTTP 403, enforcing server-side what the docs already promised. Previously the refusal existed only in the browser, so an otherwise-authorized editor could restore such an entity by calling the endpoint directly and have the restore overwritten on the next external sync. Soft-delete recovery is deliberately unaffected — it changes visibility, not content.
|
||||
- `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.
|
||||
|
||||
|
||||
@@ -486,6 +486,39 @@ Log in as an admin user to ensure you have adequate permissions.
|
||||
|
||||
This is the best source of information about the problem. In a docker compose deployment, you can do this with a command like `docker logs superset_worker --since 1h`.
|
||||
|
||||
### CSV and Excel chart-data request failures
|
||||
|
||||
The worker uses the saved query context to POST to the chart-data export endpoint,
|
||||
falling back to the legacy GET export when a query context cannot be generated.
|
||||
`ALERT_REPORTS_CSV_REQUEST_TIMEOUT` (60 seconds by default) limits socket operations;
|
||||
the report execution budget and its delivery/cleanup reserves also cap the request.
|
||||
Connection and read timeouts are reported as CSV/Excel generation timeouts.
|
||||
These attachment timeouts are logged at error level and explicitly mark the report
|
||||
task as failed, while the report execution retains its ERROR state and separate
|
||||
error-notification history. Other HTTP 408 exception handling is unchanged.
|
||||
|
||||
To tolerate short-lived transport failures, operators can opt in with
|
||||
`ALERT_REPORTS_CSV_REQUEST_RETRY = True` (default: `False`). This permits **one** retry
|
||||
for transient connection/read failures and HTTP 429, 500, 502, 503, or 504. Other
|
||||
HTTP statuses are not retried. Backoff is 0.5 seconds, extended to at most 2 seconds
|
||||
for a numeric `Retry-After`; longer, invalid, or date-based delays are not retried
|
||||
inline. Both attempts and backoff share the initial request timeout allowance and
|
||||
respect the remaining execution budget. Unbounded requests are not retried.
|
||||
A request that consumes its entire timeout does **not** get another full timeout.
|
||||
Socket timeouts are not wall-clock cancellation: existing report task limits still
|
||||
interrupt in-flight work. A timed-out server query can continue running, so enabling
|
||||
retries can increase database load. Leave retries disabled unless appropriate for
|
||||
your deployment; disable the setting to roll back retry behavior.
|
||||
|
||||
Worker diagnostics include schedule/chart identifiers, a fixed endpoint path (no
|
||||
query string), error category, HTTP status, timeout, elapsed duration, and attempt.
|
||||
For HTTP errors, at most 4097 response bytes are read to enforce a 4096-byte limit.
|
||||
Only recognized Superset error types from up to four JSON errors are retained;
|
||||
free-form messages, extra fields, and non-JSON or oversized bodies are redacted or
|
||||
omitted. Cookies, authentication headers, URLs, SQL, and query payloads are not
|
||||
included in these transport diagnostics. HTTP 400 therefore remains a failure to
|
||||
investigate, not a reason to repeat the same request.
|
||||
|
||||
### Check web browser and webdriver installation
|
||||
|
||||
To take a screenshot, the worker visits the dashboard or chart using a headless browser, then takes a screenshot. If you are able to send a chart as CSV, XLSX, or text but can't send as PNG, your problem may lie with the browser.
|
||||
|
||||
@@ -540,6 +540,8 @@ MCP_STORE_CONFIG = {
|
||||
|
||||
When `CACHE_REDIS_URL` is set, the MCP server uses a Redis-backed EventStore for session management, allowing replicas to share state. Without Redis, each pod manages its own in-memory sessions and stateful MCP interactions may fail when requests hit different replicas.
|
||||
|
||||
`MCP_STATELESS_HTTP` (default `True`) controls whether requests get a fresh, ephemeral transport per HTTP round trip or a transport that stays alive for the session's lifetime. The default suits multi-pod deployments because it doesn't require session affinity -- any pod can handle any request. Its tradeoff: a client disconnecting mid-tool-call can crash not just its own session but other concurrent sessions on the same worker. Setting it to `False` avoids that, but it requires session-affinity (sticky session) routing on `Mcp-Session-Id` at the mesh/ingress layer, since a session's follow-up requests must land on the same pod that created it. See [`MCP_STATELESS_HTTP`](#core) below.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Reference
|
||||
@@ -555,6 +557,7 @@ All MCP settings go in `superset_config.py`. Defaults are defined in `superset/m
|
||||
| `MCP_SERVICE_URL` | `None` | Public base URL for MCP-generated links (set this when behind a reverse proxy) |
|
||||
| `MCP_DEBUG` | `False` | Enable debug logging |
|
||||
| `MCP_DEV_USERNAME` | -- | Superset username for development mode (no auth) |
|
||||
| `MCP_STATELESS_HTTP` | `True` | Streamable-HTTP session mode. `True` gives each request a fresh, ephemeral transport, torn down as soon as that request completes; a client disconnecting mid-tool-call can crash not just its own session but other concurrent sessions on the same worker. `False` keeps the transport alive for the session's lifetime, avoiding that crash, but requires session-affinity routing on `Mcp-Session-Id` for multi-pod deployments (see [Multi-Pod (Kubernetes)](#multi-pod-kubernetes)). |
|
||||
| `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). |
|
||||
|
||||
@@ -138,6 +138,18 @@ The existing `APP_NAME` Python config key continues to work for backward compati
|
||||
Email and alert/report notification subjects are driven by backend settings such as
|
||||
`EMAIL_REPORTS_SUBJECT_PREFIX` and `APP_NAME`, not by this theme token.
|
||||
|
||||
To hide the entire brand area in the navbar (both the logo image and the
|
||||
brand text), set `HIDE_NAVBAR_LOGO` in `superset_config.py`:
|
||||
|
||||
```python
|
||||
# Hide the entire brand area in the navbar, including the logo image and the
|
||||
# brand text (brandAppName / APP_NAME). Defaults to False.
|
||||
HIDE_NAVBAR_LOGO = True
|
||||
```
|
||||
|
||||
`HIDE_NAVBAR_LOGO` is a Python config flag rather than a theme token, so it
|
||||
cannot be set through the theme CRUD UI or `THEME_DEFAULT`/`THEME_DARK`.
|
||||
|
||||
### Migration from Configuration to UI
|
||||
|
||||
When `ENABLE_UI_THEME_ADMINISTRATION = True`:
|
||||
@@ -487,6 +499,118 @@ THEME_DEFAULT = {
|
||||
|
||||
This feature provides powerful theming capabilities while maintaining the flexibility of ECharts' extensive configuration options.
|
||||
|
||||
## Component Sizing & Style Tokens
|
||||
|
||||
:::note
|
||||
These tokens landed after the Superset 6.1 release and are only available on
|
||||
`master`; they are not present in any tagged release yet.
|
||||
:::
|
||||
|
||||
Beyond colors and fonts, a handful of Superset-specific tokens let you tune the
|
||||
sizing, radius, and outline behavior of individual UI components. All of these
|
||||
tokens are optional — omit them and components fall back to their existing
|
||||
defaults, so applying them is a zero-visual-change operation until you opt in.
|
||||
|
||||
### Button & DropdownButton Sizing
|
||||
|
||||
```python
|
||||
THEME_DEFAULT = {
|
||||
"token": {
|
||||
# ... other tokens
|
||||
"buttonControlHeight": 32, # default button height, in px
|
||||
"buttonControlHeightSM": 30, # small/dropdown button height, in px
|
||||
"buttonControlHeightXS": 22, # xsmall button height, in px
|
||||
"buttonPaddingInline": 18, # horizontal padding, in px
|
||||
"buttonPaddingInlineSM": 10, # horizontal padding for small buttons, in px
|
||||
"buttonFontSize": 14,
|
||||
"buttonBorderRadius": 4,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`buttonControlHeight` and `buttonBorderRadius` also drive the sizing of the
|
||||
menu-trigger button used by `PageHeaderWithActions`, so a single pair of tokens
|
||||
keeps page-header icon buttons visually consistent with regular buttons.
|
||||
|
||||
For one-off overrides that shouldn't apply to every button in the app, pass a
|
||||
`styleConfig` prop directly to `Button` or `DropdownButton` instead of setting
|
||||
a theme token:
|
||||
|
||||
```tsx
|
||||
<Button
|
||||
styleConfig={{
|
||||
controlHeight: 40,
|
||||
paddingInline: 20,
|
||||
fontSize: 16,
|
||||
fontWeight: 700,
|
||||
borderRadius: 8,
|
||||
ctaMinWidth: 120,
|
||||
ctaMinHeight: 40,
|
||||
iconGap: 8,
|
||||
}}
|
||||
>
|
||||
Click me
|
||||
</Button>
|
||||
|
||||
<DropdownButton
|
||||
styleConfig={{
|
||||
controlHeight: 32,
|
||||
fontSize: 14,
|
||||
fontWeight: 500,
|
||||
boxShadow: '0 2px 4px rgba(0,0,0,0.1)',
|
||||
}}
|
||||
menu={menuProps}
|
||||
>
|
||||
Options
|
||||
</DropdownButton>
|
||||
```
|
||||
|
||||
`styleConfig` values take precedence over the equivalent theme tokens, which in
|
||||
turn take precedence over the built-in defaults.
|
||||
|
||||
### Label Border Radius
|
||||
|
||||
```python
|
||||
THEME_DEFAULT = {
|
||||
"token": {
|
||||
"labelBorderRadius": 4, # defaults to 8px
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Select Option Outline
|
||||
|
||||
By default, hovering or navigating to an option in a `Select` dropdown draws a
|
||||
2px outline in `colorPrimary`. Set `selectOptionActiveOutline` to `False` for a
|
||||
more subtle hover style with no outline:
|
||||
|
||||
```python
|
||||
THEME_DEFAULT = {
|
||||
"token": {
|
||||
"selectOptionActiveOutline": False,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Dashboard Tile Appearance
|
||||
|
||||
Chart tiles on a dashboard (not text/markdown tiles) can be restyled via
|
||||
`dashboardTile*` tokens. All fall back to the existing look — a
|
||||
`colorBgContainer` background, a `1px solid colorBorder` border, and a
|
||||
hairline `box-shadow` while the tile is fading out (e.g. when a filter
|
||||
makes it irrelevant):
|
||||
|
||||
```python
|
||||
THEME_DEFAULT = {
|
||||
"token": {
|
||||
"dashboardTileBg": "#ffffff",
|
||||
"dashboardTileBorder": "1px solid #e0e0e0",
|
||||
"dashboardTileBorderRadius": 8,
|
||||
"dashboardTileBoxShadow": "0 1px 2px rgba(0, 0, 0, 0.08)",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced Features
|
||||
|
||||
- **System Themes**: Manage system-wide default and dark themes via UI or configuration
|
||||
|
||||
@@ -215,7 +215,7 @@ If you have a good solution for this, let us know!
|
||||
:::
|
||||
|
||||
:::note
|
||||
Superset uses [Scarf Gateway](https://about.scarf.sh/scarf-gateway) to collect telemetry
|
||||
Superset uses [Scarf Gateway](https://about.scarf.sh/) to collect telemetry
|
||||
data. Knowing the installation counts for different Superset versions informs the project's
|
||||
decisions about patching and long-term support. Scarf purges personally identifiable information
|
||||
(PII) and provides only aggregated statistics.
|
||||
|
||||
@@ -87,6 +87,7 @@ The chart will publish appropriate services to expose the Superset UI internally
|
||||
|
||||
- Configure the Service as a `LoadBalancer` or `NodePort`
|
||||
- Set up an `Ingress` for it - the chart includes a definition, but will need to be tuned to your needs (hostname, tls, annotations etc...)
|
||||
- Set up a Gateway API `HTTPRoute` for it - see [Exposing Superset via Gateway API (HTTPRoute)](#exposing-superset-via-gateway-api-httproute) below
|
||||
- Run `kubectl port-forward superset-xxxx-yyyy :8088` to directly tunnel one pod's port into your localhost
|
||||
|
||||
Depending how you configured external access, the URL will vary. Once you've identified the appropriate URL you can log in with:
|
||||
@@ -135,7 +136,7 @@ init:
|
||||
```
|
||||
|
||||
:::note
|
||||
Superset uses [Scarf Gateway](https://about.scarf.sh/scarf-gateway) to collect telemetry data. Knowing the installation counts for different Superset versions informs the project's decisions about patching and long-term support. Scarf purges personally identifiable information (PII) and provides only aggregated statistics.
|
||||
Superset uses [Scarf Gateway](https://about.scarf.sh/) to collect telemetry data. Knowing the installation counts for different Superset versions informs the project's decisions about patching and long-term support. Scarf purges personally identifiable information (PII) and provides only aggregated statistics.
|
||||
|
||||
There are two independent telemetry channels:
|
||||
|
||||
@@ -319,6 +320,53 @@ configOverrides:
|
||||
AUTH_USER_REGISTRATION_ROLE = "Admin"
|
||||
```
|
||||
|
||||
### Exposing Superset via Gateway API (HTTPRoute)
|
||||
|
||||
As an alternative to `Ingress`, the chart can create a [Gateway API](https://gateway-api.sigs.k8s.io/)
|
||||
`HTTPRoute` that attaches to a Gateway already running in your cluster. This requires the Gateway
|
||||
API CRDs serving the configured `httproute.apiVersion` (`gateway.networking.k8s.io/v1` by default)
|
||||
to be installed, along with a Gateway resource for the route to attach to. If the Gateway lives in
|
||||
a different namespace than the `HTTPRoute` (as in the
|
||||
example below), its listener's `allowedRoutes` must explicitly permit routes from this release's
|
||||
namespace, or the `HTTPRoute` will install successfully but never attach.
|
||||
|
||||
```yaml
|
||||
httproute:
|
||||
enabled: true
|
||||
parentRefs:
|
||||
- name: my-gateway
|
||||
namespace: gateway-system
|
||||
hostnames:
|
||||
- superset.example.com
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
```
|
||||
|
||||
- `httproute.parentRefs` lists the Gateway(s) the route attaches to.
|
||||
- `httproute.hostnames` matches against the HTTP `Host` header; it's templated, so values like
|
||||
`{{ .Release.Name }}` can be used.
|
||||
- `httproute.rules` are routing rules backed by the Superset service; each rule accepts standard
|
||||
`matches`, `filters`, and `timeouts` fields, and an optional `weight` (defaults to `1`) applied to
|
||||
its single backend reference. Since each rule maps to one backend, `weight` has no traffic-splitting
|
||||
effect here; it only matters if you fork the template to add multiple `backendRefs` to a rule.
|
||||
`timeouts` only joined the Gateway API Standard channel in v1.2, so it requires both v1.2+ CRDs
|
||||
and a supporting controller; drop it if either predates that.
|
||||
- If `supersetWebsockets.enabled` is set, an extra rule routing `supersetWebsockets.ingress.path`
|
||||
(default `/ws`) to the `-ws` service is appended automatically, mirroring the `Ingress` behavior.
|
||||
WebSocket upgrade support is controller-dependent under Gateway API; check your Gateway
|
||||
implementation's docs in case it needs an explicit protocol opt-in for global async queries to
|
||||
keep working behind a Gateway.
|
||||
- If `supersetMcp.enabled` and `supersetMcp.httproute.enabled` are both set, an extra rule routing
|
||||
`supersetMcp.httproute.path` to the `-mcp` service is appended as well. Don't expose this route
|
||||
without first enabling MCP authentication — see the
|
||||
[MCP Server Deployment & Authentication](/admin-docs/configuration/mcp-server#authentication) doc;
|
||||
by default the MCP server runs in dev mode with auth disabled.
|
||||
- Set `httproute.apiVersion` to `gateway.networking.k8s.io/v1beta1` if your cluster's Gateway API
|
||||
installation hasn't promoted `HTTPRoute` to `v1` yet.
|
||||
|
||||
### Enable Alerts and Reports
|
||||
|
||||
For this, as per the [Alerts and Reports doc](/admin-docs/configuration/alerts-reports), you will need to:
|
||||
|
||||
@@ -215,7 +215,7 @@ If you have a good solution for this, let us know!
|
||||
:::
|
||||
|
||||
:::note
|
||||
Superset uses [Scarf Gateway](https://about.scarf.sh/scarf-gateway) to collect telemetry
|
||||
Superset uses [Scarf Gateway](https://about.scarf.sh/) to collect telemetry
|
||||
data. Knowing the installation counts for different Superset versions informs the project's
|
||||
decisions about patching and long-term support. Scarf purges personally identifiable information
|
||||
(PII) and provides only aggregated statistics.
|
||||
|
||||
@@ -135,7 +135,7 @@ init:
|
||||
```
|
||||
|
||||
:::note
|
||||
Superset uses [Scarf Gateway](https://about.scarf.sh/scarf-gateway) to collect telemetry data. Knowing the installation counts for different Superset versions informs the project's decisions about patching and long-term support. Scarf purges personally identifiable information (PII) and provides only aggregated statistics.
|
||||
Superset uses [Scarf Gateway](https://about.scarf.sh/) to collect telemetry data. Knowing the installation counts for different Superset versions informs the project's decisions about patching and long-term support. Scarf purges personally identifiable information (PII) and provides only aggregated statistics.
|
||||
|
||||
To opt-out of this data collection in your Helm-based installation, edit the `repository:` line in your `helm/superset/values.yaml` file, replacing `apachesuperset.docker.scarf.sh/apache/superset` with `apache/superset` to pull the image directly from Docker Hub.
|
||||
:::
|
||||
|
||||
@@ -379,6 +379,15 @@ AG Grid supports server-side column filters that query the full dataset — not
|
||||
|
||||
AG Grid Interactive Table supports **Time Shift** (time comparison), matching the behavior of the standard Table chart. In the **Advanced Analytics** → **Time Comparison** section of the chart configuration, enter a shift expression (e.g., `1 year ago`, `minus 7 days`) to add comparison columns showing values from the offset period. Dashboard-level time range overrides apply to both the base and comparison periods.
|
||||
|
||||
#### Show Summary
|
||||
|
||||
The **Show summary** checkbox lives at the top of the **Visual formatting** section in the **Customize** tab, for both **Aggregate** and **Raw Records** query modes. Enabling it pins a summary row to the bottom of the grid whenever there is something to summarize: at least one metric in **Aggregate** mode, or at least one eligible numeric column in **Raw Records** mode. Otherwise no summary row is added.
|
||||
|
||||
- In **Aggregate** mode, the summary row applies each metric's own aggregation (or the **Summary aggregation** override, where available) across the full filtered dataset.
|
||||
- In **Raw Records** mode, the summary row defaults to a server-side `SUM` for each numeric column that's backed by a physical or calculated dataset column; the **Summary aggregation** control can override this to `AVG` as well. Non-numeric cells and columns built from free-form SQL expressions stay blank.
|
||||
|
||||
In both modes, the summary is computed across the full result set, independent of the chart's row limit and pagination, and it reflects dashboard and chart-level filters. It does not reflect AG Grid's own server-side column filters (the per-column filter UI in the grid header), which are excluded from the summary query.
|
||||
|
||||
### Dynamic Currency Formatting
|
||||
|
||||
Chart metric values can display currencies dynamically rather than using a fixed currency code. To enable:
|
||||
|
||||
@@ -145,3 +145,51 @@ The following URL parameters can be passed through the `urlParams` option in `da
|
||||
- **Row-level security** — pass `rls` rules in the guest token request to restrict which rows are visible to the embedded user.
|
||||
- **Allowed domains** — restrict which host origins can embed a dashboard by setting **Allowed Domains** per-dashboard in the _Embed_ settings modal. Superset checks the request's `Referer` header against this list before serving the embedded view; an empty list allows any origin, so configure this explicitly for production.
|
||||
- **Redacted errors** — API responses to a guest token report a generic `An error occurred while fetching the data.` instead of the underlying error, since engine errors quote catalog, schema, table and column names. Errors Superset raises itself — access denials, timeouts, payload validation — keep their message, and the full error is always available in the server logs.
|
||||
|
||||
|
||||
## Guest-token request-header size diagnostics
|
||||
|
||||
A successful guest-token mint does not guarantee the token can pass through your
|
||||
deployment's proxies. Limits apply to the **encoded JWT bytes plus header
|
||||
overhead**, not the number of RLS rules or identifiers. A proxy can reject the
|
||||
subsequent authentication request before it reaches Superset, including an HTTP
|
||||
400 HTML response instead of JSON. A 400 alone does not establish a size problem.
|
||||
|
||||
Operators can set a deployment-specific diagnostic budget in `superset_config.py`:
|
||||
|
||||
```python
|
||||
# Example only: choose a budget for your complete proxy path.
|
||||
GUEST_TOKEN_HEADER_MAX_BYTES = 16 * 1024
|
||||
```
|
||||
|
||||
The default is `None` (no budget warnings). Positive integer budgets count UTF-8
|
||||
bytes of `GUEST_TOKEN_HEADER_NAME`, `: `, the encoded token, and `\r\n`
|
||||
(four framing bytes). Only sizes **strictly greater** than the budget warn;
|
||||
equality does not. This is consistent diagnostic accounting, not a prediction of
|
||||
every proxy's wire-level accounting, HTTP/2 compression, or total-header limits.
|
||||
Leave a safety margin and validate your actual deployment, including custom
|
||||
header names. Zero, negative, non-integral, or non-numeric values (including strings and
|
||||
booleans) disable budget warnings, as do values above JavaScript's maximum safe
|
||||
integer (2^53 − 1). Whole-number floats are accepted. Convert environment-variable
|
||||
strings to integers in deployment configuration to enable the budget.
|
||||
|
||||
Issuance audit metadata includes `token_bytes`, `header_bytes`,
|
||||
`header_budget_bytes`, and `header_budget_exceeded`. Issuance remains HTTP 200
|
||||
with the same token and response shape. The embedded bootstrap exposes the budget
|
||||
and configured header name; reload the iframe after changing deployment config.
|
||||
The embedded client measures initial and refreshed tokens and warns in the
|
||||
developer console with sizes only. Initial authentication failures get a targeted
|
||||
suggestion only when the request's token exceeds the budget and the failure has
|
||||
no status or HTTP 400/431/494; other statuses and ambiguous in-flight
|
||||
refreshes use the generic error. Refresh warnings do not restart authentication.
|
||||
These diagnostics do not record JWTs, decoded claims, RLS SQL, or request headers.
|
||||
|
||||
[AWS Application Load Balancer quotas](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-limits.html)
|
||||
list a non-adjustable 16 K single-header limit. Increasing a Superset diagnostic
|
||||
budget does not increase that limit or add large-token support.
|
||||
|
||||
To reduce payload size, replace large inline RLS ID lists with a compact
|
||||
entitlements-table subquery where supported by your database. Keep the same
|
||||
tenant/user restrictions, derive identity from your trusted token-issuing
|
||||
backend, and verify equivalent row access and query performance before rollout.
|
||||
Do not remove RLS or broaden entitlements to make a token smaller.
|
||||
|
||||
@@ -71,17 +71,17 @@ Parses a JSON string into an object that can be used in your template.
|
||||
|
||||
---
|
||||
|
||||
#### `groupBy`
|
||||
#### `group`
|
||||
|
||||
Groups an array of objects by a key, powered by [handlebars-group-by](https://github.com/nicktindall/handlebars-group-by).
|
||||
Groups an array of objects by a key, powered by [handlebars-group-by](https://github.com/nicktindall/handlebars-group-by). The key is passed as a `by` hash argument.
|
||||
|
||||
```handlebars
|
||||
{{#groupBy data 'department'}}
|
||||
{{#group data by="department"}}
|
||||
<h3>{{value}}</h3>
|
||||
{{#each items}}
|
||||
<p>{{this.name}}</p>
|
||||
{{/each}}
|
||||
{{/groupBy}}
|
||||
{{/group}}
|
||||
```
|
||||
|
||||
---
|
||||
@@ -90,6 +90,14 @@ Groups an array of objects by a key, powered by [handlebars-group-by](https://gi
|
||||
|
||||
Superset also registers all helpers from the [just-handlebars-helpers](https://github.com/leapfrogtechnology/just-handlebars-helpers) library. These include a wide range of comparison, math, string, and conditional helpers. Commonly used ones include:
|
||||
|
||||
:::note
|
||||
These names are specific to `just-handlebars-helpers` and differ from other
|
||||
Handlebars helper libraries — notably `handlebars-helpers`, which spells the
|
||||
math helpers `add`, `subtract`, `multiply` and `divide`. Calling a helper that
|
||||
is not registered raises `Missing helper: "..."`, which renders the chart blank,
|
||||
so it is worth checking a name against the tables below before using it.
|
||||
:::
|
||||
|
||||
#### Comparison
|
||||
|
||||
| Helper | Description | Example |
|
||||
@@ -97,6 +105,7 @@ Superset also registers all helpers from the [just-handlebars-helpers](https://g
|
||||
| `eq` | Strict equality | `{{#if (eq status "active")}}` |
|
||||
| `eqw` | Weak equality | `{{#if (eqw count "5")}}` |
|
||||
| `neq` | Strict inequality | `{{#if (neq role "admin")}}` |
|
||||
| `neqw` | Weak inequality | `{{#if (neqw count "5")}}` |
|
||||
| `lt` | Less than | `{{#if (lt score 50)}}` |
|
||||
| `lte` | Less than or equal | `{{#if (lte score 100)}}` |
|
||||
| `gt` | Greater than | `{{#if (gt price 0)}}` |
|
||||
@@ -114,25 +123,52 @@ Superset also registers all helpers from the [just-handlebars-helpers](https://g
|
||||
|
||||
#### String
|
||||
|
||||
| Helper | Description | Example |
|
||||
| ------------ | ----------------------------------- | --------------------------------- |
|
||||
| `capitalize` | Capitalizes first letter | `{{capitalize name}}` |
|
||||
| `uppercase` | Converts to uppercase | `{{uppercase status}}` |
|
||||
| `lowercase` | Converts to lowercase | `{{lowercase email}}` |
|
||||
| `truncate` | Truncates a string | `{{truncate description 100}}` |
|
||||
| `contains` | Checks if string contains substring | `{{#if (contains tag "urgent")}}` |
|
||||
| Helper | Description | Example |
|
||||
| ----------------- | ----------------------------------------------- | ------------------------------ |
|
||||
| `capitalizeFirst` | Capitalizes the first letter | `{{capitalizeFirst name}}` |
|
||||
| `capitalizeEach` | Capitalizes the first letter of each word | `{{capitalizeEach title}}` |
|
||||
| `uppercase` | Converts to uppercase | `{{uppercase status}}` |
|
||||
| `lowercase` | Converts to lowercase | `{{lowercase email}}` |
|
||||
| `excerpt` | Truncates to a length and appends an ellipsis | `{{excerpt description 100}}` |
|
||||
| `sprintf` | printf-style formatting | `{{sprintf "%.1f" score}}` |
|
||||
| `concat` | Concatenates values | `{{concat first " " last}}` |
|
||||
| `join` | Joins an array with a separator | `{{join tags ", "}}` |
|
||||
| `first` / `last` | First or last element of an array | `{{first items}}` |
|
||||
| `newLineToBr` | Converts newlines to `<br>` (needs `{{{ }}}`) | `{{{newLineToBr notes}}}` |
|
||||
|
||||
#### Math
|
||||
|
||||
| Helper | Description | Example |
|
||||
| ---------- | -------------- | ----------------------------- |
|
||||
| `add` | Addition | `{{add a b}}` |
|
||||
| `subtract` | Subtraction | `{{subtract total discount}}` |
|
||||
| `multiply` | Multiplication | `{{multiply price quantity}}` |
|
||||
| `divide` | Division | `{{divide total count}}` |
|
||||
| `ceil` | Ceiling | `{{ceil value}}` |
|
||||
| `floor` | Floor | `{{floor value}}` |
|
||||
| `round` | Round | `{{round value}}` |
|
||||
| Helper | Description | Example |
|
||||
| ---------------- | ----------------------- | ------------------------------------ |
|
||||
| `sum` | Addition | `{{sum a b}}` |
|
||||
| `difference` | Subtraction | `{{difference total discount}}` |
|
||||
| `multiplication` | Multiplication | `{{multiplication price quantity}}` |
|
||||
| `division` | Division | `{{division total count}}` |
|
||||
| `remainder` | Modulo | `{{remainder index 2}}` |
|
||||
| `abs` | Absolute value | `{{abs delta}}` |
|
||||
| `ceil` | Ceiling | `{{ceil value}}` |
|
||||
| `floor` | Floor | `{{floor value}}` |
|
||||
|
||||
`sum` takes exactly two arguments — it adds a pair of numbers and does not total
|
||||
an array. There is no `round` helper; use `{{sprintf "%.0f" value}}` to round to
|
||||
a given number of decimal places.
|
||||
|
||||
#### Arrays
|
||||
|
||||
| Helper | Description | Example |
|
||||
| ---------- | ---------------------------------- | --------------------------------- |
|
||||
| `includes` | Whether an array contains a value | `{{#if (includes tags "urgent")}}` |
|
||||
| `empty` | Whether an array is empty | `{{#if (empty rows)}}` |
|
||||
| `count` | Number of items in an array | `{{count rows}}` |
|
||||
|
||||
`includes` tests array membership. It returns `false` for a string, so it cannot
|
||||
be used to check for a substring.
|
||||
|
||||
#### Formatting
|
||||
|
||||
| Helper | Description | Example |
|
||||
| ---------------- | ---------------------------- | -------------------------------- |
|
||||
| `formatCurrency` | Formats a number as currency | `{{formatCurrency revenue "$"}}` |
|
||||
|
||||
For the full list of available helpers, see the [just-handlebars-helpers documentation](https://github.com/leapfrogtechnology/just-handlebars-helpers).
|
||||
|
||||
|
||||
@@ -305,6 +305,14 @@ Ask your admin for the MCP server URL and any authentication tokens you need.
|
||||
| `list_databases` | List configured database connections |
|
||||
| `get_database_info` | Get details about a specific database connection |
|
||||
|
||||
### Themes
|
||||
|
||||
| Tool | Description |
|
||||
| ---------------- | ------------------------------------------------------------------------- |
|
||||
| `list_themes` | Discover themes (antd design-token configurations) with filters |
|
||||
| `get_theme_info` | Get a theme's tokens (`json_data`) by ID or UUID |
|
||||
| `create_theme` | Create a reusable theme from antd design tokens (requires write access) |
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
+5
-2
@@ -43,8 +43,11 @@ publish = "build"
|
||||
ignore = 'if [ -n "$CACHED_COMMIT_REF" ]; then git diff --quiet "$CACHED_COMMIT_REF" HEAD -- . ../README.md; else git fetch --no-tags origin master >/dev/null 2>&1 || true; i=0; while [ "$i" -lt 10 ] && ! git merge-base origin/master HEAD >/dev/null 2>&1; do git fetch --deepen=200 origin master >/dev/null 2>&1 || break; i=$((i+1)); done; BASE="$(git merge-base origin/master HEAD 2>/dev/null || true)"; if [ -z "$BASE" ]; then exit 1; fi; git diff --quiet "$BASE" HEAD -- . ../README.md; fi'
|
||||
|
||||
[build.environment]
|
||||
# Node version matching docs/.nvmrc
|
||||
NODE_VERSION = "20"
|
||||
# Node version is intentionally not pinned here: Netlify auto-detects it
|
||||
# from docs/.nvmrc, which is a symlink to the repo's single source of truth
|
||||
# at superset-frontend/.nvmrc. Duplicating the version here previously let
|
||||
# it drift out of sync (stuck on Node 20 after the repo moved to Node 24),
|
||||
# breaking installs once a dependency required a newer Node engine.
|
||||
# Yarn version
|
||||
YARN_VERSION = "1.22.22"
|
||||
# Increase heap size for webpack bundling of Superset UI components
|
||||
|
||||
+2
-2
@@ -43,7 +43,7 @@
|
||||
"version:remove:components": "node scripts/manage-versions.mjs remove components"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^6.2.5",
|
||||
"@ant-design/icons": "^6.3.4",
|
||||
"@docusaurus/core": "^3.10.2",
|
||||
"@docusaurus/faster": "^3.10.2",
|
||||
"@docusaurus/plugin-client-redirects": "^3.10.2",
|
||||
@@ -91,7 +91,7 @@
|
||||
"oxlint": "^1.80.0",
|
||||
"oxlint-tsgolint": "^7.0.2001",
|
||||
"typescript": "7.0.2",
|
||||
"webpack": "^5.110.1"
|
||||
"webpack": "^5.110.2"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
||||
Vendored
+6
-6
@@ -98,12 +98,6 @@
|
||||
"default": false,
|
||||
"lifecycle": "development",
|
||||
"description": "Enable Table V2 time comparison feature"
|
||||
},
|
||||
{
|
||||
"name": "TAGGING_SYSTEM",
|
||||
"default": false,
|
||||
"lifecycle": "development",
|
||||
"description": "Enables the tagging system for organizing assets"
|
||||
}
|
||||
],
|
||||
"testing": [
|
||||
@@ -240,6 +234,12 @@
|
||||
"description": "Allow users to enable SSH tunneling when creating a DB connection. DB engine must support SSH Tunnels.",
|
||||
"docs": "https://superset.apache.org/docs/configuration/setup-ssh-tunneling"
|
||||
},
|
||||
{
|
||||
"name": "TAGGING_SYSTEM",
|
||||
"default": true,
|
||||
"lifecycle": "testing",
|
||||
"description": "Enables the tagging system for organizing assets"
|
||||
},
|
||||
{
|
||||
"name": "USE_ANALOGOUS_COLORS",
|
||||
"default": false,
|
||||
|
||||
Vendored
+2
-22
@@ -1203,28 +1203,6 @@
|
||||
},
|
||||
"time_grain_sqla": {
|
||||
"description": "To what level of granularity should the temporal column be aggregated. Supports [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) durations.",
|
||||
"enum": [
|
||||
"PT1S",
|
||||
"PT5S",
|
||||
"PT30S",
|
||||
"PT1M",
|
||||
"PT5M",
|
||||
"PT10M",
|
||||
"PT15M",
|
||||
"PT30M",
|
||||
"PT1H",
|
||||
"PT6H",
|
||||
"P1D",
|
||||
"P1W",
|
||||
"P1M",
|
||||
"P3M",
|
||||
"P1Y",
|
||||
"1969-12-28T00:00:00Z/P1W",
|
||||
"1969-12-29T00:00:00Z/P1W",
|
||||
"P1W/1970-01-03T00:00:00Z",
|
||||
"P1W/1970-01-04T00:00:00Z",
|
||||
null
|
||||
],
|
||||
"example": "P1D",
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
@@ -1485,12 +1463,14 @@
|
||||
"PT10M",
|
||||
"PT15M",
|
||||
"PT30M",
|
||||
"PT0.5H",
|
||||
"PT1H",
|
||||
"PT6H",
|
||||
"P1D",
|
||||
"P1W",
|
||||
"P1M",
|
||||
"P3M",
|
||||
"P0.25Y",
|
||||
"P1Y",
|
||||
"1969-12-28T00:00:00Z/P1W",
|
||||
"1969-12-29T00:00:00Z/P1W",
|
||||
|
||||
@@ -215,7 +215,7 @@ If you have a good solution for this, let us know!
|
||||
:::
|
||||
|
||||
:::note
|
||||
Superset uses [Scarf Gateway](https://about.scarf.sh/scarf-gateway) to collect telemetry
|
||||
Superset uses [Scarf Gateway](https://about.scarf.sh/) to collect telemetry
|
||||
data. Knowing the installation counts for different Superset versions informs the project's
|
||||
decisions about patching and long-term support. Scarf purges personally identifiable information
|
||||
(PII) and provides only aggregated statistics.
|
||||
|
||||
@@ -135,7 +135,7 @@ init:
|
||||
```
|
||||
|
||||
:::note
|
||||
Superset uses [Scarf Gateway](https://about.scarf.sh/scarf-gateway) to collect telemetry data. Knowing the installation counts for different Superset versions informs the project's decisions about patching and long-term support. Scarf purges personally identifiable information (PII) and provides only aggregated statistics.
|
||||
Superset uses [Scarf Gateway](https://about.scarf.sh/) to collect telemetry data. Knowing the installation counts for different Superset versions informs the project's decisions about patching and long-term support. Scarf purges personally identifiable information (PII) and provides only aggregated statistics.
|
||||
|
||||
To opt-out of this data collection in your Helm-based installation, edit the `repository:` line in your `helm/superset/values.yaml` file, replacing `apachesuperset.docker.scarf.sh/apache/superset` with `apache/superset` to pull the image directly from Docker Hub.
|
||||
:::
|
||||
|
||||
@@ -71,17 +71,17 @@ Parses a JSON string into an object that can be used in your template.
|
||||
|
||||
---
|
||||
|
||||
#### `groupBy`
|
||||
#### `group`
|
||||
|
||||
Groups an array of objects by a key, powered by [handlebars-group-by](https://github.com/nicktindall/handlebars-group-by).
|
||||
Groups an array of objects by a key, powered by [handlebars-group-by](https://github.com/nicktindall/handlebars-group-by). The key is passed as a `by` hash argument.
|
||||
|
||||
```handlebars
|
||||
{{#groupBy data 'department'}}
|
||||
{{#group data by="department"}}
|
||||
<h3>{{value}}</h3>
|
||||
{{#each items}}
|
||||
<p>{{this.name}}</p>
|
||||
{{/each}}
|
||||
{{/groupBy}}
|
||||
{{/group}}
|
||||
```
|
||||
|
||||
---
|
||||
@@ -90,6 +90,14 @@ Groups an array of objects by a key, powered by [handlebars-group-by](https://gi
|
||||
|
||||
Superset also registers all helpers from the [just-handlebars-helpers](https://github.com/leapfrogtechnology/just-handlebars-helpers) library. These include a wide range of comparison, math, string, and conditional helpers. Commonly used ones include:
|
||||
|
||||
:::note
|
||||
These names are specific to `just-handlebars-helpers` and differ from other
|
||||
Handlebars helper libraries — notably `handlebars-helpers`, which spells the
|
||||
math helpers `add`, `subtract`, `multiply` and `divide`. Calling a helper that
|
||||
is not registered raises `Missing helper: "..."`, which renders the chart blank,
|
||||
so it is worth checking a name against the tables below before using it.
|
||||
:::
|
||||
|
||||
#### Comparison
|
||||
|
||||
| Helper | Description | Example |
|
||||
@@ -97,6 +105,7 @@ Superset also registers all helpers from the [just-handlebars-helpers](https://g
|
||||
| `eq` | Strict equality | `{{#if (eq status "active")}}` |
|
||||
| `eqw` | Weak equality | `{{#if (eqw count "5")}}` |
|
||||
| `neq` | Strict inequality | `{{#if (neq role "admin")}}` |
|
||||
| `neqw` | Weak inequality | `{{#if (neqw count "5")}}` |
|
||||
| `lt` | Less than | `{{#if (lt score 50)}}` |
|
||||
| `lte` | Less than or equal | `{{#if (lte score 100)}}` |
|
||||
| `gt` | Greater than | `{{#if (gt price 0)}}` |
|
||||
@@ -114,25 +123,52 @@ Superset also registers all helpers from the [just-handlebars-helpers](https://g
|
||||
|
||||
#### String
|
||||
|
||||
| Helper | Description | Example |
|
||||
| ------------ | ----------------------------------- | --------------------------------- |
|
||||
| `capitalize` | Capitalizes first letter | `{{capitalize name}}` |
|
||||
| `uppercase` | Converts to uppercase | `{{uppercase status}}` |
|
||||
| `lowercase` | Converts to lowercase | `{{lowercase email}}` |
|
||||
| `truncate` | Truncates a string | `{{truncate description 100}}` |
|
||||
| `contains` | Checks if string contains substring | `{{#if (contains tag "urgent")}}` |
|
||||
| Helper | Description | Example |
|
||||
| ----------------- | ----------------------------------------------- | ------------------------------ |
|
||||
| `capitalizeFirst` | Capitalizes the first letter | `{{capitalizeFirst name}}` |
|
||||
| `capitalizeEach` | Capitalizes the first letter of each word | `{{capitalizeEach title}}` |
|
||||
| `uppercase` | Converts to uppercase | `{{uppercase status}}` |
|
||||
| `lowercase` | Converts to lowercase | `{{lowercase email}}` |
|
||||
| `excerpt` | Truncates to a length and appends an ellipsis | `{{excerpt description 100}}` |
|
||||
| `sprintf` | printf-style formatting | `{{sprintf "%.1f" score}}` |
|
||||
| `concat` | Concatenates values | `{{concat first " " last}}` |
|
||||
| `join` | Joins an array with a separator | `{{join tags ", "}}` |
|
||||
| `first` / `last` | First or last element of an array | `{{first items}}` |
|
||||
| `newLineToBr` | Converts newlines to `<br>` (needs `{{{ }}}`) | `{{{newLineToBr notes}}}` |
|
||||
|
||||
#### Math
|
||||
|
||||
| Helper | Description | Example |
|
||||
| ---------- | -------------- | ----------------------------- |
|
||||
| `add` | Addition | `{{add a b}}` |
|
||||
| `subtract` | Subtraction | `{{subtract total discount}}` |
|
||||
| `multiply` | Multiplication | `{{multiply price quantity}}` |
|
||||
| `divide` | Division | `{{divide total count}}` |
|
||||
| `ceil` | Ceiling | `{{ceil value}}` |
|
||||
| `floor` | Floor | `{{floor value}}` |
|
||||
| `round` | Round | `{{round value}}` |
|
||||
| Helper | Description | Example |
|
||||
| ---------------- | ----------------------- | ------------------------------------ |
|
||||
| `sum` | Addition | `{{sum a b}}` |
|
||||
| `difference` | Subtraction | `{{difference total discount}}` |
|
||||
| `multiplication` | Multiplication | `{{multiplication price quantity}}` |
|
||||
| `division` | Division | `{{division total count}}` |
|
||||
| `remainder` | Modulo | `{{remainder index 2}}` |
|
||||
| `abs` | Absolute value | `{{abs delta}}` |
|
||||
| `ceil` | Ceiling | `{{ceil value}}` |
|
||||
| `floor` | Floor | `{{floor value}}` |
|
||||
|
||||
`sum` takes exactly two arguments — it adds a pair of numbers and does not total
|
||||
an array. There is no `round` helper; use `{{sprintf "%.0f" value}}` to round to
|
||||
a given number of decimal places.
|
||||
|
||||
#### Arrays
|
||||
|
||||
| Helper | Description | Example |
|
||||
| ---------- | ---------------------------------- | --------------------------------- |
|
||||
| `includes` | Whether an array contains a value | `{{#if (includes tags "urgent")}}` |
|
||||
| `empty` | Whether an array is empty | `{{#if (empty rows)}}` |
|
||||
| `count` | Number of items in an array | `{{count rows}}` |
|
||||
|
||||
`includes` tests array membership. It returns `false` for a string, so it cannot
|
||||
be used to check for a substring.
|
||||
|
||||
#### Formatting
|
||||
|
||||
| Helper | Description | Example |
|
||||
| ---------------- | ---------------------------- | -------------------------------- |
|
||||
| `formatCurrency` | Formats a number as currency | `{{formatCurrency revenue "$"}}` |
|
||||
|
||||
For the full list of available helpers, see the [just-handlebars-helpers documentation](https://github.com/leapfrogtechnology/just-handlebars-helpers).
|
||||
|
||||
|
||||
+85
-146
@@ -222,18 +222,18 @@
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/fast-color/-/fast-color-3.0.1.tgz#fee56b95427c0b55b216c93d9a7f3473f31615b5"
|
||||
integrity sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==
|
||||
|
||||
"@ant-design/icons-svg@^4.5.0":
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.5.0.tgz#7b1c567e489840d747f211d3688949bcba363ad2"
|
||||
integrity sha512-1BTUFyKPTBZ53MuTP8s0k5SFEXL7o3VHEOwLgzaoWKwnBeqIcqUtVshc4SKzhI6uACfqhJqBwBUE9FsWR3uULA==
|
||||
"@ant-design/icons-svg@^4.6.0":
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.6.0.tgz#5f7ebfe2a6b7c871920f73db095bd4ab50d7764d"
|
||||
integrity sha512-PRomU725ABMf/lnQp5HiB7my1kjEbFY0D10N4lXYxK6TIB1gKjIVD5MRThpDaezLgw1D774J8eOeeDB0M6wHrQ==
|
||||
|
||||
"@ant-design/icons@^6.2.5", "@ant-design/icons@^6.3.2":
|
||||
version "6.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-6.3.2.tgz#8291dffc53003db9a5df59f80ed758473cd5c8df"
|
||||
integrity sha512-B6O5a5XJ4wjtNOfZejXYwHW5zvKV5gYkjGf11dHGLEbKn0ABDGndo41+gfIiXyTFhvESj4XTotuud33mUFid0g==
|
||||
"@ant-design/icons@^6.3.2", "@ant-design/icons@^6.3.4":
|
||||
version "6.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-6.3.4.tgz#9a76f7b6b4554a65f25e3e5c439405b96c4ebd71"
|
||||
integrity sha512-kDoUlppczVyCUTFthF6cHmPzMBYqNZQhu9j6EyIX/YIdM9j9CZNZoJUjzVw7/1v1VO8WwVMmPp15r/uwIAS0AQ==
|
||||
dependencies:
|
||||
"@ant-design/colors" "^8.0.1"
|
||||
"@ant-design/icons-svg" "^4.5.0"
|
||||
"@ant-design/icons-svg" "^4.6.0"
|
||||
"@rc-component/util" "^1.11.0"
|
||||
clsx "^2.1.1"
|
||||
|
||||
@@ -4969,85 +4969,85 @@
|
||||
resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9"
|
||||
integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==
|
||||
|
||||
"@swc/html-darwin-arm64@1.15.43":
|
||||
version "1.15.43"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-darwin-arm64/-/html-darwin-arm64-1.15.43.tgz#c88069f140ead901724018f96ad709779526a368"
|
||||
integrity sha512-+PFbHbeeN+zB0zfvR1V1NmvPriuWPI+sijQXpI+wq/nLIujxvtENWjOKVHgouC9TIN/uKmL2zu9HAq6L6YxnPA==
|
||||
"@swc/html-darwin-arm64@1.16.2":
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-darwin-arm64/-/html-darwin-arm64-1.16.2.tgz#dfe45d70266262a59aaaa0d93740b6161803f192"
|
||||
integrity sha512-SNBUxkxLBXD0ATwnOG1rF8mpSrRtFDfqWnEUmbm/g4KwmCt7NuHHv9YYqA3lqfq90Ucc+Xlk7afx8KAW/utz4A==
|
||||
|
||||
"@swc/html-darwin-x64@1.15.43":
|
||||
version "1.15.43"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-darwin-x64/-/html-darwin-x64-1.15.43.tgz#9bc8171494d3b98eac017b5b73f0f63054558626"
|
||||
integrity sha512-LQJ2U8Oxcx4T1rRF25y4h+/p05nn58FugTe/uGxC5OT3K83c2MftcSZLYaahOu4GVHRZeS1NI94CkSvQV++TVw==
|
||||
"@swc/html-darwin-x64@1.16.2":
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-darwin-x64/-/html-darwin-x64-1.16.2.tgz#72066bda0d843c024dd5230aa013445d10037cde"
|
||||
integrity sha512-WVBgn6yrBPMZu+DL95/XGAXYcgd1nhd67Ml1UjMtFoFMVKY+VRpCq8JpTZTMXhWbVoRENUHk+3PHu0nNjlE/Fg==
|
||||
|
||||
"@swc/html-linux-arm-gnueabihf@1.15.43":
|
||||
version "1.15.43"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-arm-gnueabihf/-/html-linux-arm-gnueabihf-1.15.43.tgz#271a5d345719fa2c381df45355f70870faaaf35c"
|
||||
integrity sha512-DKIen6DuIRO7Xc5gAbgBT5QyRHJGEGXreIdM1VBosYWTGnnrQ//Hwd7bLD6UbT8X8eU1vqvpXwQ1E24QRqRaBQ==
|
||||
"@swc/html-linux-arm-gnueabihf@1.16.2":
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-arm-gnueabihf/-/html-linux-arm-gnueabihf-1.16.2.tgz#efa7dd85b03b941ac173ae2870355f9721993905"
|
||||
integrity sha512-V9F/Akd2TXrf5nUhdLgdy3FoVFxQbw8pA2AOyqnEOa2Mbm1R7DZJJ0GdShEMcoyMyMDB9r/4pWuWfxNtP4mFHA==
|
||||
|
||||
"@swc/html-linux-arm64-gnu@1.15.43":
|
||||
version "1.15.43"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-arm64-gnu/-/html-linux-arm64-gnu-1.15.43.tgz#93cd3202f204351e7279efd07abc280fe0b2d193"
|
||||
integrity sha512-0AuHiyfcE86CZ/CajFIszLzZVzbM2wn5p01oet8Q9RikflCGwyH79Nv9TrAKD1Cx7juUrONzDk+f2b/x73wLTg==
|
||||
"@swc/html-linux-arm64-gnu@1.16.2":
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-arm64-gnu/-/html-linux-arm64-gnu-1.16.2.tgz#4b6a02b42a40463e9ed9a96e0c42b7095b244f48"
|
||||
integrity sha512-jonZVtHc6BesMjC/muUEJGzE1L2kVdgiPVuHc7CL79MrUm0Hjf8LS4Wmtjqe2bLTfRcaMfaYl/60ZcRXHCaYSQ==
|
||||
|
||||
"@swc/html-linux-arm64-musl@1.15.43":
|
||||
version "1.15.43"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-arm64-musl/-/html-linux-arm64-musl-1.15.43.tgz#a6c0c2a1646755b1ee53a6bddbee68674f8edaea"
|
||||
integrity sha512-TweIdl/g9ugkoiYvcL/qbu+gbglDY3TqNxfXH84WXc4rSqEP20owVlxLya2NjVct8LIP2wDrtutpOwAXWC+Eew==
|
||||
"@swc/html-linux-arm64-musl@1.16.2":
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-arm64-musl/-/html-linux-arm64-musl-1.16.2.tgz#0334e071cb8a01e0423fe7da27204b71f1afed66"
|
||||
integrity sha512-dvki9/sgacHk9ouORmnIok5FbpeE9zUE8yqGGhL1kitNJi6/TKzfnMOpRxSxeDk1/ccvJTAdjRGDIGkT45+b3Q==
|
||||
|
||||
"@swc/html-linux-ppc64-gnu@1.15.43":
|
||||
version "1.15.43"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-ppc64-gnu/-/html-linux-ppc64-gnu-1.15.43.tgz#51319cc1a4184b788613e0e556fc33ff77af0c52"
|
||||
integrity sha512-4oue1pB38/W6mbudp+w0q1jbwxuwdbdbaOj85ay0pisCs213WkgP+MPN8Zqa5VVPjQnVk2CTY9kmEc74XQI/sA==
|
||||
"@swc/html-linux-ppc64-gnu@1.16.2":
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-ppc64-gnu/-/html-linux-ppc64-gnu-1.16.2.tgz#6c0d7293a1f7f7631e975c767755eb5236769193"
|
||||
integrity sha512-6m0vVWHl9MW7cmWKVgKlFW6yhRv0uahMEaDxNIvXrPC3LdbbiiYZui+ryhyQGIYeVps3OMujzUjc0GihNz/afQ==
|
||||
|
||||
"@swc/html-linux-s390x-gnu@1.15.43":
|
||||
version "1.15.43"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-s390x-gnu/-/html-linux-s390x-gnu-1.15.43.tgz#a612115a5f2f6c9df438a52ab2cf8bc4fd3865b1"
|
||||
integrity sha512-/tceMNvAxK70SKUZtcn3X+K0vcElMGk3i8Sz0CmPdtooso8MZ7WfAvVP1qi3TWgh1rpQ3cC+Al3433AHlET6+w==
|
||||
"@swc/html-linux-s390x-gnu@1.16.2":
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-s390x-gnu/-/html-linux-s390x-gnu-1.16.2.tgz#853018d7b57377e2f3b23d369a1571862c34d2b9"
|
||||
integrity sha512-TOlz6wgKyZjg4THJsNZfDz/rAMO+rBa0s2eewTeHEfuJhI+jGu7H6Co6bdbMpN3oyDvTMG7N1f1ktSbkE0erAg==
|
||||
|
||||
"@swc/html-linux-x64-gnu@1.15.43":
|
||||
version "1.15.43"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-x64-gnu/-/html-linux-x64-gnu-1.15.43.tgz#028980da812e1797316f0a06a758a49aca699318"
|
||||
integrity sha512-YE7ltlTt5ZFl59GsoHTDrIHnCBY8EDBio66CVj4bqkElFXbE/28xmpVE5ksdGoI5c5aQ/8byUCfHxqzCzQQSVg==
|
||||
"@swc/html-linux-x64-gnu@1.16.2":
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-x64-gnu/-/html-linux-x64-gnu-1.16.2.tgz#a347921e256f6ecb8847956bbacd4a23e1476adb"
|
||||
integrity sha512-5EduoVpsnuAAkG9BW8COxcIKAe5swgNAEo+BVkAJCOy1ZMZm0krQYBdvlaDCsGGE9yLDKVPm7rpYIi7vTTZTbA==
|
||||
|
||||
"@swc/html-linux-x64-musl@1.15.43":
|
||||
version "1.15.43"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-x64-musl/-/html-linux-x64-musl-1.15.43.tgz#363ac7ce3866b664db0c35d78a6a90636f280139"
|
||||
integrity sha512-nS20HmbOk+dEEzdosJqqxAeyjMIiS5yrCAti8LUf0+dgr4eRmjkH4MlkjfPjf49aayR8o+eMJ1jsDZ7whx4zog==
|
||||
"@swc/html-linux-x64-musl@1.16.2":
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-linux-x64-musl/-/html-linux-x64-musl-1.16.2.tgz#ebfbbdb05f2f991a34bc500dce0ca10bfbcdfc3b"
|
||||
integrity sha512-c0Z84dvBd0oh1ZcBHnM18itmvJFLbCZBKFF2lEDHsGBSLQ/1sPbggEKsVO4KgWkkhwQV2l9AB4jnsw1HrwZJCg==
|
||||
|
||||
"@swc/html-win32-arm64-msvc@1.15.43":
|
||||
version "1.15.43"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-win32-arm64-msvc/-/html-win32-arm64-msvc-1.15.43.tgz#d0aa3f99c091577aaaa1aaf51a3695c98278564d"
|
||||
integrity sha512-Yz7aQQhXT/Yc6QcuMDQDZP9jqf2phkVyU+qSu8ZRWEcJgIorrPL6q7YLqMk+MB5PpZyu5XJEODvc1/UVDE1Kyg==
|
||||
"@swc/html-win32-arm64-msvc@1.16.2":
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-win32-arm64-msvc/-/html-win32-arm64-msvc-1.16.2.tgz#69207d99cd80e55fe1ea722c8c8d5174b3d3cd2a"
|
||||
integrity sha512-Aq7V2B5gS23X59DzV2z892c4NBHYtJbwhvsCjJN1MBMx723htjgNE9KVIJp9dQaJBr2PrNfb/u3QFwnWV2tAoQ==
|
||||
|
||||
"@swc/html-win32-ia32-msvc@1.15.43":
|
||||
version "1.15.43"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-win32-ia32-msvc/-/html-win32-ia32-msvc-1.15.43.tgz#e2cfe4dd26ce8b8c5787ca1fdd69ef6b5dc94822"
|
||||
integrity sha512-muUgfsSQRZk6YBRuhaGKSLvXy0bV9BW6/mHLI0N/06btWuf0hekoHhIzR7dUmS98NXKCA7Hv+buBPE/0vXUwyA==
|
||||
"@swc/html-win32-ia32-msvc@1.16.2":
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-win32-ia32-msvc/-/html-win32-ia32-msvc-1.16.2.tgz#9473a17f22c65ec20059533bd8cc78b32c82a711"
|
||||
integrity sha512-9gslPcsfXxKvAZtOvDkxGuEbM7lqBrONzLAyRsyUtw8KxFcSYkGIO48RDTstGWOkgTgKjjAq/WWqt9qr/NcE3A==
|
||||
|
||||
"@swc/html-win32-x64-msvc@1.15.43":
|
||||
version "1.15.43"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-win32-x64-msvc/-/html-win32-x64-msvc-1.15.43.tgz#ee1a8a7fe4d928595268c214cf17c72867ee8f0f"
|
||||
integrity sha512-tuLDy4MxPXsLi6jW+ozCdFWO61AoMMnlhePWJxMafefC2Ojm+iILxP2zI2Hgfu6F16y1q7ITdXdpEuqptu5fHw==
|
||||
"@swc/html-win32-x64-msvc@1.16.2":
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html-win32-x64-msvc/-/html-win32-x64-msvc-1.16.2.tgz#7c7aeaa21b8848a765be2d4f7ee2ee3fecfd0f52"
|
||||
integrity sha512-Kdb4VdC8FyF5s1MQaFUNeASLckHECrb/oYy/6OCtU+hbgxQ/o/JCgE4uCe8YAg0LCWSOjhx73PCZDGwPf1TpKw==
|
||||
|
||||
"@swc/html@^1.15.40":
|
||||
version "1.15.43"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html/-/html-1.15.43.tgz#421da1ffc3226d149fd57c73f73755c6e6148427"
|
||||
integrity sha512-SKbkbdGi9SDO9cTdV+6H0/AYifnb2nDOlz5BlWxlWMXACV3kmX6WwZDo0bBdyGlO/G4jCVWdR5r84qfotU2now==
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/@swc/html/-/html-1.16.2.tgz#91ee34374e4c926c6c8a8f43561688192b8a1725"
|
||||
integrity sha512-RmWH8m5dePWDFpHpmFKquZCRe5SyD/Sb0FBPxWcWv/tsjtlJl6oHeaxBsTL2edvaHuW385Fy5nPuTjDD/a+GEA==
|
||||
dependencies:
|
||||
"@swc/counter" "^0.1.3"
|
||||
optionalDependencies:
|
||||
"@swc/html-darwin-arm64" "1.15.43"
|
||||
"@swc/html-darwin-x64" "1.15.43"
|
||||
"@swc/html-linux-arm-gnueabihf" "1.15.43"
|
||||
"@swc/html-linux-arm64-gnu" "1.15.43"
|
||||
"@swc/html-linux-arm64-musl" "1.15.43"
|
||||
"@swc/html-linux-ppc64-gnu" "1.15.43"
|
||||
"@swc/html-linux-s390x-gnu" "1.15.43"
|
||||
"@swc/html-linux-x64-gnu" "1.15.43"
|
||||
"@swc/html-linux-x64-musl" "1.15.43"
|
||||
"@swc/html-win32-arm64-msvc" "1.15.43"
|
||||
"@swc/html-win32-ia32-msvc" "1.15.43"
|
||||
"@swc/html-win32-x64-msvc" "1.15.43"
|
||||
"@swc/html-darwin-arm64" "1.16.2"
|
||||
"@swc/html-darwin-x64" "1.16.2"
|
||||
"@swc/html-linux-arm-gnueabihf" "1.16.2"
|
||||
"@swc/html-linux-arm64-gnu" "1.16.2"
|
||||
"@swc/html-linux-arm64-musl" "1.16.2"
|
||||
"@swc/html-linux-ppc64-gnu" "1.16.2"
|
||||
"@swc/html-linux-s390x-gnu" "1.16.2"
|
||||
"@swc/html-linux-x64-gnu" "1.16.2"
|
||||
"@swc/html-linux-x64-musl" "1.16.2"
|
||||
"@swc/html-win32-arm64-msvc" "1.16.2"
|
||||
"@swc/html-win32-ia32-msvc" "1.16.2"
|
||||
"@swc/html-win32-x64-msvc" "1.16.2"
|
||||
|
||||
"@swc/types@^0.1.28":
|
||||
version "0.1.28"
|
||||
@@ -6924,9 +6924,9 @@ color-name@~1.1.4:
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
colord@^2.9.3:
|
||||
version "2.9.3"
|
||||
resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43"
|
||||
integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/colord/-/colord-2.10.0.tgz#56c9050e6b06b4b6c62ddec366a48d65ef57e860"
|
||||
integrity sha512-AidJptpBJmjTclAp9BkLwJi0T93fo5epJnbaZslpg6QVzpHjAiveF55mE9AcUJiGMqRHgMDY8soMsQtuNYMHfw==
|
||||
|
||||
colorette@^1.2.0:
|
||||
version "1.4.0"
|
||||
@@ -8284,36 +8284,11 @@ escape-string-regexp@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
|
||||
integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
|
||||
|
||||
eslint-scope@5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
|
||||
integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
|
||||
dependencies:
|
||||
esrecurse "^4.3.0"
|
||||
estraverse "^4.1.1"
|
||||
|
||||
esprima@~4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
|
||||
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
|
||||
|
||||
esrecurse@^4.3.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
|
||||
integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
|
||||
dependencies:
|
||||
estraverse "^5.2.0"
|
||||
|
||||
estraverse@^4.1.1:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
|
||||
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
|
||||
|
||||
estraverse@^5.2.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
|
||||
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
|
||||
|
||||
estree-util-attach-comments@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/estree-util-attach-comments/-/estree-util-attach-comments-3.0.0.tgz#344bde6a64c8a31d15231e5ee9e297566a691c2d"
|
||||
@@ -9671,9 +9646,9 @@ jiti@^1.20.0:
|
||||
integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==
|
||||
|
||||
joi@^17.9.2:
|
||||
version "17.13.4"
|
||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.4.tgz#ad6153d97ce558eb3a3b593e0d43eab51df1c474"
|
||||
integrity sha512-1RuuER6kmt8K8I3nIWvPZKi5RQCb568ZPyY4Pwjlua+yo+63ZTmIwxLZH0heBmiKN4uxjvCiarDrjaeH84xicQ==
|
||||
version "17.13.7"
|
||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.7.tgz#92e212c50dbbbcb1a1592424f84083eb265cc778"
|
||||
integrity sha512-MF80Dm5Y2veNy8QWVx9Bj3ui4mo7+VPSPsR1M+oaHXV0Gx6zGX9a2F+OZG3Blby9tOlzU9Rs5FUimlEhbKtfnQ==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.3.0"
|
||||
"@hapi/topo" "^5.1.0"
|
||||
@@ -11137,16 +11112,6 @@ minimist@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
|
||||
minimizer-webpack-plugin@^5.6.1:
|
||||
version "5.6.1"
|
||||
resolved "https://registry.yarnpkg.com/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.6.1.tgz#289922a4c96c4ed1ddb76b8a00bd8074e89a2f7f"
|
||||
integrity sha512-DoeAZz8Q1C1znwsUzej1fdoi4jCf7/+Em27ouLqfK/+3m8G+D7yDhUwrc3CNhjSzGUN1kn7Iv4sWmjflQHenpw==
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "^0.3.25"
|
||||
jest-worker "^27.4.5"
|
||||
schema-utils "^4.3.0"
|
||||
terser "^5.31.1"
|
||||
|
||||
minimizer-webpack-plugin@^5.7.0:
|
||||
version "5.8.0"
|
||||
resolved "https://registry.yarnpkg.com/minimizer-webpack-plugin/-/minimizer-webpack-plugin-5.8.0.tgz#744f0e28da888aa1708e2be32b4ddcf2eeb09e58"
|
||||
@@ -14219,9 +14184,9 @@ svg-parser@^2.0.4:
|
||||
integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==
|
||||
|
||||
svgo@^3.0.2, svgo@^3.2.0:
|
||||
version "3.3.4"
|
||||
resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.3.4.tgz#fd2aa10ff585b3bd2b83ce3602f5582bc0718bb5"
|
||||
integrity sha512-GsNRis4e8jxn2Y9ENz/8lbJ93CstG8svtMnuRaHbiF2LTJ5tK0/q3t/URPq9Zc7zVWBJnNnJMIp6bevK7bSmNg==
|
||||
version "3.3.5"
|
||||
resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.3.5.tgz#8a3d9557ab2f386eca7e24760385849554985a1c"
|
||||
integrity sha512-8SQMzdrvWaD8deUmrnYB+ASyxBVgWUOilg+A75nE/76WdLpj6LopCwiAVvkzkcqy/9b7t2Mg7faFLjg0ZRcZ3w==
|
||||
dependencies:
|
||||
commander "^7.2.0"
|
||||
css-select "^5.1.0"
|
||||
@@ -15163,10 +15128,10 @@ webpack-virtual-modules@^0.6.2:
|
||||
resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz#057faa9065c8acf48f24cb57ac0e77739ab9a7e8"
|
||||
integrity sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==
|
||||
|
||||
webpack@^5.110.1:
|
||||
version "5.110.1"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.110.1.tgz#d662d8ff1866fcb58a6b8af09c86bc6f1b9c5c1d"
|
||||
integrity sha512-gInQB+jxXxgnZyvPwuzT5NGQmECDqeu85oxcrjinrYHqPoBex0hCAN2SFTJVyPVrK0Pq9E44VFP+e89fAc10/w==
|
||||
webpack@^5.110.2, webpack@^5.88.1, webpack@^5.95.0:
|
||||
version "5.110.2"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.110.2.tgz#ef23a0e62fe5e1ba71b033e3505b7f005d8d5c6e"
|
||||
integrity sha512-TciLrfM7zgEjqGdY851HkirDsSPQgTFsWQpl9oHqMAMYsHhEC0bKjscvjpnz+pzx10hLC8qISApGrsnrCP4UtQ==
|
||||
dependencies:
|
||||
"@types/estree" "^1.0.8"
|
||||
"@types/json-schema" "^7.0.15"
|
||||
@@ -15188,32 +15153,6 @@ webpack@^5.110.1:
|
||||
watchpack "^2.5.2"
|
||||
webpack-sources "^3.5.1"
|
||||
|
||||
webpack@^5.88.1, webpack@^5.95.0:
|
||||
version "5.109.2"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.109.2.tgz#b58dc289561c3282db35c210a99379a836a4c28d"
|
||||
integrity sha512-U9/cvLzxObKNEZ9+TtdqrHM5/9z3lgl2c+c4BzbqGxFQvQvBAq87yql5A8pQ+rrMbS496MZJeF5enVBndIy2hw==
|
||||
dependencies:
|
||||
"@types/estree" "^1.0.8"
|
||||
"@types/json-schema" "^7.0.15"
|
||||
"@webassemblyjs/ast" "^1.14.1"
|
||||
"@webassemblyjs/wasm-edit" "^1.14.1"
|
||||
"@webassemblyjs/wasm-parser" "^1.14.1"
|
||||
acorn "^8.16.0"
|
||||
browserslist "^4.28.1"
|
||||
chrome-trace-event "^1.0.2"
|
||||
enhanced-resolve "^5.24.4"
|
||||
es-module-lexer "^2.1.0"
|
||||
eslint-scope "5.1.1"
|
||||
events "^3.2.0"
|
||||
graceful-fs "^4.2.11"
|
||||
mime-db "^1.54.0"
|
||||
minimizer-webpack-plugin "^5.6.1"
|
||||
neo-async "^2.6.2"
|
||||
schema-utils "^4.3.3"
|
||||
tapable "^2.3.0"
|
||||
watchpack "^2.5.2"
|
||||
webpack-sources "^3.5.1"
|
||||
|
||||
webpackbar@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-7.0.0.tgz#7228d32881af2392381b6514499ddea73cdf218a"
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ dependencies = [
|
||||
"deprecation>=2.1.0, <2.2.0",
|
||||
"flask>=2.2.5, <4.0.0",
|
||||
"flask-appbuilder>=5.2.2, <6.0.0",
|
||||
"flask-caching>=2.4.1, <3",
|
||||
"flask-caching>=2.5.0, <3",
|
||||
"flask-compress>=1.13, <2.0",
|
||||
"flask-talisman>=1.0.0, <2.0",
|
||||
"flask-login>=0.6.0, < 1.0",
|
||||
|
||||
@@ -40,7 +40,7 @@ brotli==1.2.0
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# flask-compress
|
||||
cachelib==0.13.0
|
||||
cachelib==0.17.0
|
||||
# via
|
||||
# flask-caching
|
||||
# flask-session
|
||||
@@ -105,7 +105,7 @@ et-xmlfile==2.0.0
|
||||
# via openpyxl
|
||||
filelock==3.20.3
|
||||
# via -r requirements/base.in
|
||||
flask==2.3.3
|
||||
flask==3.1.3
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# flask-appbuilder
|
||||
@@ -124,9 +124,9 @@ flask-appbuilder==5.2.2
|
||||
# via
|
||||
# apache-superset (pyproject.toml)
|
||||
# apache-superset-core
|
||||
flask-babel==3.1.0
|
||||
flask-babel==4.0.0
|
||||
# via flask-appbuilder
|
||||
flask-caching==2.4.1
|
||||
flask-caching==2.5.0
|
||||
# via apache-superset (pyproject.toml)
|
||||
flask-compress==1.24
|
||||
# via apache-superset (pyproject.toml)
|
||||
@@ -218,6 +218,7 @@ markdown-it-py==3.0.0
|
||||
# via rich
|
||||
markupsafe==3.0.2
|
||||
# via
|
||||
# flask
|
||||
# jinja2
|
||||
# mako
|
||||
# werkzeug
|
||||
|
||||
@@ -94,7 +94,7 @@ brotli==1.2.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-compress
|
||||
cachelib==0.13.0
|
||||
cachelib==0.17.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-caching
|
||||
@@ -247,7 +247,7 @@ filelock==3.20.3
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# virtualenv
|
||||
flask==2.3.3
|
||||
flask==3.1.3
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -269,11 +269,11 @@ flask-appbuilder==5.2.2
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
# apache-superset-core
|
||||
flask-babel==3.1.0
|
||||
flask-babel==4.0.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask-appbuilder
|
||||
flask-caching==2.4.1
|
||||
flask-caching==2.5.0
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# apache-superset
|
||||
@@ -524,6 +524,7 @@ markdown-it-py==3.0.0
|
||||
markupsafe==3.0.2
|
||||
# via
|
||||
# -c requirements/base-constraint.txt
|
||||
# flask
|
||||
# jinja2
|
||||
# mako
|
||||
# werkzeug
|
||||
|
||||
Generated
+4
-4
@@ -23,7 +23,7 @@
|
||||
"tscw-config": "^1.1.2",
|
||||
"typescript": "^7.0.2",
|
||||
"vitest": "^4.0.18",
|
||||
"webpack": "^5.110.0",
|
||||
"webpack": "^5.110.2",
|
||||
"webpack-cli": "^7.2.3"
|
||||
}
|
||||
},
|
||||
@@ -5260,9 +5260,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.110.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.110.1.tgz",
|
||||
"integrity": "sha512-gInQB+jxXxgnZyvPwuzT5NGQmECDqeu85oxcrjinrYHqPoBex0hCAN2SFTJVyPVrK0Pq9E44VFP+e89fAc10/w==",
|
||||
"version": "5.110.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.110.2.tgz",
|
||||
"integrity": "sha512-TciLrfM7zgEjqGdY851HkirDsSPQgTFsWQpl9oHqMAMYsHhEC0bKjscvjpnz+pzx10hLC8qISApGrsnrCP4UtQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"tscw-config": "^1.1.2",
|
||||
"typescript": "^7.0.2",
|
||||
"vitest": "^4.0.18",
|
||||
"webpack": "^5.110.0",
|
||||
"webpack": "^5.110.2",
|
||||
"webpack-cli": "^7.2.3"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
Generated
+169
-194
@@ -215,7 +215,7 @@
|
||||
"@types/rison": "0.1.0",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"@types/unzipper": "^0.10.11",
|
||||
"@typescript-eslint/eslint-plugin": "^8.68.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.69.0",
|
||||
"@typescript-eslint/parser": "^8.63.0",
|
||||
"babel-jest": "^30.5.0",
|
||||
"babel-loader": "^10.1.1",
|
||||
@@ -272,11 +272,11 @@
|
||||
"swc-loader": "^0.2.7",
|
||||
"ts-jest": "^29.4.12",
|
||||
"tscw-config": "^1.1.2",
|
||||
"tsx": "^4.23.12",
|
||||
"tsx": "^4.23.13",
|
||||
"typescript": "5.4.5",
|
||||
"unzipper": "^0.12.5",
|
||||
"wait-on": "^9.1.0",
|
||||
"webpack": "^5.110.1",
|
||||
"webpack": "^5.110.2",
|
||||
"webpack-bundle-analyzer": "^5.3.2",
|
||||
"webpack-cli": "^7.2.3",
|
||||
"webpack-dev-server": "^6.0.0",
|
||||
@@ -420,9 +420,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@ant-design/icons-svg": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.5.0.tgz",
|
||||
"integrity": "sha512-1BTUFyKPTBZ53MuTP8s0k5SFEXL7o3VHEOwLgzaoWKwnBeqIcqUtVshc4SKzhI6uACfqhJqBwBUE9FsWR3uULA==",
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.6.0.tgz",
|
||||
"integrity": "sha512-PRomU725ABMf/lnQp5HiB7my1kjEbFY0D10N4lXYxK6TIB1gKjIVD5MRThpDaezLgw1D774J8eOeeDB0M6wHrQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@ant-design/react-slick": {
|
||||
@@ -8007,11 +8007,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/jsonlint-lines-primitives": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
|
||||
"integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.3.tgz",
|
||||
"integrity": "sha512-0SElaV0uMxEnxzBhhX9WTuPyUeMsAN/SS0i16tjuba4/mio63MG9khjC1a0JAiPGXAwvwm4UfHJURCN7nyudQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
"node": ">= 22"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/martini": {
|
||||
@@ -8027,9 +8028,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@mapbox/tiny-sdf": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.1.0.tgz",
|
||||
"integrity": "sha512-uFJhNh36BR4OCuWIEiWaEix9CA2WzT6CAIcqVjWYpnx8+QDtS+oC4QehRrx5cX4mgWs37MmKnwUejeHxVymzNg==",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.2.0.tgz",
|
||||
"integrity": "sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/@mapbox/unitbezier": {
|
||||
@@ -8047,29 +8048,23 @@
|
||||
"@mapbox/point-geometry": "~0.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/whoots-js": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
|
||||
"integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@maplibre/geojson-vt": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-5.0.4.tgz",
|
||||
"integrity": "sha512-KGg9sma45S+stfH9vPCJk1J0lSDLWZgCT9Y8u8qWZJyjFlP8MNP1WGTxIMYJZjDvVT3PDn05kN1C95Sut1HpgQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@maplibre/maplibre-gl-style-spec": {
|
||||
"version": "24.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.8.5.tgz",
|
||||
"integrity": "sha512-EzEJmMt6thioRH7GI9LWS7ahXTcAhAPGWCe6oTP2Ps4YnsXOOAfeqx854lZaiDnwURfHmcCKV1mr6oo0i23x6w==",
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-6.1.1.tgz",
|
||||
"integrity": "sha512-FVMOcmSP/yqol45t7StApEyTL5/vmqBCuFhH9n+fFuINenhaX+YgHHIt1yJ86S8kln3uJLcMvmEU2cfn6E2eCQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
|
||||
"@mapbox/unitbezier": "^0.0.1",
|
||||
"kdbush": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@maplibre/maplibre-gl-style-spec": {
|
||||
"version": "26.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-26.4.2.tgz",
|
||||
"integrity": "sha512-6J0vZqMZvRAJKtdWJdDGHEh1YJ2ZHG08/GOur8gCArYhO8ZkM//OXqtI2AAEz4jc2G+Wq4MfcePg8qNK5TZ4Kg==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@mapbox/jsonlint-lines-primitives": "^2.0.3",
|
||||
"@mapbox/unitbezier": "^1.0.0",
|
||||
"json-stringify-pretty-compact": "^4.0.0",
|
||||
"minimist": "^1.2.8",
|
||||
"quickselect": "^3.0.0",
|
||||
@@ -8081,10 +8076,16 @@
|
||||
"gl-style-validate": "dist/gl-style-validate.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/@maplibre/maplibre-gl-style-spec/node_modules/@mapbox/unitbezier": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-1.0.0.tgz",
|
||||
"integrity": "sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/@maplibre/mlt": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.1.8.tgz",
|
||||
"integrity": "sha512-8vtfYGidr1rNkv5IwIoU2lfe3Oy+Wa8HluzQYcQi9cveU9K3pweAal/poQj4GJ0K/EW4bTQp2wVAs09g2yDRZg==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.2.1.tgz",
|
||||
"integrity": "sha512-5n5dgolE2EYxwCKgx8vlwURCB8A+kyfJJyThlYimjlGgOcOe2Bhw9VxxnnHC91OC9PgHg+nVdgVPTYPkIo62vg==",
|
||||
"license": "(MIT OR Apache-2.0)",
|
||||
"dependencies": {
|
||||
"@mapbox/point-geometry": "^1.1.0"
|
||||
@@ -8097,18 +8098,14 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@maplibre/vt-pbf": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.3.0.tgz",
|
||||
"integrity": "sha512-jIvp8F5hQCcreqOOpEt42TJMUlsrEcpf/kI1T2v85YrQRV6PPXUcEXUg5karKtH6oh47XJZ4kHu56pUkOuqA7w==",
|
||||
"version": "4.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.3.2.tgz",
|
||||
"integrity": "sha512-j6p0AdjvAR19Z3XaCysle7A4ZSo08tYOzxD0Y9NQylwPAkwJJeYub5b2eVucdeDh7erhv69DahoLOevDRERRUw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mapbox/point-geometry": "^1.1.0",
|
||||
"@mapbox/vector-tile": "^2.0.4",
|
||||
"@maplibre/geojson-vt": "^5.0.4",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"@types/supercluster": "^7.1.3",
|
||||
"pbf": "^4.0.1",
|
||||
"supercluster": "^8.0.1"
|
||||
"pbf": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@maplibre/vt-pbf/node_modules/@mapbox/point-geometry": {
|
||||
@@ -8117,21 +8114,10 @@
|
||||
"integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/@maplibre/vt-pbf/node_modules/@mapbox/vector-tile": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.5.tgz",
|
||||
"integrity": "sha512-pXj8m7KTsqZt+1jsE0xIpGvqTSbblfkuEJL/NJmNePMtEwxO8V3XMDo9WMSfDeqHvCtBI9Lmt4mGcGR10zecmw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@mapbox/point-geometry": "~1.1.0",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"pbf": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@maplibre/vt-pbf/node_modules/pbf": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.2.tgz",
|
||||
"integrity": "sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz",
|
||||
"integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"resolve-protobuf-schema": "^2.1.0"
|
||||
@@ -14995,17 +14981,17 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.68.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.68.0.tgz",
|
||||
"integrity": "sha512-WASHDpCm6qO5jj9g1a+8NiW5+GCkAyLReR56/4VruYmNgfUmqpxOfZ2Yfb8xGfJPWv5Qi6LSD8sXdces3vbp/Q==",
|
||||
"version": "8.69.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.69.0.tgz",
|
||||
"integrity": "sha512-t5jQTKPIgVW1PE6dR6H6Qz5gm8zjMlX5/2gRaOGd9eO6V7J+tQc6iWKukEe7dY8u9HyYasQ0yfF0/FSSTEO2gA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.12.2",
|
||||
"@typescript-eslint/scope-manager": "8.68.0",
|
||||
"@typescript-eslint/type-utils": "8.68.0",
|
||||
"@typescript-eslint/utils": "8.68.0",
|
||||
"@typescript-eslint/visitor-keys": "8.68.0",
|
||||
"@typescript-eslint/scope-manager": "8.69.0",
|
||||
"@typescript-eslint/type-utils": "8.69.0",
|
||||
"@typescript-eslint/utils": "8.69.0",
|
||||
"@typescript-eslint/visitor-keys": "8.69.0",
|
||||
"ignore": "^7.0.5",
|
||||
"natural-compare": "^1.4.0",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
@@ -15018,7 +15004,7 @@
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/parser": "^8.68.0",
|
||||
"@typescript-eslint/parser": "^8.69.0",
|
||||
"eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
}
|
||||
@@ -15034,16 +15020,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "8.68.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.68.0.tgz",
|
||||
"integrity": "sha512-fHq2VC1kpyYfvEcbiMjOpySY4WS7voEp89yAThrHRX5sm9j2lzYppCb2umFMEed4fWcyeLjHxrz0mpjNBaBxMQ==",
|
||||
"version": "8.69.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.69.0.tgz",
|
||||
"integrity": "sha512-l4b0DhWioGg6Gt2ebGlvfkFMOjRsauxtsnDRwUSRX1qHq3HdTfQHV8wW9zEXeciai6HfeaKOedQn2Zoofx3WBw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.68.0",
|
||||
"@typescript-eslint/types": "8.68.0",
|
||||
"@typescript-eslint/typescript-estree": "8.68.0",
|
||||
"@typescript-eslint/visitor-keys": "8.68.0",
|
||||
"@typescript-eslint/scope-manager": "8.69.0",
|
||||
"@typescript-eslint/types": "8.69.0",
|
||||
"@typescript-eslint/typescript-estree": "8.69.0",
|
||||
"@typescript-eslint/visitor-keys": "8.69.0",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -15059,14 +15045,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.68.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.68.0.tgz",
|
||||
"integrity": "sha512-5GQtWZCXFcFYux955pvoS02WLc49pXNlvIxocKjS0clvwo3in1RdlzVKyiqQH9vE5AKWFLTaUgeQkOrTS+0Qxw==",
|
||||
"version": "8.69.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.69.0.tgz",
|
||||
"integrity": "sha512-yi4obFrHMmnsesWehHbkg9zMA7Jt8cXT+mKM08G999pH1yT6nqgsHx7MYm0uY1wAj8CqiBXYRJ7WAT0QdQHQXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.68.0",
|
||||
"@typescript-eslint/types": "^8.68.0",
|
||||
"@typescript-eslint/tsconfig-utils": "^8.69.0",
|
||||
"@typescript-eslint/types": "^8.69.0",
|
||||
"debug": "^4.4.3"
|
||||
},
|
||||
"engines": {
|
||||
@@ -15081,14 +15067,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.68.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.68.0.tgz",
|
||||
"integrity": "sha512-T5eXpcaJNg8bhjHJ8Rjp68Vq/QBteYtTKY8TZqVNPaUbuz0f6jI9t6aDkylwvalpAB9XTTFeFOjrjXAZ3YvmVA==",
|
||||
"version": "8.69.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.69.0.tgz",
|
||||
"integrity": "sha512-ewfspqWvSxKSOaplqAUNbaSFO0eB6w1EtQ+esfYFRm3614Ty4uNtExkcbgd6nWsXphbqKyf9ZYdbZdv2xEoWEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.68.0",
|
||||
"@typescript-eslint/visitor-keys": "8.68.0"
|
||||
"@typescript-eslint/types": "8.69.0",
|
||||
"@typescript-eslint/visitor-keys": "8.69.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -15099,9 +15085,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.68.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.68.0.tgz",
|
||||
"integrity": "sha512-F7zrGQfiJHojPwi8vhxZQC1tWtJzvL74cK/nqri2lk8YUXvYaYwl263xOJ69jDWPUk1hmcdoayFwk9lX09npVw==",
|
||||
"version": "8.69.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.69.0.tgz",
|
||||
"integrity": "sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -15116,15 +15102,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "8.68.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.68.0.tgz",
|
||||
"integrity": "sha512-X77zqoY1EjeWGs/0JNxeaMfp5C5lIz4Tw8y66F1Ne8Faq6g424sBNYM6xBAqElfGZPLpWS+CZAp0DXyKDzWiHg==",
|
||||
"version": "8.69.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.69.0.tgz",
|
||||
"integrity": "sha512-ZfoJAVg3JZndQEpEl9petVlxau3lRuElc4HRMuAlLCf8to04/iHz692RUSNmXKDjEuJmIL+KZ2/BsOcBc16dsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.68.0",
|
||||
"@typescript-eslint/typescript-estree": "8.68.0",
|
||||
"@typescript-eslint/utils": "8.68.0",
|
||||
"@typescript-eslint/types": "8.69.0",
|
||||
"@typescript-eslint/typescript-estree": "8.69.0",
|
||||
"@typescript-eslint/utils": "8.69.0",
|
||||
"debug": "^4.4.3",
|
||||
"ts-api-utils": "^2.5.0"
|
||||
},
|
||||
@@ -15141,9 +15127,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "8.68.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.68.0.tgz",
|
||||
"integrity": "sha512-9RnpsGJjrAllCMefGVVsImJM24YurhC0Q1h4UbvivtvOqXmR/vEJge2OoE++z9m6hyg8T1Q8t5SNT6tHSbrxcg==",
|
||||
"version": "8.69.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.69.0.tgz",
|
||||
"integrity": "sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -15155,16 +15141,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.68.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.68.0.tgz",
|
||||
"integrity": "sha512-OKKsD0tYmoNiU5PW2zehO1yO56jYOm1ShYlxon/Z0SJNidAkdVg86eg9ruRuoXf8xfnuWZGbwDsStkoXbZtIIA==",
|
||||
"version": "8.69.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.69.0.tgz",
|
||||
"integrity": "sha512-AdFkgqck3Vudb/kWnxlyafU/4aBhHrbQ9locP2N4psXTy5mOBg0SHJumnLvx7r6g1gV4DKvUFwV2nJZBoqOD8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.68.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.68.0",
|
||||
"@typescript-eslint/types": "8.68.0",
|
||||
"@typescript-eslint/visitor-keys": "8.68.0",
|
||||
"@typescript-eslint/project-service": "8.69.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.69.0",
|
||||
"@typescript-eslint/types": "8.69.0",
|
||||
"@typescript-eslint/visitor-keys": "8.69.0",
|
||||
"debug": "^4.4.3",
|
||||
"minimatch": "^10.2.2",
|
||||
"semver": "^7.7.3",
|
||||
@@ -15199,16 +15185,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.68.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.68.0.tgz",
|
||||
"integrity": "sha512-PB5gJMMOg0Q5P1tsgWtEAqQacJXq0qEqRHDX/YJ4FaTMLfZPpHB3gjl2EJuiZyPABxmj4ZQYiY9m1bdAJ5y7tQ==",
|
||||
"version": "8.69.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.69.0.tgz",
|
||||
"integrity": "sha512-tUbx60BBqQa31kXF5MCsOOLL5E/WzUuxIn7YpAvq+eaUlqvk8/NXnXMBNAdLCr0icjkzem7iUA5QqWHe/hJ1aw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.9.1",
|
||||
"@typescript-eslint/scope-manager": "8.68.0",
|
||||
"@typescript-eslint/types": "8.68.0",
|
||||
"@typescript-eslint/typescript-estree": "8.68.0"
|
||||
"@typescript-eslint/scope-manager": "8.69.0",
|
||||
"@typescript-eslint/types": "8.69.0",
|
||||
"@typescript-eslint/typescript-estree": "8.69.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -15223,13 +15209,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.68.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.68.0.tgz",
|
||||
"integrity": "sha512-YR65gGdGvTUAWLldC3xLOvOzamdGzB4A5/N8rehEaHs3Zvoe39BhgY+u0SPch1OvrVTfLcc55wsSgK2NcnTS/A==",
|
||||
"version": "8.69.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.69.0.tgz",
|
||||
"integrity": "sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.68.0",
|
||||
"@typescript-eslint/types": "8.69.0",
|
||||
"eslint-visitor-keys": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -28781,9 +28767,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jest-process-manager/node_modules/joi": {
|
||||
"version": "17.13.4",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-17.13.4.tgz",
|
||||
"integrity": "sha512-1RuuER6kmt8K8I3nIWvPZKi5RQCb568ZPyY4Pwjlua+yo+63ZTmIwxLZH0heBmiKN4uxjvCiarDrjaeH84xicQ==",
|
||||
"version": "17.13.7",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-17.13.7.tgz",
|
||||
"integrity": "sha512-MF80Dm5Y2veNy8QWVx9Bj3ui4mo7+VPSPsR1M+oaHXV0Gx6zGX9a2F+OZG3Blby9tOlzU9Rs5FUimlEhbKtfnQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
@@ -30783,9 +30769,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/joi": {
|
||||
"version": "18.2.3",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-18.2.3.tgz",
|
||||
"integrity": "sha512-N5A3KTWQpPWT4ExxxPlUx7WmykGXRzhNidWhV41d6Abu9YfI2NyWCJuxdPnslJCPWtbRpSVOWSnSS6GakLM/Rg==",
|
||||
"version": "18.2.8",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-18.2.8.tgz",
|
||||
"integrity": "sha512-G2TX62h58ZHuwqetJgP2F4ualakqAmZtBYe3jWen7gxQRw5xApX6crnFtuB91WC0c3ESBnva+kGSnb3+6pIQDQ==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
@@ -32288,7 +32274,8 @@
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
|
||||
"integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.isempty": {
|
||||
"version": "4.4.0",
|
||||
@@ -32300,7 +32287,8 @@
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
@@ -32575,27 +32563,25 @@
|
||||
]
|
||||
},
|
||||
"node_modules/maplibre-gl": {
|
||||
"version": "5.24.0",
|
||||
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.24.0.tgz",
|
||||
"integrity": "sha512-ALyFxgtd5R+65UqZ/++lOqwWcC0SNho9c27fYSyLmG7AfnAul2o46F05aDJGPbFU57wos9dgcIySHs0Xe6ia3A==",
|
||||
"version": "6.8.0",
|
||||
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-6.8.0.tgz",
|
||||
"integrity": "sha512-+ZkjKTodsVLY0ewQThvXRxXQsclcsSgOm5LlnrBM3G8AloMJKt6Haw8mY4xrOl8T0WMqH6DTYjktZ9zGQXZd4w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@mapbox/jsonlint-lines-primitives": "^2.0.2",
|
||||
"@mapbox/point-geometry": "^1.1.0",
|
||||
"@mapbox/tiny-sdf": "^2.1.0",
|
||||
"@mapbox/unitbezier": "^0.0.1",
|
||||
"@mapbox/vector-tile": "^2.0.4",
|
||||
"@mapbox/whoots-js": "^3.1.0",
|
||||
"@maplibre/geojson-vt": "^6.1.0",
|
||||
"@maplibre/maplibre-gl-style-spec": "^24.8.1",
|
||||
"@maplibre/mlt": "^1.1.8",
|
||||
"@maplibre/vt-pbf": "^4.3.0",
|
||||
"@mapbox/tiny-sdf": "^2.2.0",
|
||||
"@mapbox/unitbezier": "^1.0.0",
|
||||
"@mapbox/vector-tile": "^3.0.0",
|
||||
"@maplibre/geojson-vt": "^6.1.1",
|
||||
"@maplibre/maplibre-gl-style-spec": "^26.4.1",
|
||||
"@maplibre/mlt": "^1.2.1",
|
||||
"@maplibre/vt-pbf": "^4.3.2",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"earcut": "^3.0.2",
|
||||
"earcut": "^3.2.3",
|
||||
"gl-matrix": "^3.4.4",
|
||||
"kdbush": "^4.0.2",
|
||||
"kdbush": "^4.1.0",
|
||||
"murmurhash-js": "^1.0.0",
|
||||
"pbf": "^4.0.1",
|
||||
"pbf": "^5.1.2",
|
||||
"potpack": "^2.1.0",
|
||||
"quickselect": "^3.0.0",
|
||||
"tinyqueue": "^3.0.0"
|
||||
@@ -32614,36 +32600,33 @@
|
||||
"integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/maplibre-gl/node_modules/@mapbox/unitbezier": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-1.0.0.tgz",
|
||||
"integrity": "sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/maplibre-gl/node_modules/@mapbox/vector-tile": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.5.tgz",
|
||||
"integrity": "sha512-pXj8m7KTsqZt+1jsE0xIpGvqTSbblfkuEJL/NJmNePMtEwxO8V3XMDo9WMSfDeqHvCtBI9Lmt4mGcGR10zecmw==",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-3.0.0.tgz",
|
||||
"integrity": "sha512-Qf10S1uIHMk20ri/IVBnpS+esUEkVaR5Hftmz88jTInrpmWgPGJfPe3LVjjlE77trLx8tH6qjTG7uWH9hIq/0Q==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@mapbox/point-geometry": "~1.1.0",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"pbf": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/maplibre-gl/node_modules/@maplibre/geojson-vt": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-6.1.0.tgz",
|
||||
"integrity": "sha512-2eIY4gZxeKIVOZVNkAMb+5NgXhgsMQpOveTQAvnp53LYqHGJZDidk7Ew0Tged9PThidpbS+NFTh0g4zivhPDzQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"kdbush": "^4.0.2"
|
||||
"pbf": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/maplibre-gl/node_modules/earcut": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz",
|
||||
"integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==",
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/earcut/-/earcut-3.2.3.tgz",
|
||||
"integrity": "sha512-vnS4AVwp1KHAF13i1vp1/2D5evWy3k5u/iW/B81QVsUZtV8cv2tU0b2VNFlqvh4kYwrFMDdjPCfAmfyJW9y14Q==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/maplibre-gl/node_modules/pbf": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.2.tgz",
|
||||
"integrity": "sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.2.tgz",
|
||||
"integrity": "sha512-mnvGdvOrIvJOBGUEdGkrVXjN8E/VkIJCkf2eS1DH2yv82ORUlLttmDt0rWY38yYZmVwciZwBUvHM20qxBZf40w==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"resolve-protobuf-schema": "^2.1.0"
|
||||
@@ -38965,9 +38948,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-error-boundary": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.1.3.tgz",
|
||||
"integrity": "sha512-GnSKpCohFi2nQmJCWwP8O8wub7zexlePvpsejvQr35vS5RTouS1+utTNOmyc540yw5vyOXnSL1rBWsCQDmkyUA==",
|
||||
"version": "6.1.4",
|
||||
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.1.4.tgz",
|
||||
"integrity": "sha512-7FgvbyjCFhu4dtKSZu807cW1MWG2nWAM4bwGqUByAtHVJXtlzoCJX0SSyZRyFrb+aBd7wtJkcOR4DsxXVEuDsA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "^18.0.0 || ^19.0.0",
|
||||
@@ -42614,15 +42597,6 @@
|
||||
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/supercluster": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz",
|
||||
"integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"kdbush": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
@@ -42715,9 +42689,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/svgo": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.4.tgz",
|
||||
"integrity": "sha512-GsNRis4e8jxn2Y9ENz/8lbJ93CstG8svtMnuRaHbiF2LTJ5tK0/q3t/URPq9Zc7zVWBJnNnJMIp6bevK7bSmNg==",
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.5.tgz",
|
||||
"integrity": "sha512-8SQMzdrvWaD8deUmrnYB+ASyxBVgWUOilg+A75nE/76WdLpj6LopCwiAVvkzkcqy/9b7t2Mg7faFLjg0ZRcZ3w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -43761,9 +43735,9 @@
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.23.12",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.12.tgz",
|
||||
"integrity": "sha512-FDf4L4sYzKtzWYhU/Xm0AQFdTjdIxNo9ElTf2mxXM6k8YMHXzYUe4yODVaXP4V9uMFbVg8c0qyBccK2OOxb45Q==",
|
||||
"version": "4.23.13",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.13.tgz",
|
||||
"integrity": "sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -45127,9 +45101,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.110.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.110.1.tgz",
|
||||
"integrity": "sha512-gInQB+jxXxgnZyvPwuzT5NGQmECDqeu85oxcrjinrYHqPoBex0hCAN2SFTJVyPVrK0Pq9E44VFP+e89fAc10/w==",
|
||||
"version": "5.110.2",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.110.2.tgz",
|
||||
"integrity": "sha512-TciLrfM7zgEjqGdY851HkirDsSPQgTFsWQpl9oHqMAMYsHhEC0bKjscvjpnz+pzx10hLC8qISApGrsnrCP4UtQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -46922,9 +46896,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz",
|
||||
"integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==",
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-4.5.4.tgz",
|
||||
"integrity": "sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
@@ -47053,7 +47027,7 @@
|
||||
"tinycolor2": "*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ant-design/icons": "^5.6.1 || ^6.0.0",
|
||||
"@ant-design/icons": "^5.6.1",
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@superset-ui/core": "*",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
@@ -47073,7 +47047,7 @@
|
||||
"version": "0.20.4",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^6.3.2",
|
||||
"@ant-design/icons": "^6.3.4",
|
||||
"@apache-superset/core": "*",
|
||||
"@babel/runtime": "^7.29.7",
|
||||
"@braintree/sanitize-url": "^7.1.2",
|
||||
@@ -47102,9 +47076,9 @@
|
||||
"math-expression-evaluator": "^2.0.7",
|
||||
"parse-ms": "^4.0.0",
|
||||
"re-resizable": "^6.11.2",
|
||||
"react-ace": "^14.0.1",
|
||||
"react-ace": "^15.0.0",
|
||||
"react-draggable": "^4.7.1",
|
||||
"react-error-boundary": "^6.1.3",
|
||||
"react-error-boundary": "^6.1.4",
|
||||
"react-js-cron": "^6.0.2",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-resize-detector": "^7.1.2",
|
||||
@@ -47175,13 +47149,13 @@
|
||||
}
|
||||
},
|
||||
"packages/superset-ui-core/node_modules/@ant-design/icons": {
|
||||
"version": "6.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.3.2.tgz",
|
||||
"integrity": "sha512-B6O5a5XJ4wjtNOfZejXYwHW5zvKV5gYkjGf11dHGLEbKn0ABDGndo41+gfIiXyTFhvESj4XTotuud33mUFid0g==",
|
||||
"version": "6.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.3.4.tgz",
|
||||
"integrity": "sha512-kDoUlppczVyCUTFthF6cHmPzMBYqNZQhu9j6EyIX/YIdM9j9CZNZoJUjzVw7/1v1VO8WwVMmPp15r/uwIAS0AQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^8.0.1",
|
||||
"@ant-design/icons-svg": "^4.5.0",
|
||||
"@ant-design/icons-svg": "^4.6.0",
|
||||
"@rc-component/util": "^1.11.0",
|
||||
"clsx": "^2.1.1"
|
||||
},
|
||||
@@ -47203,15 +47177,14 @@
|
||||
}
|
||||
},
|
||||
"packages/superset-ui-core/node_modules/react-ace": {
|
||||
"version": "14.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-ace/-/react-ace-14.0.1.tgz",
|
||||
"integrity": "sha512-z6YAZ20PNf/FqmYEic//G/UK6uw0rn21g58ASgHJHl9rfE4nITQLqthr9rHMVQK4ezwohJbp2dGrZpkq979PYQ==",
|
||||
"version": "15.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-ace/-/react-ace-15.0.0.tgz",
|
||||
"integrity": "sha512-gdmS5ftF0hsbkcrAjZQqYbXwFg5JrHuxjki8mP6Bn9kfa6lHKfZk9sU9EfS0ifQ1CpGCmRxF/VC7GRvlJMBuZw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ace-builds": "^1.36.3",
|
||||
"diff-match-patch": "^1.0.5",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"fast-equals": "^5.3.3",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -47414,7 +47387,7 @@
|
||||
"d3-array": "^3.2.4",
|
||||
"lodash": "^4.18.1",
|
||||
"lodash-es": "^4.18.1",
|
||||
"zod": "^4.4.3"
|
||||
"zod": "^4.5.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@apache-superset/core": "*",
|
||||
@@ -47593,8 +47566,10 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@math.gl/web-mercator": "^4.1.0",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"@types/supercluster": "^7.1.3",
|
||||
"mapbox-gl": "^3.29.0",
|
||||
"maplibre-gl": "^5.24.0",
|
||||
"maplibre-gl": "^6.8.0",
|
||||
"react-map-gl": "^8.1.2",
|
||||
"supercluster": "^9.0.0"
|
||||
},
|
||||
@@ -47737,7 +47712,7 @@
|
||||
"handlebars": "^4.7.9",
|
||||
"lodash": "^4.18.1",
|
||||
"lodash-es": "^4.18.1",
|
||||
"maplibre-gl": "^5.24.0",
|
||||
"maplibre-gl": "^6.8.0",
|
||||
"mousetrap": "^1.6.5",
|
||||
"ngeohash": "^0.6.4",
|
||||
"prop-types": "^15.8.1",
|
||||
|
||||
@@ -292,7 +292,7 @@
|
||||
"@types/rison": "0.1.0",
|
||||
"@types/tinycolor2": "^1.4.3",
|
||||
"@types/unzipper": "^0.10.11",
|
||||
"@typescript-eslint/eslint-plugin": "^8.68.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.69.0",
|
||||
"@typescript-eslint/parser": "^8.63.0",
|
||||
"babel-jest": "^30.5.0",
|
||||
"babel-loader": "^10.1.1",
|
||||
@@ -349,11 +349,11 @@
|
||||
"swc-loader": "^0.2.7",
|
||||
"ts-jest": "^29.4.12",
|
||||
"tscw-config": "^1.1.2",
|
||||
"tsx": "^4.23.12",
|
||||
"tsx": "^4.23.13",
|
||||
"typescript": "5.4.5",
|
||||
"unzipper": "^0.12.5",
|
||||
"wait-on": "^9.1.0",
|
||||
"webpack": "^5.110.1",
|
||||
"webpack": "^5.110.2",
|
||||
"webpack-bundle-analyzer": "^5.3.2",
|
||||
"webpack-cli": "^7.2.3",
|
||||
"webpack-dev-server": "^6.0.0",
|
||||
|
||||
@@ -20,7 +20,3 @@
|
||||
export * from './TranslatorSingleton';
|
||||
export * from './types';
|
||||
export { default as Translator } from './Translator';
|
||||
|
||||
export default {};
|
||||
|
||||
export { default as __hack_reexport_translation } from './types';
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
import { Jed as BaseJed, JedOptions, DomainData, Translations } from './jed';
|
||||
|
||||
export { Translations, DomainConfig, DomainData, JedOptions } from './jed';
|
||||
export type { Translations, DomainConfig, DomainData, JedOptions } from './jed';
|
||||
|
||||
/**
|
||||
* Superset supported languages.
|
||||
@@ -86,5 +86,3 @@ export interface TranslatorConfig {
|
||||
* Key-value mapping of translation key and the translations.
|
||||
*/
|
||||
export type LocaleData = Partial<Record<Locale, Translations>>;
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -52,5 +52,3 @@ export interface Jed {
|
||||
|
||||
options: JedOptions;
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"tinycolor2": "*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ant-design/icons": "^5.6.1 || ^6.0.0",
|
||||
"@ant-design/icons": "^5.6.1",
|
||||
"@emotion/react": "^11.4.1",
|
||||
"@superset-ui/core": "*",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
|
||||
@@ -537,8 +537,6 @@ export type ResolvedColorFormatterResult = {
|
||||
color?: string;
|
||||
};
|
||||
|
||||
export default {};
|
||||
|
||||
export function isColumnMeta(column: AnyDict): column is ColumnMeta {
|
||||
return !!column && 'column_name' in column;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^6.3.2",
|
||||
"@ant-design/icons": "^6.3.4",
|
||||
"@apache-superset/core": "*",
|
||||
"@babel/runtime": "^7.29.7",
|
||||
"@braintree/sanitize-url": "^7.1.2",
|
||||
@@ -78,9 +78,9 @@
|
||||
"math-expression-evaluator": "^2.0.7",
|
||||
"parse-ms": "^4.0.0",
|
||||
"re-resizable": "^6.11.2",
|
||||
"react-ace": "^14.0.1",
|
||||
"react-ace": "^15.0.0",
|
||||
"react-draggable": "^4.7.1",
|
||||
"react-error-boundary": "^6.1.3",
|
||||
"react-error-boundary": "^6.1.4",
|
||||
"react-js-cron": "^6.0.2",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-resize-detector": "^7.1.2",
|
||||
|
||||
+98
@@ -566,6 +566,104 @@ test('should NOT refetch data when other string-based renderTrigger controls cha
|
||||
});
|
||||
});
|
||||
|
||||
test('should NOT refetch data when echart_options (string-based renderTrigger control) changes', async () => {
|
||||
// Matches how the Timeseries/MixedTimeseries control panels reference this
|
||||
// shared control: a bare string, e.g. ['echart_options'].
|
||||
const controlPanelConfig = {
|
||||
controlPanelSections: [
|
||||
{
|
||||
controlSetRows: [['echart_options']],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
|
||||
get: jest.fn().mockReturnValue(controlPanelConfig),
|
||||
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
|
||||
|
||||
const formDataWithEchartOptions = {
|
||||
...mockFormData,
|
||||
echart_options: '{}',
|
||||
};
|
||||
|
||||
const { rerender, getByTestId } = render(
|
||||
<StatefulChart
|
||||
formData={formDataWithEchartOptions}
|
||||
chartType="test_chart"
|
||||
/>,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockChartClient.client.post).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// Edit the ECharts Options field (e.g. from the Customize tab while the
|
||||
// chart is part of a Matrixify grid cell).
|
||||
const updatedFormData = {
|
||||
...formDataWithEchartOptions,
|
||||
echart_options: '{"title": {"text": "My Chart"}}',
|
||||
};
|
||||
|
||||
rerender(<StatefulChart formData={updatedFormData} chartType="test_chart" />);
|
||||
|
||||
await waitFor(() => {
|
||||
// Should NOT refetch data - echart_options is a renderTrigger control
|
||||
expect(mockChartClient.client.post).toHaveBeenCalledTimes(1);
|
||||
// But should re-render with the new formData
|
||||
expect(getByTestId('super-chart')).toHaveTextContent(
|
||||
JSON.stringify(updatedFormData),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('should refetch when a chart overrides a shared renderTrigger control to renderTrigger: false', async () => {
|
||||
// Matches Country Map's controlPanel.controlOverrides, which sets
|
||||
// linear_color_scheme to renderTrigger: false because it drives the
|
||||
// choropleth data query rather than just styling.
|
||||
const controlPanelConfig = {
|
||||
controlPanelSections: [
|
||||
{
|
||||
controlSetRows: [['linear_color_scheme']],
|
||||
},
|
||||
],
|
||||
controlOverrides: {
|
||||
linear_color_scheme: {
|
||||
renderTrigger: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
jest.mocked(getChartControlPanelRegistry).mockReturnValue({
|
||||
get: jest.fn().mockReturnValue(controlPanelConfig),
|
||||
} as unknown as ReturnType<typeof getChartControlPanelRegistry>);
|
||||
|
||||
const formDataWithColorScheme = {
|
||||
...mockFormData,
|
||||
linear_color_scheme: 'schemeA',
|
||||
};
|
||||
|
||||
const { rerender } = render(
|
||||
<StatefulChart formData={formDataWithColorScheme} chartType="test_chart" />,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockChartClient.client.post).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
const updatedFormData = {
|
||||
...formDataWithColorScheme,
|
||||
linear_color_scheme: 'schemeB',
|
||||
};
|
||||
|
||||
rerender(<StatefulChart formData={updatedFormData} chartType="test_chart" />);
|
||||
|
||||
await waitFor(() => {
|
||||
// Should refetch because this chart's controlOverrides mark the control
|
||||
// as data-affecting, overriding the shared-control fallback.
|
||||
expect(mockChartClient.client.post).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
test('should refetch when string control is NOT in RENDER_TRIGGER_SHARED_CONTROLS', async () => {
|
||||
// Control panel with a string control that is NOT in the renderTrigger set
|
||||
const controlPanelConfig = {
|
||||
|
||||
@@ -50,6 +50,9 @@ type LoadingState = 'uninitialized' | 'loading' | 'loaded' | 'error';
|
||||
* This list is needed because string-based control references (e.g., ['zoomable'])
|
||||
* cannot be introspected for their renderTrigger property without importing
|
||||
* sharedControls, which would create a circular dependency.
|
||||
*
|
||||
* Keep this list in sync with the `renderTrigger: true` entries in
|
||||
* @superset-ui/chart-controls's sharedControls.tsx.
|
||||
*/
|
||||
const RENDER_TRIGGER_SHARED_CONTROLS = new Set([
|
||||
'zoomable',
|
||||
@@ -57,6 +60,11 @@ const RENDER_TRIGGER_SHARED_CONTROLS = new Set([
|
||||
'time_shift_color',
|
||||
'y_axis_format',
|
||||
'currency_format',
|
||||
'color_picker',
|
||||
'linear_color_scheme',
|
||||
'x_axis_time_format',
|
||||
'x_axis_number_format',
|
||||
'echart_options',
|
||||
]);
|
||||
|
||||
/**
|
||||
@@ -111,6 +119,28 @@ function shouldRefetchData(
|
||||
}
|
||||
});
|
||||
|
||||
// Individual chart types can override a shared control's renderTrigger
|
||||
// behavior (e.g., Country Map sets `linear_color_scheme` to
|
||||
// renderTrigger: false because it drives the choropleth query, not just
|
||||
// styling). Apply those overrides on top of the shared-control fallback
|
||||
// so such controls still trigger a refetch for that chart type.
|
||||
const { controlOverrides } = controlPanel;
|
||||
if (controlOverrides) {
|
||||
Object.entries(controlOverrides).forEach(([controlName, override]) => {
|
||||
if (
|
||||
override &&
|
||||
typeof override === 'object' &&
|
||||
'renderTrigger' in override
|
||||
) {
|
||||
if ((override as { renderTrigger?: boolean }).renderTrigger) {
|
||||
renderTriggerControls.add(controlName);
|
||||
} else {
|
||||
renderTriggerControls.delete(controlName);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Check which fields changed
|
||||
const changedFields = Object.keys(nextFormData).filter(
|
||||
key =>
|
||||
|
||||
@@ -44,7 +44,3 @@ export * from './types/TransformFunction';
|
||||
export * from './types/QueryResponse';
|
||||
export * from './types/VizType';
|
||||
export * from './types/matrixify';
|
||||
|
||||
export { default as __hack_reexport_chart_Base } from './types/Base';
|
||||
export { default as __hack_reexport_chart_TransformFunction } from './types/TransformFunction';
|
||||
export { default as __hack_reexport_chart_QueryResponse } from './types/QueryResponse';
|
||||
|
||||
@@ -153,5 +153,3 @@ export enum AxisType {
|
||||
export interface LegendState {
|
||||
[key: string]: boolean;
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -48,5 +48,3 @@ export type LegacyQueryData = PlainObject;
|
||||
* Don't use this for a specific chart (since you know which API it uses already).
|
||||
*/
|
||||
export type QueryData = LegacyQueryData | ChartDataResponseResult;
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -50,5 +50,3 @@ export type BuildQueryFunction<T extends QueryFormData> = (
|
||||
};
|
||||
},
|
||||
) => QueryContext;
|
||||
|
||||
export default {};
|
||||
|
||||
+55
@@ -604,6 +604,61 @@ test('cleans up event listeners on unmount', async () => {
|
||||
offSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('re-applies annotations only when their content actually changes across renders (react-ace 15 fast-equals regression guard)', async () => {
|
||||
// react-ace's componentDidUpdate decides whether to call
|
||||
// session.setAnnotations() by deep-comparing the new/old `annotations`
|
||||
// prop (lib/ace.js, using an internal deep-equality helper -- lodash's
|
||||
// isEqual through react-ace 14.x, fast-equals's deepEqual from 15.0.0
|
||||
// onward). Superset's own AceEditorProvider/EditorWrapper always pass a
|
||||
// freshly `.map()`-derived annotations array on every render, so this
|
||||
// guards the actual behavior Superset relies on: a same-content-but-
|
||||
// different-reference array must NOT re-trigger setAnnotations (or the
|
||||
// editor would thrash on every keystroke-driven re-render), while a
|
||||
// genuinely different array must still update the editor.
|
||||
const ref = createRef<AceEditor>();
|
||||
const annotationsV1 = [{ row: 0, column: 0, type: 'error', text: 'oops' }];
|
||||
|
||||
const { rerender, container } = render(
|
||||
<SQLEditor ref={ref as React.Ref<never>} annotations={annotationsV1} />,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(container.querySelector(selector)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const session = ref.current?.editor?.getSession();
|
||||
expect(session).toBeDefined();
|
||||
if (!session) return;
|
||||
|
||||
// The initial mount already applies annotations via componentDidMount,
|
||||
// not componentDidUpdate, so start observing only from the first update.
|
||||
const setAnnotationsSpy = jest.spyOn(session, 'setAnnotations');
|
||||
|
||||
// Same content, new array/object references -- must be a no-op.
|
||||
const annotationsV1SameContent = [
|
||||
{ row: 0, column: 0, type: 'error', text: 'oops' },
|
||||
];
|
||||
rerender(
|
||||
<SQLEditor
|
||||
ref={ref as React.Ref<never>}
|
||||
annotations={annotationsV1SameContent}
|
||||
/>,
|
||||
);
|
||||
expect(setAnnotationsSpy).not.toHaveBeenCalled();
|
||||
|
||||
// Genuinely different content -- must update, with the new value.
|
||||
const annotationsV2 = [
|
||||
{ row: 1, column: 2, type: 'warning', text: 'different' },
|
||||
];
|
||||
rerender(
|
||||
<SQLEditor ref={ref as React.Ref<never>} annotations={annotationsV2} />,
|
||||
);
|
||||
expect(setAnnotationsSpy).toHaveBeenCalledTimes(1);
|
||||
expect(setAnnotationsSpy).toHaveBeenCalledWith(annotationsV2);
|
||||
|
||||
setAnnotationsSpy.mockRestore();
|
||||
});
|
||||
|
||||
test('does not move autocomplete popup if target container is document.body', async () => {
|
||||
const ref = createRef<AceEditor>();
|
||||
const { container } = render(<SQLEditor ref={ref as React.Ref<never>} />);
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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 { Icons } from '../Icons';
|
||||
import { Button } from '../Button';
|
||||
import MetadataBar, { MetadataType } from '../MetadataBar';
|
||||
import { Menu } from '../Menu';
|
||||
import { PageHeaderWithActions, PageHeaderWithActionsProps } from '.';
|
||||
|
||||
export default {
|
||||
title: 'Design System/Components/PageHeaderWithActions',
|
||||
component: PageHeaderWithActions,
|
||||
parameters: {
|
||||
layout: 'fullscreen',
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
'Header used on entity pages (e.g. the dashboard page) combining an editable title with badges, a metadata bar, and page-level actions.',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Mirrors src/dashboard/components/Header's real composition: an editable
|
||||
// title, the certified badge, and a titlePanelAdditionalItems cluster of a
|
||||
// refresh button, an auto-refresh indicator, a published-status toggle, and
|
||||
// a MetadataBar (Last Modified + Editor) -- the same items the real
|
||||
// dashboard header packs into that space -- so this story reproduces the
|
||||
// header's real narrow-viewport layout behavior, not just the isolated
|
||||
// MetadataBar. The real RefreshButton/AutoRefreshIndicator/PublishedStatus
|
||||
// components live in src/dashboard and depend on this package, so they
|
||||
// can't be imported here without inverting that dependency; these are
|
||||
// same-sized stand-ins built from core components instead.
|
||||
export const DashboardHeader = (args: PageHeaderWithActionsProps) => (
|
||||
<PageHeaderWithActions {...args} />
|
||||
);
|
||||
|
||||
DashboardHeader.args = {
|
||||
editableTitleProps: {
|
||||
title: 'Q3 Executive Revenue and Growth Overview Dashboard',
|
||||
placeholder: 'Add the name of the dashboard',
|
||||
onSave: () => {},
|
||||
canEdit: true,
|
||||
label: 'Dashboard title',
|
||||
},
|
||||
showTitlePanelItems: true,
|
||||
certificatiedBadgeProps: {
|
||||
certifiedBy: 'Jane Doe',
|
||||
details: 'Certified by the BI team',
|
||||
},
|
||||
showFaveStar: true,
|
||||
faveStarProps: { itemId: 1, saveFaveStar: () => {}, isStarred: false },
|
||||
titlePanelAdditionalItems: [
|
||||
<Button key="refresh-button" buttonStyle="link" tooltip="Refresh dashboard">
|
||||
<Icons.ReloadOutlined iconSize="l" />
|
||||
</Button>,
|
||||
<Icons.SyncOutlined key="auto-refresh-indicator" iconSize="l" />,
|
||||
<Button key="published-status" buttonStyle="link">
|
||||
Published
|
||||
</Button>,
|
||||
<MetadataBar
|
||||
key="metadata-bar"
|
||||
tooltipPlacement="bottom"
|
||||
items={[
|
||||
{
|
||||
type: MetadataType.LastModified,
|
||||
value: '2 hours ago',
|
||||
modifiedBy: 'Jane Doe',
|
||||
},
|
||||
{
|
||||
type: MetadataType.Editor,
|
||||
createdBy: 'Jane Doe',
|
||||
editors: ['Jane Doe', 'John Smith'],
|
||||
createdOn: 'a week ago',
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
],
|
||||
rightPanelAdditionalItems: <button type="button">Edit dashboard</button>,
|
||||
additionalActionsMenu: (
|
||||
<Menu
|
||||
items={[{ label: 'Edit properties', key: '1' }]}
|
||||
data-test="additional-actions-menu"
|
||||
/>
|
||||
),
|
||||
menuDropdownProps: {},
|
||||
};
|
||||
+18
-1
@@ -18,7 +18,12 @@
|
||||
*/
|
||||
|
||||
import { render, screen, userEvent } from '@superset-ui/core/spec';
|
||||
import { PageHeaderWithActions, PageHeaderWithActionsProps } from './index';
|
||||
import { supersetTheme } from '@apache-superset/core/theme';
|
||||
import {
|
||||
buttonsStyles,
|
||||
PageHeaderWithActions,
|
||||
PageHeaderWithActionsProps,
|
||||
} from './index';
|
||||
import { Menu } from '../Menu';
|
||||
|
||||
const defaultProps: PageHeaderWithActionsProps = {
|
||||
@@ -54,3 +59,15 @@ test('Renders', async () => {
|
||||
await userEvent.click(screen.getByLabelText('Menu actions trigger'));
|
||||
expect(defaultProps.menuDropdownProps.onOpenChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('clips the title panel buttons/metadata cluster instead of letting it overflow into the actions menu', () => {
|
||||
// jsdom doesn't compute real flexbox layout, so it can't verify the
|
||||
// overlap itself is fixed; this guards the underlying CSS from
|
||||
// regressing instead. Without `overflow: hidden`, this wrapper's
|
||||
// automatic flex minimum size is based on its content rather than 0, so
|
||||
// it refuses to shrink -- forcing the title to absorb all the space
|
||||
// pressure until the cluster's content renders outside its box and
|
||||
// overlaps the actions menu once the title has fully collapsed.
|
||||
const { styles } = buttonsStyles(supersetTheme);
|
||||
expect(styles).toMatch(/overflow:\s*hidden/);
|
||||
});
|
||||
|
||||
+5
-1
@@ -99,9 +99,13 @@ const headerStyles = (theme: SupersetTheme) => css`
|
||||
}
|
||||
`;
|
||||
|
||||
const buttonsStyles = (theme: SupersetTheme) => css`
|
||||
// Exported only so PageHeaderWithActions.test.tsx can assert on the
|
||||
// `overflow: hidden` declaration directly; not part of the component's
|
||||
// public API.
|
||||
export const buttonsStyles = (theme: SupersetTheme) => css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
padding-left: ${theme.sizeUnit * 2}px;
|
||||
|
||||
& .anticon-star {
|
||||
|
||||
@@ -26,4 +26,3 @@ export type { NormalizeOptions } from './normalizeBackendUrls';
|
||||
|
||||
export * from './types';
|
||||
export * from './constants';
|
||||
export { default as __hack_reexport_connection } from './types';
|
||||
|
||||
@@ -176,5 +176,3 @@ export interface SupersetClientInterface extends Pick<
|
||||
}
|
||||
|
||||
export type SupersetClientResponse = Response | JsonResponse | TextResponse;
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -24,4 +24,3 @@ export { default as mergeMargin } from './mergeMargin';
|
||||
export { default as parseLength } from './parseLength';
|
||||
|
||||
export * from './types';
|
||||
export { default as __hack_reexport_dimension } from './types';
|
||||
|
||||
@@ -37,5 +37,3 @@ export interface Dimension {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -21,4 +21,3 @@ export { default as getFormData } from './getFormData';
|
||||
export { default as getDatasourceMetadata } from './getDatasourceMetadata';
|
||||
|
||||
export * from './types';
|
||||
export { default as __hack_reexport_query_api_legacy } from './types';
|
||||
|
||||
@@ -24,5 +24,3 @@ export interface LegacyChartDataResponse extends Omit<
|
||||
> {
|
||||
data: Record<string, unknown>[] | Record<string, unknown>;
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -39,5 +39,3 @@ export interface TimeCompare {
|
||||
time_compare?: string;
|
||||
comparison_type?: ComparisonType;
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -82,5 +82,3 @@ export function isAdhocColumnReference(column?: any): column is AdhocColumn {
|
||||
export function isQueryFormColumn(column: any): column is QueryFormColumn {
|
||||
return isPhysicalColumn(column) || isAdhocColumn(column);
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -271,5 +271,3 @@ export interface LegacyChartCustomizationItem {
|
||||
chartId?: number;
|
||||
customization: LegacyChartCustomizationConfig;
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -71,5 +71,3 @@ export const DEFAULT_METRICS: Metric[] = [
|
||||
uuid: nanoid(),
|
||||
},
|
||||
];
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -97,5 +97,3 @@ export function isQueryFormMetric(metric: any): metric is QueryFormMetric {
|
||||
isAdhocMetricSQL(metric)
|
||||
);
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -494,5 +494,3 @@ export enum ContributionType {
|
||||
export type DatasourceSamplesQuery = {
|
||||
filters?: QueryObjectFilterClause[];
|
||||
};
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -214,5 +214,3 @@ export type LatestQueryFormData = Partial<QueryFormData>;
|
||||
//---------------------------------------------------
|
||||
// Type guards
|
||||
//---------------------------------------------------
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -114,5 +114,3 @@ export interface TimeseriesChartDataResponseResult extends ChartDataResponseResu
|
||||
export interface ChartDataResponse {
|
||||
queries: ChartDataResponseResult[];
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -36,5 +36,3 @@ export type TimeColumnConfigKey =
|
||||
export type AppliedTimeExtras = Partial<
|
||||
Record<TimeColumnConfigKey, keyof QueryObject>
|
||||
>;
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -28,15 +28,3 @@ export * from './Time';
|
||||
export * from './AdvancedAnalytics';
|
||||
export * from './PostProcessing';
|
||||
export * from './Dashboard';
|
||||
|
||||
export { default as __hack_reexport_Datasource } from './Datasource';
|
||||
export { default as __hack_reexport_Column } from './Column';
|
||||
export { default as __hack_reexport_Metric } from './Metric';
|
||||
export { default as __hack_reexport_Query } from './Query';
|
||||
export { default as __hack_reexport_QueryResponse } from './QueryResponse';
|
||||
export { default as __hack_reexport_QueryFormData } from './QueryFormData';
|
||||
export { default as __hack_reexport_Time } from './Time';
|
||||
export { default as __hack_reexport_AdvancedAnalytics } from './AdvancedAnalytics';
|
||||
export { default as __hack_reexport_Dashboard } from './Dashboard';
|
||||
|
||||
export default {};
|
||||
|
||||
+8
-5
@@ -16,15 +16,18 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
DataRecordValue,
|
||||
normalizeTimestamp,
|
||||
TimeFormatFunction,
|
||||
} from '@superset-ui/core';
|
||||
import type { DataRecordValue } from '../query/types/QueryResponse';
|
||||
import type { TimeFormatFunction } from './types';
|
||||
import normalizeTimestamp from './utils/normalizeTimestamp';
|
||||
|
||||
/**
|
||||
* Extended Date object with a custom formatter, and retains the original input
|
||||
* when the formatter is simple `String(..)`.
|
||||
*
|
||||
* `toString()` never formats an Invalid Date: it returns the original input
|
||||
* instead. `stringifyTimeInput` relies on that when it falls back to
|
||||
* `${value}` for an unparseable input, otherwise the two would call each other
|
||||
* forever.
|
||||
*/
|
||||
export default class DateWithFormatter extends Date {
|
||||
formatter: TimeFormatFunction;
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
export { default as TimeFormats, LOCAL_PREFIX } from './TimeFormats';
|
||||
export { default as TimeFormatter, PREVIEW_TIME } from './TimeFormatter';
|
||||
export { default as DateWithFormatter } from './DateWithFormatter';
|
||||
export { DEFAULT_D3_TIME_FORMAT } from './D3FormatConfig';
|
||||
|
||||
export {
|
||||
|
||||
+13
-2
@@ -28,8 +28,17 @@ export default function stringifyTimeInput(
|
||||
let time: Date;
|
||||
if (typeof value === 'string') {
|
||||
const trimmed = value.trim();
|
||||
// A bare four-digit string is the ISO 8601 year-only form ("2017"), which
|
||||
// every engine parses as January 1st of that year. Any other integer
|
||||
// string is an epoch timestamp in milliseconds that was stringified on
|
||||
// its way here, e.g. by the pivot table, and is not a valid Date input.
|
||||
const isYear = /^\d{4}$/.test(trimmed);
|
||||
const isIntegerString = /^-?\d+$/.test(trimmed);
|
||||
time = new Date(isIntegerString ? Number(trimmed) : value);
|
||||
if (isYear) {
|
||||
time = new Date(trimmed);
|
||||
} else {
|
||||
time = new Date(isIntegerString ? Number(trimmed) : value);
|
||||
}
|
||||
} else {
|
||||
time = value instanceof Date ? value : new Date(value);
|
||||
}
|
||||
@@ -37,7 +46,9 @@ export default function stringifyTimeInput(
|
||||
// An input that does not resolve to a valid date - a duration such as
|
||||
// "00:01:54", for instance - would otherwise be formatted from an Invalid
|
||||
// Date and render as "NaN:NaN:NaN". Fall back to its own representation,
|
||||
// as is already done for null and undefined above.
|
||||
// as is already done for null and undefined above. For a `DateWithFormatter`
|
||||
// this calls its `toString()`, which returns the original input rather than
|
||||
// re-entering the formatter; that guard is what keeps the fallback finite.
|
||||
if (Number.isNaN(time.getTime())) {
|
||||
return `${value}`;
|
||||
}
|
||||
|
||||
+2
-21
@@ -1,4 +1,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
|
||||
@@ -16,11 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { getTimeFormatter } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import DateWithFormatter from '../../src/utils/DateWithFormatter';
|
||||
import { formatColumnValue } from '../../src/utils/formatValue';
|
||||
import { DataColumnMeta } from '../../src/types';
|
||||
import { DateWithFormatter, getTimeFormatter } from '@superset-ui/core';
|
||||
|
||||
const formatter = getTimeFormatter('%H:%M:%S');
|
||||
|
||||
@@ -43,18 +39,3 @@ test('retains the original input when the formatter is String', () => {
|
||||
const value = new DateWithFormatter('00:01:54');
|
||||
expect(String(value)).toBe('00:01:54');
|
||||
});
|
||||
|
||||
test('renders a duration cell through the column formatter without producing NaN', () => {
|
||||
// The cell text is produced by formatColumnValue, which hands the wrapped
|
||||
// value straight to the formatter rather than going through toString().
|
||||
const column: DataColumnMeta = {
|
||||
key: 'call_period',
|
||||
label: 'call_period',
|
||||
dataType: GenericDataType.Temporal,
|
||||
formatter,
|
||||
isNumeric: false,
|
||||
};
|
||||
const value = new DateWithFormatter('00:01:54', { formatter });
|
||||
|
||||
expect(formatColumnValue(column, value)).toEqual([false, '00:01:54']);
|
||||
});
|
||||
+23
@@ -16,6 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { DateWithFormatter, getTimeFormatter } from '@superset-ui/core';
|
||||
import stringifyTimeInput from '../../../src/time-format/utils/stringifyTimeInput';
|
||||
|
||||
const format = (time: Date) => time.toISOString();
|
||||
@@ -56,3 +57,25 @@ test('returns unparseable strings unchanged instead of formatting an Invalid Dat
|
||||
test('returns the representation of a Date that could not be resolved', () => {
|
||||
expect(stringifyTimeInput(new Date('00:01:54'), format)).toBe('Invalid Date');
|
||||
});
|
||||
|
||||
test('treats a four-digit integer string as a year, not as milliseconds', () => {
|
||||
// "2017" is the ISO 8601 year-only form. Reading it as an epoch offset
|
||||
// would silently turn it into two seconds past 1970.
|
||||
expect(stringifyTimeInput('2017', format)).toBe('2017-01-01T00:00:00.000Z');
|
||||
expect(stringifyTimeInput(' 1987 ', format)).toBe('1987-01-01T00:00:00.000Z');
|
||||
// Longer digit strings stay epoch milliseconds.
|
||||
expect(stringifyTimeInput('1704067200000', format)).toBe(
|
||||
'2024-01-01T00:00:00.000Z',
|
||||
);
|
||||
});
|
||||
|
||||
test('returns the original input of an unparseable DateWithFormatter without re-entering the formatter', () => {
|
||||
// The `${value}` fallback calls `DateWithFormatter.toString()`, which must
|
||||
// return the input rather than call the formatter again, or the two would
|
||||
// recurse until the stack overflows.
|
||||
const formatter = getTimeFormatter('%H:%M:%S');
|
||||
const value = new DateWithFormatter('00:01:54', { formatter });
|
||||
|
||||
expect(stringifyTimeInput(value, time => formatter(time))).toBe('00:01:54');
|
||||
expect(formatter(value)).toBe('00:01:54');
|
||||
});
|
||||
|
||||
@@ -31,8 +31,6 @@ import controlPanel from './controlPanel';
|
||||
import buildQuery from './buildQuery';
|
||||
import { TableChartFormData, TableChartProps } from './types';
|
||||
|
||||
// must export something for the module to be exist in dev mode
|
||||
export { default as __hack__ } from './types';
|
||||
export * from './types';
|
||||
export {
|
||||
convertAgGridStateToOwnState,
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
TimeFormatter,
|
||||
AgGridChartState,
|
||||
AgGridFilterModel,
|
||||
DateWithFormatter,
|
||||
} from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import { isEmpty, isEqual, merge } from 'lodash-es';
|
||||
@@ -45,7 +46,6 @@ import {
|
||||
ColorSchemeEnum,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import isEqualColumns from './utils/isEqualColumns';
|
||||
import DateWithFormatter from './utils/DateWithFormatter';
|
||||
import { BASIC_COLOR_FORMATTERS_ROW_KEY } from './consts';
|
||||
import {
|
||||
DataColumnMeta,
|
||||
|
||||
@@ -234,5 +234,3 @@ export type Dataset = {
|
||||
metrics?: Metric[];
|
||||
verbose_map?: Record<string, string>;
|
||||
};
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
isDefined,
|
||||
isProbablyHTML,
|
||||
sanitizeHtml,
|
||||
DateWithFormatter,
|
||||
} from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import {
|
||||
@@ -29,7 +30,6 @@ import {
|
||||
ValueGetterParams,
|
||||
} from '@superset-ui/core/components/ThemedAgGridReact';
|
||||
import { DataColumnMeta, InputColumn } from '../types';
|
||||
import DateWithFormatter from './DateWithFormatter';
|
||||
|
||||
/**
|
||||
* Format text for cell value.
|
||||
|
||||
@@ -25,7 +25,11 @@ import {
|
||||
CellClassParams,
|
||||
} from '@superset-ui/core/components/ThemedAgGridReact';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { DataRecordValue, JsonObject } from '@superset-ui/core';
|
||||
import {
|
||||
DataRecordValue,
|
||||
DateWithFormatter,
|
||||
JsonObject,
|
||||
} from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import { useTheme } from '@apache-superset/core/theme';
|
||||
import { ColorFormatters } from '@superset-ui/chart-controls';
|
||||
@@ -42,7 +46,6 @@ import htmlTextFilterValueGetter, {
|
||||
htmlTextComparator,
|
||||
} from './htmlTextFilterValueGetter';
|
||||
import dateFilterComparator from './dateFilterComparator';
|
||||
import DateWithFormatter from './DateWithFormatter';
|
||||
import { getAggFunc } from './getAggFunc';
|
||||
import { TextCellRenderer } from '../renderers/TextCellRenderer';
|
||||
import { NumericCellRenderer } from '../renderers/NumericCellRenderer';
|
||||
|
||||
+1
-22
@@ -16,34 +16,13 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { getTimeFormatter } from '@superset-ui/core';
|
||||
import { DateWithFormatter, getTimeFormatter } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import DateWithFormatter from '../../src/utils/DateWithFormatter';
|
||||
import { formatColumnValue } from '../../src/utils/formatValue';
|
||||
import { DataColumnMeta } from '../../src/types';
|
||||
|
||||
const formatter = getTimeFormatter('%H:%M:%S');
|
||||
|
||||
test('formats a parseable timestamp with the configured formatter', () => {
|
||||
const value = new DateWithFormatter('2017-02-14T11:22:33Z', { formatter });
|
||||
expect(String(value)).toBe('11:22:33');
|
||||
});
|
||||
|
||||
test('renders the original value when it is not a parseable timestamp', () => {
|
||||
// Duration columns hold values like these. They produce an Invalid Date,
|
||||
// which used to be formatted and rendered as "NaN:NaN:NaN".
|
||||
['00:01:54', '0 days 00:01:54'].forEach(input => {
|
||||
const value = new DateWithFormatter(input, { formatter });
|
||||
expect(Number.isNaN(value.getTime())).toBe(true);
|
||||
expect(String(value)).toBe(input);
|
||||
});
|
||||
});
|
||||
|
||||
test('retains the original input when the formatter is String', () => {
|
||||
const value = new DateWithFormatter('00:01:54');
|
||||
expect(String(value)).toBe('00:01:54');
|
||||
});
|
||||
|
||||
test('renders a duration cell through the column formatter without producing NaN', () => {
|
||||
// The cell text is produced by formatColumnValue, which hands the wrapped
|
||||
// value straight to the formatter rather than going through toString().
|
||||
@@ -33,7 +33,7 @@
|
||||
"d3-array": "^3.2.4",
|
||||
"lodash": "^4.18.1",
|
||||
"lodash-es": "^4.18.1",
|
||||
"zod": "^4.4.3"
|
||||
"zod": "^4.5.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@apache-superset/core": "*",
|
||||
|
||||
+50
-2
@@ -44,9 +44,14 @@ import {
|
||||
ValueFormatter,
|
||||
} from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import { getOriginalSeries } from '@superset-ui/chart-controls';
|
||||
import {
|
||||
getOriginalSeries,
|
||||
getTimeOffset,
|
||||
isDerivedSeries,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import type { EChartsCoreOption } from 'echarts/core';
|
||||
import type { SeriesOption } from 'echarts';
|
||||
import type { LineStyleOption } from 'echarts/types/src/util/types';
|
||||
import {
|
||||
DEFAULT_FORM_DATA,
|
||||
EchartsMixedTimeseriesChartTransformedProps,
|
||||
@@ -100,7 +105,11 @@ import {
|
||||
transformSeries,
|
||||
transformTimeseriesAnnotation,
|
||||
} from '../Timeseries/transformers';
|
||||
import { TIMEGRAIN_TO_TIMESTAMP, TIMESERIES_CONSTANTS } from '../constants';
|
||||
import {
|
||||
TIMEGRAIN_TO_TIMESTAMP,
|
||||
TIMESERIES_CONSTANTS,
|
||||
OpacityEnum,
|
||||
} from '../constants';
|
||||
import { getDefaultTooltip } from '../utils/tooltip';
|
||||
import {
|
||||
createSpacedXAxisFormatter,
|
||||
@@ -453,6 +462,10 @@ export default function transformProps(
|
||||
|
||||
const array = ensureIsArray(chartProps.rawFormData?.time_compare);
|
||||
const inverted = invert(verboseMap);
|
||||
// Tracks a stable pattern index per time offset so that derived series
|
||||
// sharing the same comparison window (across both queries A and B) get
|
||||
// the same dash pattern, mirroring the regular Timeseries transform.
|
||||
const offsetPatterns: { [key: string]: number } = {};
|
||||
|
||||
// The rendered ECharts series names are display names that can diverge from
|
||||
// the backend `label_map` keys: the metric display name is prepended when
|
||||
@@ -467,6 +480,22 @@ export default function transformProps(
|
||||
rawSeriesA.forEach(entry => {
|
||||
const entryName = String(entry.name || '');
|
||||
const seriesName = inverted[entryName] || entryName;
|
||||
const derivedSeries = isDerivedSeries(
|
||||
entry,
|
||||
chartProps.rawFormData,
|
||||
seriesName,
|
||||
);
|
||||
const lineStyle: LineStyleOption = {};
|
||||
if (derivedSeries && timeShiftColor) {
|
||||
const offset = getTimeOffset(entry, array) || seriesName;
|
||||
if (!offsetPatterns[offset]) {
|
||||
offsetPatterns[offset] = Object.keys(offsetPatterns).length + 1;
|
||||
}
|
||||
const patternIndex = offsetPatterns[offset];
|
||||
// use a combination of dash and dot for the line style
|
||||
lineStyle.type = [(patternIndex % 5) + 1, (patternIndex % 3) + 1];
|
||||
lineStyle.opacity = OpacityEnum.DerivedSeries;
|
||||
}
|
||||
const colorScaleKey = getOriginalSeries(seriesName, array);
|
||||
|
||||
const labelMapValues = rawLabelMap?.[seriesName];
|
||||
@@ -544,6 +573,7 @@ export default function transformProps(
|
||||
timeShiftColor,
|
||||
theme,
|
||||
labelPosition,
|
||||
lineStyle,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -556,6 +586,23 @@ export default function transformProps(
|
||||
rawSeriesB.forEach(entry => {
|
||||
const entryName = String(entry.name || '');
|
||||
const seriesEntry = inverted[entryName] || entryName;
|
||||
const derivedSeries = isDerivedSeries(
|
||||
entry,
|
||||
chartProps.rawFormData,
|
||||
seriesEntry,
|
||||
);
|
||||
const lineStyle: LineStyleOption = {};
|
||||
if (derivedSeries && timeShiftColor) {
|
||||
const offset = getTimeOffset(entry, array) || seriesEntry;
|
||||
if (!offsetPatterns[offset]) {
|
||||
offsetPatterns[offset] = Object.keys(offsetPatterns).length + 1;
|
||||
}
|
||||
const patternIndex = offsetPatterns[offset];
|
||||
// use a combination of dash and dot for the line style
|
||||
lineStyle.type = [(patternIndex % 5) + 1, (patternIndex % 3) + 1];
|
||||
lineStyle.opacity = OpacityEnum.DerivedSeries;
|
||||
}
|
||||
|
||||
const colorScaleKey = getOriginalSeries(seriesEntry, array);
|
||||
|
||||
const labelMapValuesB = rawLabelMapB?.[seriesEntry];
|
||||
@@ -634,6 +681,7 @@ export default function transformProps(
|
||||
timeShiftColor,
|
||||
theme,
|
||||
labelPosition: labelPositionB,
|
||||
lineStyle,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
+161
@@ -21,6 +21,7 @@ import {
|
||||
AnnotationType,
|
||||
AnnotationSourceType,
|
||||
AxisType,
|
||||
ComparisonType,
|
||||
DataRecord,
|
||||
FormulaAnnotationLayer,
|
||||
IntervalAnnotationLayer,
|
||||
@@ -1562,6 +1563,7 @@ test('y-axis title position: non-Left sets nameLocation to end', () => {
|
||||
expect(yAxis[1].nameGap).toEqual(30);
|
||||
expect(yAxis[1].nameLocation).toEqual('end');
|
||||
});
|
||||
|
||||
describe('EchartsMixedTimeseries tooltip truncation', () => {
|
||||
const longSeriesName = 'prod-us-east-1-service-checkout-latency-p99';
|
||||
const marker = '<span style="background-color:#1f77b4;"></span>';
|
||||
@@ -1763,3 +1765,162 @@ test('hides the ticks on the x axis and both y axes', () => {
|
||||
expect(yAxis[0].axisTick.show).toBe(false);
|
||||
expect(yAxis[1].axisTick.show).toBe(false);
|
||||
});
|
||||
|
||||
test('should apply a dashed lineStyle to derived (time comparison) series only', () => {
|
||||
const queryAData = createTestQueryData(
|
||||
[
|
||||
{
|
||||
sum__num: 100,
|
||||
'sum__num__1 week ago': 80,
|
||||
ds: 599616000000,
|
||||
},
|
||||
{
|
||||
sum__num: 150,
|
||||
'sum__num__1 week ago': 120,
|
||||
ds: 599916000000,
|
||||
},
|
||||
],
|
||||
{
|
||||
label_map: {
|
||||
ds: ['ds'],
|
||||
sum__num: ['sum__num'],
|
||||
'sum__num__1 week ago': ['sum__num__1 week ago'],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const chartProps = createEchartsTimeseriesTestChartProps<
|
||||
EchartsMixedTimeseriesFormData,
|
||||
EchartsMixedTimeseriesProps
|
||||
>({
|
||||
...MIXED_TIMESERIES_CHART_PROPS_DEFAULTS,
|
||||
defaultQueriesData: [queryAData, queriesData[1]],
|
||||
formData: {
|
||||
...formData,
|
||||
metrics: ['sum__num'],
|
||||
groupby: [],
|
||||
time_compare: ['1 week ago'],
|
||||
comparison_type: ComparisonType.Values,
|
||||
timeShiftColor: true,
|
||||
},
|
||||
queriesData: [queryAData, queriesData[1]],
|
||||
});
|
||||
|
||||
const transformed = transformProps(chartProps);
|
||||
const series = (transformed.echartOptions.series as SeriesOption[]) || [];
|
||||
|
||||
const mainSeries = series.find(s => s.name === 'sum__num') as
|
||||
| (SeriesOption & { lineStyle?: { type?: number[] | string } })
|
||||
| undefined;
|
||||
const derivedSeries = series.find(s => s.name === 'sum__num__1 week ago') as
|
||||
| (SeriesOption & { lineStyle?: { type?: number[] | string } })
|
||||
| undefined;
|
||||
|
||||
expect(mainSeries).toBeDefined();
|
||||
expect(derivedSeries).toBeDefined();
|
||||
// The primary (non-derived) series should not receive a dash pattern
|
||||
expect(mainSeries?.lineStyle?.type).toBeUndefined();
|
||||
// The derived (time comparison) series should receive a dash pattern array
|
||||
expect(Array.isArray(derivedSeries?.lineStyle?.type)).toBe(true);
|
||||
});
|
||||
|
||||
test('should not apply a dashed lineStyle when comparison_type is not Values', () => {
|
||||
const queryAData = createTestQueryData(
|
||||
[
|
||||
{
|
||||
sum__num: 100,
|
||||
'sum__num__1 week ago': 80,
|
||||
ds: 599616000000,
|
||||
},
|
||||
{
|
||||
sum__num: 150,
|
||||
'sum__num__1 week ago': 120,
|
||||
ds: 599916000000,
|
||||
},
|
||||
],
|
||||
{
|
||||
label_map: {
|
||||
ds: ['ds'],
|
||||
sum__num: ['sum__num'],
|
||||
'sum__num__1 week ago': ['sum__num__1 week ago'],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const chartProps = createEchartsTimeseriesTestChartProps<
|
||||
EchartsMixedTimeseriesFormData,
|
||||
EchartsMixedTimeseriesProps
|
||||
>({
|
||||
...MIXED_TIMESERIES_CHART_PROPS_DEFAULTS,
|
||||
defaultQueriesData: [queryAData, queriesData[1]],
|
||||
formData: {
|
||||
...formData,
|
||||
metrics: ['sum__num'],
|
||||
groupby: [],
|
||||
time_compare: ['1 week ago'],
|
||||
comparison_type: ComparisonType.Difference,
|
||||
},
|
||||
queriesData: [queryAData, queriesData[1]],
|
||||
});
|
||||
|
||||
const transformed = transformProps(chartProps);
|
||||
const series = (transformed.echartOptions.series as SeriesOption[]) || [];
|
||||
|
||||
const derivedSeries = series.find(s => s.name === 'sum__num__1 week ago') as
|
||||
| (SeriesOption & { lineStyle?: { type?: number[] | string } })
|
||||
| undefined;
|
||||
|
||||
expect(derivedSeries).toBeDefined();
|
||||
expect(derivedSeries?.lineStyle?.type).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should not apply a dashed lineStyle when timeShiftColor is disabled', () => {
|
||||
const queryAData = createTestQueryData(
|
||||
[
|
||||
{
|
||||
sum__num: 100,
|
||||
'sum__num__1 week ago': 80,
|
||||
ds: 599616000000,
|
||||
},
|
||||
{
|
||||
sum__num: 150,
|
||||
'sum__num__1 week ago': 120,
|
||||
ds: 599916000000,
|
||||
},
|
||||
],
|
||||
{
|
||||
label_map: {
|
||||
ds: ['ds'],
|
||||
sum__num: ['sum__num'],
|
||||
'sum__num__1 week ago': ['sum__num__1 week ago'],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const chartProps = createEchartsTimeseriesTestChartProps<
|
||||
EchartsMixedTimeseriesFormData,
|
||||
EchartsMixedTimeseriesProps
|
||||
>({
|
||||
...MIXED_TIMESERIES_CHART_PROPS_DEFAULTS,
|
||||
defaultQueriesData: [queryAData, queriesData[1]],
|
||||
formData: {
|
||||
...formData,
|
||||
metrics: ['sum__num'],
|
||||
groupby: [],
|
||||
time_compare: ['1 week ago'],
|
||||
comparison_type: ComparisonType.Values,
|
||||
timeShiftColor: false,
|
||||
},
|
||||
queriesData: [queryAData, queriesData[1]],
|
||||
});
|
||||
|
||||
const transformed = transformProps(chartProps);
|
||||
const series = (transformed.echartOptions.series as SeriesOption[]) || [];
|
||||
|
||||
const derivedSeries = series.find(s => s.name === 'sum__num__1 week ago') as
|
||||
| (SeriesOption & { lineStyle?: { type?: number[] | string } })
|
||||
| undefined;
|
||||
|
||||
expect(derivedSeries).toBeDefined();
|
||||
expect(derivedSeries?.lineStyle?.type).toBeUndefined();
|
||||
});
|
||||
|
||||
+19
-15
@@ -44,6 +44,23 @@ import {
|
||||
import { PivotData, flatKey } from './utilities';
|
||||
import { Styles } from './Styles';
|
||||
|
||||
/**
|
||||
* Pivot keys are stringified on their way through `PivotData`, so a temporal
|
||||
* header holding an epoch timestamp arrives as e.g. "1700000000000". Coerce
|
||||
* such numeric strings back to numbers so temporal formatters (which expect
|
||||
* an epoch) render correctly. A bare four-digit string is the ISO 8601
|
||||
* year-only form ("2017"), which the shared `stringifyTimeInput` in core
|
||||
* reads as that calendar year; coercing it would turn the year into two
|
||||
* seconds past 1970, so it is passed through untouched.
|
||||
*/
|
||||
const toDateFormatterInput = (value: unknown): unknown =>
|
||||
typeof value === 'string' &&
|
||||
value.trim() !== '' &&
|
||||
!/^\d{4}$/.test(value.trim()) &&
|
||||
Number.isFinite(Number(value))
|
||||
? Number(value)
|
||||
: value;
|
||||
|
||||
type ClickCallback = (
|
||||
e: MouseEvent,
|
||||
value: unknown,
|
||||
@@ -989,15 +1006,9 @@ export function TableRenderer(props: TableRendererProps) {
|
||||
/>
|
||||
);
|
||||
};
|
||||
// Coerce numeric timestamp strings to numbers so temporal formatters
|
||||
// (which typically expect an epoch) render correctly.
|
||||
const rawHeaderCellValue = colKey[attrIdx];
|
||||
const headerCellFormatterValue =
|
||||
typeof rawHeaderCellValue === 'string' &&
|
||||
rawHeaderCellValue.trim() !== '' &&
|
||||
Number.isFinite(Number(rawHeaderCellValue))
|
||||
? Number(rawHeaderCellValue)
|
||||
: rawHeaderCellValue;
|
||||
toDateFormatterInput(rawHeaderCellValue);
|
||||
const headerCellFormattedValue =
|
||||
dateFormatters?.[attrName]?.(headerCellFormatterValue) ??
|
||||
rawHeaderCellValue;
|
||||
@@ -1263,14 +1274,7 @@ export function TableRenderer(props: TableRendererProps) {
|
||||
? toggleRowKey(flatRowKeySlice)
|
||||
: null;
|
||||
|
||||
// Coerce numeric timestamp strings to numbers so temporal formatters
|
||||
// (which typically expect an epoch) render correctly.
|
||||
const headerFormatterValue =
|
||||
typeof r === 'string' &&
|
||||
r.trim() !== '' &&
|
||||
Number.isFinite(Number(r))
|
||||
? Number(r)
|
||||
: r;
|
||||
const headerFormatterValue = toDateFormatterInput(r);
|
||||
const headerCellFormattedValue =
|
||||
dateFormatters?.[settingsRowAttrs[i]]?.(headerFormatterValue) ?? r;
|
||||
const isActiveHeader = valueCellClassName.includes('active');
|
||||
|
||||
+39
@@ -21,6 +21,7 @@ import type { ReactElement } from 'react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { supersetTheme, ThemeProvider } from '@apache-superset/core/theme';
|
||||
import { getTimeFormatter } from '@superset-ui/core';
|
||||
import { TableRenderer } from '../../src/react-pivottable/TableRenderers';
|
||||
import {
|
||||
aggregatorTemplates,
|
||||
@@ -689,6 +690,44 @@ test('TableRenderer coerces numeric timestamp strings to numbers for row header
|
||||
expect(screen.getByText('row:red')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('TableRenderer passes four-digit year strings through to column header date formatters uncoerced', () => {
|
||||
const data = [
|
||||
{ shape: '2017', color: 'blue', value: 1 },
|
||||
{ shape: '1700000000000', color: 'blue', value: 2 },
|
||||
];
|
||||
const props = buildDefaultProps({
|
||||
data,
|
||||
rows: ['color'],
|
||||
cols: ['shape'],
|
||||
tableOptions: { dateFormatters: { shape: getTimeFormatter('%Y') } },
|
||||
});
|
||||
renderWithTheme(<TableRenderer {...props} />);
|
||||
|
||||
// "2017" is the ISO year-only form and must render as that year, not as
|
||||
// 2.017 seconds past the epoch; a stringified epoch still coerces.
|
||||
expect(screen.getByText('2017')).toBeInTheDocument();
|
||||
expect(screen.getByText('2023')).toBeInTheDocument();
|
||||
expect(screen.queryByText('1970')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('TableRenderer passes four-digit year strings through to row header date formatters uncoerced', () => {
|
||||
const data = [
|
||||
{ color: '2017', shape: 'circle', value: 1 },
|
||||
{ color: '1700000000000', shape: 'circle', value: 2 },
|
||||
];
|
||||
const props = buildDefaultProps({
|
||||
data,
|
||||
rows: ['color'],
|
||||
cols: ['shape'],
|
||||
tableOptions: { dateFormatters: { color: getTimeFormatter('%Y') } },
|
||||
});
|
||||
renderWithTheme(<TableRenderer {...props} />);
|
||||
|
||||
expect(screen.getByText('2017')).toBeInTheDocument();
|
||||
expect(screen.getByText('2023')).toBeInTheDocument();
|
||||
expect(screen.queryByText('1970')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('TableRenderer applies cellColorFormatters background and contrast color to column headers', () => {
|
||||
const cellColorFormatters = {
|
||||
shape: [
|
||||
|
||||
@@ -30,8 +30,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@math.gl/web-mercator": "^4.1.0",
|
||||
"@types/geojson": "^7946.0.16",
|
||||
"@types/supercluster": "^7.1.3",
|
||||
"mapbox-gl": "^3.29.0",
|
||||
"maplibre-gl": "^5.24.0",
|
||||
"maplibre-gl": "^6.8.0",
|
||||
"react-map-gl": "^8.1.2",
|
||||
"supercluster": "^9.0.0"
|
||||
},
|
||||
|
||||
@@ -103,6 +103,42 @@ const sortTypes = {
|
||||
alphanumeric: sortAlphanumericCaseInsensitive,
|
||||
};
|
||||
|
||||
// Prefer a stable identifier from original row data; otherwise use a deterministic
|
||||
// concatenation of visible values (keys sorted so the result does not depend on
|
||||
// column order).
|
||||
function stableRowKey<D extends object>(r: Row<D>): string {
|
||||
const orig = r.original as Record<string, unknown> | undefined;
|
||||
if (orig) {
|
||||
const idLike = orig.id ?? orig.ID ?? orig.key ?? orig.uuid;
|
||||
if (idLike != null) return String(idLike);
|
||||
}
|
||||
|
||||
// Fallback: derive from row.values, sorting the keys so that reordering the
|
||||
// columns does not change the key.
|
||||
const v = r.values as Record<string, unknown>;
|
||||
const keys = Object.keys(v).sort();
|
||||
return keys.map(k => String(v[k] ?? '')).join('|');
|
||||
}
|
||||
|
||||
// Very small, fast hash for strings (no crypto dependency).
|
||||
function hashString(s: string): string {
|
||||
let h = 0;
|
||||
for (let i = 0; i < s.length; i += 1) {
|
||||
// oxlint-disable-next-line unicorn/prefer-math-trunc -- | 0 is intentional for 32-bit integer wrapping in hash
|
||||
h = (h * 31 + s.charCodeAt(i)) | 0;
|
||||
}
|
||||
return String(h);
|
||||
}
|
||||
|
||||
function signatureOfRows<D extends object>(rs: Row<D>[]): string {
|
||||
const keys = rs.map(stableRowKey);
|
||||
const len = keys.length;
|
||||
const first = keys[0] ?? '';
|
||||
const last = keys[len - 1] ?? '';
|
||||
const digest = hashString(keys.join('\u0001')); // non-printable separator to avoid collisions
|
||||
return `${len}|${first}|${last}|${digest}`;
|
||||
}
|
||||
|
||||
// Be sure to pass our updateMyData and the skipReset option
|
||||
export default typedMemo(function DataTable<D extends object>({
|
||||
tableClassName,
|
||||
@@ -289,6 +325,46 @@ export default typedMemo(function DataTable<D extends object>({
|
||||
onFilteredDataChange(rowsRef.current, searchText);
|
||||
}, [filterValue, onFilteredDataChange, rowSignature]);
|
||||
|
||||
// Emit filtered rows to parent in client-side mode (debounced via RAF)
|
||||
const isMountedRef = useRef(true);
|
||||
useEffect(() => {
|
||||
isMountedRef.current = true;
|
||||
return () => {
|
||||
isMountedRef.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const rafRef = useRef<number | null>(null);
|
||||
const lastSigRef = useRef<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
if (serverPagination || typeof onFilteredRowsChange !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
const sig = signatureOfRows(rows);
|
||||
|
||||
if (sig !== lastSigRef.current) {
|
||||
lastSigRef.current = sig;
|
||||
if (rafRef.current != null) {
|
||||
cancelAnimationFrame(rafRef.current);
|
||||
}
|
||||
rafRef.current = requestAnimationFrame(() => {
|
||||
if (isMountedRef.current) {
|
||||
// Only emit originals when the signature truly changed
|
||||
onFilteredRowsChange(rows.map(r => r.original as D));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (rafRef.current != null) {
|
||||
cancelAnimationFrame(rafRef.current);
|
||||
rafRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [rows, serverPagination, onFilteredRowsChange]);
|
||||
|
||||
const handleSearchChange = useCallback(
|
||||
(query: string) => {
|
||||
if (manualSearch && onSearchChange) {
|
||||
@@ -472,84 +548,6 @@ export default typedMemo(function DataTable<D extends object>({
|
||||
onServerPaginationChange(pageNumber, serverPageSize);
|
||||
}
|
||||
|
||||
// Emit filtered rows to parent in client-side mode (debounced via RAF)
|
||||
const isMountedRef = useRef(true);
|
||||
useEffect(() => {
|
||||
isMountedRef.current = true;
|
||||
return () => {
|
||||
isMountedRef.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const rafRef = useRef<number | null>(null);
|
||||
const lastSigRef = useRef<string>('');
|
||||
|
||||
// Prefer a stable identifier from original row data; otherwise use a deterministic
|
||||
// concatenation of visible values (keys sorted so column order changes are detected).
|
||||
function stableRowKey<D extends object>(r: Row<D>): string {
|
||||
const orig = r.original as Record<string, unknown> | undefined;
|
||||
if (orig) {
|
||||
const idLike =
|
||||
(orig as any).id ??
|
||||
(orig as any).ID ??
|
||||
(orig as any).key ??
|
||||
(orig as any).uuid;
|
||||
if (idLike != null) return String(idLike);
|
||||
}
|
||||
|
||||
// Fallback: derive from row.values, but make it stable against column order changes.
|
||||
const v = r.values as Record<string, unknown>;
|
||||
const keys = Object.keys(v).sort(); // detect column order changes
|
||||
return keys.map(k => String(v[k] ?? '')).join('|');
|
||||
}
|
||||
|
||||
// Very small, fast hash for strings (no crypto dependency).
|
||||
function hashString(s: string): string {
|
||||
let h = 0;
|
||||
for (let i = 0; i < s.length; i += 1) {
|
||||
// oxlint-disable-next-line unicorn/prefer-math-trunc -- | 0 is intentional for 32-bit integer wrapping in hash
|
||||
h = (h * 31 + s.charCodeAt(i)) | 0;
|
||||
}
|
||||
return String(h);
|
||||
}
|
||||
|
||||
function signatureOfRows<D extends object>(rs: Row<D>[]): string {
|
||||
const keys = rs.map(stableRowKey);
|
||||
const len = keys.length;
|
||||
const first = keys[0] ?? '';
|
||||
const last = keys[len - 1] ?? '';
|
||||
const digest = hashString(keys.join('\u0001')); // non-printable separator to avoid collisions
|
||||
return `${len}|${first}|${last}|${digest}`;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (serverPagination || typeof onFilteredRowsChange !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
const sig = signatureOfRows(rows);
|
||||
|
||||
if (sig !== lastSigRef.current) {
|
||||
lastSigRef.current = sig;
|
||||
if (rafRef.current != null) {
|
||||
cancelAnimationFrame(rafRef.current);
|
||||
}
|
||||
rafRef.current = requestAnimationFrame(() => {
|
||||
if (isMountedRef.current) {
|
||||
// Only emit originals when the signature truly changed
|
||||
onFilteredRowsChange(rows.map(r => r.original as D));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (rafRef.current != null) {
|
||||
cancelAnimationFrame(rafRef.current);
|
||||
rafRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [rows, serverPagination, onFilteredRowsChange]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={wrapperRef}
|
||||
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
extractTextFromHTML,
|
||||
TimeGranularity,
|
||||
forceHexAlpha,
|
||||
DateWithFormatter,
|
||||
} from '@superset-ui/core';
|
||||
import {
|
||||
styled,
|
||||
@@ -98,7 +99,6 @@ import { formatColumnValue } from './utils/formatValue';
|
||||
import { PAGE_SIZE_OPTIONS, SERVER_PAGE_SIZE_OPTIONS } from './consts';
|
||||
import { updateTableOwnState } from './DataTable/utils/externalAPIs';
|
||||
import getScrollBarSize from './DataTable/utils/getScrollBarSize';
|
||||
import DateWithFormatter from './utils/DateWithFormatter';
|
||||
|
||||
type ValueRange = [number, number];
|
||||
|
||||
|
||||
@@ -31,8 +31,6 @@ import controlPanel from './controlPanel';
|
||||
import cachedBuildQueryInstance from './buildQuery';
|
||||
import { TableChartFormData, TableChartProps } from './types';
|
||||
|
||||
// must export something for the module to be exist in dev mode
|
||||
export { default as __hack__ } from './types';
|
||||
export * from './types';
|
||||
|
||||
const metadata = new ChartMetadata({
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
SMART_DATE_ID,
|
||||
TimeFormats,
|
||||
TimeFormatter,
|
||||
DateWithFormatter,
|
||||
} from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import {
|
||||
@@ -47,7 +48,6 @@ import {
|
||||
|
||||
import { isEmpty, merge } from 'lodash-es';
|
||||
import isEqualColumns from './utils/isEqualColumns';
|
||||
import DateWithFormatter from './utils/DateWithFormatter';
|
||||
import {
|
||||
BasicColorFormatterType,
|
||||
DataColumnMeta,
|
||||
|
||||
@@ -129,5 +129,3 @@ export interface TableChartTransformedProps<D extends DataRecord = DataRecord> {
|
||||
// emit the correct column name in cross-filter data masks
|
||||
columnLabelToNameMap?: Record<string, string>;
|
||||
}
|
||||
|
||||
export default {};
|
||||
|
||||
@@ -1,62 +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 {
|
||||
DataRecordValue,
|
||||
normalizeTimestamp,
|
||||
TimeFormatFunction,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
/**
|
||||
* Extended Date object with a custom formatter, and retains the original input
|
||||
* when the formatter is simple `String(..)`.
|
||||
*/
|
||||
export default class DateWithFormatter extends Date {
|
||||
formatter: TimeFormatFunction;
|
||||
|
||||
input: DataRecordValue;
|
||||
|
||||
constructor(
|
||||
input: DataRecordValue,
|
||||
{ formatter = String }: { formatter?: TimeFormatFunction } = {},
|
||||
) {
|
||||
let value = input;
|
||||
// assuming timestamps without a timezone is in UTC time
|
||||
if (typeof value === 'string') {
|
||||
value = normalizeTimestamp(value);
|
||||
}
|
||||
|
||||
super(value as string);
|
||||
|
||||
this.input = input;
|
||||
this.formatter = formatter;
|
||||
this.toString = (): string => {
|
||||
if (this.formatter === String) {
|
||||
return String(this.input);
|
||||
}
|
||||
// Values that are not parseable timestamps - durations such as
|
||||
// "00:01:54" or "0 days 00:01:54", for instance - produce an Invalid
|
||||
// Date, and formatting one renders as "NaN:NaN:NaN". Fall back to the
|
||||
// original value instead.
|
||||
if (Number.isNaN(this.getTime())) {
|
||||
return String(this.input);
|
||||
}
|
||||
return this.formatter ? this.formatter(this) : Date.toString.call(this);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,10 @@ import {
|
||||
getSmallNumberFormatter,
|
||||
isProbablyHTML,
|
||||
sanitizeHtml,
|
||||
DateWithFormatter,
|
||||
} from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import { DataColumnMeta } from '../types';
|
||||
import DateWithFormatter from './DateWithFormatter';
|
||||
|
||||
/**
|
||||
* Format text for cell value.
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* 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 '@testing-library/jest-dom';
|
||||
import { Component, type ReactNode } from 'react';
|
||||
import { render, screen } from '@superset-ui/core/spec';
|
||||
import { CellProps, Column, HeaderProps } from 'react-table';
|
||||
import DataTable from '../../src/DataTable/DataTable';
|
||||
import { ProviderWrapper } from '../testHelpers';
|
||||
|
||||
type DataRow = {
|
||||
city: string;
|
||||
firstName: string;
|
||||
};
|
||||
|
||||
interface RenderErrorBoundaryProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
interface RenderErrorBoundaryState {
|
||||
hasError: boolean;
|
||||
}
|
||||
|
||||
const columns: Column<DataRow>[] = [
|
||||
{
|
||||
Header: ({ column }: HeaderProps<DataRow>) => (
|
||||
<th data-column-name={column.id}>First name</th>
|
||||
),
|
||||
Cell: ({ value }: CellProps<DataRow>) => <td>{value}</td>,
|
||||
id: 'firstName',
|
||||
accessor: 'firstName' as never,
|
||||
},
|
||||
{
|
||||
Header: ({ column }: HeaderProps<DataRow>) => (
|
||||
<th data-column-name={column.id}>City</th>
|
||||
),
|
||||
Cell: ({ value }: CellProps<DataRow>) => <td>{value}</td>,
|
||||
id: 'city',
|
||||
accessor: 'city' as never,
|
||||
},
|
||||
];
|
||||
|
||||
const data: DataRow[] = [
|
||||
{ firstName: 'Michael', city: 'Paris' },
|
||||
{ firstName: 'Jordan', city: 'London' },
|
||||
];
|
||||
|
||||
// Turns a render-phase throw, such as a Rules of Hooks violation, into a
|
||||
// readable assertion instead of an unhandled error.
|
||||
class RenderErrorBoundary extends Component<
|
||||
RenderErrorBoundaryProps,
|
||||
RenderErrorBoundaryState
|
||||
> {
|
||||
state: RenderErrorBoundaryState = {
|
||||
hasError: false,
|
||||
};
|
||||
|
||||
static getDerivedStateFromError() {
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
// data-test is the configured testIdAttribute (see spec/helpers/setup.ts),
|
||||
// so *ByTestId('render-error') resolves this node.
|
||||
return <div data-test="render-error">Render error</div>;
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
const renderDataTable = (tableColumns: Column<DataRow>[]) => (
|
||||
<ProviderWrapper>
|
||||
<RenderErrorBoundary>
|
||||
<DataTable<DataRow>
|
||||
columns={tableColumns}
|
||||
data={data}
|
||||
rowCount={data.length}
|
||||
serverPagination={false}
|
||||
serverPaginationData={{}}
|
||||
onServerPaginationChange={jest.fn()}
|
||||
handleSortByChange={jest.fn()}
|
||||
sortByFromParent={[]}
|
||||
onSearchColChange={jest.fn()}
|
||||
searchOptions={[]}
|
||||
onFilteredRowsChange={jest.fn()}
|
||||
sticky={false}
|
||||
/>
|
||||
</RenderErrorBoundary>
|
||||
</ProviderWrapper>
|
||||
);
|
||||
|
||||
test('keeps the hook order stable when the columns disappear', () => {
|
||||
const { rerender } = render(renderDataTable(columns));
|
||||
|
||||
expect(screen.getByText('Michael')).toBeInTheDocument();
|
||||
|
||||
rerender(renderDataTable([]));
|
||||
|
||||
expect(screen.queryByTestId('render-error')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('No data found')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('keeps the hook order stable when the columns appear', () => {
|
||||
const { rerender } = render(renderDataTable([]));
|
||||
|
||||
expect(screen.getByText('No data found')).toBeInTheDocument();
|
||||
|
||||
rerender(renderDataTable(columns));
|
||||
|
||||
expect(screen.queryByTestId('render-error')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('Michael')).toBeInTheDocument();
|
||||
});
|
||||
@@ -40,13 +40,13 @@ import {
|
||||
TimeGranularity,
|
||||
SMART_DATE_ID,
|
||||
getTimeFormatterForGranularity,
|
||||
DateWithFormatter,
|
||||
} from '@superset-ui/core';
|
||||
import { CellProps, Column, HeaderProps } from 'react-table';
|
||||
import DataTable from '../src/DataTable/DataTable';
|
||||
import TableChart, { sanitizeHeaderId } from '../src/TableChart';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import transformProps from '../src/transformProps';
|
||||
import DateWithFormatter from '../src/utils/DateWithFormatter';
|
||||
import testData from './testData';
|
||||
import { ProviderWrapper } from './testHelpers';
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 { DateWithFormatter, getTimeFormatter } from '@superset-ui/core';
|
||||
import { GenericDataType } from '@apache-superset/core/common';
|
||||
import { formatColumnValue } from '../../src/utils/formatValue';
|
||||
import { DataColumnMeta } from '../../src/types';
|
||||
|
||||
const formatter = getTimeFormatter('%H:%M:%S');
|
||||
|
||||
test('renders a duration cell through the column formatter without producing NaN', () => {
|
||||
// The cell text is produced by formatColumnValue, which hands the wrapped
|
||||
// value straight to the formatter rather than going through toString().
|
||||
const column: DataColumnMeta = {
|
||||
key: 'call_period',
|
||||
label: 'call_period',
|
||||
dataType: GenericDataType.Temporal,
|
||||
formatter,
|
||||
isNumeric: false,
|
||||
};
|
||||
const value = new DateWithFormatter('00:01:54', { formatter });
|
||||
|
||||
expect(formatColumnValue(column, value)).toEqual([false, '00:01:54']);
|
||||
});
|
||||
@@ -49,7 +49,7 @@
|
||||
"handlebars": "^4.7.9",
|
||||
"lodash": "^4.18.1",
|
||||
"lodash-es": "^4.18.1",
|
||||
"maplibre-gl": "^5.24.0",
|
||||
"maplibre-gl": "^6.8.0",
|
||||
"mousetrap": "^1.6.5",
|
||||
"ngeohash": "^0.6.4",
|
||||
"prop-types": "^15.8.1",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user