codeant-ai-for-open-source[bot] commented on code in PR #37805: URL: https://github.com/apache/superset/pull/37805#discussion_r2783028241
########## superset-frontend/src/features/themes/AccessibilityScore.tsx: ########## @@ -0,0 +1,534 @@ +/** + * 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 { useMemo, useState, useCallback } from 'react'; +import { t } from '@apache-superset/core'; +import { css, styled, useTheme } from '@apache-superset/core/ui'; +import { + Progress, + Collapse, + Tooltip, + Button, +} from '@superset-ui/core/components'; +import { Icons } from '@superset-ui/core/components/Icons'; +import { Typography } from '@superset-ui/core/components/Typography'; +import { + analyzeThemeAccessibility, + formatContrastRatio, + getScoreColor, + type AccessibilityAnalysis, + type ContrastIssue, + WCAG_REQUIREMENTS, +} from 'src/theme/accessibility'; + +interface AccessibilityScoreProps { + /** JSON string of the theme configuration */ + themeJson: string | undefined; +} + +const StyledContainer = styled.div` + ${({ theme }) => css` + margin-top: ${theme.sizeUnit * 4}px; + padding: ${theme.sizeUnit * 3}px; + background: ${theme.colorBgContainer}; + border: 1px solid ${theme.colorBorderSecondary}; + border-radius: ${theme.borderRadius}px; + `} +`; + +const StyledHeader = styled.div` + ${({ theme }) => css` + display: flex; + align-items: center; + gap: ${theme.sizeUnit * 2}px; + margin-bottom: ${theme.sizeUnit * 2}px; + + .header-icon { + font-size: ${theme.fontSizeLG}px; + } + + .score-text { + font-weight: ${theme.fontWeightStrong}; + } + + .level-badge { + padding: 0 ${theme.sizeUnit}px; + border-radius: ${theme.borderRadiusSM}px; + font-size: ${theme.fontSizeSM}px; + font-weight: ${theme.fontWeightStrong}; + + &.level-aaa { + background: ${theme.colorSuccessBg}; + color: ${theme.colorSuccess}; + } + &.level-aa { + background: ${theme.colorSuccessBg}; + color: ${theme.colorSuccess}; + } + &.level-a { + background: ${theme.colorWarningBg}; + color: ${theme.colorWarning}; + } + &.level-fail { + background: ${theme.colorErrorBg}; + color: ${theme.colorError}; + } + } + `} +`; + +const StyledProgressWrapper = styled.div` + ${({ theme }) => css` + margin-bottom: ${theme.sizeUnit * 2}px; + `} +`; + +const StyledIssuesList = styled.div` + ${({ theme }) => css` + .issue-item { + display: flex; + align-items: flex-start; + gap: ${theme.sizeUnit}px; + padding: ${theme.sizeUnit}px 0; + font-size: ${theme.fontSizeSM}px; + + &:not(:last-child) { + border-bottom: 1px solid ${theme.colorBorderSecondary}; + } + + .issue-icon { + flex-shrink: 0; + margin-top: 2px; + } + + .issue-icon-error { + color: ${theme.colorError}; + } + + .issue-icon-warning { + color: ${theme.colorWarning}; + } + + .issue-content { + flex: 1; + } + + .issue-tokens { + font-family: ${theme.fontFamilyCode}; + color: ${theme.colorTextSecondary}; + } + + .issue-ratio { + font-weight: ${theme.fontWeightStrong}; + color: ${theme.colorError}; + } + + .issue-required { + color: ${theme.colorTextTertiary}; + } + + .color-swatch { + display: inline-block; + width: 12px; + height: 12px; + border-radius: 2px; + border: 1px solid ${theme.colorBorder}; + vertical-align: middle; + margin: 0 2px; + } + } + + .ant-collapse { + background: transparent; + border: none; + } + + .ant-collapse-item { + border: none !important; + } + + .ant-collapse-header { + padding: ${theme.sizeUnit}px 0 !important; + color: ${theme.colorText} !important; + } + + .ant-collapse-content { + border: none !important; + } + + .ant-collapse-content-box { + padding: 0 !important; + } + `} +`; + +const StyledEmptyState = styled.div` + ${({ theme }) => css` + text-align: center; + color: ${theme.colorTextSecondary}; + padding: ${theme.sizeUnit * 2}px; + `} +`; + +/** + * Renders a single contrast issue item + */ +function IssueItem({ issue }: { issue: ContrastIssue }) { + const IconComponent = + issue.severity === 'error' + ? Icons.CloseCircleOutlined + : Icons.ExclamationCircleOutlined; + + return ( + <div className="issue-item"> + <IconComponent + className={`issue-icon issue-icon-${issue.severity}`} + iconSize="s" + /> + <div className="issue-content"> + <div> + <span className="issue-tokens"> + {issue.foreground} + <span + className="color-swatch" + style={{ backgroundColor: issue.foregroundColor }} + title={issue.foregroundColor} + /> + </span> + {' vs '} + <span className="issue-tokens"> + {issue.background} + <span + className="color-swatch" + style={{ backgroundColor: issue.backgroundColor }} + title={issue.backgroundColor} + /> + </span> + </div> + <div> + <span className="issue-ratio"> + {formatContrastRatio(issue.ratio)} + </span> + <span className="issue-required"> + {' '} + ({t('need')} {formatContrastRatio(issue.required)}) + </span> + </div> + <Typography.Text type="secondary">{issue.description}</Typography.Text> + </div> + </div> + ); +} + +/** + * Checks if a key looks like a color token. + */ +function isColorKey(key: string): boolean { + return ( + key.startsWith('color') || + key.startsWith('colorBg') || + key.startsWith('colorText') + ); +} + +/** + * Checks if a theme config has any meaningful color tokens defined. + * Returns false for empty objects or objects with no color-related tokens. + * Searches recursively through nested objects. + */ +function hasColorTokens(themeConfig: Record<string, unknown>): boolean { + // Check if there's a token object with any color-related keys + const token = themeConfig.token as Record<string, unknown> | undefined; + if (token && typeof token === 'object') { + const colorKeys = Object.keys(token).filter(isColorKey); + if (colorKeys.length > 0) { + return true; + } + } + + // Check root level for color keys + const rootColorKeys = Object.keys(themeConfig).filter(isColorKey); + if (rootColorKeys.length > 0) { + return true; + } + + // Check nested objects (e.g., neutrals, brand, semantic) + for (const value of Object.values(themeConfig)) { + if (value && typeof value === 'object' && !Array.isArray(value)) { + const nestedObj = value as Record<string, unknown>; + const nestedColorKeys = Object.keys(nestedObj).filter(isColorKey); + if (nestedColorKeys.length > 0) { Review Comment: **Suggestion:** The helper that detects whether a theme has color tokens only inspects one nesting level of child objects, even though the comment claims it searches recursively. This means themes that store color tokens deeper than one level will be misclassified as having no colors, disabling contrast analysis and causing "No contrast pairs found" messages incorrectly. Refactor the nested-object loop to call `hasColorTokens` recursively so arbitrarily nested color tokens are detected. [logic error] <details> <summary><b>Severity Level:</b> Major ⚠️</summary> ```mdx - ⚠️ Contrast analysis disabled for deeply nested custom theme tokens. - ⚠️ Theme designers misled that themes lack analyzable color data. ``` </details> ```suggestion // Recursively check nested objects (e.g., neutrals, brand, semantic) for (const value of Object.values(themeConfig)) { if (value && typeof value === 'object' && !Array.isArray(value)) { if (hasColorTokens(value as Record<string, unknown>)) { ``` <details> <summary><b>Steps of Reproduction ✅ </b></summary> ```mdx 1. Use the theme editor / accessibility tooling that relies on the `useThemeAnalysis` hook exported from `superset-frontend/src/features/themes/AccessibilityScore.tsx:322-352` (this is the hook backing the "Analyze Contrast" feature described in the PR). 2. Provide a custom theme JSON where all `color*` tokens are nested deeper than one level and not under `token`, for example: `{"groups":{"neutrals":{"light":{"colorText":"#000000"},"background":{"colorBgBase":"#ffffff"}}}}`. In this structure, the top-level object and its immediate children do not themselves have keys starting with `color`. 3. When `useThemeAnalysis(themeJson)` runs, it calls `parseThemeJson()` at `AccessibilityScore.tsx:296-316`, which in turn calls `hasColorTokens()` at `AccessibilityScore.tsx:255-283` to compute `hasColors`. 4. Inside `hasColorTokens`, only the root object, `themeConfig.token`, and the first nesting level of child objects are checked for keys matching `isColorKey`. Because the `colorText` and `colorBgBase` keys live two levels deeper, `hasColorTokens()` returns `false`, `parseThemeJson()` sets `hasColors: false`, and `useThemeAnalysis.runAnalysis()` exits early without calling `analyzeThemeAccessibility()`. As a result, the AccessibilityScore-based contrast analysis for that theme never runs even though valid color tokens are present. ``` </details> <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** superset-frontend/src/features/themes/AccessibilityScore.tsx **Line:** 271:276 **Comment:** *Logic Error: The helper that detects whether a theme has color tokens only inspects one nesting level of child objects, even though the comment claims it searches recursively. This means themes that store color tokens deeper than one level will be misclassified as having no colors, disabling contrast analysis and causing "No contrast pairs found" messages incorrectly. Refactor the nested-object loop to call `hasColorTokens` recursively so arbitrarily nested color tokens are detected. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37805&comment_hash=c8a96f45a4b3511fa3cb5ec31c38af352353a49c14416d88a6f32ce9e56ee942&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37805&comment_hash=c8a96f45a4b3511fa3cb5ec31c38af352353a49c14416d88a6f32ce9e56ee942&reaction=dislike'>👎</a> ########## superset/config.py: ########## @@ -961,6 +961,86 @@ class D3TimeFormat(TypedDict, total=False): "algorithm": "dark", } +# Built-in accessibility themes for color vision deficiency +# These themes are automatically seeded to the database on startup +# and available for selection in the theme UI +BUILT_IN_ACCESSIBILITY_THEMES: dict[str, Theme] = { + "ACCESSIBILITY_IBM_CARBON": { + "token": { + **THEME_DEFAULT["token"], + # IBM Carbon Design System - WCAG AAA compliant + "colorPrimary": "#0F62FE", + "colorLink": "#0F62FE", + "colorError": "#DA1E28", + "colorWarning": "#F1C21B", + "colorSuccess": "#24A148", + "colorInfo": "#0043CE", + "colorText": "#161616", + "colorTextSecondary": "#525252", + "colorBgBase": "#FFFFFF", + "colorBgContainer": "#F4F4F4", + "colorBorder": "#8D8D8D", + "borderRadius": 4, + "fontFamily": "IBM Plex Sans, Inter, Helvetica, Arial, sans-serif", + "fontSize": 14, + "fontSizeLG": 16, + "fontSizeSM": 12, + "fontWeightStrong": "600", + "colorFillSecondary": "#E8F0FE", + "colorBgTextHover": "#E5F0FF", + "colorTextDisabled": "#A8A8A8", + }, + "algorithm": "default", + }, + "ACCESSIBILITY_OKABE_ITO": { + "token": { + **THEME_DEFAULT["token"], + # Okabe-Ito palette - optimized for deuteranopia and protanopia + "colorBgBase": "#FFFFFF", + "colorBgContainer": "#FFFFFF", + "colorBgElevated": "#FFFFFF", + "colorText": "#000000", + "colorTextSecondary": "#1A1A1A", + "colorTextTertiary": "#333333", + "colorBorder": "#666666", + "colorBorderSecondary": "#999999", + "colorPrimary": "#0072B2", + "colorLink": "#0072B2", + "colorInfo": "#56B4E9", + "colorSuccess": "#009E73", + "colorWarning": "#E69F00", + "colorError": "#D55E00", + "controlHeight": 32, + "borderRadius": 4, + }, + "algorithm": "default", + }, + "ACCESSIBILITY_PAUL_TOL_BRIGHT": { + "token": { + **THEME_DEFAULT["token"], + # Paul Tol's Bright palette - scientifically validated for CVD + "colorPrimary": "#4477AA", + "colorLink": "#4477AA", + "colorError": "#EE6677", + "colorWarning": "#CCBB44", + "colorSuccess": "#228833", + "colorInfo": "#66CCEE", + "colorTextBase": "#000000", + "colorBgBase": "#FFFFFF", Review Comment: **Suggestion:** In the Paul Tol Bright accessibility theme the primary text color is set under a `colorTextBase` key, but the base theme and other themes use `colorText`, so this value is likely ignored and the theme will inherit the default text color instead of the intended accessible one. [logic error] <details> <summary><b>Severity Level:</b> Major ⚠️</summary> ```mdx - ⚠️ Paul Tol Bright theme ignores intended accessible text color. - ⚠️ Accessibility contrast checks misrepresent Paul Tol Bright theme. ``` </details> ```suggestion "colorText": "#000000", ``` <details> <summary><b>Steps of Reproduction ✅ </b></summary> ```mdx 1. Start Superset with this PR so that `superset/config.py` is loaded and `BUILT_IN_ACCESSIBILITY_THEMES` is defined (around lines 964–1042), including the `"ACCESSIBILITY_PAUL_TOL_BRIGHT"` entry where `"colorTextBase": "#000000"` is set at line 1029. 2. From the Superset UI, open the theme selector/theme admin page (as described in the PR summary: "UI-based theme administration for admins" using `BUILT_IN_ACCESSIBILITY_THEMES` from `superset/config.py`) and choose the "Accessibility Paul Tol Bright" theme as the active/system theme. 3. Load any dashboard or page that uses standard body text (rendered via the Ant Design theme tokens originating from `THEME_DEFAULT`/`BUILT_IN_ACCESSIBILITY_THEMES` in `superset/config.py`) and observe that the primary text color remains whatever the default Ant Design `colorText` is, not the intended black (`#000000`). 4. Compare behavior with the "Accessibility IBM Carbon" and "Accessibility Okabe-Ito" themes in `superset/config.py` (lines 968–1017), which correctly specify `"colorText": ...`; note that only `"ACCESSIBILITY_PAUL_TOL_BRIGHT"` uses `"colorTextBase"` so its text color override is ignored, demonstrating that the key name is inconsistent and the accessible text color is not applied. ``` </details> <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** superset/config.py **Line:** 1029:1029 **Comment:** *Logic Error: In the Paul Tol Bright accessibility theme the primary text color is set under a `colorTextBase` key, but the base theme and other themes use `colorText`, so this value is likely ignored and the theme will inherit the default text color instead of the intended accessible one. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37805&comment_hash=303b4fb03bfbf900e5323c831b5a95d58368d940726d765faf78cf1ee6b9150c&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37805&comment_hash=303b4fb03bfbf900e5323c831b5a95d58368d940726d765faf78cf1ee6b9150c&reaction=dislike'>👎</a> ########## superset-frontend/src/theme/accessibility.ts: ########## @@ -0,0 +1,542 @@ +/** + * 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. + */ + +/** + * WCAG 2.1 Accessibility utilities for theme contrast analysis. + * + * This module provides functions to analyze theme configurations for + * accessibility compliance, specifically contrast ratios between + * text and background colors. + * + * @see https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum.html + */ + +// eslint-disable-next-line no-restricted-syntax +import { theme as antdTheme } from 'antd'; +import type { ThemeConfig } from 'antd'; + +/** + * WCAG contrast ratio requirements + */ +export const WCAG_REQUIREMENTS = { + AA_NORMAL_TEXT: 4.5, + AA_LARGE_TEXT: 3.0, + AAA_NORMAL_TEXT: 7.0, + AAA_LARGE_TEXT: 4.5, +} as const; + +/** + * Contrast issue severity levels + */ +export type ContrastSeverity = 'error' | 'warning'; + +/** + * Represents a single contrast issue found during analysis + */ +export interface ContrastIssue { + /** Token name of the foreground color (e.g., 'colorText') */ + foreground: string; + /** Token name of the background color (e.g., 'colorBgBase') */ + background: string; + /** Actual hex color value of the foreground */ + foregroundColor: string; + /** Actual hex color value of the background */ + backgroundColor: string; + /** Calculated contrast ratio */ + ratio: number; + /** Required ratio for AA compliance */ + required: number; + /** Severity: error (fails AA) or warning (fails AAA) */ + severity: ContrastSeverity; + /** Human-readable description of the issue */ + description: string; +} + +/** + * WCAG compliance level + */ +export type WCAGLevel = 'AAA' | 'AA' | 'A' | 'Fail'; + +/** + * Complete accessibility analysis result + */ +export interface AccessibilityAnalysis { + /** Overall score from 0-100 */ + score: number; + /** WCAG compliance level achieved */ + level: WCAGLevel; + /** List of contrast issues found */ + issues: ContrastIssue[]; + /** Number of checks that passed AA requirements */ + passedChecks: number; + /** Total number of contrast checks performed */ + totalChecks: number; +} + +/** + * Contrast pairs to check in theme analysis. + * Each pair represents a foreground/background color combination + * that should meet WCAG contrast requirements. + */ +const CONTRAST_PAIRS: Array<{ + foreground: string; + background: string; + description: string; + isLargeText?: boolean; +}> = [ + { + foreground: 'colorText', + background: 'colorBgBase', + description: 'Primary text on base background', + }, + { + foreground: 'colorText', + background: 'colorBgContainer', + description: 'Primary text on container background', + }, + { + foreground: 'colorTextSecondary', + background: 'colorBgBase', + description: 'Secondary text on base background', + }, + { + foreground: 'colorTextSecondary', + background: 'colorBgContainer', + description: 'Secondary text on container background', + }, + { + foreground: 'colorTextDescription', + background: 'colorBgBase', + description: 'Description text on base background', + }, + { + foreground: 'colorPrimary', + background: 'colorBgBase', + description: 'Primary color (buttons/links) on base background', + }, + { + foreground: 'colorPrimary', + background: 'colorBgContainer', + description: 'Primary color on container background', + }, + { + foreground: 'colorError', + background: 'colorBgBase', + description: 'Error text on base background', + }, + { + foreground: 'colorError', + background: 'colorBgContainer', + description: 'Error text on container background', + }, + { + foreground: 'colorWarning', + background: 'colorBgBase', + description: 'Warning text on base background', + }, + { + foreground: 'colorSuccess', + background: 'colorBgBase', + description: 'Success text on base background', + }, + { + foreground: 'colorLink', + background: 'colorBgBase', + description: 'Link text on base background', + }, + { + foreground: 'colorTextHeading', + background: 'colorBgBase', + description: 'Heading text on base background', + isLargeText: true, + }, +]; + +/** + * Converts a hex color string to RGB values (0-255). + * Supports 3-char, 6-char, and 8-char (with alpha) hex formats. + */ +function hexToRgb(hex: string): [number, number, number] | null { + const cleanHex = hex.replace(/^#/, ''); + + let fullHex = cleanHex; + + // Handle shorthand (#fff -> #ffffff) + if (cleanHex.length === 3) { + fullHex = cleanHex + .split('') + .map(c => c + c) + .join(''); + } + + // Handle 8-char hex (with alpha) - just take the first 6 chars + if (fullHex.length === 8) { + fullHex = fullHex.substring(0, 6); + } + + if (fullHex.length !== 6) { + return null; + } + + const r = parseInt(fullHex.substring(0, 2), 16); + const g = parseInt(fullHex.substring(2, 4), 16); + const b = parseInt(fullHex.substring(4, 6), 16); + + if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) { + return null; + } + + return [r, g, b]; +} + +/** + * Parses an rgba() color string to RGB values. + */ +function rgbaToRgb(rgba: string): [number, number, number] | null { + const match = rgba.match( + /rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*[\d.]+\s*)?\)/, + ); + if (!match) return null; + return [ + parseInt(match[1], 10), + parseInt(match[2], 10), + parseInt(match[3], 10), + ]; +} + +/** + * Converts any color format to a normalized hex string. + * Handles hex (3, 6, or 8 char), rgb(), and rgba() formats. + */ +function normalizeColor(color: string): string | null { + if (!color || typeof color !== 'string') return null; + + const trimmed = color.trim(); + + // Handle hex colors + if (trimmed.startsWith('#')) { + const rgb = hexToRgb(trimmed); + if (!rgb) return null; + return `#${rgb.map(c => c.toString(16).padStart(2, '0')).join('')}`; + } + + // Handle rgb/rgba colors + if (trimmed.startsWith('rgb')) { + const rgb = rgbaToRgb(trimmed); + if (!rgb) return null; + return `#${rgb.map(c => c.toString(16).padStart(2, '0')).join('')}`; + } + + return null; +} + +/** + * Converts algorithm string to Ant Design algorithm function(s). + */ +function getAlgorithm( + algorithmConfig: string | string[] | undefined, +): ThemeConfig['algorithm'] { + const { darkAlgorithm, compactAlgorithm, defaultAlgorithm } = antdTheme; + + if (!algorithmConfig) { + return defaultAlgorithm; + } + + const algorithms = Array.isArray(algorithmConfig) + ? algorithmConfig + : [algorithmConfig]; + + const algorithmFns = algorithms + .map(alg => { + switch (alg) { + case 'dark': + return darkAlgorithm; + case 'compact': + return compactAlgorithm; + default: + return defaultAlgorithm; + } + }) + .filter(Boolean); + + return algorithmFns.length === 1 ? algorithmFns[0] : algorithmFns; +} + +/** + * Flattens nested color tokens from custom theme structures. + * Handles formats like { neutrals: { colorText: '#000' }, brand: { colorPrimary: '#00f' } } + */ +function flattenColorTokens( + themeConfig: Record<string, unknown>, +): Record<string, unknown> { + const flattened: Record<string, unknown> = {}; + + // First, add any root-level color tokens + for (const [key, value] of Object.entries(themeConfig)) { + if (key.startsWith('color') && typeof value === 'string') { + flattened[key] = value; + } + } + + // Then, flatten nested objects that might contain color tokens + for (const [, value] of Object.entries(themeConfig)) { + if (value && typeof value === 'object' && !Array.isArray(value)) { + const nestedObj = value as Record<string, unknown>; + for (const [nestedKey, nestedValue] of Object.entries(nestedObj)) { + if (nestedKey.startsWith('color') && typeof nestedValue === 'string') { + // Don't overwrite if already exists (root takes precedence) + if (!(nestedKey in flattened)) { + flattened[nestedKey] = nestedValue; + } + } + } + } + } + + return flattened; +} + +/** + * Resolves a theme configuration to computed design tokens using Ant Design. + * This allows us to analyze themes that only define seed colors. + * Also handles custom nested structures by flattening color tokens first. + */ +function resolveThemeTokens( + themeConfig: Record<string, unknown>, +): Record<string, unknown> { + // First, flatten any custom nested color structures + const flattenedColors = flattenColorTokens(themeConfig); + + try { + // Build Ant Design ThemeConfig from the user's config + // Merge flattened colors with existing token object + const tokenConfig = { + ...(themeConfig.token as Record<string, unknown>), + ...flattenedColors, + }; + + const antdConfig: ThemeConfig = { + token: tokenConfig as ThemeConfig['token'], + algorithm: getAlgorithm(themeConfig.algorithm as string | string[]), + }; + + // Use Ant Design's getDesignToken to compute all tokens + const resolvedTokens = antdTheme.getDesignToken(antdConfig); + + // Merge flattened colors back in case Ant Design didn't recognize them + return { + ...(resolvedTokens as unknown as Record<string, unknown>), + ...flattenedColors, + }; + } catch { + // If resolution fails, return the flattened colors plus original token object + return { + ...(themeConfig.token as Record<string, unknown>), Review Comment: **Suggestion:** In `resolveThemeTokens`, the object spread `...(themeConfig.token as Record<string, unknown>)` will throw a runtime TypeError if `themeConfig.token` is `undefined` or `null`, and the same unsafe spread is repeated in the catch block, so calling `analyzeThemeAccessibility` with a theme config that omits `token` will crash instead of falling back. Use a default empty object for `themeConfig.token` and reuse it in both the try and catch paths to avoid spreading `undefined`. [possible bug] <details> <summary><b>Severity Level:</b> Critical 🚨</summary> ```mdx - ❌ Theme contrast analysis crashes for themes without token object. - ⚠️ Accessibility analysis unusable for simpler custom theme configs. ``` </details> ```suggestion // Safely handle cases where themeConfig.token is undefined or null const baseToken = (themeConfig.token as Record<string, unknown> | undefined) ?? {}; try { // Build Ant Design ThemeConfig from the user's config // Merge flattened colors with existing token object const tokenConfig = { ...baseToken, ...flattenedColors, }; const antdConfig: ThemeConfig = { token: tokenConfig as ThemeConfig['token'], algorithm: getAlgorithm(themeConfig.algorithm as string | string[]), }; // Use Ant Design's getDesignToken to compute all tokens const resolvedTokens = antdTheme.getDesignToken(antdConfig); // Merge flattened colors back in case Ant Design didn't recognize them return { ...(resolvedTokens as unknown as Record<string, unknown>), ...flattenedColors, }; } catch { // If resolution fails, return the flattened colors plus original token object return { ...baseToken, ``` <details> <summary><b>Steps of Reproduction ✅ </b></summary> ```mdx 1. Import and call `analyzeThemeAccessibility` from `superset-frontend/src/theme/accessibility.ts:443` with a theme object that has root-level colors but no `token` property, e.g. `{ colorText: '#000000', colorBgBase: '#ffffff' }`. 2. `analyzeThemeAccessibility` calls `resolveThemeTokens(themeConfig)` at `superset-frontend/src/theme/accessibility.ts:448`, passing the same object. 3. Inside `resolveThemeTokens` at `superset-frontend/src/theme/accessibility.ts:320-333`, `flattenColorTokens(themeConfig)` succeeds because it only inspects top-level properties and does not require `themeConfig.token`. 4. Still in `resolveThemeTokens`, construction of `tokenConfig` at `superset-frontend/src/theme/accessibility.ts:328-332` executes `{ ...(themeConfig.token as Record<string, unknown>), ...flattenedColors }`; since `themeConfig.token` is `undefined`, the spread causes `TypeError: Cannot convert undefined or null to object`, the catch block then attempts another spread of `themeConfig.token` at `superset-frontend/src/theme/accessibility.ts:348-352`, rethrowing the same error and causing `analyzeThemeAccessibility` to crash instead of returning an analysis result. ``` </details> <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** superset-frontend/src/theme/accessibility.ts **Line:** 326:350 **Comment:** *Possible Bug: In `resolveThemeTokens`, the object spread `...(themeConfig.token as Record<string, unknown>)` will throw a runtime TypeError if `themeConfig.token` is `undefined` or `null`, and the same unsafe spread is repeated in the catch block, so calling `analyzeThemeAccessibility` with a theme config that omits `token` will crash instead of falling back. Use a default empty object for `themeConfig.token` and reuse it in both the try and catch paths to avoid spreading `undefined`. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37805&comment_hash=0b7708cb8ec2efe4250ec554ec24fd600d513d63c04dc40eb711af0ea0a3a636&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37805&comment_hash=0b7708cb8ec2efe4250ec554ec24fd600d513d63c04dc40eb711af0ea0a3a636&reaction=dislike'>👎</a> ########## superset/static/service-worker.js: ########## @@ -1,27 +1,1471 @@ -/** - * 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. +/* + * ATTENTION: An "eval-source-map" devtool has been used. + * This devtool is neither made for production nor for readable output files. + * It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools. + * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/) + * or disable the default devtool with "devtool: false". + * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/). */ +/******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ({ -// Minimal service worker for PWA file handling support -self.addEventListener('install', event => { - event.waitUntil(self.skipWaiting()); -}); +/***/ "./src/service-worker.ts" +/*!*******************************!*\ + !*** ./src/service-worker.ts ***! + \*******************************/ +() { -self.addEventListener('activate', event => { - event.waitUntil(self.clients.claim()); -}); +eval("{/**\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */ // Service Worker types (declared locally to avoid polluting global scope)\nself.addEventListener('install', (event)=>{\n event.waitUntil(self.skipWaiting()) ;\n});\nself.addEventListener('activate', (event)=>{\n event.waitUntil(self.clients.claim());\n});\n\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiLi9zcmMvc2VydmljZS13b3JrZXIudHMiLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBaUJBO0FBWUE7QUFDQTtBQUNBO0FBRUE7QUFDQTtBQUNBO0FBRUEiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9zdXBlcnNldC8uL3NyYy9zZXJ2aWNlLXdvcmtlci50cz83ZjU4Il0sInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogTGljZW5zZWQgdG8gdGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uIChBU0YpIHVuZGVyIG9uZVxuICogb3IgbW9yZSBjb250cmlidXRvciBsaWNlbnNlIGFncmVlbWVudHMuICBTZWUgdGhlIE5PVElDRSBmaWxlXG4gKiBkaXN0cmlidXRlZCB3aXRoIHRoaXMgd29yayBmb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvblxuICogcmVnYXJkaW5nIGNvcHlyaWdodCBvd25lcnNoaXAuICBUaGUgQVNGIGxpY2Vuc2VzIHRoaXMgZmlsZVxuICogdG8geW91IHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZVxuICogXCJMaWNlbnNlXCIpOyB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlXG4gKiB3aXRoIHRoZSBMaWNlbnNlLiAgWW91IG1h eSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0XG4gKlxuICogICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjBcbiAqXG4gKiBVbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsXG4gKiBzb2Z0d2FyZSBkaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhblxuICogXCJBUyBJU1wiIEJBU0lTLCBXSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTllcbiAqIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuICBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZVxuICogc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZCBsaW1pdGF0aW9uc1xuICogdW5kZXIgdGhlIExpY2Vuc2UuXG4gKi9cblxuLy8gU2VydmljZSBXb3JrZXIgdHlwZXMgKGRlY2xhcmVkIGxvY2FsbHkgdG8gYXZvaWQgcG9sbHV0aW5nIGdsb2JhbCBzY29wZSlcbmRlY2xhcmUgY29uc3Qgc2VsZjoge1xuICBza2lwV2FpdGluZygpOiBQcm9taXNlPHZvaWQ+O1xuICBjbGllbnRzOiB7IGNsYWltKCk6IFByb21pc2U8dm9pZD4gfTtcbiAgYWRkRXZlbnRMaXN0ZW5lcihcbiAgICB0eXBlOiAnaW5zdGFsbCcgfCAnYWN0aXZhdGUnLFxuICAgIGxpc3RlbmVyOiAoZXZlbnQ6IHsgd2FpdFVudGlsKHByb21pc2U6IFByb21pc2U8dW5rbm93bj4pOiB2b2lkIH0pID0+IHZvaWQsXG4gICk6IHZva WQ7XG59O1xuXG5zZWxmLmFkZEV2ZW50TGlzdGVuZXIoJ2luc3RhbGwnLCBldmVudCA9PiB7XG4gIGV2ZW50LndhaXRVbnRpbChzZWxmLnNraXBXYWl0aW5nKCkpO1xufSk7XG5cbnNlbGYuYWRkRXZlbnRMaXN0ZW5lcignYWN0aXZhdGUnLCBldmVudCA9PiB7XG4gIGV2ZW50LndhaXRVbnRpbChzZWxmLmNsaWVudHMuY2xhaW0oKSk7XG59KTtcblxuZXhwb3J0IHt9O1xuIl0sIm5hbWVzIjpbXSwiaWdub3JlTGlzdCI6W10sInNvdXJjZVJvb3QiOiIifQ==\n//# sourceURL=webpack-internal:///./src/service-worker.ts\n\n}"); + +/***/ } + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Check if module exists (development only) +/******/ if (__webpack_modules__[moduleId] === undefined) { +/******/ var e = new Error("Cannot find module '" + moduleId + "'"); +/******/ e.code = 'MODULE_NOT_FOUND'; +/******/ throw e; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ id: moduleId, +/******/ loaded: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ var execOptions = { id: moduleId, module: module, factory: __webpack_modules__[moduleId], require: __webpack_require__ }; +/******/ __webpack_require__.i.forEach(function(handler) { handler(execOptions); }); +/******/ module = execOptions.module; +/******/ execOptions.factory.call(module.exports, module, module.exports, execOptions.require); +/******/ +/******/ // Flag the module as loaded +/******/ module.loaded = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = __webpack_modules__; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = __webpack_module_cache__; +/******/ +/******/ // expose the module execution interceptor +/******/ __webpack_require__.i = []; +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/chunk loaded */ +/******/ (() => { +/******/ var deferred = []; +/******/ __webpack_require__.O = (result, chunkIds, fn, priority) => { +/******/ if(chunkIds) { +/******/ priority = priority || 0; +/******/ for(var i = deferred.length; i > 0 && deferred[i - 1][2] > priority; i--) deferred[i] = deferred[i - 1]; +/******/ deferred[i] = [chunkIds, fn, priority]; +/******/ return; +/******/ } +/******/ var notFulfilled = Infinity; +/******/ for (var i = 0; i < deferred.length; i++) { +/******/ var [chunkIds, fn, priority] = deferred[i]; +/******/ var fulfilled = true; +/******/ for (var j = 0; j < chunkIds.length; j++) { +/******/ if ((priority & 1 === 0 || notFulfilled >= priority) && Object.keys(__webpack_require__.O).every((key) => (__webpack_require__.O[key](chunkIds[j])))) { +/******/ chunkIds.splice(j--, 1); +/******/ } else { +/******/ fulfilled = false; +/******/ if(priority < notFulfilled) notFulfilled = priority; +/******/ } +/******/ } +/******/ if(fulfilled) { +/******/ deferred.splice(i--, 1) +/******/ var r = fn(); +/******/ if (r !== undefined) result = r; +/******/ } +/******/ } +/******/ return result; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/compat get default export */ +/******/ (() => { +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = (module) => { +/******/ var getter = module && module.__esModule ? +/******/ () => (module['default']) : +/******/ () => (module); +/******/ __webpack_require__.d(getter, { a: getter }); +/******/ return getter; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/create fake namespace object */ +/******/ (() => { +/******/ var getProto = Object.getPrototypeOf ? (obj) => (Object.getPrototypeOf(obj)) : (obj) => (obj.__proto__); +/******/ var leafPrototypes; +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 16: return value when it's Promise-like +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = this(value); +/******/ if(mode & 8) return value; +/******/ if(typeof value === 'object' && value) { +/******/ if((mode & 4) && value.__esModule) return value; +/******/ if((mode & 16) && typeof value.then === 'function') return value; +/******/ } +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ var def = {}; +/******/ leafPrototypes = leafPrototypes || [null, getProto({}), getProto([]), getProto(getProto)]; +/******/ for(var current = mode & 2 && value; (typeof current == 'object' || typeof current == 'function') && !~leafPrototypes.indexOf(current); current = getProto(current)) { +/******/ Object.getOwnPropertyNames(current).forEach((key) => (def[key] = () => (value[key]))); +/******/ } +/******/ def['default'] = () => (value); +/******/ __webpack_require__.d(ns, def); +/******/ return ns; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/get javascript update chunk filename */ +/******/ (() => { +/******/ // This function allow to reference all chunks +/******/ __webpack_require__.hu = (chunkId) => { +/******/ // return url for filenames based on template +/******/ return "" + chunkId + "." + __webpack_require__.h() + ".hot-update.js"; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/get update manifest filename */ +/******/ (() => { +/******/ __webpack_require__.hmrF = () => ("service-worker." + __webpack_require__.h() + ".hot-update.json"); +/******/ })(); +/******/ +/******/ /* webpack/runtime/getFullHash */ +/******/ (() => { +/******/ __webpack_require__.h = () => ("f4461569ed3749bcd919") +/******/ })(); +/******/ +/******/ /* webpack/runtime/harmony module decorator */ +/******/ (() => { +/******/ __webpack_require__.hmd = (module) => { +/******/ module = Object.create(module); +/******/ if (!module.children) module.children = []; +/******/ Object.defineProperty(module, 'exports', { +/******/ enumerable: true, +/******/ set: () => { +/******/ throw new Error('ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: ' + module.id); +/******/ } +/******/ }); +/******/ return module; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/load script */ +/******/ (() => { +/******/ var inProgress = {}; +/******/ var dataWebpackPrefix = "superset:"; +/******/ // loadScript function to load a script via script tag +/******/ __webpack_require__.l = (url, done, key, chunkId) => { +/******/ if(inProgress[url]) { inProgress[url].push(done); return; } +/******/ var script, needAttach; +/******/ if(key !== undefined) { +/******/ var scripts = document.getElementsByTagName("script"); +/******/ for(var i = 0; i < scripts.length; i++) { +/******/ var s = scripts[i]; +/******/ if(s.getAttribute("src") == url || s.getAttribute("data-webpack") == dataWebpackPrefix + key) { script = s; break; } +/******/ } +/******/ } +/******/ if(!script) { +/******/ needAttach = true; +/******/ script = document.createElement('script'); +/******/ +/******/ script.charset = 'utf-8'; +/******/ if (__webpack_require__.nc) { +/******/ script.setAttribute("nonce", __webpack_require__.nc); +/******/ } +/******/ script.setAttribute("data-webpack", dataWebpackPrefix + key); +/******/ +/******/ script.src = url; +/******/ } +/******/ inProgress[url] = [done]; +/******/ var onScriptComplete = (prev, event) => { +/******/ // avoid mem leaks in IE. +/******/ script.onerror = script.onload = null; +/******/ clearTimeout(timeout); +/******/ var doneFns = inProgress[url]; +/******/ delete inProgress[url]; +/******/ script.parentNode && script.parentNode.removeChild(script); +/******/ doneFns && doneFns.forEach((fn) => (fn(event))); +/******/ if(prev) return prev(event); +/******/ } +/******/ var timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), 120000); +/******/ script.onerror = onScriptComplete.bind(null, script.onerror); +/******/ script.onload = onScriptComplete.bind(null, script.onload); +/******/ needAttach && document.head.appendChild(script); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/node module decorator */ +/******/ (() => { +/******/ __webpack_require__.nmd = (module) => { +/******/ module.paths = []; +/******/ if (!module.children) module.children = []; +/******/ return module; +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/sharing */ +/******/ (() => { +/******/ __webpack_require__.S = {}; +/******/ var initPromises = {}; +/******/ var initTokens = {}; +/******/ __webpack_require__.I = (name, initScope) => { +/******/ if(!initScope) initScope = []; +/******/ // handling circular init calls +/******/ var initToken = initTokens[name]; +/******/ if(!initToken) initToken = initTokens[name] = {}; +/******/ if(initScope.indexOf(initToken) >= 0) return; +/******/ initScope.push(initToken); +/******/ // only runs once +/******/ if(initPromises[name]) return initPromises[name]; +/******/ // creates a new share scope if needed +/******/ if(!__webpack_require__.o(__webpack_require__.S, name)) __webpack_require__.S[name] = {}; +/******/ // runs all init snippets from all modules reachable +/******/ var scope = __webpack_require__.S[name]; +/******/ var warn = (msg) => { +/******/ if (typeof console !== "undefined" && console.warn) console.warn(msg); +/******/ }; +/******/ var uniqueName = "superset"; +/******/ var register = (name, version, factory, eager) => { +/******/ var versions = scope[name] = scope[name] || {}; +/******/ var activeVersion = versions[version]; +/******/ if(!activeVersion || (!activeVersion.loaded && (!eager != !activeVersion.eager ? eager : uniqueName > activeVersion.from))) versions[version] = { get: factory, from: uniqueName, eager: !!eager }; +/******/ }; +/******/ var initExternal = (id) => { +/******/ var handleError = (err) => (warn("Initialization of sharing external failed: " + err)); +/******/ try { +/******/ var module = __webpack_require__(id); +/******/ if(!module) return; +/******/ var initFn = (module) => (module && module.init && module.init(__webpack_require__.S[name], initScope)) +/******/ if(module.then) return promises.push(module.then(initFn, handleError)); +/******/ var initResult = initFn(module); +/******/ if(initResult && initResult.then) return promises.push(initResult['catch'](handleError)); +/******/ } catch(err) { handleError(err); } +/******/ } +/******/ var promises = []; +/******/ switch(name) { +/******/ case "default": { +/******/ register("antd", "5.27.6", () => (() => (__webpack_require__(/*! ./node_modules/antd/es/index.js */ "./node_modules/antd/es/index.js"))), 1); +/******/ register("react-dom", "17.0.2", () => (() => (__webpack_require__(/*! ./node_modules/react-dom/index.js */ "./node_modules/react-dom/index.js"))), 1); +/******/ register("react", "17.0.2", () => (() => (__webpack_require__(/*! ./node_modules/react/index.js */ "./node_modules/react/index.js"))), 1); +/******/ } +/******/ break; +/******/ } +/******/ if(!promises.length) return initPromises[name] = 1; +/******/ return initPromises[name] = Promise.all(promises).then(() => (initPromises[name] = 1)); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hot module replacement */ +/******/ (() => { +/******/ var currentModuleData = {}; +/******/ var installedModules = __webpack_require__.c; +/******/ +/******/ // module and require creation +/******/ var currentChildModule; +/******/ var currentParents = []; +/******/ +/******/ // status +/******/ var registeredStatusHandlers = []; +/******/ var currentStatus = "idle"; +/******/ +/******/ // while downloading +/******/ var blockingPromises = 0; +/******/ var blockingPromisesWaiting = []; +/******/ +/******/ // The update info +/******/ var currentUpdateApplyHandlers; +/******/ var queuedInvalidatedModules; +/******/ +/******/ __webpack_require__.hmrD = currentModuleData; +/******/ +/******/ __webpack_require__.i.push(function (options) { +/******/ var module = options.module; +/******/ var require = createRequire(options.require, options.id); +/******/ module.hot = createModuleHotObject(options.id, module); +/******/ module.parents = currentParents; +/******/ module.children = []; +/******/ currentParents = []; +/******/ options.require = require; +/******/ }); +/******/ +/******/ __webpack_require__.hmrC = {}; +/******/ __webpack_require__.hmrI = {}; +/******/ +/******/ function createRequire(require, moduleId) { +/******/ var me = installedModules[moduleId]; +/******/ if (!me) return require; +/******/ var fn = function (request) { +/******/ if (me.hot.active) { +/******/ if (installedModules[request]) { +/******/ var parents = installedModules[request].parents; +/******/ if (parents.indexOf(moduleId) === -1) { +/******/ parents.push(moduleId); +/******/ } +/******/ } else { +/******/ currentParents = [moduleId]; +/******/ currentChildModule = request; +/******/ } +/******/ if (me.children.indexOf(request) === -1) { +/******/ me.children.push(request); +/******/ } +/******/ } else { +/******/ console.warn( +/******/ "[HMR] unexpected require(" + +/******/ request + +/******/ ") from disposed module " + +/******/ moduleId +/******/ ); +/******/ currentParents = []; +/******/ } +/******/ return require(request); +/******/ }; +/******/ var createPropertyDescriptor = function (name) { +/******/ return { +/******/ configurable: true, +/******/ enumerable: true, +/******/ get: function () { +/******/ return require[name]; +/******/ }, +/******/ set: function (value) { +/******/ require[name] = value; +/******/ } +/******/ }; +/******/ }; +/******/ for (var name in require) { +/******/ if (Object.prototype.hasOwnProperty.call(require, name) && name !== "e") { +/******/ Object.defineProperty(fn, name, createPropertyDescriptor(name)); +/******/ } +/******/ } +/******/ fn.e = function (chunkId, fetchPriority) { +/******/ return trackBlockingPromise(require.e(chunkId, fetchPriority)); +/******/ }; +/******/ return fn; +/******/ } +/******/ +/******/ function createModuleHotObject(moduleId, me) { +/******/ var _main = currentChildModule !== moduleId; +/******/ var hot = { +/******/ // private stuff +/******/ _acceptedDependencies: {}, +/******/ _acceptedErrorHandlers: {}, +/******/ _declinedDependencies: {}, +/******/ _selfAccepted: false, +/******/ _selfDeclined: false, +/******/ _selfInvalidated: false, +/******/ _disposeHandlers: [], +/******/ _main: _main, +/******/ _requireSelf: function () { +/******/ currentParents = me.parents.slice(); +/******/ currentChildModule = _main ? undefined : moduleId; +/******/ __webpack_require__(moduleId); +/******/ }, +/******/ +/******/ // Module API +/******/ active: true, +/******/ accept: function (dep, callback, errorHandler) { +/******/ if (dep === undefined) hot._selfAccepted = true; +/******/ else if (typeof dep === "function") hot._selfAccepted = dep; +/******/ else if (typeof dep === "object" && dep !== null) { +/******/ for (var i = 0; i < dep.length; i++) { +/******/ hot._acceptedDependencies[dep[i]] = callback || function () {}; +/******/ hot._acceptedErrorHandlers[dep[i]] = errorHandler; +/******/ } +/******/ } else { +/******/ hot._acceptedDependencies[dep] = callback || function () {}; +/******/ hot._acceptedErrorHandlers[dep] = errorHandler; +/******/ } +/******/ }, +/******/ decline: function (dep) { +/******/ if (dep === undefined) hot._selfDeclined = true; +/******/ else if (typeof dep === "object" && dep !== null) +/******/ for (var i = 0; i < dep.length; i++) +/******/ hot._declinedDependencies[dep[i]] = true; +/******/ else hot._declinedDependencies[dep] = true; +/******/ }, +/******/ dispose: function (callback) { +/******/ hot._disposeHandlers.push(callback); +/******/ }, +/******/ addDisposeHandler: function (callback) { +/******/ hot._disposeHandlers.push(callback); +/******/ }, +/******/ removeDisposeHandler: function (callback) { +/******/ var idx = hot._disposeHandlers.indexOf(callback); +/******/ if (idx >= 0) hot._disposeHandlers.splice(idx, 1); +/******/ }, +/******/ invalidate: function () { +/******/ this._selfInvalidated = true; +/******/ switch (currentStatus) { +/******/ case "idle": +/******/ currentUpdateApplyHandlers = []; +/******/ Object.keys(__webpack_require__.hmrI).forEach(function (key) { +/******/ __webpack_require__.hmrI[key]( +/******/ moduleId, +/******/ currentUpdateApplyHandlers +/******/ ); +/******/ }); +/******/ setStatus("ready"); +/******/ break; +/******/ case "ready": +/******/ Object.keys(__webpack_require__.hmrI).forEach(function (key) { +/******/ __webpack_require__.hmrI[key]( +/******/ moduleId, +/******/ currentUpdateApplyHandlers +/******/ ); +/******/ }); +/******/ break; +/******/ case "prepare": +/******/ case "check": +/******/ case "dispose": +/******/ case "apply": +/******/ (queuedInvalidatedModules = queuedInvalidatedModules || []).push( +/******/ moduleId +/******/ ); +/******/ break; +/******/ default: +/******/ // ignore requests in error states +/******/ break; +/******/ } +/******/ }, +/******/ +/******/ // Management API +/******/ check: hotCheck, +/******/ apply: hotApply, +/******/ status: function (l) { +/******/ if (!l) return currentStatus; +/******/ registeredStatusHandlers.push(l); +/******/ }, +/******/ addStatusHandler: function (l) { +/******/ registeredStatusHandlers.push(l); +/******/ }, +/******/ removeStatusHandler: function (l) { +/******/ var idx = registeredStatusHandlers.indexOf(l); +/******/ if (idx >= 0) registeredStatusHandlers.splice(idx, 1); +/******/ }, +/******/ +/******/ // inherit from previous dispose call +/******/ data: currentModuleData[moduleId] +/******/ }; +/******/ currentChildModule = undefined; +/******/ return hot; +/******/ } +/******/ +/******/ function setStatus(newStatus) { +/******/ currentStatus = newStatus; +/******/ var results = []; +/******/ +/******/ for (var i = 0; i < registeredStatusHandlers.length; i++) +/******/ results[i] = registeredStatusHandlers[i].call(null, newStatus); +/******/ +/******/ return Promise.all(results).then(function () {}); +/******/ } +/******/ +/******/ function unblock() { +/******/ if (--blockingPromises === 0) { +/******/ setStatus("ready").then(function () { +/******/ if (blockingPromises === 0) { +/******/ var list = blockingPromisesWaiting; +/******/ blockingPromisesWaiting = []; +/******/ for (var i = 0; i < list.length; i++) { +/******/ list[i](); +/******/ } +/******/ } +/******/ }); +/******/ } +/******/ } +/******/ +/******/ function trackBlockingPromise(promise) { +/******/ switch (currentStatus) { +/******/ case "ready": +/******/ setStatus("prepare"); +/******/ /* fallthrough */ +/******/ case "prepare": +/******/ blockingPromises++; +/******/ promise.then(unblock, unblock); +/******/ return promise; +/******/ default: +/******/ return promise; +/******/ } +/******/ } +/******/ +/******/ function waitForBlockingPromises(fn) { +/******/ if (blockingPromises === 0) return fn(); +/******/ return new Promise(function (resolve) { +/******/ blockingPromisesWaiting.push(function () { +/******/ resolve(fn()); +/******/ }); +/******/ }); +/******/ } +/******/ +/******/ function hotCheck(applyOnUpdate) { +/******/ if (currentStatus !== "idle") { +/******/ throw new Error("check() is only allowed in idle status"); +/******/ } +/******/ return setStatus("check") +/******/ .then(__webpack_require__.hmrM) +/******/ .then(function (update) { +/******/ if (!update) { +/******/ return setStatus(applyInvalidatedModules() ? "ready" : "idle").then( +/******/ function () { +/******/ return null; +/******/ } +/******/ ); +/******/ } +/******/ +/******/ return setStatus("prepare").then(function () { +/******/ var updatedModules = []; +/******/ currentUpdateApplyHandlers = []; +/******/ +/******/ return Promise.all( +/******/ Object.keys(__webpack_require__.hmrC).reduce(function ( +/******/ promises, +/******/ key +/******/ ) { +/******/ __webpack_require__.hmrC[key]( +/******/ update.c, +/******/ update.r, +/******/ update.m, +/******/ promises, +/******/ currentUpdateApplyHandlers, +/******/ updatedModules, +/******/ update.css +/******/ ); +/******/ return promises; +/******/ }, []) +/******/ ).then(function () { +/******/ return waitForBlockingPromises(function () { +/******/ if (applyOnUpdate) { +/******/ return internalApply(applyOnUpdate); +/******/ } +/******/ return setStatus("ready").then(function () { +/******/ return updatedModules; +/******/ }); +/******/ }); +/******/ }); +/******/ }); +/******/ }); +/******/ } +/******/ +/******/ function hotApply(options) { +/******/ if (currentStatus !== "ready") { +/******/ return Promise.resolve().then(function () { +/******/ throw new Error( +/******/ "apply() is only allowed in ready status (state: " + +/******/ currentStatus + +/******/ ")" +/******/ ); +/******/ }); +/******/ } +/******/ return internalApply(options); +/******/ } +/******/ +/******/ function internalApply(options) { +/******/ options = options || {}; +/******/ +/******/ applyInvalidatedModules(); +/******/ +/******/ var results = currentUpdateApplyHandlers.map(function (handler) { +/******/ return handler(options); +/******/ }); +/******/ currentUpdateApplyHandlers = undefined; +/******/ +/******/ var errors = results +/******/ .map(function (r) { +/******/ return r.error; +/******/ }) +/******/ .filter(Boolean); +/******/ +/******/ if (errors.length > 0) { +/******/ return setStatus("abort").then(function () { +/******/ throw errors[0]; +/******/ }); +/******/ } +/******/ +/******/ // Now in "dispose" phase +/******/ var disposePromise = setStatus("dispose"); +/******/ +/******/ results.forEach(function (result) { +/******/ if (result.dispose) result.dispose(); +/******/ }); +/******/ +/******/ // Now in "apply" phase +/******/ var applyPromise = setStatus("apply"); +/******/ +/******/ var error; +/******/ var reportError = function (err) { +/******/ if (!error) error = err; +/******/ }; +/******/ +/******/ var outdatedModules = []; +/******/ +/******/ var onAccepted = function () { +/******/ return Promise.all([disposePromise, applyPromise]).then(function () { +/******/ // handle errors in accept handlers and self accepted module load +/******/ if (error) { +/******/ return setStatus("fail").then(function () { +/******/ throw error; +/******/ }); +/******/ } +/******/ +/******/ if (queuedInvalidatedModules) { +/******/ return internalApply(options).then(function (list) { +/******/ outdatedModules.forEach(function (moduleId) { +/******/ if (list.indexOf(moduleId) < 0) list.push(moduleId); +/******/ }); +/******/ return list; +/******/ }); +/******/ } +/******/ +/******/ return setStatus("idle").then(function () { +/******/ return outdatedModules; +/******/ }); +/******/ }); +/******/ }; +/******/ +/******/ return Promise.all( +/******/ results +/******/ .filter(function (result) { +/******/ return result.apply; +/******/ }) +/******/ .map(function (result) { +/******/ return result.apply(reportError); +/******/ }) +/******/ ) +/******/ .then(function (applyResults) { +/******/ applyResults.forEach(function (modules) { +/******/ if (modules) { +/******/ for (var i = 0; i < modules.length; i++) { +/******/ outdatedModules.push(modules[i]); +/******/ } +/******/ } +/******/ }); +/******/ }) +/******/ .then(onAccepted); +/******/ } +/******/ +/******/ function applyInvalidatedModules() { +/******/ if (queuedInvalidatedModules) { +/******/ if (!currentUpdateApplyHandlers) currentUpdateApplyHandlers = []; +/******/ Object.keys(__webpack_require__.hmrI).forEach(function (key) { +/******/ queuedInvalidatedModules.forEach(function (moduleId) { +/******/ __webpack_require__.hmrI[key]( +/******/ moduleId, +/******/ currentUpdateApplyHandlers +/******/ ); +/******/ }); +/******/ }); +/******/ queuedInvalidatedModules = undefined; +/******/ return true; +/******/ } +/******/ } +/******/ })(); +/******/ +/******/ /* webpack/runtime/publicPath */ +/******/ (() => { +/******/ __webpack_require__.p = "/static/assets/"; +/******/ })(); +/******/ +/******/ /* webpack/runtime/react refresh */ +/******/ (() => { +/******/ const setup = (moduleId) => { +/******/ const refresh = { +/******/ moduleId: moduleId, +/******/ register: (type, id) => { +/******/ const typeId = moduleId + ' ' + id; +/******/ refresh.runtime.register(type, typeId); +/******/ }, +/******/ signature: () => (refresh.runtime.createSignatureFunctionForTransform()), +/******/ runtime: { +/******/ createSignatureFunctionForTransform: () => ((type) => (type)), +/******/ register: x => {} +/******/ }, +/******/ }; +/******/ return refresh; +/******/ }; +/******/ +/******/ __webpack_require__.i.push((options) => { +/******/ const originalFactory = options.factory; +/******/ options.factory = function(moduleObject, moduleExports, webpackRequire) { +/******/ const hotRequire = (request) => (webpackRequire(request)); +/******/ const createPropertyDescriptor = (name) => { +/******/ return { +/******/ configurable: true, +/******/ enumerable: true, +/******/ get: () => (webpackRequire[name]), +/******/ set: (value) => { +/******/ webpackRequire[name] = value; +/******/ }, +/******/ }; +/******/ }; +/******/ for (const name in webpackRequire) { +/******/ if (name === "$Refresh$") continue; +/******/ if (Object.prototype.hasOwnProperty.call(webpackRequire, name)) { +/******/ Object.defineProperty(hotRequire, name, createPropertyDescriptor(name)); +/******/ } +/******/ } +/******/ hotRequire.$Refresh$ = setup(options.id); +/******/ originalFactory.call(this, moduleObject, moduleExports, hotRequire); +/******/ }; +/******/ }); +/******/ })(); +/******/ +/******/ /* webpack/runtime/consumes */ +/******/ (() => { +/******/ var parseVersion = (str) => { +/******/ // see webpack/lib/util/semver.js for original code +/******/ var p=p=>{return p.split(".").map(p=>{return+p==p?+p:p})},n=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(str),r=n[1]?p(n[1]):[];return n[2]&&(r.length++,r.push.apply(r,p(n[2]))),n[3]&&(r.push([]),r.push.apply(r,p(n[3]))),r; +/******/ } +/******/ var versionLt = (a, b) => { +/******/ // see webpack/lib/util/semver.js for original code +/******/ a=parseVersion(a),b=parseVersion(b);for(var r=0;;){if(r>=a.length)return r<b.length&&"u"!=(typeof b[r])[0];var e=a[r],n=(typeof e)[0];if(r>=b.length)return"u"==n;var t=b[r],f=(typeof t)[0];if(n!=f)return"o"==n&&"n"==f||("s"==f||"u"==n);if("o"!=n&&"u"!=n&&e!=t)return e<t;r++} +/******/ } +/******/ var rangeToString = (range) => { +/******/ // see webpack/lib/util/semver.js for original code +/******/ var r=range[0],n="";if(1===range.length)return"*";if(r+.5){n+=0==r?">=":-1==r?"<":1==r?"^":2==r?"~":r>0?"=":"!=";for(var e=1,a=1;a<range.length;a++){e--,n+="u"==(typeof(t=range[a]))[0]?"-":(e>0?".":"")+(e=2,t)}return n}var g=[];for(a=1;a<range.length;a++){var t=range[a];g.push(0===t?"not("+o()+")":1===t?"("+o()+" || "+o()+")":2===t?g.pop()+" "+g.pop():rangeToString(t))}return o();function o(){return g.pop().replace(/^\((.+)\)$/,"$1")} +/******/ } +/******/ var satisfy = (range, version) => { +/******/ // see webpack/lib/util/semver.js for original code +/******/ if(0 in range){version=parseVersion(version);var e=range[0],r=e<0;r&&(e=-e-1);for(var n=0,i=1,a=!0;;i++,n++){var f,s,g=i<range.length?(typeof range[i])[0]:"";if(n>=version.length||"o"==(s=(typeof(f=version[n]))[0]))return!a||("u"==g?i>e&&!r:""==g!=r);if("u"==s){if(!a||"u"!=g)return!1}else if(a)if(g==s)if(i<=e){if(f!=range[i])return!1}else{if(r?f>range[i]:f<range[i])return!1;f!=range[i]&&(a=!1)}else if("s"!=g&&"n"!=g){if(r||i<=e)return!1;a=!1,i--}else{if(i<=e||s<g!=r)return!1;a=!1}else"s"!=g&&"n"!=g&&(a=!1,i--)}}var t=[],o=t.pop.bind(t);for(n=1;n<range.length;n++){var u=range[n];t.push(1==u?o()|o():2==u?o()&o():u?satisfy(u,version):!o())}return!!o(); +/******/ } +/******/ var exists = (scope, key) => { +/******/ return scope && __webpack_require__.o(scope, key); +/******/ } +/******/ var get = (entry) => { +/******/ entry.loaded = 1; +/******/ return entry.get() +/******/ }; +/******/ var eagerOnly = (versions) => { +/******/ return Object.keys(versions).reduce((filtered, version) => { +/******/ if (versions[version].eager) { +/******/ filtered[version] = versions[version]; +/******/ } +/******/ return filtered; +/******/ }, {}); +/******/ }; +/******/ var findLatestVersion = (scope, key, eager) => { +/******/ var versions = eager ? eagerOnly(scope[key]) : scope[key]; +/******/ var key = Object.keys(versions).reduce((a, b) => { +/******/ return !a || versionLt(a, b) ? b : a; +/******/ }, 0); +/******/ return key && versions[key]; +/******/ }; +/******/ var findSatisfyingVersion = (scope, key, requiredVersion, eager) => { +/******/ var versions = eager ? eagerOnly(scope[key]) : scope[key]; +/******/ var key = Object.keys(versions).reduce((a, b) => { +/******/ if (!satisfy(requiredVersion, b)) return a; +/******/ return !a || versionLt(a, b) ? b : a; +/******/ }, 0); +/******/ return key && versions[key] +/******/ }; +/******/ var findSingletonVersionKey = (scope, key, eager) => { +/******/ var versions = eager ? eagerOnly(scope[key]) : scope[key]; +/******/ return Object.keys(versions).reduce((a, b) => { +/******/ return !a || (!versions[a].loaded && versionLt(a, b)) ? b : a; +/******/ }, 0); +/******/ }; +/******/ var getInvalidSingletonVersionMessage = (scope, key, version, requiredVersion) => { +/******/ return "Unsatisfied version " + version + " from " + (version && scope[key][version].from) + " of shared singleton module " + key + " (required " + rangeToString(requiredVersion) + ")" +/******/ }; +/******/ var getInvalidVersionMessage = (scope, scopeName, key, requiredVersion, eager) => { +/******/ var versions = scope[key]; +/******/ return "No satisfying version (" + rangeToString(requiredVersion) + ")" + (eager ? " for eager consumption" : "") + " of shared module " + key + " found in shared scope " + scopeName + ".\n" + +/******/ "Available versions: " + Object.keys(versions).map((key) => { +/******/ return key + " from " + versions[key].from; +/******/ }).join(", "); +/******/ }; +/******/ var fail = (msg) => { +/******/ throw new Error(msg); +/******/ } +/******/ var failAsNotExist = (scopeName, key) => { +/******/ return fail("Shared module " + key + " doesn't exist in shared scope " + scopeName); +/******/ } +/******/ var warn = /*#__PURE__*/ (msg) => { +/******/ if (typeof console !== "undefined" && console.warn) console.warn(msg); +/******/ }; +/******/ var init = (fn) => (function(scopeName, key, eager, c, d) { +/******/ var promise = __webpack_require__.I(scopeName); +/******/ if (promise && promise.then && !eager) { +/******/ return promise.then(fn.bind(fn, scopeName, __webpack_require__.S[scopeName], key, false, c, d)); +/******/ } +/******/ return fn(scopeName, __webpack_require__.S[scopeName], key, eager, c, d); +/******/ }); +/******/ +/******/ var useFallback = (scopeName, key, fallback) => { +/******/ return fallback ? fallback() : failAsNotExist(scopeName, key); +/******/ } +/******/ var load = /*#__PURE__*/ init((scopeName, scope, key, eager, fallback) => { +/******/ if (!exists(scope, key)) return useFallback(scopeName, key, fallback); +/******/ return get(findLatestVersion(scope, key, eager)); +/******/ }); +/******/ var loadVersion = /*#__PURE__*/ init((scopeName, scope, key, eager, requiredVersion, fallback) => { +/******/ if (!exists(scope, key)) return useFallback(scopeName, key, fallback); +/******/ var satisfyingVersion = findSatisfyingVersion(scope, key, requiredVersion, eager); +/******/ if (satisfyingVersion) return get(satisfyingVersion); +/******/ warn(getInvalidVersionMessage(scope, scopeName, key, requiredVersion, eager)) +/******/ return get(findLatestVersion(scope, key, eager)); +/******/ }); +/******/ var loadStrictVersion = /*#__PURE__*/ init((scopeName, scope, key, eager, requiredVersion, fallback) => { +/******/ if (!exists(scope, key)) return useFallback(scopeName, key, fallback); +/******/ var satisfyingVersion = findSatisfyingVersion(scope, key, requiredVersion, eager); +/******/ if (satisfyingVersion) return get(satisfyingVersion); +/******/ if (fallback) return fallback(); +/******/ fail(getInvalidVersionMessage(scope, scopeName, key, requiredVersion, eager)); +/******/ }); +/******/ var loadSingleton = /*#__PURE__*/ init((scopeName, scope, key, eager, fallback) => { +/******/ if (!exists(scope, key)) return useFallback(scopeName, key, fallback); +/******/ var version = findSingletonVersionKey(scope, key, eager); +/******/ return get(scope[key][version]); +/******/ }); +/******/ var loadSingletonVersion = /*#__PURE__*/ init((scopeName, scope, key, eager, requiredVersion, fallback) => { +/******/ if (!exists(scope, key)) return useFallback(scopeName, key, fallback); +/******/ var version = findSingletonVersionKey(scope, key, eager); +/******/ if (!satisfy(requiredVersion, version)) { +/******/ warn(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion)); +/******/ } +/******/ return get(scope[key][version]); +/******/ }); +/******/ var loadStrictSingletonVersion = /*#__PURE__*/ init((scopeName, scope, key, eager, requiredVersion, fallback) => { +/******/ if (!exists(scope, key)) return useFallback(scopeName, key, fallback); +/******/ var version = findSingletonVersionKey(scope, key, eager); +/******/ if (!satisfy(requiredVersion, version)) { +/******/ fail(getInvalidSingletonVersionMessage(scope, key, version, requiredVersion)); +/******/ } +/******/ return get(scope[key][version]); +/******/ }); +/******/ var installedModules = {}; +/******/ var moduleToHandlerMapping = { +/******/ "webpack/sharing/consume/default/react-dom/react-dom": () => (loadSingletonVersion("default", "react-dom", true, [1,17,0,2], () => (() => (__webpack_require__(/*! react-dom */ "./node_modules/react-dom/index.js"))))), +/******/ "webpack/sharing/consume/default/react/react": () => (loadSingletonVersion("default", "react", true, [1,17,0,2], () => (() => (__webpack_require__(/*! react */ "./node_modules/react/index.js"))))) +/******/ }; +/******/ var initialConsumes = ["webpack/sharing/consume/default/react-dom/react-dom","webpack/sharing/consume/default/react/react"]; +/******/ initialConsumes.forEach((id) => { +/******/ __webpack_require__.m[id] = (module) => { +/******/ // Handle case when module is used sync +/******/ installedModules[id] = 0; +/******/ delete __webpack_require__.c[id]; +/******/ var factory = moduleToHandlerMapping[id](); +/******/ if(typeof factory !== "function") throw new Error("Shared module is not available for eager consumption: " + id); +/******/ module.exports = factory(); +/******/ } +/******/ }); +/******/ // no chunk loading of consumes +/******/ })(); +/******/ +/******/ /* webpack/runtime/jsonp chunk loading */ +/******/ (() => { +/******/ // no baseURI +/******/ +/******/ // object to store loaded and loading chunks +/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched +/******/ // [resolve, reject, Promise] = chunk loading, 0 = chunk loaded +/******/ var installedChunks = __webpack_require__.hmrS_jsonp = __webpack_require__.hmrS_jsonp || { +/******/ "service-worker": 0, +/******/ "webpack_sharing_consume_default_react-dom_react-dom-webpack_sharing_consume_default_react_rea-153fef": 0 +/******/ }; +/******/ +/******/ // no chunk on demand loading +/******/ +/******/ // no prefetching +/******/ +/******/ // no preloaded +/******/ +/******/ var currentUpdatedModulesList; +/******/ var waitingUpdateResolves = {}; +/******/ function loadUpdateChunk(chunkId, updatedModulesList) { +/******/ currentUpdatedModulesList = updatedModulesList; +/******/ return new Promise((resolve, reject) => { +/******/ waitingUpdateResolves[chunkId] = resolve; +/******/ // start update chunk loading +/******/ var url = __webpack_require__.p + __webpack_require__.hu(chunkId); +/******/ // create error before stack unwound to get useful stacktrace later +/******/ var error = new Error(); +/******/ var loadingEnded = (event) => { +/******/ if(waitingUpdateResolves[chunkId]) { +/******/ waitingUpdateResolves[chunkId] = undefined +/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type); +/******/ var realSrc = event && event.target && event.target.src; +/******/ error.message = 'Loading hot update chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')'; +/******/ error.name = 'ChunkLoadError'; +/******/ error.type = errorType; +/******/ error.request = realSrc; +/******/ reject(error); +/******/ } +/******/ }; +/******/ __webpack_require__.l(url, loadingEnded); +/******/ }); +/******/ } +/******/ +/******/ globalThis["webpackHotUpdatesuperset"] = (chunkId, moreModules, runtime) => { +/******/ for(var moduleId in moreModules) { +/******/ if(__webpack_require__.o(moreModules, moduleId)) { +/******/ currentUpdate[moduleId] = moreModules[moduleId]; +/******/ if(currentUpdatedModulesList) currentUpdatedModulesList.push(moduleId); +/******/ } +/******/ } +/******/ if(runtime) currentUpdateRuntime.push(runtime); +/******/ if(waitingUpdateResolves[chunkId]) { +/******/ waitingUpdateResolves[chunkId](); +/******/ waitingUpdateResolves[chunkId] = undefined; +/******/ } +/******/ }; +/******/ +/******/ var currentUpdateChunks; +/******/ var currentUpdate; +/******/ var currentUpdateRemovedChunks; +/******/ var currentUpdateRuntime; +/******/ function applyHandler(options) { +/******/ if (__webpack_require__.f) delete __webpack_require__.f.jsonpHmr; +/******/ currentUpdateChunks = undefined; +/******/ function getAffectedModuleEffects(updateModuleId) { +/******/ var outdatedModules = [updateModuleId]; +/******/ var outdatedDependencies = {}; +/******/ +/******/ var queue = outdatedModules.map(function (id) { +/******/ return { +/******/ chain: [id], +/******/ id: id +/******/ }; +/******/ }); +/******/ while (queue.length > 0) { +/******/ var queueItem = queue.pop(); +/******/ var moduleId = queueItem.id; +/******/ var chain = queueItem.chain; +/******/ var module = __webpack_require__.c[moduleId]; +/******/ if ( +/******/ !module || +/******/ (module.hot._selfAccepted && !module.hot._selfInvalidated) +/******/ ) +/******/ continue; +/******/ if (module.hot._selfDeclined) { +/******/ return { +/******/ type: "self-declined", +/******/ chain: chain, +/******/ moduleId: moduleId +/******/ }; +/******/ } +/******/ if (module.hot._main) { +/******/ return { +/******/ type: "unaccepted", +/******/ chain: chain, +/******/ moduleId: moduleId +/******/ }; +/******/ } +/******/ for (var i = 0; i < module.parents.length; i++) { +/******/ var parentId = module.parents[i]; +/******/ var parent = __webpack_require__.c[parentId]; +/******/ if (!parent) continue; +/******/ if (parent.hot._declinedDependencies[moduleId]) { +/******/ return { +/******/ type: "declined", +/******/ chain: chain.concat([parentId]), +/******/ moduleId: moduleId, +/******/ parentId: parentId +/******/ }; +/******/ } +/******/ if (outdatedModules.indexOf(parentId) !== -1) continue; +/******/ if (parent.hot._acceptedDependencies[moduleId]) { +/******/ if (!outdatedDependencies[parentId]) +/******/ outdatedDependencies[parentId] = []; +/******/ addAllToSet(outdatedDependencies[parentId], [moduleId]); +/******/ continue; +/******/ } +/******/ delete outdatedDependencies[parentId]; +/******/ outdatedModules.push(parentId); +/******/ queue.push({ +/******/ chain: chain.concat([parentId]), +/******/ id: parentId +/******/ }); +/******/ } +/******/ } +/******/ +/******/ return { +/******/ type: "accepted", +/******/ moduleId: updateModuleId, +/******/ outdatedModules: outdatedModules, +/******/ outdatedDependencies: outdatedDependencies +/******/ }; +/******/ } +/******/ +/******/ function addAllToSet(a, b) { +/******/ for (var i = 0; i < b.length; i++) { +/******/ var item = b[i]; +/******/ if (a.indexOf(item) === -1) a.push(item); +/******/ } +/******/ } +/******/ +/******/ // at begin all updates modules are outdated +/******/ // the "outdated" status can propagate to parents if they don't accept the children +/******/ var outdatedDependencies = {}; +/******/ var outdatedModules = []; +/******/ var appliedUpdate = {}; +/******/ +/******/ var warnUnexpectedRequire = function warnUnexpectedRequire(module) { +/******/ console.warn( +/******/ "[HMR] unexpected require(" + module.id + ") to disposed module" +/******/ ); +/******/ }; +/******/ +/******/ for (var moduleId in currentUpdate) { +/******/ if (__webpack_require__.o(currentUpdate, moduleId)) { +/******/ var newModuleFactory = currentUpdate[moduleId]; +/******/ var result = newModuleFactory +/******/ ? getAffectedModuleEffects(moduleId) +/******/ : { +/******/ type: "disposed", +/******/ moduleId: moduleId +/******/ }; +/******/ /** @type {Error|false} */ +/******/ var abortError = false; +/******/ var doApply = false; +/******/ var doDispose = false; +/******/ var chainInfo = ""; +/******/ if (result.chain) { +/******/ chainInfo = "\nUpdate propagation: " + result.chain.join(" -> "); +/******/ } +/******/ switch (result.type) { +/******/ case "self-declined": +/******/ if (options.onDeclined) options.onDeclined(result); +/******/ if (!options.ignoreDeclined) +/******/ abortError = new Error( +/******/ "Aborted because of self decline: " + +/******/ result.moduleId + +/******/ chainInfo +/******/ ); +/******/ break; +/******/ case "declined": +/******/ if (options.onDeclined) options.onDeclined(result); +/******/ if (!options.ignoreDeclined) +/******/ abortError = new Error( +/******/ "Aborted because of declined dependency: " + +/******/ result.moduleId + +/******/ " in " + +/******/ result.parentId + +/******/ chainInfo +/******/ ); +/******/ break; +/******/ case "unaccepted": +/******/ if (options.onUnaccepted) options.onUnaccepted(result); +/******/ if (!options.ignoreUnaccepted) +/******/ abortError = new Error( +/******/ "Aborted because " + moduleId + " is not accepted" + chainInfo +/******/ ); +/******/ break; +/******/ case "accepted": +/******/ if (options.onAccepted) options.onAccepted(result); +/******/ doApply = true; +/******/ break; +/******/ case "disposed": +/******/ if (options.onDisposed) options.onDisposed(result); +/******/ doDispose = true; +/******/ break; +/******/ default: +/******/ throw new Error("Unexception type " + result.type); +/******/ } +/******/ if (abortError) { +/******/ return { +/******/ error: abortError +/******/ }; +/******/ } +/******/ if (doApply) { +/******/ appliedUpdate[moduleId] = newModuleFactory; +/******/ addAllToSet(outdatedModules, result.outdatedModules); +/******/ for (moduleId in result.outdatedDependencies) { +/******/ if (__webpack_require__.o(result.outdatedDependencies, moduleId)) { +/******/ if (!outdatedDependencies[moduleId]) +/******/ outdatedDependencies[moduleId] = []; +/******/ addAllToSet( +/******/ outdatedDependencies[moduleId], +/******/ result.outdatedDependencies[moduleId] +/******/ ); +/******/ } +/******/ } +/******/ } +/******/ if (doDispose) { +/******/ addAllToSet(outdatedModules, [result.moduleId]); +/******/ appliedUpdate[moduleId] = warnUnexpectedRequire; +/******/ } +/******/ } +/******/ } +/******/ currentUpdate = undefined; +/******/ +/******/ // Store self accepted outdated modules to require them later by the module system +/******/ var outdatedSelfAcceptedModules = []; +/******/ for (var j = 0; j < outdatedModules.length; j++) { +/******/ var outdatedModuleId = outdatedModules[j]; +/******/ var module = __webpack_require__.c[outdatedModuleId]; +/******/ if ( +/******/ module && +/******/ (module.hot._selfAccepted || module.hot._main) && +/******/ // removed self-accepted modules should not be required +/******/ appliedUpdate[outdatedModuleId] !== warnUnexpectedRequire && +/******/ // when called invalidate self-accepting is not possible +/******/ !module.hot._selfInvalidated +/******/ ) { +/******/ outdatedSelfAcceptedModules.push({ +/******/ module: outdatedModuleId, +/******/ require: module.hot._requireSelf, +/******/ errorHandler: module.hot._selfAccepted +/******/ }); +/******/ } +/******/ } +/******/ +/******/ var moduleOutdatedDependencies; +/******/ +/******/ return { +/******/ dispose: function () { +/******/ currentUpdateRemovedChunks.forEach(function (chunkId) { +/******/ delete installedChunks[chunkId]; +/******/ }); +/******/ currentUpdateRemovedChunks = undefined; +/******/ +/******/ var idx; +/******/ var queue = outdatedModules.slice(); +/******/ while (queue.length > 0) { +/******/ var moduleId = queue.pop(); +/******/ var module = __webpack_require__.c[moduleId]; +/******/ if (!module) continue; +/******/ +/******/ var data = {}; +/******/ +/******/ // Call dispose handlers +/******/ var disposeHandlers = module.hot._disposeHandlers; +/******/ for (j = 0; j < disposeHandlers.length; j++) { +/******/ disposeHandlers[j].call(null, data); +/******/ } +/******/ __webpack_require__.hmrD[moduleId] = data; +/******/ +/******/ // disable module (this disables requires from this module) +/******/ module.hot.active = false; +/******/ +/******/ // remove module from cache +/******/ delete __webpack_require__.c[moduleId]; +/******/ +/******/ // when disposing there is no need to call dispose handler +/******/ delete outdatedDependencies[moduleId]; +/******/ +/******/ // remove "parents" references from all children +/******/ for (j = 0; j < module.children.length; j++) { +/******/ var child = __webpack_require__.c[module.children[j]]; +/******/ if (!child) continue; +/******/ idx = child.parents.indexOf(moduleId); +/******/ if (idx >= 0) { +/******/ child.parents.splice(idx, 1); +/******/ } +/******/ } +/******/ } +/******/ +/******/ // remove outdated dependency from module children +/******/ var dependency; +/******/ for (var outdatedModuleId in outdatedDependencies) { +/******/ if (__webpack_require__.o(outdatedDependencies, outdatedModuleId)) { +/******/ module = __webpack_require__.c[outdatedModuleId]; +/******/ if (module) { +/******/ moduleOutdatedDependencies = +/******/ outdatedDependencies[outdatedModuleId]; +/******/ for (j = 0; j < moduleOutdatedDependencies.length; j++) { +/******/ dependency = moduleOutdatedDependencies[j]; +/******/ idx = module.children.indexOf(dependency); +/******/ if (idx >= 0) module.children.splice(idx, 1); +/******/ } +/******/ } +/******/ } +/******/ } +/******/ }, +/******/ apply: function (reportError) { +/******/ var acceptPromises = []; +/******/ // insert new code +/******/ for (var updateModuleId in appliedUpdate) { +/******/ if (__webpack_require__.o(appliedUpdate, updateModuleId)) { +/******/ __webpack_require__.m[updateModuleId] = appliedUpdate[updateModuleId]; +/******/ } +/******/ } +/******/ +/******/ // run new runtime modules +/******/ for (var i = 0; i < currentUpdateRuntime.length; i++) { +/******/ currentUpdateRuntime[i](__webpack_require__); +/******/ } +/******/ +/******/ // call accept handlers +/******/ for (var outdatedModuleId in outdatedDependencies) { +/******/ if (__webpack_require__.o(outdatedDependencies, outdatedModuleId)) { +/******/ var module = __webpack_require__.c[outdatedModuleId]; +/******/ if (module) { +/******/ moduleOutdatedDependencies = +/******/ outdatedDependencies[outdatedModuleId]; +/******/ var callbacks = []; +/******/ var errorHandlers = []; +/******/ var dependenciesForCallbacks = []; +/******/ for (var j = 0; j < moduleOutdatedDependencies.length; j++) { +/******/ var dependency = moduleOutdatedDependencies[j]; +/******/ var acceptCallback = +/******/ module.hot._acceptedDependencies[dependency]; +/******/ var errorHandler = +/******/ module.hot._acceptedErrorHandlers[dependency]; +/******/ if (acceptCallback) { +/******/ if (callbacks.indexOf(acceptCallback) !== -1) continue; +/******/ callbacks.push(acceptCallback); +/******/ errorHandlers.push(errorHandler); +/******/ dependenciesForCallbacks.push(dependency); +/******/ } +/******/ } +/******/ for (var k = 0; k < callbacks.length; k++) { +/******/ var result; +/******/ try { +/******/ result = callbacks[k].call(null, moduleOutdatedDependencies); +/******/ } catch (err) { +/******/ if (typeof errorHandlers[k] === "function") { +/******/ try { +/******/ errorHandlers[k](err, { +/******/ moduleId: outdatedModuleId, +/******/ dependencyId: dependenciesForCallbacks[k] +/******/ }); +/******/ } catch (err2) { +/******/ if (options.onErrored) { +/******/ options.onErrored({ +/******/ type: "accept-error-handler-errored", +/******/ moduleId: outdatedModuleId, +/******/ dependencyId: dependenciesForCallbacks[k], +/******/ error: err2, +/******/ originalError: err +/******/ }); +/******/ } +/******/ if (!options.ignoreErrored) { +/******/ reportError(err2); +/******/ reportError(err); +/******/ } +/******/ } +/******/ } else { +/******/ if (options.onErrored) { +/******/ options.onErrored({ +/******/ type: "accept-errored", +/******/ moduleId: outdatedModuleId, +/******/ dependencyId: dependenciesForCallbacks[k], +/******/ error: err +/******/ }); +/******/ } +/******/ if (!options.ignoreErrored) { +/******/ reportError(err); +/******/ } +/******/ } +/******/ } +/******/ if (result && typeof result.then === "function") { +/******/ acceptPromises.push(result); +/******/ } +/******/ } +/******/ } +/******/ } +/******/ } +/******/ +/******/ var onAccepted = function () { +/******/ // Load self accepted modules +/******/ for (var o = 0; o < outdatedSelfAcceptedModules.length; o++) { +/******/ var item = outdatedSelfAcceptedModules[o]; +/******/ var moduleId = item.module; +/******/ try { +/******/ item.require(moduleId); +/******/ } catch (err) { +/******/ if (typeof item.errorHandler === "function") { +/******/ try { +/******/ item.errorHandler(err, { +/******/ moduleId: moduleId, +/******/ module: __webpack_require__.c[moduleId] +/******/ }); +/******/ } catch (err1) { +/******/ if (options.onErrored) { +/******/ options.onErrored({ +/******/ type: "self-accept-error-handler-errored", +/******/ moduleId: moduleId, +/******/ error: err1, +/******/ originalError: err +/******/ }); +/******/ } +/******/ if (!options.ignoreErrored) { +/******/ reportError(err1); +/******/ reportError(err); +/******/ } +/******/ } +/******/ } else { +/******/ if (options.onErrored) { +/******/ options.onErrored({ +/******/ type: "self-accept-errored", +/******/ moduleId: moduleId, +/******/ error: err +/******/ }); +/******/ } +/******/ if (!options.ignoreErrored) { +/******/ reportError(err); +/******/ } +/******/ } +/******/ } +/******/ } +/******/ }; +/******/ +/******/ return Promise.all(acceptPromises) +/******/ .then(onAccepted) +/******/ .then(function () { +/******/ return outdatedModules; +/******/ }); +/******/ } +/******/ }; +/******/ } +/******/ __webpack_require__.hmrI.jsonp = function (moduleId, applyHandlers) { +/******/ if (!currentUpdate) { +/******/ currentUpdate = {}; +/******/ currentUpdateRuntime = []; +/******/ currentUpdateRemovedChunks = []; +/******/ applyHandlers.push(applyHandler); +/******/ } +/******/ if (!__webpack_require__.o(currentUpdate, moduleId)) { +/******/ currentUpdate[moduleId] = __webpack_require__.m[moduleId]; +/******/ } +/******/ }; +/******/ __webpack_require__.hmrC.jsonp = function ( +/******/ chunkIds, +/******/ removedChunks, +/******/ removedModules, +/******/ promises, +/******/ applyHandlers, +/******/ updatedModulesList +/******/ ) { +/******/ applyHandlers.push(applyHandler); +/******/ currentUpdateChunks = {}; +/******/ currentUpdateRemovedChunks = removedChunks; +/******/ currentUpdate = removedModules.reduce(function (obj, key) { +/******/ obj[key] = false; +/******/ return obj; +/******/ }, {}); +/******/ currentUpdateRuntime = []; +/******/ chunkIds.forEach(function (chunkId) { +/******/ if ( +/******/ __webpack_require__.o(installedChunks, chunkId) && +/******/ installedChunks[chunkId] !== undefined +/******/ ) { +/******/ promises.push(loadUpdateChunk(chunkId, updatedModulesList)); +/******/ currentUpdateChunks[chunkId] = true; +/******/ } else { +/******/ currentUpdateChunks[chunkId] = false; +/******/ } +/******/ }); +/******/ if (__webpack_require__.f) { +/******/ __webpack_require__.f.jsonpHmr = function (chunkId, promises) { +/******/ if ( +/******/ currentUpdateChunks && +/******/ __webpack_require__.o(currentUpdateChunks, chunkId) && +/******/ !currentUpdateChunks[chunkId] +/******/ ) { +/******/ promises.push(loadUpdateChunk(chunkId)); +/******/ currentUpdateChunks[chunkId] = true; +/******/ } +/******/ }; +/******/ } +/******/ }; +/******/ +/******/ __webpack_require__.hmrM = () => { +/******/ if (typeof fetch === "undefined") throw new Error("No browser support: need fetch API"); +/******/ return fetch(__webpack_require__.p + __webpack_require__.hmrF()).then((response) => { +/******/ if(response.status === 404) return; // no update available +/******/ if(!response.ok) throw new Error("Failed to fetch update manifest " + response.statusText); +/******/ return response.json(); +/******/ }); +/******/ }; +/******/ +/******/ __webpack_require__.O.j = (chunkId) => (installedChunks[chunkId] === 0); +/******/ +/******/ // install a JSONP callback for chunk loading +/******/ var webpackJsonpCallback = (parentChunkLoadingFunction, data) => { +/******/ var [chunkIds, moreModules, runtime] = data; +/******/ // add "moreModules" to the modules object, +/******/ // then flag all "chunkIds" as loaded and fire callback +/******/ var moduleId, chunkId, i = 0; +/******/ if(chunkIds.some((id) => (installedChunks[id] !== 0))) { +/******/ for(moduleId in moreModules) { +/******/ if(__webpack_require__.o(moreModules, moduleId)) { +/******/ __webpack_require__.m[moduleId] = moreModules[moduleId]; +/******/ } +/******/ } +/******/ if(runtime) var result = runtime(__webpack_require__); +/******/ } +/******/ if(parentChunkLoadingFunction) parentChunkLoadingFunction(data); +/******/ for(;i < chunkIds.length; i++) { +/******/ chunkId = chunkIds[i]; +/******/ if(__webpack_require__.o(installedChunks, chunkId) && installedChunks[chunkId]) { +/******/ installedChunks[chunkId][0](); +/******/ } +/******/ installedChunks[chunkId] = 0; +/******/ } +/******/ return __webpack_require__.O(result); +/******/ } +/******/ +/******/ var chunkLoadingGlobal = globalThis["webpackChunksuperset"] = globalThis["webpackChunksuperset"] || []; +/******/ chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0)); +/******/ chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal)); +/******/ })(); +/******/ +/************************************************************************/ +/******/ +/******/ // module cache are used so entry inlining is disabled +/******/ // startup +/******/ // Load entry module and return exports +/******/ __webpack_require__.O(undefined, ["vendors","vendors-node_modules_rc-component_color-picker_es_index_js-node_modules_rc-component_mutate-o-484854","webpack_sharing_consume_default_react-dom_react-dom-webpack_sharing_consume_default_react_rea-153fef"], () => (__webpack_require__("./node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ReactRefreshEntry.js"))) +/******/ __webpack_require__.O(undefined, ["vendors","vendors-node_modules_rc-component_color-picker_es_index_js-node_modules_rc-component_mutate-o-484854","webpack_sharing_consume_default_react-dom_react-dom-webpack_sharing_consume_default_react_rea-153fef"], () => (__webpack_require__("./node_modules/webpack-dev-server/client/index.js?protocol=ws%3A&hostname=0.0.0.0&port=0&pathname=%2Fws&logging=error&overlay=%7B%22errors%22%3Atrue%2C%22warnings%22%3Afalse%2C%22runtimeErrors%22%3A%22error%2520%253D%253E%2520%21%252FResizeObserver%252F.test%28error.message%29%22%7D&reconnect=10&hot=true&live-reload=true"))) +/******/ __webpack_require__.O(undefined, ["vendors","vendors-node_modules_rc-component_color-picker_es_index_js-node_modules_rc-component_mutate-o-484854","webpack_sharing_consume_default_react-dom_react-dom-webpack_sharing_consume_default_react_rea-153fef"], () => (__webpack_require__("./node_modules/webpack/hot/dev-server.js"))) Review Comment: **Suggestion:** The service worker bundle is initializing webpack-dev-server and React Refresh client modules that assume a window/document DOM environment; when this script is executed as a service worker (which runs in a worker context without document or window), these modules will throw ReferenceError/TypeError on startup and prevent the service worker from installing. Restrict the startup to only load the actual service worker entry module so no browser-only dev tooling executes in the worker context. [logic error] <details> <summary><b>Severity Level:</b> Major ⚠️</summary> ```mdx - ❌ Service worker script fails to evaluate in worker context. - ⚠️ `./src/service-worker.ts` install/activate handlers never execute. - ⚠️ PWA-style file handling for Superset is effectively disabled. - ⚠️ Dev-only tooling bloats and destabilizes service-worker bundle. ``` </details> ```suggestion /******/ var __webpack_exports__ = __webpack_require__("./src/service-worker.ts"); ``` <details> <summary><b>Steps of Reproduction ✅ </b></summary> ```mdx 1. Build/run the Superset frontend in development mode so that the generated service worker bundle at `superset/static/service-worker.js` (shown in the final file state) is served to the browser as the service worker script. The bundle's main logic is the module bootstrap plus the startup block at lines 1461–1468. 2. In the browser, register the service worker using this script URL (e.g. `navigator.serviceWorker.register('/static/service-worker.js')`), which causes the browser to execute `superset/static/service-worker.js` in a `ServiceWorkerGlobalScope`. During evaluation, the webpack bootstrap at lines 29–59 sets up `__webpack_require__`, and the startup code at lines 1461–1468 immediately runs. 3. The startup block first calls `__webpack_require__.O(..., () => __webpack_require__("./node_modules/@pmmmwh/react-refresh-webpack-plugin/client/ReactRefreshEntry.js"))`, `__webpack_require__("./node_modules/webpack-dev-server/client/index.js?...")`, and `__webpack_require__("./node_modules/webpack/hot/dev-server.js")` (lines 1464–1466). These modules are dev-only browser clients that assume a `window`/`document` DOM environment, while the current global is `ServiceWorkerGlobalScope` with no `document`. 4. When those dev modules execute, they or the shared webpack runtime use `document`-based APIs (e.g. `document.getElementsByTagName("script")` in the chunk loader at lines 205–221) and other DOM APIs that are not available in the worker context, causing a `ReferenceError`/`TypeError` during service worker script evaluation. The exception aborts evaluation, and the service worker never reaches the actual entry module `./src/service-worker.ts`, so the SW installation/activation handlers defined there (see lines 13–19 in the eval'd source) never take effect. ``` </details> <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** superset/static/service-worker.js **Line:** 1464:1465 **Comment:** *Logic Error: The service worker bundle is initializing webpack-dev-server and React Refresh client modules that assume a window/document DOM environment; when this script is executed as a service worker (which runs in a worker context without document or window), these modules will throw ReferenceError/TypeError on startup and prevent the service worker from installing. Restrict the startup to only load the actual service worker entry module so no browser-only dev tooling executes in the worker context. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37805&comment_hash=2cb3b7bd4f9fe69f396c15b585b261e39576a80188fd01003b970b29da802c24&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37805&comment_hash=2cb3b7bd4f9fe69f396c15b585b261e39576a80188fd01003b970b29da802c24&reaction=dislike'>👎</a> ########## superset-frontend/src/features/themes/ThemeModal.tsx: ########## @@ -509,26 +523,51 @@ const ThemeModal: FunctionComponent<ThemeModalProps> = ({ annotations={toEditorAnnotations(jsonAnnotations)} /> </StyledEditorWrapper> - {canDevelopThemes && ( - <div className="apply-button-container"> + {accessibilityAnalysis && ( Review Comment: **Suggestion:** The accessibility analysis panel continues to display the last successful contrast analysis even after the JSON becomes invalid or no longer contains color tokens, because the render condition only checks for the presence of a previous analysis. This can mislead users into thinking the current configuration has been analyzed successfully when the "Analyze Contrast" button is disabled. Guard the rendering of the results with the current JSON validity and color-token checks so stale results are hidden when they no longer apply. [logic error] <details> <summary><b>Severity Level:</b> Major ⚠️</summary> ```mdx - ⚠️ Theme modal shows contrast score for outdated JSON configuration. - ⚠️ Disabled analyze button conflicts with still-visible old results. - ⚠️ Can mislead admins validating WCAG of new theme JSON. ``` </details> ```suggestion {accessibilityAnalysis && isAccessibilityJsonValid && hasColorTokens && ( ``` <details> <summary><b>Steps of Reproduction ✅ </b></summary> ```mdx 1. Open the Theme editor modal so that `ThemeModal` from `superset-frontend/src/features/themes/ThemeModal.tsx` is rendered and visible (the `Modal` returned at the bottom of the file). 2. In the JSON editor inside the modal (`EditorHost` configured around lines 160–190), paste a valid theme JSON that includes color tokens so that `useThemeAnalysis(currentTheme?.json_data)` (around lines 80–90) reports `isAccessibilityJsonValid === true` and `hasColors === true`. 3. Click the "Analyze Contrast" button (`Button` with `icon={<Icons.EyeOutlined />}` and `onClick={runAccessibilityAnalysis}` inside the `.apply-button-container` at lines 513–570). This sets `accessibilityAnalysis` in the `useThemeAnalysis` hook and renders `<AccessibilityScoreResults />` because the JSX condition `{accessibilityAnalysis && ( ... )}` (lines 526–531) evaluates to true. 4. Edit the JSON in the same editor so that it becomes invalid (e.g., delete a closing brace) or remove the color tokens entirely. `useThemeAnalysis` updates `isAccessibilityJsonValid` to `false` or `hasColors` to `false`, which disables the "Analyze Contrast" button via `disabled={!isAccessibilityJsonValid || !hasColorTokens}` (lines 545–548), but the render guard for the results still only checks `accessibilityAnalysis`. Since the previous analysis object remains truthy, `<AccessibilityScoreResults />` continues to render, showing stale results for JSON that is now invalid or lacks colors. ``` </details> <details> <summary><b>Prompt for AI Agent 🤖 </b></summary> ```mdx This is a comment left during a code review. **Path:** superset-frontend/src/features/themes/ThemeModal.tsx **Line:** 526:526 **Comment:** *Logic Error: The accessibility analysis panel continues to display the last successful contrast analysis even after the JSON becomes invalid or no longer contains color tokens, because the render condition only checks for the presence of a previous analysis. This can mislead users into thinking the current configuration has been analyzed successfully when the "Analyze Contrast" button is disabled. Guard the rendering of the results with the current JSON validity and color-token checks so stale results are hidden when they no longer apply. Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise. ``` </details> <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37805&comment_hash=2460a122f63a25ec74b486f06100015bddf64cbe8d8897f2c7d8b73a001a2f52&reaction=like'>👍</a> | <a href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F37805&comment_hash=2460a122f63a25ec74b486f06100015bddf64cbe8d8897f2c7d8b73a001a2f52&reaction=dislike'>👎</a> -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
