diff --git a/.codex/skills/component-refactoring b/.codex/skills/component-refactoring deleted file mode 120000 index 53ae67e2f2..0000000000 --- a/.codex/skills/component-refactoring +++ /dev/null @@ -1 +0,0 @@ -../../.agents/skills/component-refactoring \ No newline at end of file diff --git a/.codex/skills/frontend-code-review b/.codex/skills/frontend-code-review deleted file mode 120000 index 55654ffbd7..0000000000 --- a/.codex/skills/frontend-code-review +++ /dev/null @@ -1 +0,0 @@ -../../.agents/skills/frontend-code-review \ No newline at end of file diff --git a/.codex/skills/frontend-testing b/.codex/skills/frontend-testing deleted file mode 120000 index 092cec7745..0000000000 --- a/.codex/skills/frontend-testing +++ /dev/null @@ -1 +0,0 @@ -../../.agents/skills/frontend-testing \ No newline at end of file diff --git a/.codex/skills/orpc-contract-first b/.codex/skills/orpc-contract-first deleted file mode 120000 index da47b335c7..0000000000 --- a/.codex/skills/orpc-contract-first +++ /dev/null @@ -1 +0,0 @@ -../../.agents/skills/orpc-contract-first \ No newline at end of file diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index 4a8c61e7d2..4571fd1cd1 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -79,29 +79,6 @@ jobs: find . -name "*.py" -type f -exec sed -i.bak -E 's/"([^"]+)" \| None/Optional["\1"]/g; s/'"'"'([^'"'"']+)'"'"' \| None/Optional['"'"'\1'"'"']/g' {} \; find . -name "*.py.bak" -type f -delete - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - package_json_file: web/package.json - run_install: false - - - name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: 24 - cache: pnpm - cache-dependency-path: ./web/pnpm-lock.yaml - - - name: Install web dependencies - run: | - cd web - pnpm install --frozen-lockfile - - - name: ESLint autofix - run: | - cd web - pnpm lint:fix || true - # mdformat breaks YAML front matter in markdown files. Add --exclude for directories containing YAML front matter. - name: mdformat run: | diff --git a/api/.importlinter b/api/.importlinter index 98f87710ed..2bf68c41b7 100644 --- a/api/.importlinter +++ b/api/.importlinter @@ -106,6 +106,8 @@ forbidden_modules = core.trigger core.variables ignore_imports = + core.workflow.nodes.agent.agent_node -> core.db.session_factory + core.workflow.nodes.agent.agent_node -> models.tools core.workflow.nodes.loop.loop_node -> core.app.workflow.node_factory core.workflow.graph_engine.command_channels.redis_channel -> extensions.ext_redis core.workflow.workflow_entry -> core.app.workflow.layers.observability @@ -140,7 +142,6 @@ ignore_imports = core.workflow.nodes.llm.llm_utils -> models.provider core.workflow.nodes.llm.llm_utils -> services.credit_pool_service core.workflow.nodes.llm.node -> core.tools.signature - core.workflow.nodes.template_transform.template_transform_node -> configs core.workflow.nodes.tool.tool_node -> core.callback_handler.workflow_tool_callback_handler core.workflow.nodes.tool.tool_node -> core.tools.tool_engine core.workflow.nodes.tool.tool_node -> core.tools.tool_manager diff --git a/api/commands.py b/api/commands.py index c4f2c9edbb..93855bc3b8 100644 --- a/api/commands.py +++ b/api/commands.py @@ -739,8 +739,10 @@ def upgrade_db(): click.echo(click.style("Database migration successful!", fg="green")) - except Exception: + except Exception as e: logger.exception("Failed to execute database migration") + click.echo(click.style(f"Database migration failed: {e}", fg="red")) + raise SystemExit(1) finally: lock.release() else: diff --git a/api/controllers/console/app/app.py b/api/controllers/console/app/app.py index 8c371da596..91034f2d87 100644 --- a/api/controllers/console/app/app.py +++ b/api/controllers/console/app/app.py @@ -1,3 +1,4 @@ +import logging import uuid from datetime import datetime from typing import Any, Literal, TypeAlias @@ -54,6 +55,8 @@ ALLOW_CREATE_APP_MODES = ["chat", "agent-chat", "advanced-chat", "workflow", "co register_enum_models(console_ns, IconType) +_logger = logging.getLogger(__name__) + class AppListQuery(BaseModel): page: int = Field(default=1, ge=1, le=99999, description="Page number (1-99999)") @@ -499,6 +502,7 @@ class AppListApi(Resource): select(Workflow).where( Workflow.version == Workflow.VERSION_DRAFT, Workflow.app_id.in_(workflow_capable_app_ids), + Workflow.tenant_id == current_tenant_id, ) ) .scalars() @@ -510,12 +514,14 @@ class AppListApi(Resource): NodeType.TRIGGER_PLUGIN, } for workflow in draft_workflows: + node_id = None try: - for _, node_data in workflow.walk_nodes(): + for node_id, node_data in workflow.walk_nodes(): if node_data.get("type") in trigger_node_types: draft_trigger_app_ids.add(str(workflow.app_id)) break except Exception: + _logger.exception("error while walking nodes, workflow_id=%s, node_id=%s", workflow.id, node_id) continue for app in app_pagination.items: diff --git a/api/core/app/workflow/node_factory.py b/api/core/app/workflow/node_factory.py index a5773bbef8..6717be3ae6 100644 --- a/api/core/app/workflow/node_factory.py +++ b/api/core/app/workflow/node_factory.py @@ -47,6 +47,7 @@ class DifyNodeFactory(NodeFactory): code_providers: Sequence[type[CodeNodeProvider]] | None = None, code_limits: CodeNodeLimits | None = None, template_renderer: Jinja2TemplateRenderer | None = None, + template_transform_max_output_length: int | None = None, http_request_http_client: HttpClientProtocol | None = None, http_request_tool_file_manager_factory: Callable[[], ToolFileManager] = ToolFileManager, http_request_file_manager: FileManagerProtocol | None = None, @@ -68,6 +69,9 @@ class DifyNodeFactory(NodeFactory): max_object_array_length=dify_config.CODE_MAX_OBJECT_ARRAY_LENGTH, ) self._template_renderer = template_renderer or CodeExecutorJinja2TemplateRenderer() + self._template_transform_max_output_length = ( + template_transform_max_output_length or dify_config.TEMPLATE_TRANSFORM_MAX_LENGTH + ) self._http_request_http_client = http_request_http_client or ssrf_proxy self._http_request_tool_file_manager_factory = http_request_tool_file_manager_factory self._http_request_file_manager = http_request_file_manager or file_manager @@ -122,6 +126,7 @@ class DifyNodeFactory(NodeFactory): graph_init_params=self.graph_init_params, graph_runtime_state=self.graph_runtime_state, template_renderer=self._template_renderer, + max_output_length=self._template_transform_max_output_length, ) if node_type == NodeType.HTTP_REQUEST: diff --git a/api/core/helper/marketplace.py b/api/core/helper/marketplace.py index 25dc4ba9ed..d7b6e82062 100644 --- a/api/core/helper/marketplace.py +++ b/api/core/helper/marketplace.py @@ -6,7 +6,8 @@ from yarl import URL from configs import dify_config from core.helper.download import download_with_size_limit -from core.plugin.entities.marketplace import MarketplacePluginDeclaration +from core.plugin.entities.marketplace import MarketplacePluginDeclaration, MarketplacePluginSnapshot +from extensions.ext_redis import redis_client marketplace_api_url = URL(str(dify_config.MARKETPLACE_API_URL)) logger = logging.getLogger(__name__) @@ -43,28 +44,37 @@ def batch_fetch_plugin_by_ids(plugin_ids: list[str]) -> list[dict]: return data.get("data", {}).get("plugins", []) -def batch_fetch_plugin_manifests_ignore_deserialization_error( - plugin_ids: list[str], -) -> Sequence[MarketplacePluginDeclaration]: - if len(plugin_ids) == 0: - return [] - - url = str(marketplace_api_url / "api/v1/plugins/batch") - response = httpx.post(url, json={"plugin_ids": plugin_ids}, headers={"X-Dify-Version": dify_config.project.version}) - response.raise_for_status() - result: list[MarketplacePluginDeclaration] = [] - for plugin in response.json()["data"]["plugins"]: - try: - result.append(MarketplacePluginDeclaration.model_validate(plugin)) - except Exception: - logger.exception( - "Failed to deserialize marketplace plugin manifest for %s", plugin.get("plugin_id", "unknown") - ) - - return result - - def record_install_plugin_event(plugin_unique_identifier: str): url = str(marketplace_api_url / "api/v1/stats/plugins/install_count") response = httpx.post(url, json={"unique_identifier": plugin_unique_identifier}) response.raise_for_status() + + +def fetch_global_plugin_manifest(cache_key_prefix: str, cache_ttl: int) -> None: + """ + Fetch all plugin manifests from marketplace and cache them in Redis. + This should be called once per check cycle to populate the instance-level cache. + + Args: + cache_key_prefix: Redis key prefix for caching plugin manifests + cache_ttl: Cache TTL in seconds + + Raises: + httpx.HTTPError: If the HTTP request fails + Exception: If any other error occurs during fetching or caching + """ + url = str(marketplace_api_url / "api/v1/dist/plugins/manifest.json") + response = httpx.get(url, headers={"X-Dify-Version": dify_config.project.version}, timeout=30) + response.raise_for_status() + + raw_json = response.json() + plugins_data = raw_json.get("plugins", []) + + # Parse and cache all plugin snapshots + for plugin_data in plugins_data: + plugin_snapshot = MarketplacePluginSnapshot.model_validate(plugin_data) + redis_client.setex( + name=f"{cache_key_prefix}{plugin_snapshot.plugin_id}", + time=cache_ttl, + value=plugin_snapshot.model_dump_json(), + ) diff --git a/api/core/plugin/entities/marketplace.py b/api/core/plugin/entities/marketplace.py index e0762619e6..cf1f7ff0dd 100644 --- a/api/core/plugin/entities/marketplace.py +++ b/api/core/plugin/entities/marketplace.py @@ -1,4 +1,4 @@ -from pydantic import BaseModel, Field, model_validator +from pydantic import BaseModel, Field, computed_field, model_validator from core.model_runtime.entities.provider_entities import ProviderEntity from core.plugin.entities.endpoint import EndpointProviderDeclaration @@ -48,3 +48,15 @@ class MarketplacePluginDeclaration(BaseModel): if "tool" in data and not data["tool"]: del data["tool"] return data + + +class MarketplacePluginSnapshot(BaseModel): + org: str + name: str + latest_version: str + latest_package_identifier: str + latest_package_url: str + + @computed_field + def plugin_id(self) -> str: + return f"{self.org}/{self.name}" diff --git a/api/core/variables/variables.py b/api/core/variables/variables.py index a19c53918d..338d81df78 100644 --- a/api/core/variables/variables.py +++ b/api/core/variables/variables.py @@ -112,7 +112,7 @@ class ArrayBooleanVariable(ArrayBooleanSegment, ArrayVariable): class RAGPipelineVariable(BaseModel): belong_to_node_id: str = Field(description="belong to which node id, shared means public") - type: str = Field(description="variable type, text-input, paragraph, select, number, file, file-list") + type: str = Field(description="variable type, text-input, paragraph, select, number, file, file-list") label: str = Field(description="label") description: str | None = Field(description="description", default="") variable: str = Field(description="variable key", default="") diff --git a/api/core/workflow/nodes/agent/agent_node.py b/api/core/workflow/nodes/agent/agent_node.py index e195aebe6d..e64a83034c 100644 --- a/api/core/workflow/nodes/agent/agent_node.py +++ b/api/core/workflow/nodes/agent/agent_node.py @@ -2,7 +2,7 @@ from __future__ import annotations import json from collections.abc import Generator, Mapping, Sequence -from typing import TYPE_CHECKING, Any, cast +from typing import TYPE_CHECKING, Any, Union, cast from packaging.version import Version from pydantic import ValidationError @@ -11,6 +11,7 @@ from sqlalchemy.orm import Session from core.agent.entities import AgentToolEntity from core.agent.plugin_entities import AgentStrategyParameter +from core.db.session_factory import session_factory from core.file import File, FileTransferMethod from core.memory.token_buffer_memory import TokenBufferMemory from core.model_manager import ModelInstance, ModelManager @@ -49,6 +50,12 @@ from factories import file_factory from factories.agent_factory import get_plugin_agent_strategy from models import ToolFile from models.model import Conversation +from models.tools import ( + ApiToolProvider, + BuiltinToolProvider, + MCPToolProvider, + WorkflowToolProvider, +) from services.tools.builtin_tools_manage_service import BuiltinToolManageService from .exc import ( @@ -259,7 +266,7 @@ class AgentNode(Node[AgentNodeData]): value = cast(list[dict[str, Any]], value) tool_value = [] for tool in value: - provider_type = ToolProviderType(tool.get("type", ToolProviderType.BUILT_IN)) + provider_type = self._infer_tool_provider_type(tool, self.tenant_id) setting_params = tool.get("settings", {}) parameters = tool.get("parameters", {}) manual_input_params = [key for key, value in parameters.items() if value is not None] @@ -748,3 +755,34 @@ class AgentNode(Node[AgentNodeData]): llm_usage=llm_usage, ) ) + + @staticmethod + def _infer_tool_provider_type(tool_config: dict[str, Any], tenant_id: str) -> ToolProviderType: + provider_type_str = tool_config.get("type") + if provider_type_str: + return ToolProviderType(provider_type_str) + + provider_id = tool_config.get("provider_name") + if not provider_id: + return ToolProviderType.BUILT_IN + + with session_factory.create_session() as session: + provider_map: dict[ + type[Union[WorkflowToolProvider, MCPToolProvider, ApiToolProvider, BuiltinToolProvider]], + ToolProviderType, + ] = { + WorkflowToolProvider: ToolProviderType.WORKFLOW, + MCPToolProvider: ToolProviderType.MCP, + ApiToolProvider: ToolProviderType.API, + BuiltinToolProvider: ToolProviderType.BUILT_IN, + } + + for provider_model, provider_type in provider_map.items(): + stmt = select(provider_model).where( + provider_model.id == provider_id, + provider_model.tenant_id == tenant_id, + ) + if session.scalar(stmt): + return provider_type + + raise AgentNodeError(f"Tool provider with ID '{provider_id}' not found.") diff --git a/api/core/workflow/nodes/template_transform/template_transform_node.py b/api/core/workflow/nodes/template_transform/template_transform_node.py index f7e0bccccf..3dc8afd9be 100644 --- a/api/core/workflow/nodes/template_transform/template_transform_node.py +++ b/api/core/workflow/nodes/template_transform/template_transform_node.py @@ -1,7 +1,6 @@ from collections.abc import Mapping, Sequence from typing import TYPE_CHECKING, Any -from configs import dify_config from core.workflow.enums import NodeType, WorkflowNodeExecutionStatus from core.workflow.node_events import NodeRunResult from core.workflow.nodes.base.node import Node @@ -16,12 +15,13 @@ if TYPE_CHECKING: from core.workflow.entities import GraphInitParams from core.workflow.runtime import GraphRuntimeState -MAX_TEMPLATE_TRANSFORM_OUTPUT_LENGTH = dify_config.TEMPLATE_TRANSFORM_MAX_LENGTH +DEFAULT_TEMPLATE_TRANSFORM_MAX_OUTPUT_LENGTH = 400_000 class TemplateTransformNode(Node[TemplateTransformNodeData]): node_type = NodeType.TEMPLATE_TRANSFORM _template_renderer: Jinja2TemplateRenderer + _max_output_length: int def __init__( self, @@ -31,6 +31,7 @@ class TemplateTransformNode(Node[TemplateTransformNodeData]): graph_runtime_state: "GraphRuntimeState", *, template_renderer: Jinja2TemplateRenderer | None = None, + max_output_length: int | None = None, ) -> None: super().__init__( id=id, @@ -40,6 +41,10 @@ class TemplateTransformNode(Node[TemplateTransformNodeData]): ) self._template_renderer = template_renderer or CodeExecutorJinja2TemplateRenderer() + if max_output_length is not None and max_output_length <= 0: + raise ValueError("max_output_length must be a positive integer") + self._max_output_length = max_output_length or DEFAULT_TEMPLATE_TRANSFORM_MAX_OUTPUT_LENGTH + @classmethod def get_default_config(cls, filters: Mapping[str, object] | None = None) -> Mapping[str, object]: """ @@ -69,11 +74,11 @@ class TemplateTransformNode(Node[TemplateTransformNodeData]): except TemplateRenderError as e: return NodeRunResult(inputs=variables, status=WorkflowNodeExecutionStatus.FAILED, error=str(e)) - if len(rendered) > MAX_TEMPLATE_TRANSFORM_OUTPUT_LENGTH: + if len(rendered) > self._max_output_length: return NodeRunResult( inputs=variables, status=WorkflowNodeExecutionStatus.FAILED, - error=f"Output length exceeds {MAX_TEMPLATE_TRANSFORM_OUTPUT_LENGTH} characters", + error=f"Output length exceeds {self._max_output_length} characters", ) return NodeRunResult( diff --git a/api/migrations/versions/2025_12_25_1039-7df29de0f6be_add_credit_pool.py b/api/migrations/versions/2025_12_25_1039-7df29de0f6be_add_credit_pool.py index e89fcee7e5..6a9bfd2be0 100644 --- a/api/migrations/versions/2025_12_25_1039-7df29de0f6be_add_credit_pool.py +++ b/api/migrations/versions/2025_12_25_1039-7df29de0f6be_add_credit_pool.py @@ -10,6 +10,10 @@ import models as models import sqlalchemy as sa from sqlalchemy.dialects import postgresql + +def _is_pg(conn): + return conn.dialect.name == "postgresql" + # revision identifiers, used by Alembic. revision = '7df29de0f6be' down_revision = '03ea244985ce' @@ -19,16 +23,31 @@ depends_on = None def upgrade(): # ### commands auto generated by Alembic - please adjust! ### - op.create_table('tenant_credit_pools', - sa.Column('id', models.types.StringUUID(), server_default=sa.text('uuid_generate_v4()'), nullable=False), - sa.Column('tenant_id', models.types.StringUUID(), nullable=False), - sa.Column('pool_type', sa.String(length=40), server_default='trial', nullable=False), - sa.Column('quota_limit', sa.BigInteger(), nullable=False), - sa.Column('quota_used', sa.BigInteger(), nullable=False), - sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False), - sa.Column('updated_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False), - sa.PrimaryKeyConstraint('id', name='tenant_credit_pool_pkey') - ) + conn = op.get_bind() + + if _is_pg(conn): + op.create_table('tenant_credit_pools', + sa.Column('id', models.types.StringUUID(), server_default=sa.text('uuid_generate_v4()'), nullable=False), + sa.Column('tenant_id', models.types.StringUUID(), nullable=False), + sa.Column('pool_type', sa.String(length=40), server_default='trial', nullable=False), + sa.Column('quota_limit', sa.BigInteger(), nullable=False), + sa.Column('quota_used', sa.BigInteger(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False), + sa.PrimaryKeyConstraint('id', name='tenant_credit_pool_pkey') + ) + else: + # For MySQL and other databases, UUID should be generated at application level + op.create_table('tenant_credit_pools', + sa.Column('id', models.types.StringUUID(), nullable=False), + sa.Column('tenant_id', models.types.StringUUID(), nullable=False), + sa.Column('pool_type', sa.String(length=40), server_default='trial', nullable=False), + sa.Column('quota_limit', sa.BigInteger(), nullable=False), + sa.Column('quota_used', sa.BigInteger(), nullable=False), + sa.Column('created_at', sa.DateTime(), server_default=sa.func.current_timestamp(), nullable=False), + sa.Column('updated_at', sa.DateTime(), server_default=sa.func.current_timestamp(), nullable=False), + sa.PrimaryKeyConstraint('id', name='tenant_credit_pool_pkey') + ) with op.batch_alter_table('tenant_credit_pools', schema=None) as batch_op: batch_op.create_index('tenant_credit_pool_pool_type_idx', ['pool_type'], unique=False) batch_op.create_index('tenant_credit_pool_tenant_id_idx', ['tenant_id'], unique=False) diff --git a/api/models/model.py b/api/models/model.py index c12362f359..02ad16401a 100644 --- a/api/models/model.py +++ b/api/models/model.py @@ -2176,7 +2176,9 @@ class TenantCreditPool(TypeBase): sa.Index("tenant_credit_pool_pool_type_idx", "pool_type"), ) - id: Mapped[str] = mapped_column(StringUUID, primary_key=True, server_default=text("uuid_generate_v4()"), init=False) + id: Mapped[str] = mapped_column( + StringUUID, insert_default=lambda: str(uuid4()), default_factory=lambda: str(uuid4()), init=False + ) tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False) pool_type: Mapped[str] = mapped_column(String(40), nullable=False, default="trial", server_default="trial") quota_limit: Mapped[int] = mapped_column(BigInteger, nullable=False, default=0) diff --git a/api/schedule/check_upgradable_plugin_task.py b/api/schedule/check_upgradable_plugin_task.py index e91ce07be3..13d2f24ca0 100644 --- a/api/schedule/check_upgradable_plugin_task.py +++ b/api/schedule/check_upgradable_plugin_task.py @@ -1,16 +1,24 @@ +import logging import math import time import click import app +from core.helper.marketplace import fetch_global_plugin_manifest from extensions.ext_database import db from models.account import TenantPluginAutoUpgradeStrategy from tasks import process_tenant_plugin_autoupgrade_check_task as check_task +logger = logging.getLogger(__name__) + AUTO_UPGRADE_MINIMAL_CHECKING_INTERVAL = 15 * 60 # 15 minutes MAX_CONCURRENT_CHECK_TASKS = 20 +# Import cache constants from the task module +CACHE_REDIS_KEY_PREFIX = check_task.CACHE_REDIS_KEY_PREFIX +CACHE_REDIS_TTL = check_task.CACHE_REDIS_TTL + @app.celery.task(queue="plugin") def check_upgradable_plugin_task(): @@ -40,6 +48,22 @@ def check_upgradable_plugin_task(): ) # make sure all strategies are checked in this interval batch_interval_time = (AUTO_UPGRADE_MINIMAL_CHECKING_INTERVAL / batch_chunk_count) if batch_chunk_count > 0 else 0 + if total_strategies == 0: + click.echo(click.style("no strategies to process, skipping plugin manifest fetch.", fg="green")) + return + + # Fetch and cache all plugin manifests before processing tenants + # This reduces load on marketplace from 300k requests to 1 request per check cycle + logger.info("fetching global plugin manifest from marketplace") + try: + fetch_global_plugin_manifest(CACHE_REDIS_KEY_PREFIX, CACHE_REDIS_TTL) + logger.info("successfully fetched and cached global plugin manifest") + except Exception as e: + logger.exception("failed to fetch global plugin manifest") + click.echo(click.style(f"failed to fetch global plugin manifest: {e}", fg="red")) + click.echo(click.style("skipping plugin upgrade check for this cycle", fg="yellow")) + return + for i in range(0, total_strategies, MAX_CONCURRENT_CHECK_TASKS): batch_strategies = strategies[i : i + MAX_CONCURRENT_CHECK_TASKS] for strategy in batch_strategies: diff --git a/api/tasks/process_tenant_plugin_autoupgrade_check_task.py b/api/tasks/process_tenant_plugin_autoupgrade_check_task.py index b5e6508006..6ad04aab0d 100644 --- a/api/tasks/process_tenant_plugin_autoupgrade_check_task.py +++ b/api/tasks/process_tenant_plugin_autoupgrade_check_task.py @@ -6,8 +6,8 @@ import typing import click from celery import shared_task -from core.helper import marketplace -from core.helper.marketplace import MarketplacePluginDeclaration +from core.helper.marketplace import record_install_plugin_event +from core.plugin.entities.marketplace import MarketplacePluginSnapshot from core.plugin.entities.plugin import PluginInstallationSource from core.plugin.impl.plugin import PluginInstaller from extensions.ext_redis import redis_client @@ -16,7 +16,7 @@ from models.account import TenantPluginAutoUpgradeStrategy logger = logging.getLogger(__name__) RETRY_TIMES_OF_ONE_PLUGIN_IN_ONE_TENANT = 3 -CACHE_REDIS_KEY_PREFIX = "plugin_autoupgrade_check_task:cached_plugin_manifests:" +CACHE_REDIS_KEY_PREFIX = "plugin_autoupgrade_check_task:cached_plugin_snapshot:" CACHE_REDIS_TTL = 60 * 60 # 1 hour @@ -25,11 +25,11 @@ def _get_redis_cache_key(plugin_id: str) -> str: return f"{CACHE_REDIS_KEY_PREFIX}{plugin_id}" -def _get_cached_manifest(plugin_id: str) -> typing.Union[MarketplacePluginDeclaration, None, bool]: +def _get_cached_manifest(plugin_id: str) -> typing.Union[MarketplacePluginSnapshot, None, bool]: """ Get cached plugin manifest from Redis. Returns: - - MarketplacePluginDeclaration: if found in cache + - MarketplacePluginSnapshot: if found in cache - None: if cached as not found (marketplace returned no result) - False: if not in cache at all """ @@ -43,76 +43,31 @@ def _get_cached_manifest(plugin_id: str) -> typing.Union[MarketplacePluginDeclar if cached_json is None: return None - return MarketplacePluginDeclaration.model_validate(cached_json) + return MarketplacePluginSnapshot.model_validate(cached_json) except Exception: logger.exception("Failed to get cached manifest for plugin %s", plugin_id) return False -def _set_cached_manifest(plugin_id: str, manifest: typing.Union[MarketplacePluginDeclaration, None]) -> None: - """ - Cache plugin manifest in Redis. - Args: - plugin_id: The plugin ID - manifest: The manifest to cache, or None if not found in marketplace - """ - try: - key = _get_redis_cache_key(plugin_id) - if manifest is None: - # Cache the fact that this plugin was not found - redis_client.setex(key, CACHE_REDIS_TTL, json.dumps(None)) - else: - # Cache the manifest data - redis_client.setex(key, CACHE_REDIS_TTL, manifest.model_dump_json()) - except Exception: - # If Redis fails, continue without caching - # traceback.print_exc() - logger.exception("Failed to set cached manifest for plugin %s", plugin_id) - - def marketplace_batch_fetch_plugin_manifests( plugin_ids_plain_list: list[str], -) -> list[MarketplacePluginDeclaration]: - """Fetch plugin manifests with Redis caching support.""" - cached_manifests: dict[str, typing.Union[MarketplacePluginDeclaration, None]] = {} - not_cached_plugin_ids: list[str] = [] +) -> list[MarketplacePluginSnapshot]: + """ + Fetch plugin manifests from Redis cache only. + This function assumes fetch_global_plugin_manifest() has been called + to pre-populate the cache with all marketplace plugins. + """ + result: list[MarketplacePluginSnapshot] = [] # Check Redis cache for each plugin for plugin_id in plugin_ids_plain_list: cached_result = _get_cached_manifest(plugin_id) - if cached_result is False: - # Not in cache, need to fetch - not_cached_plugin_ids.append(plugin_id) - else: - # Either found manifest or cached as None (not found in marketplace) - # At this point, cached_result is either MarketplacePluginDeclaration or None - if isinstance(cached_result, bool): - # This should never happen due to the if condition above, but for type safety - continue - cached_manifests[plugin_id] = cached_result + if not isinstance(cached_result, MarketplacePluginSnapshot): + # cached_result is False (not in cache) or None (cached as not found) + logger.warning("plugin %s not found in cache, skipping", plugin_id) + continue - # Fetch uncached plugins from marketplace - if not_cached_plugin_ids: - manifests = marketplace.batch_fetch_plugin_manifests_ignore_deserialization_error(not_cached_plugin_ids) - - # Cache the fetched manifests - for manifest in manifests: - cached_manifests[manifest.plugin_id] = manifest - _set_cached_manifest(manifest.plugin_id, manifest) - - # Cache plugins that were not found in marketplace - fetched_plugin_ids = {manifest.plugin_id for manifest in manifests} - for plugin_id in not_cached_plugin_ids: - if plugin_id not in fetched_plugin_ids: - cached_manifests[plugin_id] = None - _set_cached_manifest(plugin_id, None) - - # Build result list from cached manifests - result: list[MarketplacePluginDeclaration] = [] - for plugin_id in plugin_ids_plain_list: - cached_manifest: typing.Union[MarketplacePluginDeclaration, None] = cached_manifests.get(plugin_id) - if cached_manifest is not None: - result.append(cached_manifest) + result.append(cached_result) return result @@ -211,7 +166,7 @@ def process_tenant_plugin_autoupgrade_check_task( # execute upgrade new_unique_identifier = manifest.latest_package_identifier - marketplace.record_install_plugin_event(new_unique_identifier) + record_install_plugin_event(new_unique_identifier) click.echo( click.style( f"Upgrade plugin: {original_unique_identifier} -> {new_unique_identifier}", diff --git a/api/tests/unit_tests/core/workflow/nodes/agent/__init__.py b/api/tests/unit_tests/core/workflow/nodes/agent/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/api/tests/unit_tests/core/workflow/nodes/agent/test_agent_node.py b/api/tests/unit_tests/core/workflow/nodes/agent/test_agent_node.py new file mode 100644 index 0000000000..a95892d0b6 --- /dev/null +++ b/api/tests/unit_tests/core/workflow/nodes/agent/test_agent_node.py @@ -0,0 +1,197 @@ +from unittest.mock import MagicMock, patch + +import pytest + +from core.tools.entities.tool_entities import ToolProviderType +from core.workflow.nodes.agent.agent_node import AgentNode + + +class TestInferToolProviderType: + """Test cases for AgentNode._infer_tool_provider_type method.""" + + def test_infer_type_from_config_workflow(self): + """Test inferring workflow provider type from config.""" + tool_config = { + "type": "workflow", + "provider_name": "workflow-provider-id", + } + tenant_id = "test-tenant" + + result = AgentNode._infer_tool_provider_type(tool_config, tenant_id) + + assert result == ToolProviderType.WORKFLOW + + def test_infer_type_from_config_builtin(self): + """Test inferring builtin provider type from config.""" + tool_config = { + "type": "builtin", + "provider_name": "builtin-provider-id", + } + tenant_id = "test-tenant" + + result = AgentNode._infer_tool_provider_type(tool_config, tenant_id) + + assert result == ToolProviderType.BUILT_IN + + def test_infer_type_from_config_api(self): + """Test inferring API provider type from config.""" + tool_config = { + "type": "api", + "provider_name": "api-provider-id", + } + tenant_id = "test-tenant" + + result = AgentNode._infer_tool_provider_type(tool_config, tenant_id) + + assert result == ToolProviderType.API + + def test_infer_type_from_config_mcp(self): + """Test inferring MCP provider type from config.""" + tool_config = { + "type": "mcp", + "provider_name": "mcp-provider-id", + } + tenant_id = "test-tenant" + + result = AgentNode._infer_tool_provider_type(tool_config, tenant_id) + + assert result == ToolProviderType.MCP + + def test_infer_type_invalid_config_value_raises_error(self): + """Test that invalid type value in config raises ValueError.""" + tool_config = { + "type": "invalid-type", + "provider_name": "workflow-provider-id", + } + tenant_id = "test-tenant" + + with pytest.raises(ValueError): + AgentNode._infer_tool_provider_type(tool_config, tenant_id) + + def test_infer_workflow_type_from_database(self): + """Test inferring workflow provider type from database.""" + tool_config = { + "provider_name": "workflow-provider-id", + } + tenant_id = "test-tenant" + + with patch("core.db.session_factory.session_factory.create_session") as mock_create_session: + mock_session = MagicMock() + mock_create_session.return_value.__enter__.return_value = mock_session + + # First query (WorkflowToolProvider) returns a result + mock_session.scalar.return_value = True + + result = AgentNode._infer_tool_provider_type(tool_config, tenant_id) + + assert result == ToolProviderType.WORKFLOW + # Should only query once (after finding WorkflowToolProvider) + assert mock_session.scalar.call_count == 1 + + def test_infer_mcp_type_from_database(self): + """Test inferring MCP provider type from database.""" + tool_config = { + "provider_name": "mcp-provider-id", + } + tenant_id = "test-tenant" + + with patch("core.db.session_factory.session_factory.create_session") as mock_create_session: + mock_session = MagicMock() + mock_create_session.return_value.__enter__.return_value = mock_session + + # First query (WorkflowToolProvider) returns None + # Second query (MCPToolProvider) returns a result + mock_session.scalar.side_effect = [None, True] + + result = AgentNode._infer_tool_provider_type(tool_config, tenant_id) + + assert result == ToolProviderType.MCP + assert mock_session.scalar.call_count == 2 + + def test_infer_api_type_from_database(self): + """Test inferring API provider type from database.""" + tool_config = { + "provider_name": "api-provider-id", + } + tenant_id = "test-tenant" + + with patch("core.db.session_factory.session_factory.create_session") as mock_create_session: + mock_session = MagicMock() + mock_create_session.return_value.__enter__.return_value = mock_session + + # First query (WorkflowToolProvider) returns None + # Second query (MCPToolProvider) returns None + # Third query (ApiToolProvider) returns a result + mock_session.scalar.side_effect = [None, None, True] + + result = AgentNode._infer_tool_provider_type(tool_config, tenant_id) + + assert result == ToolProviderType.API + assert mock_session.scalar.call_count == 3 + + def test_infer_builtin_type_from_database(self): + """Test inferring builtin provider type from database.""" + tool_config = { + "provider_name": "builtin-provider-id", + } + tenant_id = "test-tenant" + + with patch("core.db.session_factory.session_factory.create_session") as mock_create_session: + mock_session = MagicMock() + mock_create_session.return_value.__enter__.return_value = mock_session + + # First three queries return None + # Fourth query (BuiltinToolProvider) returns a result + mock_session.scalar.side_effect = [None, None, None, True] + + result = AgentNode._infer_tool_provider_type(tool_config, tenant_id) + + assert result == ToolProviderType.BUILT_IN + assert mock_session.scalar.call_count == 4 + + def test_infer_type_default_when_not_found(self): + """Test raising AgentNodeError when provider is not found in database.""" + tool_config = { + "provider_name": "unknown-provider-id", + } + tenant_id = "test-tenant" + + with patch("core.db.session_factory.session_factory.create_session") as mock_create_session: + mock_session = MagicMock() + mock_create_session.return_value.__enter__.return_value = mock_session + + # All queries return None + mock_session.scalar.return_value = None + + # Current implementation raises AgentNodeError when provider not found + from core.workflow.nodes.agent.exc import AgentNodeError + + with pytest.raises(AgentNodeError, match="Tool provider with ID 'unknown-provider-id' not found"): + AgentNode._infer_tool_provider_type(tool_config, tenant_id) + + def test_infer_type_default_when_no_provider_name(self): + """Test defaulting to BUILT_IN when provider_name is missing.""" + tool_config = {} + tenant_id = "test-tenant" + + result = AgentNode._infer_tool_provider_type(tool_config, tenant_id) + + assert result == ToolProviderType.BUILT_IN + + def test_infer_type_database_exception_propagates(self): + """Test that database exception propagates (current implementation doesn't catch it).""" + tool_config = { + "provider_name": "provider-id", + } + tenant_id = "test-tenant" + + with patch("core.db.session_factory.session_factory.create_session") as mock_create_session: + mock_session = MagicMock() + mock_create_session.return_value.__enter__.return_value = mock_session + + # Database query raises exception + mock_session.scalar.side_effect = Exception("Database error") + + # Current implementation doesn't catch exceptions, so it propagates + with pytest.raises(Exception, match="Database error"): + AgentNode._infer_tool_provider_type(tool_config, tenant_id) diff --git a/api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py b/api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py index 66d6c3c56b..61bdcbd250 100644 --- a/api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py +++ b/api/tests/unit_tests/core/workflow/nodes/template_transform/template_transform_node_spec.py @@ -217,7 +217,6 @@ class TestTemplateTransformNode: @patch( "core.workflow.nodes.template_transform.template_transform_node.CodeExecutorJinja2TemplateRenderer.render_template" ) - @patch("core.workflow.nodes.template_transform.template_transform_node.MAX_TEMPLATE_TRANSFORM_OUTPUT_LENGTH", 10) def test_run_output_length_exceeds_limit( self, mock_execute, basic_node_data, mock_graph, mock_graph_runtime_state, graph_init_params ): @@ -231,6 +230,7 @@ class TestTemplateTransformNode: graph_init_params=graph_init_params, graph=mock_graph, graph_runtime_state=mock_graph_runtime_state, + max_output_length=10, ) result = node._run() diff --git a/web/app/components/base/effect/index.stories.tsx b/web/app/components/base/effect/index.stories.tsx index 36a0e668cf..8452e9aefe 100644 --- a/web/app/components/base/effect/index.stories.tsx +++ b/web/app/components/base/effect/index.stories.tsx @@ -1,4 +1,3 @@ -/* eslint-disable tailwindcss/classnames-order */ import type { Meta, StoryObj } from '@storybook/nextjs-vite' import Effect from '.' @@ -29,8 +28,8 @@ type Story = StoryObj export const Playground: Story = { render: () => (
- - + +
Accent glow
diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/error-message.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/error-message.tsx index f0a1fb64a9..9bc97d9970 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/error-message.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/error-message.tsx @@ -14,7 +14,6 @@ const ErrorMessage = ({ errorMsg, }: ErrorMessageProps) => { return ( - // eslint-disable-next-line tailwindcss/migration-from-tailwind-2
import type { PrecacheEntry, SerwistGlobalConfig } from 'serwist' -import { CacheableResponsePlugin, CacheFirst, ExpirationPlugin, NetworkFirst, Serwist, StaleWhileRevalidate } from 'serwist' +import { defaultCache } from '@serwist/turbopack/worker' +import { Serwist } from 'serwist' +import { withLeadingSlash } from 'ufo' declare global { // eslint-disable-next-line ts/consistent-type-definitions @@ -18,78 +20,30 @@ const scopePathname = new URL(self.registration.scope).pathname const basePath = scopePathname.replace(/\/serwist\/$/, '').replace(/\/$/, '') const offlineUrl = `${basePath}/_offline.html` +const normalizeManifestUrl = (url: string): string => { + if (url.startsWith('/serwist/')) + return url.replace(/^\/serwist\//, '/') + + return withLeadingSlash(url) +} + +const manifest = self.__SW_MANIFEST?.map((entry) => { + if (typeof entry === 'string') + return normalizeManifestUrl(entry) + + return { + ...entry, + url: normalizeManifestUrl(entry.url), + } +}) + const serwist = new Serwist({ - precacheEntries: self.__SW_MANIFEST, + precacheEntries: manifest, skipWaiting: true, disableDevLogs: true, clientsClaim: true, navigationPreload: true, - runtimeCaching: [ - { - matcher: ({ url }) => url.origin === 'https://fonts.googleapis.com', - handler: new CacheFirst({ - cacheName: 'google-fonts', - plugins: [ - new CacheableResponsePlugin({ statuses: [0, 200] }), - new ExpirationPlugin({ - maxEntries: 4, - maxAgeSeconds: 365 * 24 * 60 * 60, - }), - ], - }), - }, - { - matcher: ({ url }) => url.origin === 'https://fonts.gstatic.com', - handler: new CacheFirst({ - cacheName: 'google-fonts-webfonts', - plugins: [ - new CacheableResponsePlugin({ statuses: [0, 200] }), - new ExpirationPlugin({ - maxEntries: 4, - maxAgeSeconds: 365 * 24 * 60 * 60, - }), - ], - }), - }, - { - matcher: ({ request }) => request.destination === 'image', - handler: new CacheFirst({ - cacheName: 'images', - plugins: [ - new CacheableResponsePlugin({ statuses: [0, 200] }), - new ExpirationPlugin({ - maxEntries: 64, - maxAgeSeconds: 30 * 24 * 60 * 60, - }), - ], - }), - }, - { - matcher: ({ request }) => request.destination === 'script' || request.destination === 'style', - handler: new StaleWhileRevalidate({ - cacheName: 'static-resources', - plugins: [ - new ExpirationPlugin({ - maxEntries: 32, - maxAgeSeconds: 24 * 60 * 60, - }), - ], - }), - }, - { - matcher: ({ url, sameOrigin }) => sameOrigin && url.pathname.startsWith('/api/'), - handler: new NetworkFirst({ - cacheName: 'api-cache', - networkTimeoutSeconds: 10, - plugins: [ - new ExpirationPlugin({ - maxEntries: 16, - maxAgeSeconds: 60 * 60, - }), - ], - }), - }, - ], + runtimeCaching: defaultCache, fallbacks: { entries: [ { diff --git a/web/docs/lint.md b/web/docs/lint.md index 051f9e6ecd..a0ec9d58ad 100644 --- a/web/docs/lint.md +++ b/web/docs/lint.md @@ -38,6 +38,11 @@ pnpm lint:tss This command lints the entire project and is intended for final verification before committing or pushing changes. +### Introducing New Plugins or Rules + +If a new rule causes many existing code errors or automatic fixes generate too many diffs, do not use the `--fix` option for automatic fixes. +You can introduce the rule first, then use the `--suppress-all` option to temporarily suppress these errors, and gradually fix them in subsequent changes. + ## Type Check You should be able to see suggestions from TypeScript in your editor for all open files. diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json index 9ae9266375..f1e7af211d 100644 --- a/web/eslint-suppressions.json +++ b/web/eslint-suppressions.json @@ -81,6 +81,11 @@ "count": 1 } }, + "app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/time-range-picker/range-selector.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/__tests__/svg-attribute-error-reproduction.spec.tsx": { "no-console": { "count": 19 @@ -125,6 +130,9 @@ } }, "app/account/(commonLayout)/account-page/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + }, "ts/no-explicit-any": { "count": 1 } @@ -261,6 +269,9 @@ } }, "app/components/app/app-publisher/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 6 } @@ -275,6 +286,11 @@ "count": 2 } }, + "app/components/app/configuration/config-prompt/confirm-add-var/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/app/configuration/config-prompt/conversation-history/edit-modal.tsx": { "ts/no-explicit-any": { "count": 1 @@ -285,6 +301,11 @@ "count": 1 } }, + "app/components/app/configuration/config-prompt/message-type-selector.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/app/configuration/config-prompt/simple-prompt-input.tsx": { "ts/no-explicit-any": { "count": 3 @@ -295,16 +316,46 @@ "count": 6 } }, + "app/components/app/configuration/config-var/config-select/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/app/configuration/config-var/input-type-icon.tsx": { + "tailwindcss/enforce-consistent-class-order": { + "count": 1 + } + }, + "app/components/app/configuration/config-var/select-type-item/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/app/configuration/config-var/select-var-type.tsx": { "ts/no-explicit-any": { "count": 1 } }, + "app/components/app/configuration/config-var/var-item.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/app/configuration/config-vision/index.spec.tsx": { "ts/no-explicit-any": { "count": 1 } }, + "app/components/app/configuration/config-vision/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + } + }, + "app/components/app/configuration/config-vision/param-config-content.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/app/configuration/config/agent-setting-button.spec.tsx": { "ts/no-explicit-any": { "count": 2 @@ -324,6 +375,9 @@ } }, "app/components/app/configuration/config/agent/agent-tools/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 9 } @@ -337,11 +391,22 @@ "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 4 } }, + "app/components/app/configuration/config/agent/prompt-editor.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/app/configuration/config/assistant-type-picker/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 3 + }, "ts/no-explicit-any": { "count": 1 } @@ -354,6 +419,11 @@ "count": 1 } }, + "app/components/app/configuration/config/automatic/idea-output.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/app/configuration/config/automatic/instruction-editor.tsx": { "ts/no-explicit-any": { "count": 2 @@ -369,10 +439,18 @@ "count": 1 } }, + "app/components/app/configuration/config/automatic/version-selector.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/app/configuration/config/code-generator/get-code-generator-res.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 4 }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 2 } @@ -392,11 +470,26 @@ "count": 6 } }, + "app/components/app/configuration/ctrl-btn-group/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/app/configuration/dataset-config/card-item/index.spec.tsx": { "ts/no-explicit-any": { "count": 1 } }, + "app/components/app/configuration/dataset-config/context-var/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/app/configuration/dataset-config/context-var/var-picker.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 4 + } + }, "app/components/app/configuration/dataset-config/index.spec.tsx": { "ts/no-explicit-any": { "count": 37 @@ -420,6 +513,9 @@ "app/components/app/configuration/dataset-config/select-dataset/index.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 2 + }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 } }, "app/components/app/configuration/dataset-config/settings-modal/index.tsx": { @@ -498,6 +594,9 @@ "style/multiline-ternary": { "count": 2 }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + }, "ts/no-explicit-any": { "count": 24 } @@ -513,15 +612,28 @@ } }, "app/components/app/configuration/tools/external-data-tool-modal.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 2 } }, + "app/components/app/configuration/tools/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/app/create-app-dialog/app-card/index.spec.tsx": { "ts/no-explicit-any": { "count": 1 } }, + "app/components/app/create-app-dialog/app-card/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/app/create-app-dialog/app-list/index.spec.tsx": { "ts/no-explicit-any": { "count": 1 @@ -548,6 +660,11 @@ "count": 1 } }, + "app/components/app/create-from-dsl-modal/uploader.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/app/log/filter.tsx": { "react-refresh/only-export-components": { "count": 1 @@ -579,10 +696,18 @@ "count": 1 } }, + "app/components/app/overview/apikey-info-panel/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 3 + } + }, "app/components/app/overview/app-card.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 3 }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 1 } @@ -595,6 +720,9 @@ "app/components/app/overview/embedded/index.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 + }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 } }, "app/components/app/overview/settings/index.tsx": { @@ -627,10 +755,18 @@ } }, "app/components/app/text-generate/item/result-tab.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 2 } }, + "app/components/app/text-generate/saved-items/no-data/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/app/workflow-log/filter.tsx": { "react-refresh/only-export-components": { "count": 1 @@ -720,6 +856,11 @@ "count": 2 } }, + "app/components/base/alert.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/base/amplitude/AmplitudeProvider.tsx": { "react-refresh/only-export-components": { "count": 1 @@ -735,6 +876,11 @@ "count": 1 } }, + "app/components/base/app-icon/index.tsx": { + "tailwindcss/enforce-consistent-class-order": { + "count": 15 + } + }, "app/components/base/audio-btn/audio.ts": { "node/prefer-global/buffer": { "count": 1 @@ -756,6 +902,11 @@ "count": 1 } }, + "app/components/base/auto-height-textarea/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/base/avatar/index.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 @@ -778,6 +929,12 @@ }, "react/no-nested-component-definitions": { "count": 1 + }, + "tailwindcss/enforce-consistent-class-order": { + "count": 1 + }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 } }, "app/components/base/button/add-button.stories.tsx": { @@ -820,6 +977,11 @@ "count": 2 } }, + "app/components/base/chat/chat-with-history/header/operation.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/base/chat/chat-with-history/hooks.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 4 @@ -989,6 +1151,14 @@ "count": 1 } }, + "app/components/base/divider/index.tsx": { + "tailwindcss/enforce-consistent-class-order": { + "count": 2 + }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/base/emoji-picker/Inner.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 @@ -1025,6 +1195,14 @@ "app/components/base/features/new-feature-panel/conversation-opener/modal.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 + }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/base/features/new-feature-panel/dialog-wrapper.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 } }, "app/components/base/features/new-feature-panel/feature-card.tsx": { @@ -1057,6 +1235,11 @@ "count": 1 } }, + "app/components/base/file-thumb/index.tsx": { + "tailwindcss/enforce-consistent-class-order": { + "count": 1 + } + }, "app/components/base/file-uploader/dynamic-pdf-preview.tsx": { "ts/no-explicit-any": { "count": 1 @@ -1065,6 +1248,14 @@ "app/components/base/file-uploader/file-list-in-log.tsx": { "react/no-missing-key": { "count": 1 + }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/base/file-uploader/file-uploader-in-chat-input/file-image-item.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 } }, "app/components/base/file-uploader/hooks.ts": { @@ -1232,6 +1423,14 @@ "app/components/base/input/index.tsx": { "react-refresh/only-export-components": { "count": 1 + }, + "tailwindcss/enforce-consistent-class-order": { + "count": 2 + } + }, + "app/components/base/linked-apps-panel/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 } }, "app/components/base/logo/dify-logo.tsx": { @@ -1338,6 +1537,9 @@ "regexp/no-super-linear-backtracking": { "count": 3 }, + "tailwindcss/enforce-consistent-class-order": { + "count": 2 + }, "ts/no-explicit-any": { "count": 2 } @@ -1392,6 +1594,9 @@ "app/components/base/node-status/index.tsx": { "react-refresh/only-export-components": { "count": 1 + }, + "tailwindcss/enforce-consistent-class-order": { + "count": 1 } }, "app/components/base/notion-connector/index.stories.tsx": { @@ -1450,31 +1655,104 @@ "count": 1 } }, + "app/components/base/prompt-editor/plugins/component-picker-block/prompt-option.tsx": { + "tailwindcss/no-duplicate-classes": { + "count": 2 + } + }, "app/components/base/prompt-editor/plugins/context-block/component.tsx": { + "tailwindcss/no-duplicate-classes": { + "count": 1 + }, "ts/no-explicit-any": { "count": 1 } }, + "app/components/base/prompt-editor/plugins/context-block/index.tsx": { + "react-refresh/only-export-components": { + "count": 2 + } + }, + "app/components/base/prompt-editor/plugins/current-block/component.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + } + }, + "app/components/base/prompt-editor/plugins/current-block/index.tsx": { + "react-refresh/only-export-components": { + "count": 2 + } + }, "app/components/base/prompt-editor/plugins/draggable-plugin/index.tsx": { "ts/no-explicit-any": { "count": 2 } }, + "app/components/base/prompt-editor/plugins/error-message-block/component.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + } + }, + "app/components/base/prompt-editor/plugins/error-message-block/index.tsx": { + "react-refresh/only-export-components": { + "count": 2 + } + }, "app/components/base/prompt-editor/plugins/history-block/component.tsx": { "ts/no-explicit-any": { "count": 1 } }, + "app/components/base/prompt-editor/plugins/history-block/index.tsx": { + "react-refresh/only-export-components": { + "count": 2 + } + }, "app/components/base/prompt-editor/plugins/hitl-input-block/hitl-input-block-replacement-block.tsx": { "ts/no-explicit-any": { "count": 1 } }, + "app/components/base/prompt-editor/plugins/hitl-input-block/index.tsx": { + "react-refresh/only-export-components": { + "count": 3 + } + }, + "app/components/base/prompt-editor/plugins/hitl-input-block/input-field.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/base/prompt-editor/plugins/hitl-input-block/tag-label.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/base/prompt-editor/plugins/last-run-block/component.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + } + }, + "app/components/base/prompt-editor/plugins/last-run-block/index.tsx": { + "react-refresh/only-export-components": { + "count": 2 + } + }, "app/components/base/prompt-editor/plugins/on-blur-or-focus-block.tsx": { "ts/no-explicit-any": { "count": 1 } }, + "app/components/base/prompt-editor/plugins/query-block/index.tsx": { + "react-refresh/only-export-components": { + "count": 2 + } + }, + "app/components/base/prompt-editor/plugins/request-url-block/index.tsx": { + "react-refresh/only-export-components": { + "count": 2 + } + }, "app/components/base/prompt-editor/plugins/shortcuts-popup-plugin/index.tsx": { "ts/no-explicit-any": { "count": 2 @@ -1485,6 +1763,16 @@ "count": 2 } }, + "app/components/base/prompt-editor/plugins/variable-block/index.tsx": { + "react-refresh/only-export-components": { + "count": 2 + } + }, + "app/components/base/prompt-editor/plugins/workflow-variable-block/index.tsx": { + "react-refresh/only-export-components": { + "count": 4 + } + }, "app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx": { "ts/no-explicit-any": { "count": 5 @@ -1552,6 +1840,11 @@ "count": 1 } }, + "app/components/base/select/locale.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/base/slider/index.stories.tsx": { "no-console": { "count": 2 @@ -1560,6 +1853,11 @@ "count": 1 } }, + "app/components/base/slider/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/base/sort/index.tsx": { "ts/no-explicit-any": { "count": 2 @@ -1581,6 +1879,14 @@ "app/components/base/switch/index.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 + }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/base/tab-slider-plain/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 } }, "app/components/base/tab-slider/index.tsx": { @@ -1596,6 +1902,11 @@ "count": 1 } }, + "app/components/base/tag-management/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/base/text-generation/hooks.ts": { "ts/no-explicit-any": { "count": 1 @@ -1612,6 +1923,9 @@ "app/components/base/textarea/index.tsx": { "react-refresh/only-export-components": { "count": 1 + }, + "tailwindcss/enforce-consistent-class-order": { + "count": 3 } }, "app/components/base/toast/index.tsx": { @@ -1632,6 +1946,11 @@ "count": 1 } }, + "app/components/base/voice-input/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + } + }, "app/components/base/voice-input/utils.ts": { "ts/no-explicit-any": { "count": 4 @@ -1682,6 +2001,11 @@ "count": 1 } }, + "app/components/billing/pricing/plans/self-hosted-plan-item/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/billing/upgrade-btn/index.spec.tsx": { "ts/no-explicit-any": { "count": 9 @@ -1698,6 +2022,9 @@ } }, "app/components/custom/custom-web-app-brand/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 2 } @@ -1737,6 +2064,11 @@ "count": 1 } }, + "app/components/datasets/create-from-pipeline/header.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/datasets/create/file-preview/index.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 @@ -1752,6 +2084,11 @@ "count": 1 } }, + "app/components/datasets/create/step-one/upgrade-card.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/datasets/create/step-three/index.spec.tsx": { "ts/no-explicit-any": { "count": 7 @@ -1784,6 +2121,9 @@ } }, "app/components/datasets/create/website/firecrawl/options.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 1 } @@ -1800,6 +2140,9 @@ } }, "app/components/datasets/create/website/jina-reader/options.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 1 } @@ -1816,6 +2159,9 @@ } }, "app/components/datasets/create/website/watercrawl/options.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 1 } @@ -1915,6 +2261,16 @@ "count": 2 } }, + "app/components/datasets/documents/create-from-pipeline/left-header.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/datasets/documents/create-from-pipeline/preview/web-preview.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/datasets/documents/create-from-pipeline/process-documents/form.tsx": { "ts/no-explicit-any": { "count": 3 @@ -1925,6 +2281,11 @@ "count": 2 } }, + "app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/datasets/documents/detail/batch-modal/index.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 @@ -1973,6 +2334,11 @@ "count": 6 } }, + "app/components/datasets/documents/detail/settings/pipeline-settings/left-header.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/datasets/documents/detail/settings/pipeline-settings/process-documents/index.tsx": { "ts/no-explicit-any": { "count": 3 @@ -2008,6 +2374,16 @@ "count": 1 } }, + "app/components/datasets/hit-testing/components/child-chunks-item.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/datasets/hit-testing/components/records.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 3 + } + }, "app/components/datasets/hit-testing/components/score.tsx": { "unicorn/prefer-number-properties": { "count": 1 @@ -2018,11 +2394,21 @@ "count": 1 } }, + "app/components/datasets/metadata/edit-metadata-batch/edit-row.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/datasets/metadata/edit-metadata-batch/input-combined.tsx": { "ts/no-explicit-any": { "count": 2 } }, + "app/components/datasets/metadata/edit-metadata-batch/input-has-set-multiple-value.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/datasets/metadata/hooks/use-edit-dataset-metadata.ts": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 @@ -2043,6 +2429,21 @@ "count": 1 } }, + "app/components/datasets/metadata/metadata-dataset/dataset-metadata-drawer.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/datasets/metadata/metadata-dataset/select-metadata.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + } + }, + "app/components/datasets/metadata/metadata-document/info-group.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + } + }, "app/components/datasets/settings/permission-selector/index.tsx": { "react/no-missing-key": { "count": 1 @@ -2077,6 +2478,11 @@ "count": 1 } }, + "app/components/explore/category.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/explore/create-app-modal/index.tsx": { "ts/no-explicit-any": { "count": 1 @@ -2100,6 +2506,16 @@ "count": 1 } }, + "app/components/explore/try-app/app/text-generation.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/explore/try-app/preview/basic-app-preview.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + } + }, "app/components/explore/try-app/tab.tsx": { "react-refresh/only-export-components": { "count": 1 @@ -2141,6 +2557,11 @@ "count": 1 } }, + "app/components/header/account-dropdown/workplace-selector/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + } + }, "app/components/header/account-setting/data-source-page-new/card.tsx": { "ts/no-explicit-any": { "count": 2 @@ -2255,6 +2676,9 @@ } }, "app/components/header/account-setting/model-provider-page/model-modal/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 5 } @@ -2269,11 +2693,21 @@ "count": 1 } }, + "app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx": { + "tailwindcss/enforce-consistent-class-order": { + "count": 1 + } + }, "app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx": { "ts/no-explicit-any": { "count": 2 } }, + "app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + } + }, "app/components/header/account-setting/model-provider-page/provider-added-card/cooldown-timer.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 2 @@ -2320,6 +2754,26 @@ "count": 1 } }, + "app/components/header/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/header/nav/nav-selector/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/plugins/base/key-value-item.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/plugins/card/base/corner-mark.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/plugins/install-plugin/hooks.ts": { "ts/no-explicit-any": { "count": 4 @@ -2366,6 +2820,16 @@ "count": 2 } }, + "app/components/plugins/marketplace/list/card-wrapper.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/plugins/marketplace/list/list-with-collection.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/plugins/marketplace/sort-dropdown/index.spec.tsx": { "unused-imports/no-unused-vars": { "count": 1 @@ -2494,6 +2958,11 @@ "count": 1 } }, + "app/components/plugins/plugin-detail-panel/subscription-list/index.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, "app/components/plugins/plugin-detail-panel/subscription-list/log-viewer.tsx": { "ts/no-explicit-any": { "count": 2 @@ -2562,6 +3031,11 @@ "count": 7 } }, + "app/components/plugins/reference-setting-modal/auto-update-setting/tool-item.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/plugins/reference-setting-modal/index.spec.tsx": { "ts/no-explicit-any": { "count": 7 @@ -2642,6 +3116,11 @@ "count": 4 } }, + "app/components/rag-pipeline/components/publish-as-knowledge-pipeline-modal.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/rag-pipeline/components/rag-pipeline-children.tsx": { "ts/no-explicit-any": { "count": 1 @@ -2757,6 +3236,9 @@ } }, "app/components/tools/edit-custom-collection-modal/get-schema.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + }, "ts/no-explicit-any": { "count": 1 } @@ -2765,15 +3247,31 @@ "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 4 }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + }, "ts/no-explicit-any": { "count": 2 } }, "app/components/tools/edit-custom-collection-modal/test-api.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 1 } }, + "app/components/tools/labels/filter.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/tools/labels/selector.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/tools/mcp/create-card.tsx": { "ts/no-explicit-any": { "count": 1 @@ -2795,6 +3293,9 @@ } }, "app/components/tools/mcp/provider-card.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 3 } @@ -2810,6 +3311,9 @@ } }, "app/components/tools/setting/build-in/config-credentials.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 3 } @@ -2819,6 +3323,16 @@ "count": 4 } }, + "app/components/tools/workflow-tool/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/tools/workflow-tool/method-selector.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow-app/components/workflow-children.tsx": { "no-console": { "count": 1 @@ -2887,15 +3401,13 @@ "count": 1 } }, - "app/components/workflow/block-selector/constants.tsx": { - "react-refresh/only-export-components": { - "count": 1 - } - }, "app/components/workflow/block-selector/featured-tools.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 2 }, + "tailwindcss/no-duplicate-classes": { + "count": 1 + }, "ts/no-explicit-any": { "count": 2 } @@ -2904,6 +3416,9 @@ "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 2 }, + "tailwindcss/no-duplicate-classes": { + "count": 1 + }, "ts/no-explicit-any": { "count": 2 } @@ -2923,6 +3438,11 @@ "count": 1 } }, + "app/components/workflow/block-selector/market-place-plugin/item.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/block-selector/rag-tool-recommendations/index.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 @@ -2959,6 +3479,9 @@ "app/components/workflow/block-selector/view-type-select.tsx": { "react-refresh/only-export-components": { "count": 1 + }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 } }, "app/components/workflow/candidate-node-main.tsx": { @@ -2976,6 +3499,11 @@ "count": 1 } }, + "app/components/workflow/header/header-in-restoring.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/header/run-mode.tsx": { "no-console": { "count": 1 @@ -3110,6 +3638,11 @@ "count": 11 } }, + "app/components/workflow/nodes/_base/components/before-run-form/panel-wrap.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 @@ -3147,6 +3680,9 @@ } }, "app/components/workflow/nodes/_base/components/input-support-select-var.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 1 } @@ -3184,9 +3720,15 @@ "app/components/workflow/nodes/_base/components/node-handle.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 + }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 } }, "app/components/workflow/nodes/_base/components/prompt/editor.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + }, "ts/no-explicit-any": { "count": 4 } @@ -3197,6 +3739,9 @@ } }, "app/components/workflow/nodes/_base/components/selector.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + }, "ts/no-explicit-any": { "count": 2 } @@ -3216,18 +3761,34 @@ "count": 32 } }, + "app/components/workflow/nodes/_base/components/variable/var-full-path-panel.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 2 }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 3 } }, + "app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 3 + } + }, "app/components/workflow/nodes/_base/components/workflow-panel/index.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 3 }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + }, "ts/no-explicit-any": { "count": 6 } @@ -3280,6 +3841,9 @@ } }, "app/components/workflow/nodes/_base/node.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 3 } @@ -3438,6 +4002,11 @@ "count": 1 } }, + "app/components/workflow/nodes/http/components/api-input.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/nodes/http/components/key-value/key-value-edit/index.tsx": { "ts/no-explicit-any": { "count": 2 @@ -3453,6 +4022,11 @@ "count": 1 } }, + "app/components/workflow/nodes/http/panel.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + } + }, "app/components/workflow/nodes/http/use-config.ts": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 @@ -3478,6 +4052,9 @@ "react/no-nested-component-definitions": { "count": 1 }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + }, "ts/no-explicit-any": { "count": 3 } @@ -3490,11 +4067,31 @@ "count": 8 } }, + "app/components/workflow/nodes/human-input/panel.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/nodes/if-else/components/condition-list/condition-input.tsx": { "ts/no-explicit-any": { "count": 1 } }, + "app/components/workflow/nodes/if-else/components/condition-number-input.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/workflow/nodes/if-else/components/condition-wrap.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + } + }, "app/components/workflow/nodes/if-else/default.ts": { "ts/no-explicit-any": { "count": 1 @@ -3515,6 +4112,11 @@ "count": 1 } }, + "app/components/workflow/nodes/iteration/panel.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/nodes/iteration/use-single-run-form-params.ts": { "ts/no-explicit-any": { "count": 6 @@ -3540,11 +4142,21 @@ "count": 3 } }, + "app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-item.tsx": { "ts/no-explicit-any": { "count": 1 } }, + "app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + } + }, "app/components/workflow/nodes/knowledge-retrieval/default.ts": { "ts/no-explicit-any": { "count": 1 @@ -3570,12 +4182,20 @@ "count": 5 } }, + "app/components/workflow/nodes/list-operator/components/extract-input.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/nodes/list-operator/components/filter-condition.tsx": { "ts/no-explicit-any": { "count": 1 } }, "app/components/workflow/nodes/list-operator/components/sub-variable-picker.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 1 } @@ -3658,11 +4278,26 @@ "count": 10 } }, + "app/components/workflow/nodes/loop/components/condition-files-list-value.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/nodes/loop/components/condition-list/condition-input.tsx": { "ts/no-explicit-any": { "count": 1 } }, + "app/components/workflow/nodes/loop/components/condition-number-input.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/workflow/nodes/loop/components/condition-wrap.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/nodes/loop/components/loop-variables/form-item.tsx": { "ts/no-explicit-any": { "count": 3 @@ -3744,6 +4379,11 @@ "count": 8 } }, + "app/components/workflow/nodes/start/node.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/nodes/start/panel.tsx": { "ts/no-explicit-any": { "count": 2 @@ -3779,6 +4419,11 @@ "count": 1 } }, + "app/components/workflow/nodes/tool/components/copy-id.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/nodes/tool/components/input-var-list.tsx": { "ts/no-explicit-any": { "count": 7 @@ -3809,6 +4454,11 @@ "count": 7 } }, + "app/components/workflow/nodes/tool/node.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/nodes/tool/output-schema-utils.ts": { "ts/no-explicit-any": { "count": 4 @@ -3898,6 +4548,9 @@ } }, "app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 1 } @@ -3961,6 +4614,9 @@ "react-refresh/only-export-components": { "count": 1 }, + "tailwindcss/no-unnecessary-whitespace": { + "count": 2 + }, "ts/no-explicit-any": { "count": 5 }, @@ -4019,11 +4675,41 @@ "count": 4 } }, + "app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/workflow/panel/version-history-panel/delete-confirm-modal.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/workflow/panel/version-history-panel/filter/index.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/panel/version-history-panel/index.spec.tsx": { "ts/no-explicit-any": { "count": 2 } }, + "app/components/workflow/panel/version-history-panel/loading/item.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/workflow/panel/version-history-panel/restore-confirm-modal.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, + "app/components/workflow/panel/version-history-panel/version-history-item.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/panel/workflow-preview.tsx": { "ts/no-explicit-any": { "count": 2 @@ -4052,6 +4738,11 @@ "count": 1 } }, + "app/components/workflow/run/loop-result-panel.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/components/workflow/run/node.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 @@ -4068,6 +4759,9 @@ } }, "app/components/workflow/run/result-text.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + }, "ts/no-explicit-any": { "count": 2 } @@ -4249,7 +4943,12 @@ }, "app/components/workflow/workflow-history-store.tsx": { "react-refresh/only-export-components": { - "count": 2 + "count": 3 + } + }, + "app/components/workflow/workflow-preview/components/nodes/base.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 } }, "app/components/workflow/workflow-preview/components/nodes/constants.ts": { @@ -4262,6 +4961,11 @@ "count": 5 } }, + "app/education-apply/role-selector.tsx": { + "tailwindcss/no-unnecessary-whitespace": { + "count": 1 + } + }, "app/education-apply/search-input.tsx": { "ts/no-explicit-any": { "count": 1 @@ -4287,6 +4991,11 @@ "count": 7 } }, + "app/install/installForm.tsx": { + "tailwindcss/enforce-consistent-class-order": { + "count": 1 + } + }, "app/reset-password/layout.tsx": { "ts/no-explicit-any": { "count": 1 @@ -4738,4 +5447,4 @@ "count": 2 } } -} +} \ No newline at end of file diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index 3f3bef8c03..8d885e1ebc 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -1,9 +1,9 @@ // @ts-check import antfu from '@antfu/eslint-config' import pluginQuery from '@tanstack/eslint-plugin-query' +import tailwindcss from 'eslint-plugin-better-tailwindcss' import sonar from 'eslint-plugin-sonarjs' import storybook from 'eslint-plugin-storybook' -import tailwind from 'eslint-plugin-tailwindcss' import dify from './eslint-rules/index.js' export default antfu( @@ -23,7 +23,7 @@ export default antfu( }, }, nextjs: true, - ignores: ['public', 'types/doc-paths.ts'], + ignores: ['public', 'types/doc-paths.ts', 'eslint-suppressions.json'], typescript: { overrides: { 'ts/consistent-type-definitions': ['error', 'type'], @@ -66,42 +66,16 @@ export default antfu( sonarjs: sonar, }, }, - tailwind.configs['flat/recommended'], { - settings: { - tailwindcss: { - // These are the default values but feel free to customize - callees: ['classnames', 'clsx', 'ctl', 'cn', 'classNames'], - config: 'tailwind.config.js', // returned from `loadConfig()` utility if not provided - cssFiles: [ - '**/*.css', - '!**/node_modules', - '!**/.*', - '!**/dist', - '!**/build', - '!**/.storybook', - '!**/.next', - '!**/.public', - ], - cssFilesRefreshRate: 5_000, - removeDuplicates: true, - skipClassAttribute: false, - whitelist: [], - tags: [], // can be set to e.g. ['tw'] for use in tw`bg-blue` - classRegex: '^class(Name)?$', // can be modified to support custom attributes. E.g. "^tw$" for `twin.macro` - }, + files: ['**/*.{ts,tsx}'], + plugins: { + tailwindcss, }, rules: { - // due to 1k lines of tailwind config, these rule have performance issue - 'tailwindcss/no-contradicting-classname': 'off', - 'tailwindcss/enforces-shorthand': 'off', - 'tailwindcss/no-custom-classname': 'off', - 'tailwindcss/no-unnecessary-arbitrary-value': 'off', - - 'tailwindcss/no-arbitrary-value': 'off', - 'tailwindcss/classnames-order': 'warn', - 'tailwindcss/enforces-negative-arbitrary-values': 'warn', - 'tailwindcss/migration-from-tailwind-2': 'warn', + 'tailwindcss/enforce-consistent-class-order': 'error', + 'tailwindcss/no-duplicate-classes': 'error', + 'tailwindcss/no-unnecessary-whitespace': 'error', + 'tailwindcss/no-unknown-classes': 'warn', }, }, { diff --git a/web/next.config.ts b/web/next.config.ts index 05f4158ac8..0bbdbaf32c 100644 --- a/web/next.config.ts +++ b/web/next.config.ts @@ -29,7 +29,7 @@ const remoteImageURLs = ([hasSetWebPrefix ? new URL(`${process.env.NEXT_PUBLIC_W const nextConfig: NextConfig = { basePath: process.env.NEXT_PUBLIC_BASE_PATH || '', - serverExternalPackages: ['esbuild-wasm'], + serverExternalPackages: ['esbuild'], transpilePackages: ['echarts', 'zrender'], turbopack: { rules: codeInspectorPlugin({ diff --git a/web/package.json b/web/package.json index b37a46681f..bde1135bdb 100644 --- a/web/package.json +++ b/web/package.json @@ -154,8 +154,9 @@ "sharp": "0.33.5", "sortablejs": "1.15.6", "string-ts": "2.3.1", - "tailwind-merge": "2.6.0", + "tailwind-merge": "2.6.1", "tldts": "7.0.17", + "ufo": "1.6.3", "use-context-selector": "2.0.0", "uuid": "10.0.0", "zod": "3.25.76", @@ -165,21 +166,21 @@ "devDependencies": { "@antfu/eslint-config": "7.2.0", "@chromatic-com/storybook": "5.0.0", - "@eslint-react/eslint-plugin": "2.8.1", + "@eslint-react/eslint-plugin": "2.9.4", "@mdx-js/loader": "3.1.1", "@mdx-js/react": "3.1.1", "@next/bundle-analyzer": "16.1.5", "@next/eslint-plugin-next": "16.1.6", "@next/mdx": "16.1.5", "@rgrove/parse-xml": "4.2.0", - "@serwist/turbopack": "9.5.0", + "@serwist/turbopack": "9.5.4", "@storybook/addon-docs": "10.2.0", "@storybook/addon-links": "10.2.0", "@storybook/addon-onboarding": "10.2.0", "@storybook/addon-themes": "10.2.0", "@storybook/nextjs-vite": "10.2.0", "@storybook/react": "10.2.0", - "@tanstack/eslint-plugin-query": "5.91.3", + "@tanstack/eslint-plugin-query": "5.91.4", "@tanstack/react-devtools": "0.9.2", "@tanstack/react-form-devtools": "0.2.12", "@tanstack/react-query-devtools": "5.90.2", @@ -210,13 +211,13 @@ "autoprefixer": "10.4.21", "code-inspector-plugin": "1.3.6", "cross-env": "10.1.0", - "esbuild-wasm": "0.27.2", + "esbuild": "0.27.2", "eslint": "9.39.2", + "eslint-plugin-better-tailwindcss": "4.1.1", "eslint-plugin-react-hooks": "7.0.1", - "eslint-plugin-react-refresh": "0.4.26", + "eslint-plugin-react-refresh": "0.5.0", "eslint-plugin-sonarjs": "3.0.6", - "eslint-plugin-storybook": "10.2.1", - "eslint-plugin-tailwindcss": "3.18.2", + "eslint-plugin-storybook": "10.2.6", "husky": "9.1.7", "jsdom": "27.3.0", "jsdom-testing-mocks": "1.16.0", @@ -226,9 +227,9 @@ "postcss": "8.5.6", "react-scan": "0.4.3", "sass": "1.93.2", - "serwist": "9.5.0", + "serwist": "9.5.4", "storybook": "10.2.0", - "tailwindcss": "3.4.18", + "tailwindcss": "3.4.19", "tsx": "4.21.0", "typescript": "5.9.3", "uglify-js": "3.19.3", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 5266b4ac6f..7024688ade 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -127,7 +127,7 @@ importers: version: 3.2.5 '@tailwindcss/typography': specifier: 0.5.19 - version: 0.5.19(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.2)) + version: 0.5.19(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2)) '@tanstack/react-form': specifier: 1.23.7 version: 1.23.7(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -342,11 +342,14 @@ importers: specifier: 2.3.1 version: 2.3.1 tailwind-merge: - specifier: 2.6.0 - version: 2.6.0 + specifier: 2.6.1 + version: 2.6.1 tldts: specifier: 7.0.17 version: 7.0.17 + ufo: + specifier: 1.6.3 + version: 1.6.3 use-context-selector: specifier: 2.0.0 version: 2.0.0(react@19.2.4)(scheduler@0.27.0) @@ -365,13 +368,13 @@ importers: devDependencies: '@antfu/eslint-config': specifier: 7.2.0 - version: 7.2.0(@eslint-react/eslint-plugin@2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@next/eslint-plugin-next@16.1.6)(@vue/compiler-sfc@3.5.27)(eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@1.21.7)))(eslint-plugin-react-refresh@0.4.26(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)(vitest@4.0.17) + version: 7.2.0(@eslint-react/eslint-plugin@2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@next/eslint-plugin-next@16.1.6)(@vue/compiler-sfc@3.5.27)(eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@1.21.7)))(eslint-plugin-react-refresh@0.5.0(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)(vitest@4.0.17) '@chromatic-com/storybook': specifier: 5.0.0 version: 5.0.0(storybook@10.2.0(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) '@eslint-react/eslint-plugin': - specifier: 2.8.1 - version: 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + specifier: 2.9.4 + version: 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@mdx-js/loader': specifier: 3.1.1 version: 3.1.1(webpack@5.104.1(esbuild@0.27.2)(uglify-js@3.19.3)) @@ -391,8 +394,8 @@ importers: specifier: 4.2.0 version: 4.2.0 '@serwist/turbopack': - specifier: 9.5.0 - version: 9.5.0(@swc/helpers@0.5.18)(esbuild-wasm@0.27.2)(next@16.1.5(@babel/core@7.28.6)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.93.2))(react@19.2.4)(typescript@5.9.3) + specifier: 9.5.4 + version: 9.5.4(@swc/helpers@0.5.18)(esbuild-wasm@0.27.2)(esbuild@0.27.2)(next@16.1.5(@babel/core@7.28.6)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.93.2))(react@19.2.4)(typescript@5.9.3) '@storybook/addon-docs': specifier: 10.2.0 version: 10.2.0(@types/react@19.2.9)(esbuild@0.27.2)(rollup@4.56.0)(storybook@10.2.0(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@18.15.0)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))(webpack@5.104.1(esbuild@0.27.2)(uglify-js@3.19.3)) @@ -412,8 +415,8 @@ importers: specifier: 10.2.0 version: 10.2.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(storybook@10.2.0(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) '@tanstack/eslint-plugin-query': - specifier: 5.91.3 - version: 5.91.3(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + specifier: 5.91.4 + version: 5.91.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@tanstack/react-devtools': specifier: 0.9.2 version: 0.9.2(@types/react-dom@19.2.3(@types/react@19.2.9))(@types/react@19.2.9)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(solid-js@1.9.11) @@ -504,27 +507,27 @@ importers: cross-env: specifier: 10.1.0 version: 10.1.0 - esbuild-wasm: + esbuild: specifier: 0.27.2 version: 0.27.2 eslint: specifier: 9.39.2 version: 9.39.2(jiti@1.21.7) + eslint-plugin-better-tailwindcss: + specifier: 4.1.1 + version: 4.1.1(eslint@9.39.2(jiti@1.21.7))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))(typescript@5.9.3) eslint-plugin-react-hooks: specifier: 7.0.1 version: 7.0.1(eslint@9.39.2(jiti@1.21.7)) eslint-plugin-react-refresh: - specifier: 0.4.26 - version: 0.4.26(eslint@9.39.2(jiti@1.21.7)) + specifier: 0.5.0 + version: 0.5.0(eslint@9.39.2(jiti@1.21.7)) eslint-plugin-sonarjs: specifier: 3.0.6 version: 3.0.6(eslint@9.39.2(jiti@1.21.7)) eslint-plugin-storybook: - specifier: 10.2.1 - version: 10.2.1(eslint@9.39.2(jiti@1.21.7))(storybook@10.2.0(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) - eslint-plugin-tailwindcss: - specifier: 3.18.2 - version: 3.18.2(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.2)) + specifier: 10.2.6 + version: 10.2.6(eslint@9.39.2(jiti@1.21.7))(storybook@10.2.0(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3) husky: specifier: 9.1.7 version: 9.1.7 @@ -553,14 +556,14 @@ importers: specifier: 1.93.2 version: 1.93.2 serwist: - specifier: 9.5.0 - version: 9.5.0(typescript@5.9.3) + specifier: 9.5.4 + version: 9.5.4(browserslist@4.28.1)(typescript@5.9.3) storybook: specifier: 10.2.0 version: 10.2.0(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) tailwindcss: - specifier: 3.4.18 - version: 3.4.18(tsx@4.21.0)(yaml@2.8.2) + specifier: 3.4.19 + version: 3.4.19(tsx@4.21.0)(yaml@2.8.2) tsx: specifier: 4.21.0 version: 4.21.0 @@ -1121,40 +1124,40 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint-react/ast@2.8.1': - resolution: {integrity: sha512-4D442lxeFvvd9PMvBbA621rfz/Ne8Kod8RW0/FLKO0vx+IOxm74pP6be1uU56rqL9TvoIHxjclBjfgXplEF+Yw==} + '@eslint-react/ast@2.9.4': + resolution: {integrity: sha512-WI9iq5ePTlcWo0xhSs4wxLUC6u4QuBmQkKeSiXexkEO8C2p8QE7ECNIXhRVkYs3p3AKH5xTez9V8C/CBIGxeXA==} engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@eslint-react/core@2.8.1': - resolution: {integrity: sha512-zF73p8blyuX+zrfgyTtpKesichYzK+G54TEjFWtzagWIbnqQjtVscebL/eGep72oWzAOd5B04ACBvJ2hW4fp5g==} + '@eslint-react/core@2.9.4': + resolution: {integrity: sha512-Ob+Dip1vyR9ch9XL7LUAsGXc0UUf9Kuzn9BEiwOLT7l+cF91ieKeCvIzNPp0LmTuanPfQweJ9iDT9i295SqBZA==} engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@eslint-react/eff@2.8.1': - resolution: {integrity: sha512-ZASOs8oTZJSiu1giue7V87GEKQvlKLfGfLppal6Rl+aKnfIEz+vartmjpH12pkFQZ9ESRyHzYbU533S6pEDoNg==} + '@eslint-react/eff@2.9.4': + resolution: {integrity: sha512-7AOmozmfa0HgXY9O+J+iX3ciZfViz+W+jhRe2y0YqqkDR7PwV2huzhk/Bxq6sRzzf2uFHqoh/AQNZUhRJ3A05A==} engines: {node: '>=20.19.0'} - '@eslint-react/eslint-plugin@2.8.1': - resolution: {integrity: sha512-ob+SSDnTPnA5dhiWWJLfyHRLEzWnjilCsohgo5s9PPKF5b5bjxG+c/rwqhQwT3M9Ey83mGNdkrLzt00SOfr4pw==} + '@eslint-react/eslint-plugin@2.9.4': + resolution: {integrity: sha512-B1LOEUBuT4L7EmY3E9F7+K8Jdr9nAzx66USz4uWEtg8ZMn82E2O5TzOBPw6eeL0O9BoyLBoslZotXNQVazR2dA==} engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@eslint-react/shared@2.8.1': - resolution: {integrity: sha512-NDmJBiMiPDXR6qeZzYOtiILHxWjYwBHxquQ/bMQkWcWK+1qF5LeD8UTRcWtBpZoMPi3sNBWwR3k2Sc5HWZpJ7g==} + '@eslint-react/shared@2.9.4': + resolution: {integrity: sha512-PU7C4JzDZ6OffAWD+HwJdvzGSho25UPYJRyb4wZ/pDaI8QPTDj8AtKWKK69SEOQl2ic89ht1upjQX+jrXhN15w==} engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@eslint-react/var@2.8.1': - resolution: {integrity: sha512-iHIdEBz6kgW4dEFdhEjpy9SEQ6+d4RYg+WBzHg5J5ktT2xSQFi77Dq6Wtemik6QvvAPnYLRseQxgW+m+1rQlfA==} + '@eslint-react/var@2.9.4': + resolution: {integrity: sha512-Qiih6hT+D2vZmCbAGUooReKlqXjtb/g3SzYj2zNlci6YcWxsQB/pqhR0ayU2AOdW6U9YdeCCfPIwBBQ4AEpyBA==} engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1205,6 +1208,10 @@ packages: resolution: {integrity: sha512-r18fEAj9uCk+VjzGt2thsbOmychS+4kxI14spVNibUO2vqKX7obOG+ymZljAwuPZl+S3clPGwCwTDtrdqTiY6Q==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@eslint/css-tree@3.6.8': + resolution: {integrity: sha512-s0f40zY7dlMp8i0Jf0u6l/aSswS0WRAgkhgETgiCJRcxIWb4S/Sp9uScKHWbkM3BnoFLbJbmOYk5AZUDFVxaLA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + '@eslint/eslintrc@3.3.3': resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2542,8 +2549,8 @@ packages: peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x - '@serwist/build@9.5.0': - resolution: {integrity: sha512-8D330WwYjBI5MadyVOphwUqJLMNQK76KWBoDykIPrbtt0C3uGFPxG4XNZCFXBkRG3O1QLedv9BWqH27SeqhcLg==} + '@serwist/build@9.5.4': + resolution: {integrity: sha512-FTiNsNb3luKsLIxjKCvkPiqFZSbx7yVNOFGSUhp4lyfzgnelT1M3/lMC88kLiak90emkuFjSkQgwa6OnyhMZlQ==} engines: {node: '>=18.0.0'} peerDependencies: typescript: '>=5.0.0' @@ -2551,23 +2558,33 @@ packages: typescript: optional: true - '@serwist/turbopack@9.5.0': - resolution: {integrity: sha512-MPxDapkN6LPG25I8LgOxQHc2ifIWK8WY+4pOdAbAFmN4FvsgLwYJ/W4531lsRWu08sq5IZ9JlRtt6KLNm5DHSQ==} + '@serwist/turbopack@9.5.4': + resolution: {integrity: sha512-HerOIc2z3LWbFVq/gXK44I99KdF+x0uBI7cPHb+Q3q0WpF50d/i5fV5pZZXCf3LCqtc9oH0VlY6FWDcjWjHI8g==} engines: {node: '>=18.0.0'} peerDependencies: + esbuild: 0.27.2 esbuild-wasm: '>=0.25.0 <1.0.0' next: '>=14.0.0' react: '>=18.0.0' typescript: '>=5.0.0' peerDependenciesMeta: + esbuild: + optional: true + esbuild-wasm: + optional: true typescript: optional: true - '@serwist/utils@9.5.0': - resolution: {integrity: sha512-DBzmJgL63/VYcQ/TpSYXW1FJKRILesOxytu+1MHY0vW2WFhW7pYkLgVuHqksP5K+3ypt54M3+2QNDDMixrnutQ==} + '@serwist/utils@9.5.4': + resolution: {integrity: sha512-uyriGQF1qjNEHXXfsd8XJ5kfK3/MezEaUw//XdHjZeJ0LvLamrgnLJGQQoyJqUfEPCiJ4jJwc4uYMB9LjLiHxA==} + peerDependencies: + browserslist: '>=4' + peerDependenciesMeta: + browserslist: + optional: true - '@serwist/window@9.5.0': - resolution: {integrity: sha512-WqmEZjJ+u841sbUJh2LAbtPNrz8mU/wCTo9sEVqsMOk+EM5oBz5FRpF3kDzx1cF5rTIfXer1df0D354lIdFw1Q==} + '@serwist/window@9.5.4': + resolution: {integrity: sha512-52t2G+TgiWDdRwGG0ArU28uy6/oQYICQfNLHs4ywybyS6mHy3BxHFl+JjB5vhg8znIG1LMpGvOmS5b7AuPVYDw==} peerDependencies: typescript: '>=5.0.0' peerDependenciesMeta: @@ -2719,68 +2736,68 @@ packages: '@svgdotjs/svg.js@3.2.5': resolution: {integrity: sha512-/VNHWYhNu+BS7ktbYoVGrCmsXDh+chFMaONMwGNdIBcFHrWqk2jY8fNyr3DLdtQUIalvkPfM554ZSFa3dm3nxQ==} - '@swc/core-darwin-arm64@1.15.8': - resolution: {integrity: sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg==} + '@swc/core-darwin-arm64@1.15.11': + resolution: {integrity: sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.15.8': - resolution: {integrity: sha512-j47DasuOvXl80sKJHSi2X25l44CMc3VDhlJwA7oewC1nV1VsSzwX+KOwE5tLnfORvVJJyeiXgJORNYg4jeIjYQ==} + '@swc/core-darwin-x64@1.15.11': + resolution: {integrity: sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.15.8': - resolution: {integrity: sha512-siAzDENu2rUbwr9+fayWa26r5A9fol1iORG53HWxQL1J8ym4k7xt9eME0dMPXlYZDytK5r9sW8zEA10F2U3Xwg==} + '@swc/core-linux-arm-gnueabihf@1.15.11': + resolution: {integrity: sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.15.8': - resolution: {integrity: sha512-o+1y5u6k2FfPYbTRUPvurwzNt5qd0NTumCTFscCNuBksycloXY16J8L+SMW5QRX59n4Hp9EmFa3vpvNHRVv1+Q==} + '@swc/core-linux-arm64-gnu@1.15.11': + resolution: {integrity: sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.15.8': - resolution: {integrity: sha512-koiCqL09EwOP1S2RShCI7NbsQuG6r2brTqUYE7pV7kZm9O17wZ0LSz22m6gVibpwEnw8jI3IE1yYsQTVpluALw==} + '@swc/core-linux-arm64-musl@1.15.11': + resolution: {integrity: sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.15.8': - resolution: {integrity: sha512-4p6lOMU3bC+Vd5ARtKJ/FxpIC5G8v3XLoPEZ5s7mLR8h7411HWC/LmTXDHcrSXRC55zvAVia1eldy6zDLz8iFQ==} + '@swc/core-linux-x64-gnu@1.15.11': + resolution: {integrity: sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.15.8': - resolution: {integrity: sha512-z3XBnbrZAL+6xDGAhJoN4lOueIxC/8rGrJ9tg+fEaeqLEuAtHSW2QHDHxDwkxZMjuF/pZ6MUTjHjbp8wLbuRLA==} + '@swc/core-linux-x64-musl@1.15.11': + resolution: {integrity: sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.15.8': - resolution: {integrity: sha512-djQPJ9Rh9vP8GTS/Df3hcc6XP6xnG5c8qsngWId/BLA9oX6C7UzCPAn74BG/wGb9a6j4w3RINuoaieJB3t+7iQ==} + '@swc/core-win32-arm64-msvc@1.15.11': + resolution: {integrity: sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.15.8': - resolution: {integrity: sha512-/wfAgxORg2VBaUoFdytcVBVCgf1isWZIEXB9MZEUty4wwK93M/PxAkjifOho9RN3WrM3inPLabICRCEgdHpKKQ==} + '@swc/core-win32-ia32-msvc@1.15.11': + resolution: {integrity: sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.15.8': - resolution: {integrity: sha512-GpMePrh9Sl4d61o4KAHOOv5is5+zt6BEXCOCgs/H0FLGeii7j9bWDE8ExvKFy2GRRZVNR1ugsnzaGWHKM6kuzA==} + '@swc/core-win32-x64-msvc@1.15.11': + resolution: {integrity: sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.15.8': - resolution: {integrity: sha512-T8keoJjXaSUoVBCIjgL6wAnhADIb09GOELzKg10CjNg+vLX48P93SME6jTfte9MZIm5m+Il57H3rTSk/0kzDUw==} + '@swc/core@1.15.11': + resolution: {integrity: sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -2854,10 +2871,14 @@ packages: peerDependencies: solid-js: 1.9.11 - '@tanstack/eslint-plugin-query@5.91.3': - resolution: {integrity: sha512-5GMGZMYFK9dOvjpdedjJs4hU40EdPuO2AjzObQzP7eOSsikunCfrXaU3oNGXSsvoU9ve1Z1xQZZuDyPi0C1M7Q==} + '@tanstack/eslint-plugin-query@5.91.4': + resolution: {integrity: sha512-8a+GAeR7oxJ5laNyYBQ6miPK09Hi18o5Oie/jx8zioXODv/AUFLZQecKabPdpQSLmuDXEBPKFh+W5DKbWlahjQ==} peerDependencies: eslint: ^8.57.0 || ^9.0.0 + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true '@tanstack/form-core@1.24.3': resolution: {integrity: sha512-e+HzSD49NWr4aIqJWtPPzmi+/phBJAP3nSPN8dvxwmJWqAxuB/cH138EcmCFf3+oA7j3BXvwvTY0I+8UweGPjQ==} @@ -3365,6 +3386,11 @@ packages: '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@valibot/to-json-schema@1.5.0': + resolution: {integrity: sha512-GE7DmSr1C2UCWPiV0upRH6mv0cCPsqYGs819fb6srCS1tWhyXrkGGe+zxUiwzn/L1BOfADH4sNjY/YHCuP8phQ==} + peerDependencies: + valibot: ^1.2.0 + '@vitejs/plugin-react@5.1.2': resolution: {integrity: sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4430,6 +4456,19 @@ packages: peerDependencies: eslint: '*' + eslint-plugin-better-tailwindcss@4.1.1: + resolution: {integrity: sha512-ctw461TGJi8iM0P01mNVjSW7jeUAdyUgmrrd59np5/VxqX50nayMbwKZkfmjWpP1PWOqlh4CSMOH/WW6ICWmJw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 + oxlint: ^1.35.0 + tailwindcss: ^3.3.0 || ^4.1.17 + peerDependenciesMeta: + eslint: + optional: true + oxlint: + optional: true + eslint-plugin-command@3.4.0: resolution: {integrity: sha512-EW4eg/a7TKEhG0s5IEti72kh3YOTlnhfFNuctq5WnB1fst37/IHTd5OkD+vnlRf3opTvUcSRihAateP6bT5ZcA==} peerDependencies: @@ -4480,15 +4519,15 @@ packages: peerDependencies: eslint: ^9.0.0 - eslint-plugin-react-dom@2.8.1: - resolution: {integrity: sha512-VAVs3cp/0XTxdjTeLePtZVadj+om+N1VNVy7hyzSPACfh5ncAicC0zOIc5MB15KUWCj8PoG/ZnVny0YqeubgRg==} + eslint-plugin-react-dom@2.9.4: + resolution: {integrity: sha512-lRa3iN082cX3HRKdbKSESmlj+z4zMR10DughwagV7h+IOd3O07UGnYQhenH08GMSyLy1f2D6QJmKBLGbx2p20g==} engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - eslint-plugin-react-hooks-extra@2.8.1: - resolution: {integrity: sha512-YeZLGzcib6UxlY7Gf+3zz8Mfl7u+OoVj3MukGaTuU6zkm1XQMI8/k4o16bKHuWtUauhn7Udl1bLAWfLgQM5UFw==} + eslint-plugin-react-hooks-extra@2.9.4: + resolution: {integrity: sha512-8hQArFHpXubT+i++8TwIL24vQ5b/ZcnVT3EFOSvy1TdBZw8NqrcFNBVqywQ6YUWX0utuPiTQgeJB0qnBF7gx4g==} engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -4500,27 +4539,34 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - eslint-plugin-react-naming-convention@2.8.1: - resolution: {integrity: sha512-fVj+hSzIe2I6HyPTf1nccMBXq72c4jbM3gk0T+szo/wewEF8/LgenjfquJoxHPpheb1fujFgdlo5HBhsilAX7Q==} + eslint-plugin-react-naming-convention@2.9.4: + resolution: {integrity: sha512-Ow9ikJ49tDjeTaO2wfUYlSlVBsbG8AZVqoVFu4HH69FZe6I5LEdjZf/gdXnN2W+/JAy7Ru5vYQ8H8LU3tTZERg==} engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - eslint-plugin-react-refresh@0.4.26: - resolution: {integrity: sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==} + eslint-plugin-react-refresh@0.5.0: + resolution: {integrity: sha512-ZYvmh7VfVgqR/7wR71I3Zl6hK/C5CcxdWYKZSpHawS5JCNgE4efhQWg/+/WPpgGAp9Ngp/rRZYyaIwmPQBq/lA==} peerDependencies: - eslint: '>=8.40' + eslint: '>=9' - eslint-plugin-react-web-api@2.8.1: - resolution: {integrity: sha512-NYsZKW1aJZ2XZuYTPzbwYLShvGcuXKRV/5TW61VO56gik/btil4Snt5UtyxshHbvT/zXx/Z+QsHul51/XM4/Qw==} + eslint-plugin-react-rsc@2.9.4: + resolution: {integrity: sha512-RwBYSLkcGXQV6SQYABdHLrafUmpfdPBYsAa/kvg6smqEn+/vPKSk0I+uAuzkmiw4y4KXW94Q9rlIdJlzOMdJfQ==} engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - eslint-plugin-react-x@2.8.1: - resolution: {integrity: sha512-4IpCMrsb63AVEa9diOApIm+T3wUGIzK+EB5vyYocO31YYPJ16+R7Fh4lV3S3fOuX1+aQ+Ad4SE0cYuZ2pF2Tlg==} + eslint-plugin-react-web-api@2.9.4: + resolution: {integrity: sha512-/k++qhGoYtMNZrsQT+M08fCGi/VurL1fE/LNiz2fMwOIU7KjXD9N0kGWPFdIAISnYXGzOg53O5WW/mnNR78emQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + eslint-plugin-react-x@2.9.4: + resolution: {integrity: sha512-a078MHeM/FdjRu3KJsFX+PCHewZyC77EjAO7QstL/vvwjsFae3PCWMZ8Q4b+mzUsT4FkFxi5mEW43ZHksPWDFw==} engines: {node: '>=20.19.0'} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -4537,17 +4583,11 @@ packages: peerDependencies: eslint: ^8.0.0 || ^9.0.0 - eslint-plugin-storybook@10.2.1: - resolution: {integrity: sha512-5+V+dlzTuZfNKUD8hPbLvCVtggcWfI2lDGTpiq0AENrHeAgcztj17wwDva96lbg/sAG20uX71l8HQo3s/GmpHw==} + eslint-plugin-storybook@10.2.6: + resolution: {integrity: sha512-Ykf0hDS97oJlQel21WG+SYtGnzFkkSfifupJ92NQtMMSMLXsWm4P0x8ZQqu9/EQa+dUkGoj9EWyNmmbB/54uhA==} peerDependencies: eslint: '>=8' - storybook: ^10.2.1 - - eslint-plugin-tailwindcss@3.18.2: - resolution: {integrity: sha512-QbkMLDC/OkkjFQ1iz/5jkMdHfiMu/uwujUHLAJK5iwNHD8RTxVTlsUezE0toTZ6VhybNBsk+gYGPDq2agfeRNA==} - engines: {node: '>=18.12.0'} - peerDependencies: - tailwindcss: ^3.4.0 + storybook: ^10.2.6 eslint-plugin-toml@1.0.3: resolution: {integrity: sha512-GlCBX+R313RvFY2Tj0ZmvzCEv8FDp1z2itvTFTV4bW/Bkbl3xEp9inWNsRWH3SiDUlxo8Pew31ILEp/3J0WxaA==} @@ -4877,11 +4917,13 @@ packages: glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@11.1.0: resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} engines: {node: 20 || >=22} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true globals@14.0.0: @@ -5570,6 +5612,9 @@ packages: mdn-data@2.12.2: resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + mdn-data@2.23.0: + resolution: {integrity: sha512-786vq1+4079JSeu2XdcDjrhi/Ry7BWtjDl9WtGPWLiIHb2T66GvIVflZTBoSNZ5JqTtJGYEVMuFA/lbQlMOyDQ==} + memoize-one@5.2.1: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} @@ -6601,8 +6646,8 @@ packages: server-only@0.0.1: resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} - serwist@9.5.0: - resolution: {integrity: sha512-wjrsPWHI5ZM20jIsVKZGN/uAdS2aKOgmIOE4dqUaFhK6SVIzgoJZjTnZ3v29T+NmneuD753jlhGui9eYypsj0A==} + serwist@9.5.4: + resolution: {integrity: sha512-uTHBzpIeA6rE3oyRt392MbtNQDs2JVZelKD1KkT18UkhX6HRwCeassoI1Nd1h52DqYqa7ZfBeldJ4awy+PYrnQ==} peerDependencies: typescript: '>=5.0.0' peerDependenciesMeta: @@ -6835,11 +6880,15 @@ packages: resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} engines: {node: '>=20'} - tailwind-merge@2.6.0: - resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} + tailwind-csstree@0.1.4: + resolution: {integrity: sha512-FzD187HuFIZEyeR7Xy6sJbJll2d4SybS90satC8SKIuaNRC05CxMvdzN7BUsfDQffcnabckRM5OIcfArjsZ0mg==} + engines: {node: '>=18.18'} - tailwindcss@3.4.18: - resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} + tailwind-merge@2.6.1: + resolution: {integrity: sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==} + + tailwindcss@3.4.19: + resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==} engines: {node: '>=14.0.0'} hasBin: true @@ -6988,6 +7037,10 @@ packages: typescript: optional: true + tsconfig-paths-webpack-plugin@4.2.0: + resolution: {integrity: sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==} + engines: {node: '>=10.13.0'} + tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} @@ -7156,6 +7209,14 @@ packages: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true + valibot@1.2.0: + resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} @@ -7480,9 +7541,6 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.3.5: - resolution: {integrity: sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==} - zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} @@ -7682,7 +7740,7 @@ snapshots: idb: 8.0.3 tslib: 2.8.1 - '@antfu/eslint-config@7.2.0(@eslint-react/eslint-plugin@2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@next/eslint-plugin-next@16.1.6)(@vue/compiler-sfc@3.5.27)(eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@1.21.7)))(eslint-plugin-react-refresh@0.4.26(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)(vitest@4.0.17)': + '@antfu/eslint-config@7.2.0(@eslint-react/eslint-plugin@2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@next/eslint-plugin-next@16.1.6)(@vue/compiler-sfc@3.5.27)(eslint-plugin-react-hooks@7.0.1(eslint@9.39.2(jiti@1.21.7)))(eslint-plugin-react-refresh@0.5.0(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)(vitest@4.0.17)': dependencies: '@antfu/install-pkg': 1.1.0 '@clack/prompts': 0.11.0 @@ -7722,10 +7780,10 @@ snapshots: vue-eslint-parser: 10.2.0(eslint@9.39.2(jiti@1.21.7)) yaml-eslint-parser: 2.0.0 optionalDependencies: - '@eslint-react/eslint-plugin': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eslint-plugin': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@next/eslint-plugin-next': 16.1.6 eslint-plugin-react-hooks: 7.0.1(eslint@9.39.2(jiti@1.21.7)) - eslint-plugin-react-refresh: 0.4.26(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-react-refresh: 0.5.0(eslint@9.39.2(jiti@1.21.7)) transitivePeerDependencies: - '@eslint/json' - '@vue/compiler-sfc' @@ -8125,9 +8183,9 @@ snapshots: '@eslint-community/regexpp@4.12.2': {} - '@eslint-react/ast@2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@eslint-react/ast@2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@eslint-react/eff': 2.8.1 + '@eslint-react/eff': 2.9.4 '@typescript-eslint/types': 8.54.0 '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) @@ -8137,12 +8195,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint-react/core@2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@eslint-react/core@2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@eslint-react/ast': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 2.8.1 - '@eslint-react/shared': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/var': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/ast': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.9.4 + '@eslint-react/shared': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/var': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.54.0 '@typescript-eslint/types': 8.54.0 '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) @@ -8152,30 +8210,31 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint-react/eff@2.8.1': {} + '@eslint-react/eff@2.9.4': {} - '@eslint-react/eslint-plugin@2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@eslint-react/eslint-plugin@2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@eslint-react/eff': 2.8.1 - '@eslint-react/shared': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.9.4 + '@eslint-react/shared': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.54.0 '@typescript-eslint/type-utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/types': 8.54.0 '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) eslint: 9.39.2(jiti@1.21.7) - eslint-plugin-react-dom: 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - eslint-plugin-react-hooks-extra: 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - eslint-plugin-react-naming-convention: 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - eslint-plugin-react-web-api: 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - eslint-plugin-react-x: 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-react-dom: 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-react-hooks-extra: 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-react-naming-convention: 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-react-rsc: 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-react-web-api: 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint-plugin-react-x: 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@eslint-react/shared@2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@eslint-react/shared@2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@eslint-react/eff': 2.8.1 + '@eslint-react/eff': 2.9.4 '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) eslint: 9.39.2(jiti@1.21.7) ts-pattern: 5.9.0 @@ -8184,11 +8243,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint-react/var@2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@eslint-react/var@2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@eslint-react/ast': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 2.8.1 - '@eslint-react/shared': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/ast': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.9.4 + '@eslint-react/shared': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.54.0 '@typescript-eslint/types': 8.54.0 '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) @@ -8246,6 +8305,11 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 + '@eslint/css-tree@3.6.8': + dependencies: + mdn-data: 2.23.0 + source-map-js: 1.2.1 + '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 @@ -9603,43 +9667,51 @@ snapshots: hoist-non-react-statics: 3.3.2 react: 19.2.4 - '@serwist/build@9.5.0(typescript@5.9.3)': + '@serwist/build@9.5.4(browserslist@4.28.1)(typescript@5.9.3)': dependencies: - '@serwist/utils': 9.5.0 + '@serwist/utils': 9.5.4(browserslist@4.28.1) common-tags: 1.8.2 glob: 10.5.0 pretty-bytes: 6.1.1 source-map: 0.8.0-beta.0 - zod: 4.3.5 + zod: 4.3.6 optionalDependencies: typescript: 5.9.3 + transitivePeerDependencies: + - browserslist - '@serwist/turbopack@9.5.0(@swc/helpers@0.5.18)(esbuild-wasm@0.27.2)(next@16.1.5(@babel/core@7.28.6)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.93.2))(react@19.2.4)(typescript@5.9.3)': + '@serwist/turbopack@9.5.4(@swc/helpers@0.5.18)(esbuild-wasm@0.27.2)(esbuild@0.27.2)(next@16.1.5(@babel/core@7.28.6)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.93.2))(react@19.2.4)(typescript@5.9.3)': dependencies: - '@serwist/build': 9.5.0(typescript@5.9.3) - '@serwist/utils': 9.5.0 - '@serwist/window': 9.5.0(typescript@5.9.3) - '@swc/core': 1.15.8(@swc/helpers@0.5.18) - esbuild-wasm: 0.27.2 + '@serwist/build': 9.5.4(browserslist@4.28.1)(typescript@5.9.3) + '@serwist/utils': 9.5.4(browserslist@4.28.1) + '@serwist/window': 9.5.4(browserslist@4.28.1)(typescript@5.9.3) + '@swc/core': 1.15.11(@swc/helpers@0.5.18) + browserslist: 4.28.1 kolorist: 1.8.0 next: 16.1.5(@babel/core@7.28.6)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.93.2) react: 19.2.4 semver: 7.7.3 - serwist: 9.5.0(typescript@5.9.3) - zod: 4.3.5 + serwist: 9.5.4(browserslist@4.28.1)(typescript@5.9.3) + zod: 4.3.6 optionalDependencies: + esbuild: 0.27.2 + esbuild-wasm: 0.27.2 typescript: 5.9.3 transitivePeerDependencies: - '@swc/helpers' - '@serwist/utils@9.5.0': {} + '@serwist/utils@9.5.4(browserslist@4.28.1)': + optionalDependencies: + browserslist: 4.28.1 - '@serwist/window@9.5.0(typescript@5.9.3)': + '@serwist/window@9.5.4(browserslist@4.28.1)(typescript@5.9.3)': dependencies: '@types/trusted-types': 2.0.7 - serwist: 9.5.0(typescript@5.9.3) + serwist: 9.5.4(browserslist@4.28.1)(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 + transitivePeerDependencies: + - browserslist '@sindresorhus/base62@1.0.0': {} @@ -9821,51 +9893,51 @@ snapshots: '@svgdotjs/svg.js@3.2.5': {} - '@swc/core-darwin-arm64@1.15.8': + '@swc/core-darwin-arm64@1.15.11': optional: true - '@swc/core-darwin-x64@1.15.8': + '@swc/core-darwin-x64@1.15.11': optional: true - '@swc/core-linux-arm-gnueabihf@1.15.8': + '@swc/core-linux-arm-gnueabihf@1.15.11': optional: true - '@swc/core-linux-arm64-gnu@1.15.8': + '@swc/core-linux-arm64-gnu@1.15.11': optional: true - '@swc/core-linux-arm64-musl@1.15.8': + '@swc/core-linux-arm64-musl@1.15.11': optional: true - '@swc/core-linux-x64-gnu@1.15.8': + '@swc/core-linux-x64-gnu@1.15.11': optional: true - '@swc/core-linux-x64-musl@1.15.8': + '@swc/core-linux-x64-musl@1.15.11': optional: true - '@swc/core-win32-arm64-msvc@1.15.8': + '@swc/core-win32-arm64-msvc@1.15.11': optional: true - '@swc/core-win32-ia32-msvc@1.15.8': + '@swc/core-win32-ia32-msvc@1.15.11': optional: true - '@swc/core-win32-x64-msvc@1.15.8': + '@swc/core-win32-x64-msvc@1.15.11': optional: true - '@swc/core@1.15.8(@swc/helpers@0.5.18)': + '@swc/core@1.15.11(@swc/helpers@0.5.18)': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.25 optionalDependencies: - '@swc/core-darwin-arm64': 1.15.8 - '@swc/core-darwin-x64': 1.15.8 - '@swc/core-linux-arm-gnueabihf': 1.15.8 - '@swc/core-linux-arm64-gnu': 1.15.8 - '@swc/core-linux-arm64-musl': 1.15.8 - '@swc/core-linux-x64-gnu': 1.15.8 - '@swc/core-linux-x64-musl': 1.15.8 - '@swc/core-win32-arm64-msvc': 1.15.8 - '@swc/core-win32-ia32-msvc': 1.15.8 - '@swc/core-win32-x64-msvc': 1.15.8 + '@swc/core-darwin-arm64': 1.15.11 + '@swc/core-darwin-x64': 1.15.11 + '@swc/core-linux-arm-gnueabihf': 1.15.11 + '@swc/core-linux-arm64-gnu': 1.15.11 + '@swc/core-linux-arm64-musl': 1.15.11 + '@swc/core-linux-x64-gnu': 1.15.11 + '@swc/core-linux-x64-musl': 1.15.11 + '@swc/core-win32-arm64-msvc': 1.15.11 + '@swc/core-win32-ia32-msvc': 1.15.11 + '@swc/core-win32-x64-msvc': 1.15.11 '@swc/helpers': 0.5.18 '@swc/counter@0.1.3': {} @@ -9882,10 +9954,10 @@ snapshots: dependencies: '@swc/counter': 0.1.3 - '@tailwindcss/typography@0.5.19(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.2))': + '@tailwindcss/typography@0.5.19(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))': dependencies: postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.18(tsx@4.21.0)(yaml@2.8.2) + tailwindcss: 3.4.19(tsx@4.21.0)(yaml@2.8.2) '@tanstack/devtools-client@0.0.5': dependencies: @@ -9937,13 +10009,14 @@ snapshots: - csstype - utf-8-validate - '@tanstack/eslint-plugin-query@5.91.3(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@tanstack/eslint-plugin-query@5.91.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@typescript-eslint/utils': 8.53.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) eslint: 9.39.2(jiti@1.21.7) + optionalDependencies: + typescript: 5.9.3 transitivePeerDependencies: - supports-color - - typescript '@tanstack/form-core@1.24.3': dependencies: @@ -10573,6 +10646,10 @@ snapshots: '@ungap/structured-clone@1.3.0': {} + '@valibot/to-json-schema@1.5.0(valibot@1.2.0(typescript@5.9.3))': + dependencies: + valibot: 1.2.0(typescript@5.9.3) + '@vitejs/plugin-react@5.1.2(vite@7.3.1(@types/node@18.15.0)(jiti@1.21.7)(sass@1.93.2)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': dependencies: '@babel/core': 7.28.6 @@ -11653,7 +11730,8 @@ snapshots: esast-util-from-estree: 2.0.0 vfile-message: 4.0.3 - esbuild-wasm@0.27.2: {} + esbuild-wasm@0.27.2: + optional: true esbuild@0.27.2: optionalDependencies: @@ -11726,6 +11804,22 @@ snapshots: dependencies: eslint: 9.39.2(jiti@1.21.7) + eslint-plugin-better-tailwindcss@4.1.1(eslint@9.39.2(jiti@1.21.7))(tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2))(typescript@5.9.3): + dependencies: + '@eslint/css-tree': 3.6.8 + '@valibot/to-json-schema': 1.5.0(valibot@1.2.0(typescript@5.9.3)) + enhanced-resolve: 5.18.4 + jiti: 2.6.1 + synckit: 0.11.12 + tailwind-csstree: 0.1.4 + tailwindcss: 3.4.19(tsx@4.21.0)(yaml@2.8.2) + tsconfig-paths-webpack-plugin: 4.2.0 + valibot: 1.2.0(typescript@5.9.3) + optionalDependencies: + eslint: 9.39.2(jiti@1.21.7) + transitivePeerDependencies: + - typescript + eslint-plugin-command@3.4.0(eslint@9.39.2(jiti@1.21.7)): dependencies: '@es-joy/jsdoccomment': 0.78.0 @@ -11814,37 +11908,35 @@ snapshots: yaml: 2.8.2 yaml-eslint-parser: 2.0.0 - eslint-plugin-react-dom@2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): + eslint-plugin-react-dom@2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@eslint-react/ast': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/core': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 2.8.1 - '@eslint-react/shared': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/var': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/ast': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/core': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.9.4 + '@eslint-react/shared': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/var': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.54.0 '@typescript-eslint/types': 8.54.0 '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) compare-versions: 6.1.1 eslint: 9.39.2(jiti@1.21.7) - string-ts: 2.3.1 ts-pattern: 5.9.0 typescript: 5.9.3 transitivePeerDependencies: - supports-color - eslint-plugin-react-hooks-extra@2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): + eslint-plugin-react-hooks-extra@2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@eslint-react/ast': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/core': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 2.8.1 - '@eslint-react/shared': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/var': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/ast': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/core': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.9.4 + '@eslint-react/shared': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/var': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.54.0 '@typescript-eslint/type-utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/types': 8.54.0 '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) eslint: 9.39.2(jiti@1.21.7) - string-ts: 2.3.1 ts-pattern: 5.9.0 typescript: 5.9.3 transitivePeerDependencies: @@ -11861,13 +11953,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-react-naming-convention@2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): + eslint-plugin-react-naming-convention@2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@eslint-react/ast': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/core': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 2.8.1 - '@eslint-react/shared': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/var': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/ast': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/core': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.9.4 + '@eslint-react/shared': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/var': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.54.0 '@typescript-eslint/type-utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/types': 8.54.0 @@ -11880,35 +11972,47 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-react-refresh@0.4.26(eslint@9.39.2(jiti@1.21.7)): + eslint-plugin-react-refresh@0.5.0(eslint@9.39.2(jiti@1.21.7)): dependencies: eslint: 9.39.2(jiti@1.21.7) - eslint-plugin-react-web-api@2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): + eslint-plugin-react-rsc@2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@eslint-react/ast': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/core': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 2.8.1 - '@eslint-react/shared': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/var': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.54.0 + '@eslint-react/ast': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/shared': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/var': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/types': 8.54.0 '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - birecord: 0.1.1 eslint: 9.39.2(jiti@1.21.7) - string-ts: 2.3.1 ts-pattern: 5.9.0 typescript: 5.9.3 transitivePeerDependencies: - supports-color - eslint-plugin-react-x@2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): + eslint-plugin-react-web-api@2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@eslint-react/ast': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/core': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/eff': 2.8.1 - '@eslint-react/shared': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@eslint-react/var': 2.8.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/ast': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/core': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.9.4 + '@eslint-react/shared': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/var': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.54.0 + '@typescript-eslint/types': 8.54.0 + '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + birecord: 0.1.1 + eslint: 9.39.2(jiti@1.21.7) + ts-pattern: 5.9.0 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-x@2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): + dependencies: + '@eslint-react/ast': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/core': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/eff': 2.9.4 + '@eslint-react/shared': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@eslint-react/var': 2.9.4(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.54.0 '@typescript-eslint/type-utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) '@typescript-eslint/types': 8.54.0 @@ -11916,7 +12020,6 @@ snapshots: compare-versions: 6.1.1 eslint: 9.39.2(jiti@1.21.7) is-immutable-type: 5.0.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - string-ts: 2.3.1 ts-api-utils: 2.4.0(typescript@5.9.3) ts-pattern: 5.9.0 typescript: 5.9.3 @@ -11948,21 +12051,15 @@ snapshots: semver: 7.7.3 typescript: 5.9.3 - eslint-plugin-storybook@10.2.1(eslint@9.39.2(jiti@1.21.7))(storybook@10.2.0(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3): + eslint-plugin-storybook@10.2.6(eslint@9.39.2(jiti@1.21.7))(storybook@10.2.0(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3): dependencies: - '@typescript-eslint/utils': 8.53.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) eslint: 9.39.2(jiti@1.21.7) storybook: 10.2.0(@testing-library/dom@10.4.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) transitivePeerDependencies: - supports-color - typescript - eslint-plugin-tailwindcss@3.18.2(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.2)): - dependencies: - fast-glob: 3.3.3 - postcss: 8.5.6 - tailwindcss: 3.4.18(tsx@4.21.0)(yaml@2.8.2) - eslint-plugin-toml@1.0.3(eslint@9.39.2(jiti@1.21.7)): dependencies: '@eslint/core': 1.0.1 @@ -13229,6 +13326,8 @@ snapshots: mdn-data@2.12.2: {} + mdn-data@2.23.0: {} + memoize-one@5.2.1: {} merge-stream@2.0.0: {} @@ -14563,12 +14662,14 @@ snapshots: server-only@0.0.1: {} - serwist@9.5.0(typescript@5.9.3): + serwist@9.5.4(browserslist@4.28.1)(typescript@5.9.3): dependencies: - '@serwist/utils': 9.5.0 + '@serwist/utils': 9.5.4(browserslist@4.28.1) idb: 8.0.3 optionalDependencies: typescript: 5.9.3 + transitivePeerDependencies: + - browserslist sharp@0.33.5: dependencies: @@ -14840,9 +14941,11 @@ snapshots: tagged-tag@1.0.0: {} - tailwind-merge@2.6.0: {} + tailwind-csstree@0.1.4: {} - tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.2): + tailwind-merge@2.6.1: {} + + tailwindcss@3.4.19(tsx@4.21.0)(yaml@2.8.2): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -14997,6 +15100,13 @@ snapshots: optionalDependencies: typescript: 5.9.3 + tsconfig-paths-webpack-plugin@4.2.0: + dependencies: + chalk: 4.1.2 + enhanced-resolve: 5.18.4 + tapable: 2.3.0 + tsconfig-paths: 4.2.0 + tsconfig-paths@4.2.0: dependencies: json5: 2.2.3 @@ -15162,6 +15272,10 @@ snapshots: uuid@11.1.0: {} + valibot@1.2.0(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + vfile-location@5.0.3: dependencies: '@types/unist': 3.0.3 @@ -15487,8 +15601,6 @@ snapshots: zod@3.25.76: {} - zod@4.3.5: {} - zod@4.3.6: {} zrender@5.6.1: diff --git a/web/utils/classnames.spec.ts b/web/utils/classnames.spec.ts deleted file mode 100644 index 1b8f487856..0000000000 --- a/web/utils/classnames.spec.ts +++ /dev/null @@ -1,157 +0,0 @@ -/** - * Test suite for the classnames utility function - * This utility combines the classnames library with tailwind-merge - * to handle conditional CSS classes and merge conflicting Tailwind classes - */ -import { cn } from './classnames' - -describe('classnames', () => { - /** - * Tests basic classnames library features: - * - String concatenation - * - Array handling - * - Falsy value filtering - * - Object-based conditional classes - */ - it('classnames libs feature', () => { - expect(cn('foo')).toBe('foo') - expect(cn('foo', 'bar')).toBe('foo bar') - expect(cn(['foo', 'bar'])).toBe('foo bar') - - expect(cn(undefined)).toBe('') - expect(cn(null)).toBe('') - expect(cn(false)).toBe('') - - expect(cn({ - foo: true, - bar: false, - baz: true, - })).toBe('foo baz') - }) - - /** - * Tests tailwind-merge functionality: - * - Conflicting class resolution (last one wins) - * - Modifier handling (hover, focus, etc.) - * - Important prefix (!) - * - Custom color classes - * - Arbitrary values - */ - it('tailwind-merge', () => { - /* eslint-disable tailwindcss/classnames-order */ - expect(cn('p-0')).toBe('p-0') - expect(cn('text-right text-center text-left')).toBe('text-left') - expect(cn('pl-4 p-8')).toBe('p-8') - expect(cn('m-[2px] m-[4px]')).toBe('m-[4px]') - expect(cn('m-1 m-[4px]')).toBe('m-[4px]') - expect(cn('overflow-x-auto hover:overflow-x-hidden overflow-x-scroll')).toBe( - 'hover:overflow-x-hidden overflow-x-scroll', - ) - expect(cn('h-10 h-min')).toBe('h-min') - expect(cn('bg-grey-5 bg-hotpink')).toBe('bg-hotpink') - - expect(cn('hover:block hover:inline')).toBe('hover:inline') - - expect(cn('font-medium !font-bold')).toBe('font-medium !font-bold') - expect(cn('!font-medium !font-bold')).toBe('!font-bold') - - expect(cn('text-gray-100 text-primary-200')).toBe('text-primary-200') - expect(cn('text-some-unknown-color text-components-input-bg-disabled text-primary-200')).toBe('text-primary-200') - expect(cn('bg-some-unknown-color bg-components-input-bg-disabled bg-primary-200')).toBe('bg-primary-200') - - expect(cn('border-t border-white/10')).toBe('border-t border-white/10') - expect(cn('border-t border-white')).toBe('border-t border-white') - expect(cn('text-3.5xl text-black')).toBe('text-3.5xl text-black') - }) - - /** - * Tests the integration of classnames and tailwind-merge: - * - Object-based conditional classes with Tailwind conflict resolution - */ - it('classnames combined with tailwind-merge', () => { - expect(cn('text-right', { - 'text-center': true, - })).toBe('text-center') - - expect(cn('text-right', { - 'text-center': false, - })).toBe('text-right') - }) - - /** - * Tests handling of multiple mixed argument types: - * - Strings, arrays, and objects in a single call - * - Tailwind merge working across different argument types - */ - it('multiple mixed argument types', () => { - expect(cn('foo', ['bar', 'baz'], { qux: true, quux: false })).toBe('foo bar baz qux') - expect(cn('p-4', ['p-2', 'm-4'], { 'text-left': true, 'text-right': true })).toBe('p-2 m-4 text-right') - }) - - /** - * Tests nested array handling: - * - Deep array flattening - * - Tailwind merge with nested structures - */ - it('nested arrays', () => { - expect(cn(['foo', ['bar', 'baz']])).toBe('foo bar baz') - expect(cn(['p-4', ['p-2', 'text-center']])).toBe('p-2 text-center') - }) - - /** - * Tests empty input handling: - * - Empty strings, arrays, and objects - * - Mixed empty and non-empty values - */ - it('empty inputs', () => { - expect(cn('')).toBe('') - expect(cn([])).toBe('') - expect(cn({})).toBe('') - expect(cn('', [], {})).toBe('') - expect(cn('foo', '', 'bar')).toBe('foo bar') - }) - - /** - * Tests number input handling: - * - Truthy numbers converted to strings - * - Zero treated as falsy - */ - it('numbers as inputs', () => { - expect(cn(1)).toBe('1') - expect(cn(0)).toBe('') - expect(cn('foo', 1, 'bar')).toBe('foo 1 bar') - }) - - /** - * Tests multiple object arguments: - * - Object merging - * - Tailwind conflict resolution across objects - */ - it('multiple objects', () => { - expect(cn({ foo: true }, { bar: true })).toBe('foo bar') - expect(cn({ foo: true, bar: false }, { bar: true, baz: true })).toBe('foo bar baz') - expect(cn({ 'p-4': true }, { 'p-2': true })).toBe('p-2') - }) - - /** - * Tests complex edge cases: - * - Mixed falsy values - * - Nested arrays with falsy values - * - Multiple conflicting Tailwind classes - */ - it('complex edge cases', () => { - expect(cn('foo', null, undefined, false, 'bar', 0, 1, '')).toBe('foo bar 1') - expect(cn(['foo', null, ['bar', undefined, 'baz']])).toBe('foo bar baz') - expect(cn('text-sm', { 'text-lg': false, 'text-xl': true }, 'text-2xl')).toBe('text-2xl') - }) - - /** - * Tests important (!) modifier behavior: - * - Important modifiers in objects - * - Conflict resolution with important prefix - */ - it('important modifier with objects', () => { - expect(cn({ '!font-medium': true }, { '!font-bold': true })).toBe('!font-bold') - expect(cn('font-normal', { '!font-bold': true })).toBe('font-normal !font-bold') - }) -})