diff --git a/api/controllers/console/app/workflow.py b/api/controllers/console/app/workflow.py index 97fb3e585c..8d7b3f1d99 100644 --- a/api/controllers/console/app/workflow.py +++ b/api/controllers/console/app/workflow.py @@ -60,7 +60,8 @@ _file_access_controller = DatabaseFileAccessController() LISTENING_RETRY_IN = 2000 DEFAULT_REF_TEMPLATE_SWAGGER_2_0 = "#/definitions/{model}" RESTORE_SOURCE_WORKFLOW_MUST_BE_PUBLISHED_MESSAGE = "source workflow must be published" -MAX_WORKFLOW_ONLINE_USERS_QUERY_IDS = 50 +MAX_WORKFLOW_ONLINE_USERS_REQUEST_IDS = 1000 +WORKFLOW_ONLINE_USERS_REDIS_BATCH_SIZE = 50 # Register models for flask_restx to avoid dict type issues in Swagger # Register in dependency order: base models first, then dependent models @@ -175,8 +176,13 @@ class WorkflowFeaturesPayload(BaseModel): features: dict[str, Any] = Field(..., description="Workflow feature configuration") -class WorkflowOnlineUsersQuery(BaseModel): - app_ids: str = Field(..., description="Comma-separated app IDs") +class WorkflowOnlineUsersPayload(BaseModel): + app_ids: list[str] = Field(default_factory=list, description="App IDs") + + @field_validator("app_ids") + @classmethod + def normalize_app_ids(cls, app_ids: list[str]) -> list[str]: + return list(dict.fromkeys(app_id.strip() for app_id in app_ids if app_id.strip())) class DraftWorkflowTriggerRunPayload(BaseModel): @@ -204,7 +210,7 @@ reg(WorkflowListQuery) reg(WorkflowUpdatePayload) reg(WorkflowTypeConvertQuery) reg(WorkflowFeaturesPayload) -reg(WorkflowOnlineUsersQuery) +reg(WorkflowOnlineUsersPayload) reg(DraftWorkflowTriggerRunPayload) reg(DraftWorkflowTriggerRunAllPayload) @@ -1496,19 +1502,19 @@ class DraftWorkflowTriggerRunAllApi(Resource): @console_ns.route("/apps/workflows/online-users") class WorkflowOnlineUsersApi(Resource): - @console_ns.expect(console_ns.models[WorkflowOnlineUsersQuery.__name__]) + @console_ns.expect(console_ns.models[WorkflowOnlineUsersPayload.__name__]) @console_ns.doc("get_workflow_online_users") @console_ns.doc(description="Get workflow online users") @setup_required @login_required @account_initialization_required @marshal_with(online_user_list_fields) - def get(self): - args = WorkflowOnlineUsersQuery.model_validate(request.args.to_dict(flat=True)) # type: ignore + def post(self): + args = WorkflowOnlineUsersPayload.model_validate(console_ns.payload or {}) - app_ids = list(dict.fromkeys(app_id.strip() for app_id in args.app_ids.split(",") if app_id.strip())) - if len(app_ids) > MAX_WORKFLOW_ONLINE_USERS_QUERY_IDS: - raise BadRequest(f"Maximum {MAX_WORKFLOW_ONLINE_USERS_QUERY_IDS} app_ids are allowed per request.") + app_ids = args.app_ids + if len(app_ids) > MAX_WORKFLOW_ONLINE_USERS_REQUEST_IDS: + raise BadRequest(f"Maximum {MAX_WORKFLOW_ONLINE_USERS_REQUEST_IDS} app_ids are allowed per request.") if not app_ids: return {"data": []} @@ -1516,13 +1522,24 @@ class WorkflowOnlineUsersApi(Resource): _, current_tenant_id = current_account_with_tenant() workflow_service = WorkflowService() accessible_app_ids = workflow_service.get_accessible_app_ids(app_ids, current_tenant_id) + ordered_accessible_app_ids = [app_id for app_id in app_ids if app_id in accessible_app_ids] + + users_json_by_app_id: dict[str, Any] = {} + for start_index in range(0, len(ordered_accessible_app_ids), WORKFLOW_ONLINE_USERS_REDIS_BATCH_SIZE): + app_id_batch = ordered_accessible_app_ids[ + start_index : start_index + WORKFLOW_ONLINE_USERS_REDIS_BATCH_SIZE + ] + pipe = redis_client.pipeline(transaction=False) + for app_id in app_id_batch: + pipe.hgetall(f"{WORKFLOW_ONLINE_USERS_PREFIX}{app_id}") + + users_json_batch = pipe.execute() + for app_id, users_json in zip(app_id_batch, users_json_batch): + users_json_by_app_id[app_id] = users_json results = [] - for app_id in app_ids: - if app_id not in accessible_app_ids: - continue - - users_json = redis_client.hgetall(f"{WORKFLOW_ONLINE_USERS_PREFIX}{app_id}") + for app_id in ordered_accessible_app_ids: + users_json = users_json_by_app_id.get(app_id, {}) users = [] for _, user_info_json in users_json.items(): diff --git a/api/core/rag/datasource/retrieval_service.py b/api/core/rag/datasource/retrieval_service.py index 780a42dbad..d2e2a16252 100644 --- a/api/core/rag/datasource/retrieval_service.py +++ b/api/core/rag/datasource/retrieval_service.py @@ -200,10 +200,11 @@ class RetrievalService: """Deduplicate documents in O(n) while preserving first-seen order. Rules: - - For provider == "dify" and metadata["doc_id"] exists: keep the doc with the highest - metadata["score"] among duplicates; if a later duplicate has no score, ignore it. - - For non-dify documents (or dify without doc_id): deduplicate by content key - (provider, page_content), keeping the first occurrence. + - If metadata["doc_id"] exists (any provider): deduplicate by (provider, doc_id) key; + keep the doc with the highest metadata["score"] among duplicates. If a later duplicate + has no score, ignore it. + - If metadata["doc_id"] is absent: deduplicate by content key (provider, page_content), + keeping the first occurrence. """ if not documents: return documents @@ -214,11 +215,10 @@ class RetrievalService: order: list[tuple] = [] for doc in documents: - is_dify = doc.provider == "dify" - doc_id = (doc.metadata or {}).get("doc_id") if is_dify else None + doc_id = (doc.metadata or {}).get("doc_id") - if is_dify and doc_id: - key = ("dify", doc_id) + if doc_id: + key = (doc.provider or "dify", doc_id) if key not in chosen: chosen[key] = doc order.append(key) diff --git a/api/pyproject.toml b/api/pyproject.toml index 4aff4f3258..8e690f58a6 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -6,7 +6,7 @@ requires-python = "~=3.12.0" dependencies = [ # Legacy: mature and widely deployed "bleach>=6.3.0", - "boto3>=1.42.96", + "boto3>=1.43.3", "celery>=5.6.3", "croniter>=6.2.2", "flask>=3.1.3,<4.0.0", @@ -127,7 +127,7 @@ dev = [ "testcontainers>=4.14.2", "types-aiofiles>=25.1.0", "types-beautifulsoup4>=4.12.0", - "types-cachetools>=6.2.0", + "types-cachetools>=7.0.0.20260503", "types-colorama>=0.4.15", "types-defusedxml>=0.7.0", "types-deprecated>=1.3.1", @@ -135,7 +135,7 @@ dev = [ "types-flask-cors>=6.0.0", "types-flask-migrate>=4.1.0", "types-gevent>=26.4.0", - "types-greenlet>=3.4.0", + "types-greenlet>=3.5.0.20260428", "types-html5lib>=1.1.11", "types-markdown>=3.10.2", "types-oauthlib>=3.3.0", @@ -143,7 +143,7 @@ dev = [ "types-olefile>=0.47.0", "types-openpyxl>=3.1.5", "types-pexpect>=4.9.0", - "types-protobuf>=7.34.1", + "types-protobuf>=7.34.1.20260503", "types-psutil>=7.2.2", "types-psycopg2>=2.9.21.20260422", "types-pygments>=2.20.0", @@ -158,11 +158,11 @@ dev = [ "types-tensorflow>=2.18.0.20260408", "types-tqdm>=4.67.3.20260408", "types-ujson>=5.10.0", - "boto3-stubs>=1.42.96", + "boto3-stubs>=1.43.2", "types-jmespath>=1.1.0.20260408", - "hypothesis>=6.152.3", + "hypothesis>=6.152.4", "types_pyOpenSSL>=24.1.0", - "types_cffi>=2.0.0.20260408", + "types_cffi>=2.0.0.20260429", "types_setuptools>=82.0.0.20260408", "pandas-stubs>=3.0.0", "scipy-stubs>=1.17.1.4", @@ -184,7 +184,7 @@ dev = [ ############################################################ storage = [ "azure-storage-blob>=12.28.0", - "bce-python-sdk>=0.9.70", + "bce-python-sdk>=0.9.71", "cos-python-sdk-v5>=1.9.42", "esdk-obs-python>=3.22.2", "google-cloud-storage>=3.10.1", diff --git a/api/tests/unit_tests/controllers/console/app/test_workflow.py b/api/tests/unit_tests/controllers/console/app/test_workflow.py index 1f0bf0c477..f29a437bbf 100644 --- a/api/tests/unit_tests/controllers/console/app/test_workflow.py +++ b/api/tests/unit_tests/controllers/console/app/test_workflow.py @@ -470,7 +470,8 @@ def test_workflow_online_users_filters_inaccessible_workflow(app, monkeypatch: p ) monkeypatch.setattr(workflow_module.file_helpers, "get_signed_file_url", sign_avatar) - workflow_module.redis_client.hgetall.side_effect = lambda key: ( + redis_pipeline = Mock() + redis_pipeline.execute.return_value = [ { b"sid-1": json.dumps( { @@ -481,16 +482,16 @@ def test_workflow_online_users_filters_inaccessible_workflow(app, monkeypatch: p } ) } - if key == f"{workflow_module.WORKFLOW_ONLINE_USERS_PREFIX}{app_id_1}" - else {} - ) + ] + workflow_module.redis_client.pipeline.return_value = redis_pipeline api = workflow_module.WorkflowOnlineUsersApi() - handler = _unwrap(api.get) + handler = _unwrap(api.post) with app.test_request_context( - f"/apps/workflows/online-users?app_ids={app_id_1},{app_id_2}", - method="GET", + "/apps/workflows/online-users", + method="POST", + json={"app_ids": [app_id_1, app_id_2]}, ): response = handler(api) @@ -509,12 +510,43 @@ def test_workflow_online_users_filters_inaccessible_workflow(app, monkeypatch: p } ] } - workflow_module.redis_client.hgetall.assert_called_once_with( - f"{workflow_module.WORKFLOW_ONLINE_USERS_PREFIX}{app_id_1}" - ) + workflow_module.redis_client.pipeline.assert_called_once_with(transaction=False) + redis_pipeline.hgetall.assert_called_once_with(f"{workflow_module.WORKFLOW_ONLINE_USERS_PREFIX}{app_id_1}") + redis_pipeline.execute.assert_called_once_with() sign_avatar.assert_called_once_with("avatar-file-id") +def test_workflow_online_users_batches_redis_reads(app, monkeypatch: pytest.MonkeyPatch) -> None: + app_ids = [f"wf-{index}" for index in range(workflow_module.WORKFLOW_ONLINE_USERS_REDIS_BATCH_SIZE + 1)] + monkeypatch.setattr(workflow_module, "current_account_with_tenant", lambda: (SimpleNamespace(), "tenant-1")) + monkeypatch.setattr( + workflow_module, + "WorkflowService", + lambda: SimpleNamespace(get_accessible_app_ids=lambda app_ids, tenant_id: set(app_ids)), + ) + + first_pipeline = Mock() + first_pipeline.execute.return_value = [{} for _ in range(workflow_module.WORKFLOW_ONLINE_USERS_REDIS_BATCH_SIZE)] + second_pipeline = Mock() + second_pipeline.execute.return_value = [{}] + workflow_module.redis_client.pipeline.side_effect = [first_pipeline, second_pipeline] + + api = workflow_module.WorkflowOnlineUsersApi() + handler = _unwrap(api.post) + + with app.test_request_context( + "/apps/workflows/online-users", + method="POST", + json={"app_ids": app_ids}, + ): + response = handler(api) + + assert len(response["data"]) == len(app_ids) + assert workflow_module.redis_client.pipeline.call_count == 2 + assert first_pipeline.hgetall.call_count == workflow_module.WORKFLOW_ONLINE_USERS_REDIS_BATCH_SIZE + assert second_pipeline.hgetall.call_count == 1 + + def test_workflow_online_users_rejects_excessive_workflow_ids(app, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(workflow_module, "current_account_with_tenant", lambda: (SimpleNamespace(), "tenant-1")) accessible_app_ids = Mock(return_value=set()) @@ -524,14 +556,15 @@ def test_workflow_online_users_rejects_excessive_workflow_ids(app, monkeypatch: lambda: SimpleNamespace(get_accessible_app_ids=accessible_app_ids), ) - excessive_ids = ",".join(f"wf-{index}" for index in range(workflow_module.MAX_WORKFLOW_ONLINE_USERS_QUERY_IDS + 1)) + excessive_ids = [f"wf-{index}" for index in range(workflow_module.MAX_WORKFLOW_ONLINE_USERS_REQUEST_IDS + 1)] api = workflow_module.WorkflowOnlineUsersApi() - handler = _unwrap(api.get) + handler = _unwrap(api.post) with app.test_request_context( - f"/apps/workflows/online-users?app_ids={excessive_ids}", - method="GET", + "/apps/workflows/online-users", + method="POST", + json={"app_ids": excessive_ids}, ): with pytest.raises(HTTPException) as exc: handler(api) diff --git a/api/tests/unit_tests/core/rag/retrieval/test_dataset_retrieval.py b/api/tests/unit_tests/core/rag/retrieval/test_dataset_retrieval.py index fd607210f1..b556ddf528 100644 --- a/api/tests/unit_tests/core/rag/retrieval/test_dataset_retrieval.py +++ b/api/tests/unit_tests/core/rag/retrieval/test_dataset_retrieval.py @@ -1106,11 +1106,11 @@ class TestRetrievalService: def test_deduplicate_documents_non_dify_provider(self): """ - Test deduplication with non-dify provider documents. + Test deduplication with non-dify provider documents that have no doc_id. Verifies: - - External provider documents use content-based deduplication - - Different providers are handled correctly + - External provider documents without doc_id use content-based deduplication + - Identical content from the same provider is collapsed to one result """ # Arrange doc1 = Document( @@ -1131,7 +1131,96 @@ class TestRetrievalService: # Assert # External documents without doc_id should use content-based dedup - assert len(result) >= 1 + assert len(result) == 1 + + def test_deduplicate_documents_non_dify_provider_with_doc_id_different_sources(self): + """ + Regression test for issue #35707. + + Two chunks from different source documents share identical text content but carry + different doc_ids. Before the fix, non-dify providers were forced into content-based + deduplication and the second chunk was silently dropped. After the fix, doc_id is used + as the dedup key for any provider that exposes it, so both chunks must be retained. + + Verifies: + - Non-dify provider documents with different doc_ids are NOT deduplicated even when + their page_content is identical. + """ + # Arrange — same content, different doc_ids, non-dify provider (e.g. Weaviate / Qdrant) + doc_a = Document( + page_content="Shared identical content", + metadata={"doc_id": "doc-from-file-a", "score": 0.85}, + provider="weaviate", + ) + doc_b = Document( + page_content="Shared identical content", + metadata={"doc_id": "doc-from-file-b", "score": 0.82}, + provider="weaviate", + ) + + # Act + result = RetrievalService._deduplicate_documents([doc_a, doc_b]) + + # Assert — both documents must be kept; losing either silently drops a source citation + assert len(result) == 2 + doc_ids = {doc.metadata["doc_id"] for doc in result} + assert doc_ids == {"doc-from-file-a", "doc-from-file-b"} + + def test_deduplicate_documents_non_dify_provider_with_same_doc_id(self): + """ + Test that non-dify provider documents sharing the same doc_id are deduplicated by + doc_id key (not by content), and the higher-scored duplicate is retained. + + Verifies: + - doc_id-based deduplication now applies to any provider, not only "dify" + - The document with the highest score wins when doc_ids collide + """ + # Arrange + doc_low = Document( + page_content="Content A", + metadata={"doc_id": "chunk-1", "score": 0.5}, + provider="qdrant", + ) + doc_high = Document( + page_content="Content A", + metadata={"doc_id": "chunk-1", "score": 0.9}, + provider="qdrant", + ) + + # Act + result = RetrievalService._deduplicate_documents([doc_low, doc_high]) + + # Assert + assert len(result) == 1 + assert result[0].metadata["score"] == 0.9 + + def test_deduplicate_documents_dify_provider_without_doc_id_falls_back_to_content(self): + """ + Test that a dify provider document without doc_id still falls back to content-based + deduplication (no regression from original behaviour). + + Verifies: + - Absence of doc_id triggers content-based dedup regardless of provider + - First occurrence is kept when content is identical + """ + # Arrange — dify docs with no doc_id, same content + doc1 = Document( + page_content="Same content", + metadata={"score": 0.8}, + provider="dify", + ) + doc2 = Document( + page_content="Same content", + metadata={"score": 0.9}, + provider="dify", + ) + + # Act + result = RetrievalService._deduplicate_documents([doc1, doc2]) + + # Assert — collapsed to one; first-seen wins (no score comparison in content branch) + assert len(result) == 1 + assert result[0].metadata["score"] == 0.8 # ==================== Metadata Filtering Tests ==================== diff --git a/api/uv.lock b/api/uv.lock index 9171f2f890..e6b790be48 100644 --- a/api/uv.lock +++ b/api/uv.lock @@ -490,7 +490,7 @@ wheels = [ [[package]] name = "bce-python-sdk" -version = "0.9.70" +version = "0.9.71" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "crc32c" }, @@ -498,9 +498,9 @@ dependencies = [ { name = "pycryptodome" }, { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f7/a9/7c21a9073eb9ad7e8cacf6f8a0e47c0d01ad7bf8fd8e0dc42164b117d60b/bce_python_sdk-0.9.70.tar.gz", hash = "sha256:3b37fd7448278dd33f745a6a23198a2cc2490fded9cb8d59b72500784853df4e", size = 299967, upload-time = "2026-04-14T12:02:42.034Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/74/72058f098b9e7184376f2b3d4c1d233ca7fdc52d0f527078f3ce4d9828b9/bce_python_sdk-0.9.71.tar.gz", hash = "sha256:7a917edaee39082694776e25a9e6556ec8072400a3be649f28eb13f9c7a0b5b5", size = 301508, upload-time = "2026-04-28T06:23:21.061Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/2d/70fc866ff98d1f6bd75b0a4235694129b3c519b014254d7bcfc02ffe1bee/bce_python_sdk-0.9.70-py3-none-any.whl", hash = "sha256:fd1f31113e4a8dca314f040662b7caf07ec11cf896c5da232627a9a2c9d2e3a1", size = 415660, upload-time = "2026-04-14T12:02:40.034Z" }, + { url = "https://files.pythonhosted.org/packages/2d/2d/821ae8878dc36b77e56bb7e5dbf9a8e73209c11d38c0ba6b38b5778668ae/bce_python_sdk-0.9.71-py3-none-any.whl", hash = "sha256:9f64a99267616456bac487983d92cc778720bf4f102c8931e8e38aea3cb63268", size = 417000, upload-time = "2026-04-28T06:23:19.078Z" }, ] [[package]] @@ -613,29 +613,29 @@ wheels = [ [[package]] name = "boto3" -version = "1.42.96" +version = "1.43.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, { name = "jmespath" }, { name = "s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/2d/69fb3acd50bab83fb295c167d33c4b653faeb5fb0f42bfca4d9b69d6fb68/boto3-1.42.96.tar.gz", hash = "sha256:b38a9e4a3fbbee9017252576f1379780d0a5814768676c08df2f539d31fcdd68", size = 113203, upload-time = "2026-04-24T19:47:18.677Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/50/ea184e159c4ac64fef816a72094fb8656eb071361a39ed22c0e3b15a35b4/boto3-1.43.3.tar.gz", hash = "sha256:7c7777862ffc898f05efa566032bbabfe226dbb810e35ec11125817f128bc5c5", size = 113111, upload-time = "2026-05-04T19:34:09.731Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/9d/b3f617d011c42eb804d993103b8fa9acdce153e181a3042f58bfe33d7cb4/boto3-1.42.96-py3-none-any.whl", hash = "sha256:2f4566da2c209a98bdbfc874d813ef231c84ad24e4f815e9bc91de5f63351a24", size = 140557, upload-time = "2026-04-24T19:47:15.824Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ad/8a6946a329f0127322108e537dc1c0d9f8eea4f1d1231702c073d2e85f46/boto3-1.43.3-py3-none-any.whl", hash = "sha256:fb9fe51849ef2a78198d582756fc06f14f7de27f73e0fa90275d6aa4171eb4d0", size = 140501, upload-time = "2026-05-04T19:34:07.991Z" }, ] [[package]] name = "boto3-stubs" -version = "1.42.96" +version = "1.43.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore-stubs" }, { name = "types-s3transfer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/86/65f45f84621cccc2471871088bab8fe515b4346ba9e48d9001484ec440d6/boto3_stubs-1.42.96.tar.gz", hash = "sha256:1e7819c34d1eae8e5e3cfaf9d144fdcad65aad184b380488871de1d0b2851879", size = 102691, upload-time = "2026-04-24T20:25:13.984Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/7f/399bcdeaa60a89aafe5292c8364c313177d22b886dffc1bd7b56fe817900/boto3_stubs-1.43.2.tar.gz", hash = "sha256:0d46636f3e761a92070114b39a76b154c5da6c5794c890e1440a7f191bf1ff2e", size = 102658, upload-time = "2026-05-01T20:31:36.963Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/51/bdac1ff9fd4321091183776c5adffce5fc7b4d0fec7e38af9064e24a2497/boto3_stubs-1.42.96-py3-none-any.whl", hash = "sha256:2c112e257f40006147a53f6f62075804689154271973b2807f5656feaa804216", size = 70668, upload-time = "2026-04-24T20:25:09.736Z" }, + { url = "https://files.pythonhosted.org/packages/da/df/17647562444b2047ca325eaaf2fea738571822b7b4efdaa6bacf0fd4fff9/boto3_stubs-1.43.2-py3-none-any.whl", hash = "sha256:941f2907236223a1209704eaf708d3cdf1ecc8695618c558f9fb9e23e90c513b", size = 70653, upload-time = "2026-05-01T20:31:30.057Z" }, ] [package.optional-dependencies] @@ -645,16 +645,16 @@ bedrock-runtime = [ [[package]] name = "botocore" -version = "1.42.96" +version = "1.43.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/77/2c333622a1d47cf5bf73cdcab0cb6c92addafbef2ec05f81b9f75687d9e5/botocore-1.42.96.tar.gz", hash = "sha256:75b3b841ffacaa944f645196655a21ca777591dd8911e732bfb6614545af0250", size = 15263344, upload-time = "2026-04-24T19:47:05.283Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/ac/cd55f886e17b6b952dbc95b792d3645a73d58586a1400ababe54406073bd/botocore-1.43.3.tar.gz", hash = "sha256:eac6da0fffccf87888ebf4d89f0b2378218a707efa748cd955b838995e944695", size = 15308705, upload-time = "2026-05-04T19:33:56.28Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/45/56/152c3a859ca1b9d77ed16deac3cf81682013677c68cf5715698781fc81bd/botocore-1.42.96-py3-none-any.whl", hash = "sha256:db2c3e2006628be6fde81a24124a6563c363d6982fb92728837cf174bad9d98a", size = 14945920, upload-time = "2026-04-24T19:47:00.323Z" }, + { url = "https://files.pythonhosted.org/packages/be/99/1d9e296edf244f47e0508032f20999f8fd40704dd3c5b601fed099424eb6/botocore-1.43.3-py3-none-any.whl", hash = "sha256:ec0769eb0f7c5034856bb406a92698dbc02a3d4be0f78a384747106b161d8ea3", size = 14989027, upload-time = "2026-05-04T19:33:50.81Z" }, ] [[package]] @@ -1669,7 +1669,7 @@ requires-dist = [ { name = "aliyun-log-python-sdk", specifier = ">=0.9.44,<1.0.0" }, { name = "azure-identity", specifier = ">=1.25.3,<2.0.0" }, { name = "bleach", specifier = ">=6.3.0" }, - { name = "boto3", specifier = ">=1.42.96" }, + { name = "boto3", specifier = ">=1.43.3" }, { name = "celery", specifier = ">=5.6.3" }, { name = "croniter", specifier = ">=6.2.2" }, { name = "fastopenapi", extras = ["flask"], specifier = "~=0.7.0" }, @@ -1710,12 +1710,12 @@ requires-dist = [ [package.metadata.requires-dev] dev = [ { name = "basedpyright", specifier = ">=1.39.3" }, - { name = "boto3-stubs", specifier = ">=1.42.96" }, + { name = "boto3-stubs", specifier = ">=1.43.2" }, { name = "celery-types", specifier = ">=0.23.0" }, { name = "coverage", specifier = ">=7.13.4" }, { name = "dotenv-linter", specifier = ">=0.7.0" }, { name = "faker", specifier = ">=40.15.0" }, - { name = "hypothesis", specifier = ">=6.152.3" }, + { name = "hypothesis", specifier = ">=6.152.4" }, { name = "import-linter", specifier = ">=2.3" }, { name = "lxml-stubs", specifier = ">=0.5.1" }, { name = "mypy", specifier = ">=1.20.2" }, @@ -1733,8 +1733,8 @@ dev = [ { name = "testcontainers", specifier = ">=4.14.2" }, { name = "types-aiofiles", specifier = ">=25.1.0" }, { name = "types-beautifulsoup4", specifier = ">=4.12.0" }, - { name = "types-cachetools", specifier = ">=6.2.0" }, - { name = "types-cffi", specifier = ">=2.0.0.20260408" }, + { name = "types-cachetools", specifier = ">=7.0.0.20260503" }, + { name = "types-cffi", specifier = ">=2.0.0.20260429" }, { name = "types-colorama", specifier = ">=0.4.15" }, { name = "types-defusedxml", specifier = ">=0.7.0" }, { name = "types-deprecated", specifier = ">=1.3.1" }, @@ -1742,7 +1742,7 @@ dev = [ { name = "types-flask-cors", specifier = ">=6.0.0" }, { name = "types-flask-migrate", specifier = ">=4.1.0" }, { name = "types-gevent", specifier = ">=26.4.0" }, - { name = "types-greenlet", specifier = ">=3.4.0" }, + { name = "types-greenlet", specifier = ">=3.5.0.20260428" }, { name = "types-html5lib", specifier = ">=1.1.11" }, { name = "types-jmespath", specifier = ">=1.1.0.20260408" }, { name = "types-markdown", specifier = ">=3.10.2" }, @@ -1751,7 +1751,7 @@ dev = [ { name = "types-olefile", specifier = ">=0.47.0" }, { name = "types-openpyxl", specifier = ">=3.1.5" }, { name = "types-pexpect", specifier = ">=4.9.0" }, - { name = "types-protobuf", specifier = ">=7.34.1" }, + { name = "types-protobuf", specifier = ">=7.34.1.20260503" }, { name = "types-psutil", specifier = ">=7.2.2" }, { name = "types-psycopg2", specifier = ">=2.9.21.20260422" }, { name = "types-pygments", specifier = ">=2.20.0" }, @@ -1778,7 +1778,7 @@ evaluation = [ ] storage = [ { name = "azure-storage-blob", specifier = ">=12.28.0" }, - { name = "bce-python-sdk", specifier = ">=0.9.70" }, + { name = "bce-python-sdk", specifier = ">=0.9.71" }, { name = "cos-python-sdk-v5", specifier = ">=1.9.42" }, { name = "esdk-obs-python", specifier = ">=3.22.2" }, { name = "google-cloud-storage", specifier = ">=3.10.1" }, @@ -3437,14 +3437,14 @@ wheels = [ [[package]] name = "hypothesis" -version = "6.152.3" +version = "6.152.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "sortedcontainers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/90/fc0b263b6f2622e5f8d2aa93f2e95ba79718a5faa7d2a74bfab10d6b0905/hypothesis-6.152.3.tar.gz", hash = "sha256:c4e5300d3755b6c8a270a28fe5abff40153e927328e89d2bb0229c1384618998", size = 466478, upload-time = "2026-04-26T17:31:07.657Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/c7/3147bd903d6b18324a016d43a259cf5b4bb4545e1ead6773dc8a0374e70a/hypothesis-6.152.4.tar.gz", hash = "sha256:31c8f9ce619716f543e2710b489b1633c833586641d9e6c94cee03f109a5afc4", size = 466444, upload-time = "2026-04-27T20:18:37.594Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/90/38/15475b91a4c12721d2be3349e9d6cf8649c76ed9bc1287e2de7c8d06c261/hypothesis-6.152.3-py3-none-any.whl", hash = "sha256:4b47f00916c858ed49cf870a2f08b04e5fff5afae0bb78f3b4a6d9c74fd6c7bc", size = 532154, upload-time = "2026-04-26T17:31:04.42Z" }, + { url = "https://files.pythonhosted.org/packages/19/89/0f50dd0d92e8a7dffc24f69ab910ff81db89b2f082ba42682bd57695e4d2/hypothesis-6.152.4-py3-none-any.whl", hash = "sha256:e730fd93c7578182efadc7f90b3c5437ee4d55edf738930eb5043c81ac1d97e8", size = 532145, upload-time = "2026-04-27T20:18:35.043Z" }, ] [[package]] @@ -4314,11 +4314,11 @@ wheels = [ [[package]] name = "mypy-boto3-bedrock-runtime" -version = "1.42.42" +version = "1.43.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/46/bb/65dc1b2c5796a6ab5f60bdb57343bd6c3ecb82251c580eca415c8548333e/mypy_boto3_bedrock_runtime-1.42.42.tar.gz", hash = "sha256:3a4088218478b6fbbc26055c03c95bee4fc04624a801090b3cce3037e8275c8d", size = 29840, upload-time = "2026-02-04T20:53:05.999Z" } +sdist = { url = "https://files.pythonhosted.org/packages/21/f2/61519c0162307b1e4d47f63ed8b25390874640934f3d2d25c5d6c5078dd8/mypy_boto3_bedrock_runtime-1.43.0.tar.gz", hash = "sha256:19fc3167de6e66dd7a0ab293adc55c93e2fd67be35e8ab4fc3a7523a380752ce", size = 29903, upload-time = "2026-04-29T22:57:57.561Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/00/43/7ea062f2228f47b5779dcfa14dab48d6e29f979b35d1a5102b0ba80b9c1b/mypy_boto3_bedrock_runtime-1.42.42-py3-none-any.whl", hash = "sha256:b2d16eae22607d0685f90796b3a0afc78c0b09d45872e00eafd634a31dd9358f", size = 36077, upload-time = "2026-02-04T20:53:01.768Z" }, + { url = "https://files.pythonhosted.org/packages/40/4d/7e4c4d55af23b2b1304d6814db8c406beab7977056963200230417c1a2db/mypy_boto3_bedrock_runtime-1.43.0-py3-none-any.whl", hash = "sha256:a125296f992093d58bdcd95176002680fa81ca8a8b8bdf02afad7e5f2d8966aa", size = 36172, upload-time = "2026-04-29T22:57:54.777Z" }, ] [[package]] @@ -6369,14 +6369,14 @@ wheels = [ [[package]] name = "s3transfer" -version = "0.16.0" +version = "0.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "botocore" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/05/04/74127fc843314818edfa81b5540e26dd537353b123a4edc563109d8f17dd/s3transfer-0.16.0.tar.gz", hash = "sha256:8e990f13268025792229cd52fa10cb7163744bf56e719e0b9cb925ab79abf920", size = 153827, upload-time = "2025-12-01T02:30:59.114Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9b/ec/7c692cde9125b77e84b307354d4fb705f98b8ccad59a036d5957ca75bfc3/s3transfer-0.17.0.tar.gz", hash = "sha256:9edeb6d1c3c2f89d6050348548834ad8289610d886e5bf7b7207728bd43ce33a", size = 155337, upload-time = "2026-04-29T22:07:36.33Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/51/727abb13f44c1fcf6d145979e1535a35794db0f6e450a0cb46aa24732fe2/s3transfer-0.16.0-py3-none-any.whl", hash = "sha256:18e25d66fed509e3868dc1572b3f427ff947dd2c56f844a5bf09481ad3f3b2fe", size = 86830, upload-time = "2025-12-01T02:30:57.729Z" }, + { url = "https://files.pythonhosted.org/packages/87/72/c6c32d2b657fa3dad1de340254e14390b1e334ce38268b7ad51abda3c8c2/s3transfer-0.17.0-py3-none-any.whl", hash = "sha256:ce3801712acf4ad3e89fb9990df97b4972e93f4b3b0004d214be5bce12814c20", size = 86811, upload-time = "2026-04-29T22:07:34.966Z" }, ] [[package]] @@ -7049,23 +7049,23 @@ wheels = [ [[package]] name = "types-cachetools" -version = "6.2.0.20260408" +version = "7.0.0.20260503" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/61/475b0e8f4a92e5e33affcc6f4e6344c6dee540824021d22f695ea170da63/types_cachetools-6.2.0.20260408.tar.gz", hash = "sha256:0d8ae2dd5ba0b4cfe6a55c34396dd0415f1be07d0033d84781cdc4ed9c2ebc6b", size = 9854, upload-time = "2026-04-08T04:31:49.665Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/57/5d3b8b3e66b002911ec1274e87f904eeee1d843c8713d95476c25c29cf31/types_cachetools-7.0.0.20260503.tar.gz", hash = "sha256:dfa4dcdf453f397dfc6d69fc0a57423ac1f248393f70aa56b5d05fac2df7a96c", size = 10033, upload-time = "2026-05-03T05:19:54.128Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/7d/579f50f4f004ee93c7d1baa95339591cac1fe02f4e3fb8fc0f900ee4a80f/types_cachetools-6.2.0.20260408-py3-none-any.whl", hash = "sha256:470e0b274737feae74beed3d764885bf4664002ecc393fba3778846b13ce92cb", size = 9350, upload-time = "2026-04-08T04:31:48.826Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a8/84562723d9a3572e0851d82bdea6bed5a7dc033c6bd648f492c76b8c4ac8/types_cachetools-7.0.0.20260503-py3-none-any.whl", hash = "sha256:011b4fe0e85ef05c4a2471a4fda40254a78746b501cc1727359233872bb3a4e9", size = 9493, upload-time = "2026-05-03T05:19:53.124Z" }, ] [[package]] name = "types-cffi" -version = "2.0.0.20260408" +version = "2.0.0.20260429" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "types-setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/67/eb4ef3408fdc0b4e5af38b30c0e6ad4663b41bdae9fb85a9f09a8db61a99/types_cffi-2.0.0.20260408.tar.gz", hash = "sha256:aa8b9c456ab715c079fc655929811f21f331bfb940f4a821987c581bf4e36230", size = 17541, upload-time = "2026-04-08T04:36:03.918Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/7d/56b9be8b0f9dfbffb7c73e248aacf178693ff3c6cf765b77c43a1e886e04/types_cffi-2.0.0.20260429.tar.gz", hash = "sha256:afe7d9777a2921139623af0b94647637a5bd0b938b77ec125e5e5e068a1727bd", size = 17562, upload-time = "2026-04-29T05:16:43.29Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/a3/7fbd93ededcc7c77e9e5948b9794161733ebdbf618a27965b1bea0e728a4/types_cffi-2.0.0.20260408-py3-none-any.whl", hash = "sha256:68bd296742b4ff7c0afe3547f50bd0acc55416ecf322ffefd2b7344ef6388a42", size = 20101, upload-time = "2026-04-08T04:36:02.995Z" }, + { url = "https://files.pythonhosted.org/packages/b8/2c/79fa47a70d534f63a54b6d22e28cc842f8c6d9ebec93048355b0020bc7a9/types_cffi-2.0.0.20260429-py3-none-any.whl", hash = "sha256:6a4237bfdbd50e4d0726929070d8b9983bde541726a5a6fe0e8e24e78c1b3826", size = 20103, upload-time = "2026-04-29T05:16:42.155Z" }, ] [[package]] @@ -7144,11 +7144,11 @@ wheels = [ [[package]] name = "types-greenlet" -version = "3.4.0.20260409" +version = "3.5.0.20260428" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/27/a6/668751bc864efe820e1eb12c2a77f9e62537f433cc002e483ad01badb04b/types_greenlet-3.4.0.20260409.tar.gz", hash = "sha256:81d2cf628934a16856bb9e54136def8de5356e934f0ad5d5474f219a0c5cb205", size = 8976, upload-time = "2026-04-09T04:22:31.693Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/50/d255c0e068679d7b9441d9408424ddf9e1f35620548e121003b3660af526/types_greenlet-3.5.0.20260428.tar.gz", hash = "sha256:6c188f5e9c5775d50bd00780a3eb1fb3cde17c396cf9703e3d417936e9e7a082", size = 9003, upload-time = "2026-04-28T05:19:43.062Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4f/3f/c8a4d8782f78fccb4b5fe91c5eae2efce6648072754bc7096b1e3b5407ad/types_greenlet-3.4.0.20260409-py3-none-any.whl", hash = "sha256:cbceadb4594eccd95b57b3f7fa8a9b851488f5e6c05026f4a3db9aac02ec8333", size = 8812, upload-time = "2026-04-09T04:22:30.734Z" }, + { url = "https://files.pythonhosted.org/packages/30/e5/5ff280f02392ced53cb5e866b660b492b4245b1395a61e57d2a6dc02977b/types_greenlet-3.5.0.20260428-py3-none-any.whl", hash = "sha256:7b0f23ce84ee93474d4aa8058920f0578181e11431be92ce9a4ad4123de2c41b", size = 8809, upload-time = "2026-04-28T05:19:41.976Z" }, ] [[package]] @@ -7228,11 +7228,11 @@ wheels = [ [[package]] name = "types-protobuf" -version = "7.34.1.20260408" +version = "7.34.1.20260503" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5b/b1/4521e68c2cc17703d80eb42796751345376dd4c706f84007ef5e7c707774/types_protobuf-7.34.1.20260408.tar.gz", hash = "sha256:e2c0a0430e08c75b52671a6f0035abfdcc791aad12af16274282de1b721758ab", size = 68835, upload-time = "2026-04-08T04:26:43.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a0/31/87969cb3e62287bde7598b78b3c098d2873d54f5fb5a7cfbcaa73b8c965e/types_protobuf-7.34.1.20260503.tar.gz", hash = "sha256:effbc819aa17e02448dde99f089c6794662d66f4b2797e922f185ffe0b24e766", size = 68830, upload-time = "2026-05-03T05:19:50.739Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/b5/0bc9874d89c58fb0ce851e150055ce732d254dbb10b06becbc7635d0d635/types_protobuf-7.34.1.20260408-py3-none-any.whl", hash = "sha256:ebbcd4e27b145aef6a59bc0cb6c013b3528151c1ba5e7f7337aeee355d276a5e", size = 86012, upload-time = "2026-04-08T04:26:42.566Z" }, + { url = "https://files.pythonhosted.org/packages/f9/67/a33fb18090a927794a5ee4b1a30730b528ace0dad6b18932540d21258184/types_protobuf-7.34.1.20260503-py3-none-any.whl", hash = "sha256:75fd66121d56785c91828b8bf7b511f39ba847f11e682573e41847f01e9cd1de", size = 86019, upload-time = "2026-05-03T05:19:49.486Z" }, ] [[package]] diff --git a/eslint-suppressions.json b/eslint-suppressions.json new file mode 100644 index 0000000000..bbb5cd5af9 --- /dev/null +++ b/eslint-suppressions.json @@ -0,0 +1,5672 @@ +{ + "e2e/features/support/hooks.ts": { + "no-console": { + "count": 3 + }, + "node/prefer-global/buffer": { + "count": 1 + } + }, + "e2e/scripts/common.ts": { + "node/prefer-global/buffer": { + "count": 2 + } + }, + "e2e/support/process.ts": { + "ts/no-use-before-define": { + "count": 2 + } + }, + "packages/migrate-no-unchecked-indexed-access/src/no-unchecked-indexed-access/migrate.ts": { + "no-console": { + "count": 11 + } + }, + "packages/migrate-no-unchecked-indexed-access/src/no-unchecked-indexed-access/normalize.ts": { + "no-console": { + "count": 1 + } + }, + "packages/migrate-no-unchecked-indexed-access/src/no-unchecked-indexed-access/run.ts": { + "no-console": { + "count": 9 + } + }, + "web/.storybook/main.ts": { + "storybook/no-uninstalled-addons": { + "count": 3 + } + }, + "web/__mocks__/zustand.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/__tests__/document-detail-navigation-fix.test.tsx": { + "no-console": { + "count": 10 + } + }, + "web/__tests__/document-list-sorting.test.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/__tests__/embedded-user-id-auth.test.tsx": { + "ts/no-explicit-any": { + "count": 8 + } + }, + "web/__tests__/embedded-user-id-store.test.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/__tests__/goto-anything/command-selector.test.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/__tests__/i18n-upload-features.test.ts": { + "no-console": { + "count": 3 + } + }, + "web/__tests__/navigation-utils.test.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/__tests__/plugin-tool-workflow-error.test.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/__tests__/real-browser-flicker.test.tsx": { + "no-console": { + "count": 16 + }, + "react/set-state-in-effect": { + "count": 4 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/__tests__/unified-tags-logic.test.ts": { + "ts/no-explicit-any": { + "count": 8 + } + }, + "web/__tests__/workflow-onboarding-integration.test.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout-main.tsx": { + "react/set-state-in-effect": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/__tests__/svg-attribute-error-reproduction.spec.tsx": { + "no-console": { + "count": 19 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-panel.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/type.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/(humanInputLayout)/form/[token]/form.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/(shareLayout)/components/splash.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/(shareLayout)/webapp-reset-password/layout.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/(shareLayout)/webapp-signin/components/mail-and-password-auth.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/account/(commonLayout)/account-page/email-change-modal.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/account/(commonLayout)/account-page/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/account/(commonLayout)/delete-account/components/feed-back.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/account/(commonLayout)/delete-account/components/verify-email.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/account/(commonLayout)/delete-account/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/account/oauth/authorize/layout.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/account/oauth/authorize/page.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app-sidebar/app-info/app-operations.tsx": { + "react/set-state-in-effect": { + "count": 4 + } + }, + "web/app/components/app-sidebar/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/annotation/add-annotation-modal/edit-item/index.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/app/annotation/batch-add-annotation-modal/index.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/app/annotation/edit-annotation-modal/edit-item/index.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/app/annotation/header-opts/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/annotation/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/app/annotation/type.ts": { + "erasable-syntax-only/enums": { + "count": 2 + } + }, + "web/app/components/app/annotation/view-annotation-modal/index.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 5 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/app-access-control/specific-groups-or-members.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/app/app-publisher/features-wrapper.tsx": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/app/app-publisher/index.tsx": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/app/app-publisher/version-info-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/app/configuration/base/var-highlight/index.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/app/configuration/config-prompt/conversation-history/edit-modal.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/configuration/config-prompt/simple-prompt-input.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/app/configuration/config-var/__tests__/index.spec.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/app/configuration/config-var/config-modal/index.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/app/configuration/config-var/config-modal/type-select.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/app/configuration/config-var/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/app/configuration/config-var/select-var-type.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/configuration/config/agent/agent-setting/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/configuration/config/agent/agent-setting/item-panel.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/app/configuration/config/agent/agent-tools/index.tsx": { + "ts/no-explicit-any": { + "count": 9 + } + }, + "web/app/components/app/configuration/config/agent/agent-tools/setting-built-in-tool.tsx": { + "react-hooks/exhaustive-deps": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/app/configuration/config/assistant-type-picker/index.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/configuration/config/automatic/get-automatic-res.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 4 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/configuration/config/automatic/instruction-editor.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/app/configuration/config/automatic/prompt-res-in-workflow.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/configuration/config/automatic/prompt-res.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/app/configuration/config/automatic/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/app/configuration/config/automatic/version-selector.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/app/configuration/config/code-generator/get-code-generator-res.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 4 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/app/configuration/dataset-config/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/configuration/dataset-config/params-config/__tests__/config-content.spec.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/configuration/dataset-config/params-config/index.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/app/configuration/dataset-config/select-dataset/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/app/configuration/dataset-config/settings-modal/index.tsx": { + "react/set-state-in-effect": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/app/configuration/dataset-config/settings-modal/retrieval-section.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/configuration/debug/__tests__/index.spec.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/configuration/debug/debug-with-multiple-model/chat-item.tsx": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/app/configuration/debug/debug-with-multiple-model/index.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/app/configuration/debug/debug-with-multiple-model/text-generation-item.tsx": { + "ts/no-explicit-any": { + "count": 8 + } + }, + "web/app/components/app/configuration/debug/debug-with-single-model/index.tsx": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/app/configuration/debug/hooks.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/app/configuration/debug/index.tsx": { + "react/set-state-in-effect": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 11 + } + }, + "web/app/components/app/configuration/debug/types.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/configuration/prompt-value-panel/utils.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/create-app-dialog/app-list/sidebar.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/app/create-app-modal/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/create-from-dsl-modal/dsl-confirm-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/app/create-from-dsl-modal/index.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 2 + } + }, + "web/app/components/app/duplicate-modal/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/app/log/filter.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/app/log/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/app/log/list.tsx": { + "react/set-state-in-effect": { + "count": 6 + }, + "style/multiline-ternary": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/app/log/model-info.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/app/overview/customize/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/app/overview/embedded/index.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/app/overview/settings/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, + "react/set-state-in-effect": { + "count": 3 + }, + "regexp/no-unused-capturing-group": { + "count": 1 + } + }, + "web/app/components/app/overview/trigger-card.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/app/switch-app-modal/index.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/app/text-generate/item/index.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/app/text-generate/item/result-tab.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/app/workflow-log/detail.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/app/workflow-log/filter.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/app/workflow-log/list.tsx": { + "react/set-state-in-effect": { + "count": 2 + } + }, + "web/app/components/app/workflow-log/trigger-by-display.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/apps/app-card.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/apps/list.tsx": { + "react-hooks/exhaustive-deps": { + "count": 1 + }, + "react/unsupported-syntax": { + "count": 2 + } + }, + "web/app/components/apps/new-app-card.tsx": { + "react-hooks-extra/no-direct-set-state-in-use-effect": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/action-button/index.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/base/agent-log-modal/detail.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/agent-log-modal/index.stories.tsx": { + "no-console": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/agent-log-modal/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/base/agent-log-modal/result.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/agent-log-modal/tool-call.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/amplitude/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/base/amplitude/utils.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/app-icon-picker/ImageInput.tsx": { + "react/no-create-ref": { + "count": 1 + } + }, + "web/app/components/base/audio-btn/audio.ts": { + "node/prefer-global/buffer": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/audio-gallery/AudioPlayer.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/auto-height-textarea/index.stories.tsx": { + "no-console": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/auto-height-textarea/index.tsx": { + "react-hooks/rules-of-hooks": { + "count": 1 + }, + "react/rules-of-hooks": { + "count": 1 + } + }, + "web/app/components/base/badge/index.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/base/block-input/index.stories.tsx": { + "no-console": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/block-input/index.tsx": { + "react-refresh/only-export-components": { + "count": 1 + }, + "react/component-hook-factories": { + "count": 1 + }, + "react/no-nested-component-definitions": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/base/carousel/index.tsx": { + "react-hooks-extra/no-direct-set-state-in-use-effect": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 3 + } + }, + "web/app/components/base/chat/chat-with-history/chat-wrapper.tsx": { + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/app/components/base/chat/chat-with-history/context.ts": { + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/app/components/base/chat/chat-with-history/header-in-mobile.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/chat/chat-with-history/header/index.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/chat/chat-with-history/hooks.tsx": { + "react/set-state-in-effect": { + "count": 4 + }, + "ts/no-explicit-any": { + "count": 18 + } + }, + "web/app/components/base/chat/chat-with-history/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/base/chat/chat-with-history/inputs-form/content.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/chat/chat-with-history/sidebar/operation.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/base/chat/chat-with-history/sidebar/rename-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/base/chat/chat/answer/agent-content.tsx": { + "style/multiline-ternary": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/chat/chat/answer/human-input-content/utils.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/chat/chat/answer/index.tsx": { + "react/set-state-in-effect": { + "count": 3 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/chat/chat/answer/operation.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, + "web/app/components/base/chat/chat/answer/workflow-process.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/base/chat/chat/chat-input-area/index.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/chat/chat/check-input-forms-hooks.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/chat/chat/citation/index.tsx": { + "react-hooks/exhaustive-deps": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/base/chat/chat/hooks.ts": { + "react/set-state-in-effect": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 17 + } + }, + "web/app/components/base/chat/chat/index.tsx": { + "ts/no-explicit-any": { + "count": 3 + }, + "ts/no-non-null-asserted-optional-chain": { + "count": 1 + } + }, + "web/app/components/base/chat/chat/type.ts": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/base/chat/chat/utils.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx": { + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/app/components/base/chat/embedded-chatbot/context.ts": { + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/app/components/base/chat/embedded-chatbot/hooks.tsx": { + "react-hooks-extra/no-direct-set-state-in-use-effect": { + "count": 3 + }, + "react/set-state-in-effect": { + "count": 6 + } + }, + "web/app/components/base/chat/embedded-chatbot/inputs-form/content.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/chat/types.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/base/chat/utils.ts": { + "ts/no-explicit-any": { + "count": 10 + } + }, + "web/app/components/base/checkbox/index.stories.tsx": { + "no-console": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/chip/index.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/content-dialog/index.stories.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/base/date-and-time-picker/hooks.ts": { + "react/no-unnecessary-use-prefix": { + "count": 2 + } + }, + "web/app/components/base/date-and-time-picker/types.ts": { + "erasable-syntax-only/enums": { + "count": 2 + } + }, + "web/app/components/base/date-and-time-picker/utils/dayjs.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/dialog/index.stories.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/base/drawer-plus/index.stories.tsx": { + "react/component-hook-factories": { + "count": 1 + } + }, + "web/app/components/base/emoji-picker/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/base/error-boundary/index.tsx": { + "react-refresh/only-export-components": { + "count": 3 + }, + "react/component-hook-factories": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/features/context.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/base/features/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/features/new-feature-panel/annotation-reply/config-param-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/base/features/new-feature-panel/annotation-reply/config-param.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/base/features/new-feature-panel/annotation-reply/index.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/features/new-feature-panel/annotation-reply/type.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/base/features/new-feature-panel/annotation-reply/use-annotation-config.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/features/new-feature-panel/feature-card.tsx": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/base/features/new-feature-panel/moderation/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/features/new-feature-panel/moderation/moderation-setting-modal.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/features/new-feature-panel/text-to-speech/param-config-content.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/base/features/types.ts": { + "erasable-syntax-only/enums": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/file-uploader/dynamic-pdf-preview.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/file-uploader/file-list-in-log.tsx": { + "react/no-missing-key": { + "count": 1 + } + }, + "web/app/components/base/file-uploader/hooks.ts": { + "react/no-unnecessary-use-prefix": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/file-uploader/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 5 + } + }, + "web/app/components/base/file-uploader/pdf-highlighter-adapter.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/base/file-uploader/store.tsx": { + "react-refresh/only-export-components": { + "count": 4 + } + }, + "web/app/components/base/file-uploader/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/base/file-uploader/utils.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/form/components/base/base-field.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/form/components/base/base-form.tsx": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/base/form/components/base/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/base/form/components/field/mixed-variable-text-input/placeholder.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/form/components/field/variable-or-constant-input.tsx": { + "no-console": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/form/components/field/variable-selector.tsx": { + "no-console": { + "count": 1 + } + }, + "web/app/components/base/form/form-scenarios/base/field.tsx": { + "react/component-hook-factories": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/form/form-scenarios/base/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/form/form-scenarios/demo/index.tsx": { + "no-console": { + "count": 2 + } + }, + "web/app/components/base/form/form-scenarios/input-field/field.tsx": { + "react/component-hook-factories": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/form/form-scenarios/input-field/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/form/form-scenarios/node-panel/field.tsx": { + "react/component-hook-factories": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/form/form-scenarios/node-panel/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/form/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/base/form/hooks/use-check-validated.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/form/hooks/use-get-validators.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/form/types.ts": { + "erasable-syntax-only/enums": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 15 + } + }, + "web/app/components/base/form/utils/secret-input/index.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/ga/index.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/base/icons/src/public/avatar/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/base/icons/src/public/billing/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 4 + } + }, + "web/app/components/base/icons/src/public/common/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 6 + } + }, + "web/app/components/base/icons/src/public/education/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/icons/src/public/files/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 11 + } + }, + "web/app/components/base/icons/src/public/knowledge/dataset-card/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 4 + } + }, + "web/app/components/base/icons/src/public/knowledge/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 6 + } + }, + "web/app/components/base/icons/src/public/knowledge/online-drive/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/base/icons/src/public/llm/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 9 + } + }, + "web/app/components/base/icons/src/public/other/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 6 + } + }, + "web/app/components/base/icons/src/public/thought/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/icons/src/public/tracing/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 21 + } + }, + "web/app/components/base/icons/src/vender/features/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 10 + } + }, + "web/app/components/base/icons/src/vender/knowledge/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 15 + } + }, + "web/app/components/base/icons/src/vender/line/alertsAndFeedback/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/base/icons/src/vender/line/arrows/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 6 + } + }, + "web/app/components/base/icons/src/vender/line/communication/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/base/icons/src/vender/line/development/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/base/icons/src/vender/line/editor/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/icons/src/vender/line/education/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/icons/src/vender/line/files/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 6 + } + }, + "web/app/components/base/icons/src/vender/line/financeAndECommerce/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 4 + } + }, + "web/app/components/base/icons/src/vender/line/general/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 12 + } + }, + "web/app/components/base/icons/src/vender/line/images/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/icons/src/vender/line/layout/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/icons/src/vender/line/mediaAndDevices/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/base/icons/src/vender/line/others/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 7 + } + }, + "web/app/components/base/icons/src/vender/line/time/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/base/icons/src/vender/other/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 8 + } + }, + "web/app/components/base/icons/src/vender/pipeline/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/base/icons/src/vender/plugin/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/base/icons/src/vender/solid/FinanceAndECommerce/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/icons/src/vender/solid/alertsAndFeedback/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/icons/src/vender/solid/arrows/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/base/icons/src/vender/solid/communication/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 6 + } + }, + "web/app/components/base/icons/src/vender/solid/development/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 4 + } + }, + "web/app/components/base/icons/src/vender/solid/editor/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/icons/src/vender/solid/education/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/base/icons/src/vender/solid/files/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/base/icons/src/vender/solid/general/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 11 + } + }, + "web/app/components/base/icons/src/vender/solid/mediaAndDevices/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/base/icons/src/vender/solid/security/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/icons/src/vender/solid/shapes/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/icons/src/vender/solid/users/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/icons/src/vender/system/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/icons/src/vender/workflow/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 30 + } + }, + "web/app/components/base/icons/utils.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/image-uploader/__tests__/image-preview.spec.tsx": { + "erasable-syntax-only/parameter-properties": { + "count": 1 + } + }, + "web/app/components/base/image-uploader/__tests__/utils.spec.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/image-uploader/hooks.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/base/image-uploader/image-link-input.tsx": { + "regexp/no-unused-capturing-group": { + "count": 1 + } + }, + "web/app/components/base/image-uploader/image-preview.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/image-uploader/utils.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/inline-delete-confirm/index.stories.tsx": { + "no-console": { + "count": 2 + } + }, + "web/app/components/base/input-with-copy/index.tsx": { + "react/unsupported-syntax": { + "count": 1 + } + }, + "web/app/components/base/input/index.stories.tsx": { + "no-console": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/input/index.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/base/logo/dify-logo.tsx": { + "react-refresh/only-export-components": { + "count": 2 + } + }, + "web/app/components/base/markdown-blocks/audio-block.tsx": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/base/markdown-blocks/button.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/markdown-blocks/code-block.tsx": { + "react/set-state-in-effect": { + "count": 7 + }, + "ts/no-explicit-any": { + "count": 9 + } + }, + "web/app/components/base/markdown-blocks/form.tsx": { + "erasable-syntax-only/enums": { + "count": 3 + } + }, + "web/app/components/base/markdown-blocks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 10 + } + }, + "web/app/components/base/markdown-blocks/link.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/markdown-blocks/paragraph.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/markdown-blocks/plugin-img.tsx": { + "react/set-state-in-effect": { + "count": 2 + } + }, + "web/app/components/base/markdown-blocks/plugin-paragraph.tsx": { + "react/set-state-in-effect": { + "count": 2 + } + }, + "web/app/components/base/markdown-blocks/think-block.tsx": { + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/base/markdown-blocks/video-block.tsx": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/base/markdown/error-boundary.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/markdown/markdown-utils.ts": { + "regexp/no-unused-capturing-group": { + "count": 1 + } + }, + "web/app/components/base/mermaid/index.tsx": { + "react/set-state-in-effect": { + "count": 7 + }, + "regexp/no-super-linear-backtracking": { + "count": 3 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/mermaid/utils.ts": { + "regexp/no-unused-capturing-group": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/base/message-log-modal/index.stories.tsx": { + "no-console": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/message-log-modal/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/modal-like-wrap/index.stories.tsx": { + "no-console": { + "count": 3 + } + }, + "web/app/components/base/modal/index.stories.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/base/modal/modal.stories.tsx": { + "no-console": { + "count": 4 + }, + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/base/new-audio-button/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/node-status/index.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/base/notion-connector/index.stories.tsx": { + "no-console": { + "count": 1 + } + }, + "web/app/components/base/notion-page-selector/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/pagination/index.tsx": { + "unicorn/prefer-number-properties": { + "count": 1 + } + }, + "web/app/components/base/pagination/type.ts": { + "ts/no-empty-object-type": { + "count": 1 + } + }, + "web/app/components/base/prompt-editor/index.stories.tsx": { + "no-console": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/index.tsx": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/base/prompt-editor/plugins/component-picker-block/menu.tsx": { + "erasable-syntax-only/parameter-properties": { + "count": 1 + } + }, + "web/app/components/base/prompt-editor/plugins/context-block/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 3 + }, + "react-refresh/only-export-components": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/plugins/current-block/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 3 + }, + "react-refresh/only-export-components": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/plugins/draggable-plugin/index.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/plugins/error-message-block/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 3 + }, + "react-refresh/only-export-components": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/plugins/history-block/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 3 + }, + "react-refresh/only-export-components": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/plugins/hitl-input-block/component-ui.tsx": { + "react-hooks/exhaustive-deps": { + "count": 1 + } + }, + "web/app/components/base/prompt-editor/plugins/hitl-input-block/hitl-input-block-replacement-block.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/prompt-editor/plugins/hitl-input-block/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 3 + }, + "react-refresh/only-export-components": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/plugins/last-run-block/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 3 + }, + "react-refresh/only-export-components": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/plugins/query-block/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 3 + }, + "react-refresh/only-export-components": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/plugins/request-url-block/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 3 + }, + "react-refresh/only-export-components": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/plugins/shortcuts-popup-plugin/index.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/plugins/update-block.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/plugins/variable-block/index.tsx": { + "react-refresh/only-export-components": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/plugins/workflow-variable-block/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 3 + }, + "react-refresh/only-export-components": { + "count": 3 + } + }, + "web/app/components/base/prompt-editor/plugins/workflow-variable-block/node.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/prompt-editor/utils.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/base/prompt-log-modal/index.stories.tsx": { + "no-console": { + "count": 1 + } + }, + "web/app/components/base/prompt-log-modal/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/base/radio-card/index.stories.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/radio/component/group/index.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/radio/context/index.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/radio/index.stories.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/search-input/index.stories.tsx": { + "no-console": { + "count": 3 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/sort/index.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/svg-gallery/index.tsx": { + "node/prefer-global/buffer": { + "count": 1 + } + }, + "web/app/components/base/tab-slider/index.tsx": { + "react/set-state-in-effect": { + "count": 2 + } + }, + "web/app/components/base/tag-input/index.stories.tsx": { + "no-console": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/tag-management/__tests__/panel.spec.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/base/tag-management/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/base/tag-management/tag-item-editor.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/base/tag-management/tag-remove-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/base/text-generation/hooks.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/text-generation/types.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/base/textarea/index.stories.tsx": { + "no-console": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/video-gallery/VideoPlayer.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/base/voice-input/__tests__/index.spec.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/base/voice-input/index.stories.tsx": { + "no-console": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/voice-input/utils.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/base/with-input-validation/index.stories.tsx": { + "no-console": { + "count": 1 + }, + "react/component-hook-factories": { + "count": 1 + } + }, + "web/app/components/base/with-input-validation/index.tsx": { + "react/component-hook-factories": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/base/zendesk/utils.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/billing/annotation-full/modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/billing/billing-page/__tests__/index.spec.tsx": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/billing/plan/assets/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 4 + } + }, + "web/app/components/billing/plan/index.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/billing/pricing/assets/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 12 + } + }, + "web/app/components/billing/pricing/plan-switcher/plan-range-switcher.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/billing/pricing/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/billing/type.ts": { + "erasable-syntax-only/enums": { + "count": 4 + } + }, + "web/app/components/billing/upgrade-btn/index.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/datasets/common/image-previewer/index.tsx": { + "no-irregular-whitespace": { + "count": 1 + } + }, + "web/app/components/datasets/common/image-uploader/__tests__/store.spec.tsx": { + "react/error-boundaries": { + "count": 1 + } + }, + "web/app/components/datasets/common/image-uploader/hooks/use-upload.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/datasets/common/image-uploader/store.tsx": { + "react-refresh/only-export-components": { + "count": 3 + } + }, + "web/app/components/datasets/common/retrieval-method-info/index.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/dsl-confirm-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/hooks/use-dsl-import.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/create-from-pipeline/list/template-card/details/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/datasets/create-from-pipeline/list/template-card/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/create/empty-dataset-creation-modal/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/create/file-preview/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/datasets/create/notion-page-preview/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/datasets/create/step-one/components/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/datasets/create/step-one/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/datasets/create/step-two/components/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 5 + } + }, + "web/app/components/datasets/create/step-two/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 6 + } + }, + "web/app/components/datasets/create/step-two/hooks/use-indexing-config.ts": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 3 + } + }, + "web/app/components/datasets/create/step-two/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 1 + }, + "react-hooks/exhaustive-deps": { + "count": 1 + } + }, + "web/app/components/datasets/create/step-two/preview-item/index.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/datasets/create/stop-embedding-modal/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/create/website/firecrawl/index.tsx": { + "no-console": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/datasets/create/website/firecrawl/options.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/datasets/create/website/jina-reader/index.tsx": { + "no-console": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/datasets/create/website/jina-reader/options.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/datasets/create/website/watercrawl/index.tsx": { + "no-console": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/datasets/create/website/watercrawl/options.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/datasets/documents/components/document-list/components/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/datasets/documents/components/document-list/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 4 + } + }, + "web/app/components/datasets/documents/components/operations.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/documents/components/rename-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/__tests__/index.spec.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/list/item.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx": { + "react/set-state-in-effect": { + "count": 5 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/utils.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/data-source/store/provider.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/data-source/store/slices/online-drive.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/options/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 5 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/process-documents/form.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/process-documents/index.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/processing/embedding-process/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/steps/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/datasets/documents/create-from-pipeline/types.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/datasets/documents/detail/batch-modal/index.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/datasets/documents/detail/completed/common/chunk-content.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/datasets/documents/detail/completed/common/regeneration-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/documents/detail/completed/components/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/datasets/documents/detail/completed/components/segment-list-content.tsx": { + "ts/no-non-null-asserted-optional-chain": { + "count": 1 + } + }, + "web/app/components/datasets/documents/detail/completed/display-toggle.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/documents/detail/completed/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 5 + } + }, + "web/app/components/datasets/documents/detail/completed/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 2 + }, + "react-refresh/only-export-components": { + "count": 1 + }, + "react/use-memo": { + "count": 1 + } + }, + "web/app/components/datasets/documents/detail/completed/segment-card/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/documents/detail/context.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/datasets/documents/detail/embedding/components/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 4 + } + }, + "web/app/components/datasets/documents/detail/embedding/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/datasets/documents/detail/metadata/components/metadata-field-list.tsx": { + "ts/no-non-null-asserted-optional-chain": { + "count": 1 + } + }, + "web/app/components/datasets/documents/detail/metadata/hooks/use-metadata-state.ts": { + "react-hooks-extra/no-direct-set-state-in-use-effect": { + "count": 4 + }, + "react/set-state-in-effect": { + "count": 4 + } + }, + "web/app/components/datasets/documents/detail/metadata/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/datasets/documents/detail/segment-add/index.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/datasets/documents/detail/settings/pipeline-settings/index.tsx": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/datasets/documents/detail/settings/pipeline-settings/process-documents/index.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/datasets/external-knowledge-base/create/ExternalApiSelect.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/datasets/external-knowledge-base/create/ExternalApiSelection.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/datasets/formatted-text/flavours/type.ts": { + "ts/no-empty-object-type": { + "count": 1 + } + }, + "web/app/components/datasets/hit-testing/components/chunk-detail-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/hit-testing/components/result-item-external.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/hit-testing/components/score.tsx": { + "unicorn/prefer-number-properties": { + "count": 1 + } + }, + "web/app/components/datasets/hit-testing/index.tsx": { + "react/unsupported-syntax": { + "count": 1 + } + }, + "web/app/components/datasets/list/dataset-card/hooks/use-dataset-card-state.ts": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/datasets/metadata/edit-metadata-batch/input-combined.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/datasets/metadata/edit-metadata-batch/modal.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, + "web/app/components/datasets/metadata/hooks/use-edit-dataset-metadata.ts": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/datasets/metadata/hooks/use-metadata-document.ts": { + "ts/no-explicit-any": { + "count": 1 + }, + "ts/no-non-null-asserted-optional-chain": { + "count": 2 + } + }, + "web/app/components/datasets/metadata/metadata-dataset/create-content.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/datasets/metadata/metadata-dataset/create-metadata-modal.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/datasets/metadata/metadata-dataset/dataset-metadata-drawer.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, + "web/app/components/datasets/metadata/metadata-dataset/select-metadata-modal.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/datasets/metadata/types.ts": { + "erasable-syntax-only/enums": { + "count": 2 + } + }, + "web/app/components/datasets/rename-modal/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/datasets/settings/chunk-structure/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/datasets/settings/index-method/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/develop/code.tsx": { + "ts/no-empty-object-type": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 9 + } + }, + "web/app/components/develop/md.tsx": { + "ts/no-empty-object-type": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/develop/secret-key/secret-key-generate.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/develop/secret-key/secret-key-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/explore/banner/banner-item.tsx": { + "react-hooks-extra/no-direct-set-state-in-use-effect": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 3 + } + }, + "web/app/components/explore/banner/indicator-button.tsx": { + "react-hooks-extra/no-direct-set-state-in-use-effect": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 2 + } + }, + "web/app/components/explore/create-app-modal/index.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + }, + "unicorn/prefer-number-properties": { + "count": 1 + } + }, + "web/app/components/explore/item-operation/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/explore/try-app/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/explore/try-app/tab.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/goto-anything/actions/commands/command-bus.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/goto-anything/actions/commands/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/goto-anything/actions/commands/registry.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/goto-anything/actions/commands/slash.tsx": { + "react-refresh/only-export-components": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/goto-anything/actions/commands/types.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/goto-anything/actions/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/goto-anything/actions/types.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/goto-anything/command-selector.tsx": { + "react/unsupported-syntax": { + "count": 2 + } + }, + "web/app/components/goto-anything/components/footer.tsx": { + "react/unsupported-syntax": { + "count": 1 + } + }, + "web/app/components/goto-anything/components/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 4 + } + }, + "web/app/components/goto-anything/context.tsx": { + "react-refresh/only-export-components": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 4 + } + }, + "web/app/components/goto-anything/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 4 + } + }, + "web/app/components/goto-anything/hooks/use-goto-anything-results.ts": { + "@tanstack/query/exhaustive-deps": { + "count": 1 + } + }, + "web/app/components/header/account-about/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/header/account-setting/api-based-extension-page/modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/header/account-setting/data-source-page-new/card.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/header/account-setting/data-source-page-new/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/header/account-setting/data-source-page-new/hooks/use-marketplace-all-plugins.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/header/account-setting/data-source-page-new/install-from-marketplace.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/header/account-setting/data-source-page-new/item.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/header/account-setting/data-source-page-new/types.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/header/account-setting/key-validator/declarations.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/header/account-setting/members-page/invite-modal/index.tsx": { + "react/set-state-in-effect": { + "count": 3 + } + }, + "web/app/components/header/account-setting/members-page/transfer-ownership-modal/index.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/header/account-setting/model-provider-page/declarations.ts": { + "erasable-syntax-only/enums": { + "count": 11 + }, + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/header/account-setting/model-provider-page/hooks.ts": { + "react/no-unnecessary-use-prefix": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-auth/add-credential-in-load-balancing.tsx": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-auth/config-provider.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-auth/credential-selector.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-auth/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 6 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth.ts": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-custom-models.ts": { + "react/no-unnecessary-use-prefix": { + "count": 2 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-model-form-schemas.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-auth/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 7 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-auth/switch-credential-in-load-balancing.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-modal/Input.tsx": { + "unicorn/prefer-number-properties": { + "count": 2 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-parameter-modal/configuration-button.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-parameter-modal/model-display.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/header/account-setting/model-provider-page/model-parameter-modal/status-indicators.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/header/account-setting/model-provider-page/provider-added-card/cooldown-timer.tsx": { + "react/set-state-in-effect": { + "count": 2 + } + }, + "web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/use-activate-credential.spec.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/header/account-setting/model-provider-page/utils.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/header/account-setting/plugin-page/utils.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/header/app-nav/index.tsx": { + "react/set-state-in-effect": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/header/header-wrapper.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/plugins/card/index.tsx": { + "ts/no-non-null-asserted-optional-chain": { + "count": 1 + } + }, + "web/app/components/plugins/install-plugin/hooks.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/plugins/install-plugin/hooks/use-fold-anim-into.ts": { + "react/no-unnecessary-use-prefix": { + "count": 1 + } + }, + "web/app/components/plugins/install-plugin/install-bundle/index.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/plugins/install-plugin/install-bundle/item/github-item.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/plugins/install-plugin/install-bundle/steps/hooks/use-install-multi-state.ts": { + "react-hooks/exhaustive-deps": { + "count": 1 + } + }, + "web/app/components/plugins/install-plugin/install-from-github/index.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/plugins/install-plugin/install-from-local-package/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/plugins/install-plugin/install-from-local-package/steps/uploading.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/plugins/install-plugin/install-from-marketplace/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/plugins/marketplace/hooks.ts": { + "@tanstack/query/exhaustive-deps": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-auth/authorized-in-node.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-auth/authorized/item.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-auth/hooks/use-get-api.ts": { + "react/no-unnecessary-use-prefix": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-auth/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 12 + }, + "react-refresh/only-export-components": { + "count": 3 + } + }, + "web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-auth/types.ts": { + "erasable-syntax-only/enums": { + "count": 2 + }, + "no-barrel-files/no-barrel-files": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-auth/utils.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/plugins/plugin-detail-panel/agent-strategy-list.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/app-selector/app-inputs-form.tsx": { + "ts/no-explicit-any": { + "count": 8 + } + }, + "web/app/components/plugins/plugin-detail-panel/datasource-action-list.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/detail-header.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/detail-header/components/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/plugins/plugin-detail-panel/detail-header/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/plugins/plugin-detail-panel/endpoint-modal.tsx": { + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/app/components/plugins/plugin-detail-panel/model-list.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/plugins/plugin-detail-panel/model-selector/tts-params-panel.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/multiple-tool-selector/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/strategy-detail.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/index.spec.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-oauth-client-state.ts": { + "erasable-syntax-only/enums": { + "count": 2 + } + }, + "web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/subscription-list/create/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/subscription-list/edit/apikey-edit-modal.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/subscription-list/edit/manual-edit-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/subscription-list/edit/oauth-edit-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/subscription-list/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/plugins/plugin-detail-panel/subscription-list/log-viewer.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/plugins/plugin-detail-panel/subscription-list/subscription-card.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/subscription-list/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/tool-selector/components/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 7 + } + }, + "web/app/components/plugins/plugin-detail-panel/tool-selector/components/schema-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-item.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-detail-panel/tool-selector/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/plugins/plugin-detail-panel/trigger/event-detail-drawer.tsx": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/plugins/plugin-item/action.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-item/index.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-mutation-model/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-page/context.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/plugins/plugin-page/empty/index.tsx": { + "react/set-state-in-effect": { + "count": 2 + } + }, + "web/app/components/plugins/plugin-page/install-plugin-dropdown.tsx": { + "react/set-state-in-effect": { + "count": 2 + } + }, + "web/app/components/plugins/plugin-page/plugin-info.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/plugins/readme-panel/index.tsx": { + "react/unsupported-syntax": { + "count": 1 + } + }, + "web/app/components/plugins/readme-panel/store.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/plugins/reference-setting-modal/auto-update-setting/types.ts": { + "erasable-syntax-only/enums": { + "count": 2 + } + }, + "web/app/components/plugins/reference-setting-modal/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/plugins/types.ts": { + "erasable-syntax-only/enums": { + "count": 7 + }, + "ts/no-explicit-any": { + "count": 25 + } + }, + "web/app/components/plugins/update-plugin/from-market-place.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/components/panel/input-field/editor/form/hidden-fields.tsx": { + "react/component-hook-factories": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/components/panel/input-field/editor/form/hooks.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/rag-pipeline/components/panel/input-field/editor/form/initial-fields.tsx": { + "react/component-hook-factories": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/rag-pipeline/components/panel/input-field/editor/form/show-all-settings.tsx": { + "react/component-hook-factories": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/components/panel/input-field/editor/form/types.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/components/panel/input-field/hooks.ts": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/components/panel/input-field/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/components/panel/test-run/preparation/document-processing/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/components/panel/test-run/preparation/document-processing/options.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/rag-pipeline/components/panel/test-run/preparation/index.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/rag-pipeline/components/panel/test-run/result/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/components/panel/test-run/result/result-preview/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/components/panel/test-run/result/result-preview/utils.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/rag-pipeline/components/publish-as-knowledge-pipeline-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/components/rag-pipeline-children.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/components/rag-pipeline-header/run-mode.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/components/rag-pipeline-main.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/rag-pipeline/components/update-dsl-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/components/version-mismatch-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 9 + } + }, + "web/app/components/rag-pipeline/hooks/use-DSL.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/hooks/use-input-fields.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/rag-pipeline/hooks/use-nodes-sync-draft.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/rag-pipeline/hooks/use-pipeline-config.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/rag-pipeline/hooks/use-pipeline-init.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/rag-pipeline/hooks/use-pipeline-run.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/store/index.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/rag-pipeline/utils/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/rag-pipeline/utils/nodes.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/share/text-generation/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/share/text-generation/info-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/share/text-generation/menu-dropdown.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/share/text-generation/no-data/index.tsx": { + "ts/no-empty-object-type": { + "count": 1 + } + }, + "web/app/components/share/text-generation/result/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/share/text-generation/run-batch/csv-reader/index.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/share/text-generation/run-once/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/share/text-generation/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/share/utils.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/tools/edit-custom-collection-modal/config-credentials.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/tools/edit-custom-collection-modal/get-schema.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/tools/edit-custom-collection-modal/index.tsx": { + "react/set-state-in-effect": { + "count": 4 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/tools/edit-custom-collection-modal/test-api.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/tools/mcp/create-card.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/tools/mcp/mcp-server-modal.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/tools/mcp/mcp-server-param-item.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/tools/mcp/modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/tools/mcp/provider-card.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/tools/provider-list.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/tools/provider/empty.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/tools/setting/build-in/config-credentials.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/tools/types.ts": { + "erasable-syntax-only/enums": { + "count": 4 + }, + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/workflow-app/components/workflow-children.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow-app/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 13 + } + }, + "web/app/components/workflow-app/hooks/use-DSL.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow-app/hooks/use-workflow-init.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow-app/hooks/use-workflow-run.ts": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/workflow-app/hooks/use-workflow-template.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow-app/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow-app/store/workflow/workflow-slice.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/block-selector/all-start-blocks.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/workflow/block-selector/featured-tools.tsx": { + "react/set-state-in-effect": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/block-selector/featured-triggers.tsx": { + "react/set-state-in-effect": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/block-selector/hooks.ts": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/workflow/block-selector/index-bar.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/workflow/block-selector/market-place-plugin/action.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/workflow/block-selector/market-place-plugin/item.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/workflow/block-selector/rag-tool-recommendations/index.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/workflow/block-selector/tabs.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/block-selector/tool/tool-list-flat-view/list.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/block-selector/tool/tool.tsx": { + "react/set-state-in-effect": { + "count": 2 + } + }, + "web/app/components/workflow/block-selector/trigger-plugin/action-item.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/block-selector/trigger-plugin/item.tsx": { + "react/set-state-in-effect": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/block-selector/types.ts": { + "erasable-syntax-only/enums": { + "count": 4 + } + }, + "web/app/components/workflow/block-selector/use-check-vertical-scrollbar.ts": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/workflow/block-selector/use-sticky-scroll.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/workflow/block-selector/view-type-select.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/workflow/candidate-node-main.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/context.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/workflow/datasets-detail-store/provider.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/workflow/dsl-export-confirm-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/header/run-mode.tsx": { + "no-console": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/header/test-run-menu.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/workflow/hooks-store/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/workflow/hooks-store/provider.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/workflow/hooks-store/store.ts": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/workflow/hooks/__tests__/use-checklist.spec.ts": { + "react/error-boundaries": { + "count": 1 + } + }, + "web/app/components/workflow/hooks/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 26 + } + }, + "web/app/components/workflow/hooks/use-checklist.ts": { + "ts/no-empty-object-type": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/workflow/hooks/use-dynamic-test-run-options.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/hooks/use-helpline.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/hooks/use-inspect-vars-crud-common.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/hooks/use-nodes-interactions.ts": { + "ts/no-explicit-any": { + "count": 8 + } + }, + "web/app/components/workflow/hooks/use-serial-async-callback.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/hooks/use-workflow-interactions.ts": { + "no-barrel-files/no-barrel-files": { + "count": 5 + } + }, + "web/app/components/workflow/hooks/use-workflow-run-event/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 19 + } + }, + "web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-agent-log.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-finished.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/hooks/use-workflow-search.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/hooks/use-workflow-variables.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/add-variable-popup-with-position.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/_base/components/agent-strategy.tsx": { + "ts/no-empty-object-type": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx": { + "ts/no-explicit-any": { + "count": 11 + } + }, + "web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx": { + "ts/no-explicit-any": { + "count": 3 + }, + "ts/no-non-null-asserted-optional-chain": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/workflow/nodes/_base/components/collapse/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars.tsx": { + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/workflow/nodes/_base/components/editor/code-editor/index.tsx": { + "react-refresh/only-export-components": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/workflow/nodes/_base/components/entry-node-container.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/error-handle/default-value.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/error-handle/error-handle-type-selector.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/error-handle/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/input-support-select-var.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/input-var-type-icon.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/layout/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 7 + }, + "react-refresh/only-export-components": { + "count": 7 + } + }, + "web/app/components/workflow/nodes/_base/components/mcp-tool-availability.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/mcp-tool-not-support-tooltip.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/memory-config.tsx": { + "unicorn/prefer-number-properties": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/mixed-variable-text-input/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/mixed-variable-text-input/placeholder.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/prompt/editor.tsx": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/workflow/nodes/_base/components/readonly-input-with-select-var.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/selector.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/_base/components/setting-item.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/switch-plugin-version.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/variable/match-schema-type.ts": { + "ts/no-explicit-any": { + "count": 8 + } + }, + "web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx": { + "ts/no-non-null-asserted-optional-chain": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/variable/utils.ts": { + "ts/no-explicit-any": { + "count": 32 + } + }, + "web/app/components/workflow/nodes/_base/components/variable/var-list.tsx": { + "react/unsupported-syntax": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/variable/var-type-picker.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/components/variable/variable-label/hooks.ts": { + "react/no-unnecessary-use-prefix": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/_base/components/variable/variable-label/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 5 + } + }, + "web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx": { + "react/set-state-in-effect": { + "count": 3 + }, + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx": { + "react/set-state-in-effect": { + "count": 7 + }, + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts": { + "react/no-unnecessary-use-prefix": { + "count": 2 + }, + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/app/components/workflow/nodes/_base/components/workflow-panel/tab.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "react-refresh/only-export-components": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts": { + "react/set-state-in-effect": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 22 + } + }, + "web/app/components/workflow/nodes/_base/hooks/use-output-var-list.ts": { + "ts/no-explicit-any": { + "count": 8 + } + }, + "web/app/components/workflow/nodes/_base/hooks/use-toggle-expend.ts": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/hooks/use-var-list.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/_base/node.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/_base/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/agent/default.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/nodes/agent/node.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/agent/panel.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/agent/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/agent/use-config.ts": { + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/app/components/workflow/nodes/agent/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/nodes/answer/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/assigner/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/assigner/hooks.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/assigner/types.ts": { + "erasable-syntax-only/enums": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/assigner/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/nodes/assigner/utils.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/code/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/code/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/code/use-config.ts": { + "react/set-state-in-effect": { + "count": 2 + }, + "regexp/no-useless-assertions": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/workflow/nodes/code/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/workflow/nodes/components.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/data-source-empty/hooks.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/data-source/default.ts": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/workflow/nodes/data-source/hooks/use-before-run-form.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/data-source/hooks/use-config.ts": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/workflow/nodes/data-source/panel.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/nodes/data-source/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "no-barrel-files/no-barrel-files": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/data-source/utils.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/document-extractor/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/document-extractor/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/workflow/nodes/end/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/end/node.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/http/components/authorization/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/http/components/curl-panel.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/http/components/key-value/key-value-edit/index.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/http/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/http/types.ts": { + "erasable-syntax-only/enums": { + "count": 5 + } + }, + "web/app/components/workflow/nodes/http/use-config.ts": { + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/http/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/workflow/nodes/human-input/components/form-content-preview.tsx": { + "react/unsupported-syntax": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/human-input/components/form-content.tsx": { + "react/component-hook-factories": { + "count": 1 + }, + "react/no-nested-component-definitions": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/nodes/human-input/components/variable-in-markdown.tsx": { + "react-refresh/only-export-components": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/human-input/types.ts": { + "erasable-syntax-only/enums": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/if-else/components/condition-list/condition-input.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/if-else/components/condition-number-input.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/if-else/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/if-else/types.ts": { + "erasable-syntax-only/enums": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/if-else/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/workflow/nodes/iteration-start/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/iteration/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/iteration/node.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/iteration/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/hooks.tsx": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/search-method-option.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/type.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/knowledge-base/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "no-barrel-files/no-barrel-files": { + "count": 8 + } + }, + "web/app/components/workflow/nodes/knowledge-base/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/nodes/knowledge-base/utils.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-item.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/knowledge-retrieval/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/knowledge-retrieval/node.tsx": { + "react/set-state-in-effect": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/knowledge-retrieval/types.ts": { + "erasable-syntax-only/enums": { + "count": 4 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/knowledge-retrieval/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/workflow/nodes/list-operator/components/filter-condition.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/list-operator/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/list-operator/types.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/llm/components/config-prompt.tsx": { + "react/unsupported-syntax": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/llm/components/json-schema-config-modal/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/assets/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/generated-result.tsx": { + "style/multiline-ternary": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/context.tsx": { + "react-refresh/only-export-components": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/auto-width-input.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/type-selector.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/llm/components/structure-output.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/llm/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/llm/types.ts": { + "erasable-syntax-only/enums": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/llm/use-config.ts": { + "react/set-state-in-effect": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/llm/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 9 + } + }, + "web/app/components/workflow/nodes/llm/utils.ts": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/app/components/workflow/nodes/loop-start/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/loop/components/condition-list/condition-input.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/loop/components/condition-number-input.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/loop/components/loop-variables/form-item.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/nodes/loop/components/loop-variables/item.tsx": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/workflow/nodes/loop/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/loop/types.ts": { + "erasable-syntax-only/enums": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/loop/use-config.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/loop/use-single-run-form-params.helpers.ts": { + "ts/no-non-null-asserted-optional-chain": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/loop/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/__tests__/list.spec.tsx": { + "unused-imports/no-unused-vars": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/parameter-extractor/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/parameter-extractor/panel.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/parameter-extractor/types.ts": { + "erasable-syntax-only/enums": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/parameter-extractor/use-config.ts": { + "react/set-state-in-effect": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/parameter-extractor/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 9 + } + }, + "web/app/components/workflow/nodes/question-classifier/components/advanced-setting.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/question-classifier/components/class-item.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/question-classifier/components/class-list.tsx": { + "react/set-state-in-effect": { + "count": 1 + }, + "react/unsupported-syntax": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/question-classifier/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/question-classifier/node.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/question-classifier/use-config.ts": { + "react/set-state-in-effect": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/question-classifier/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 8 + } + }, + "web/app/components/workflow/nodes/start/panel.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/start/use-config.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/start/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/nodes/template-transform/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/template-transform/use-config.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/workflow/nodes/template-transform/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/workflow/nodes/tool/components/input-var-list.tsx": { + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/tool/components/mixed-variable-text-input/placeholder.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/tool/components/tool-form/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/tool/components/tool-form/item.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/tool/default.ts": { + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/app/components/workflow/nodes/tool/hooks/use-config.ts": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/workflow/nodes/tool/hooks/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/workflow/nodes/tool/output-schema-utils.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/workflow/nodes/tool/panel.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/tool/types.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/index.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/item.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/trigger-plugin/default.ts": { + "ts/no-explicit-any": { + "count": 11 + } + }, + "web/app/components/workflow/nodes/trigger-plugin/node.tsx": { + "react/unsupported-syntax": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/trigger-plugin/panel.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/nodes/trigger-plugin/types.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/workflow/nodes/trigger-plugin/use-config.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/trigger-plugin/utils/form-helpers.ts": { + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/app/components/workflow/nodes/trigger-schedule/default.ts": { + "regexp/no-unused-capturing-group": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 10 + } + }, + "web/app/components/workflow/nodes/trigger-webhook/components/parameter-table.tsx": { + "ts/no-non-null-asserted-optional-chain": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/trigger-webhook/default.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/trigger-webhook/panel.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/utils.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/nodes/variable-assigner/default.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/nodes/variable-assigner/use-single-run-form-params.ts": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/workflow/note-node/note-editor/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 3 + } + }, + "web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/component.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/workflow/note-node/note-editor/utils.ts": { + "regexp/no-useless-quantifier": { + "count": 1 + } + }, + "web/app/components/workflow/operator/add-block.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/operator/hooks.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/operator/zoom-in-out.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/workflow/panel/chat-record/index.tsx": { + "ts/no-explicit-any": { + "count": 8 + } + }, + "web/app/components/workflow/panel/chat-record/user-input.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/panel/chat-variable-panel/components/array-bool-list.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx": { + "react-refresh/only-export-components": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 5 + }, + "unicorn/prefer-number-properties": { + "count": 2 + } + }, + "web/app/components/workflow/panel/chat-variable-panel/components/object-value-list.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/panel/chat-variable-panel/type.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/app/components/workflow/panel/debug-and-preview/conversation-variable-modal.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/panel/debug-and-preview/hooks.ts": { + "ts/no-explicit-any": { + "count": 12 + } + }, + "web/app/components/workflow/panel/env-panel/variable-modal.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "react/set-state-in-effect": { + "count": 4 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/panel/human-input-form-list.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/panel/inputs-panel.tsx": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/workflow/panel/version-history-panel/delete-confirm-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/panel/version-history-panel/restore-confirm-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/panel/workflow-preview.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/run/agent-log/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/workflow/run/hooks.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/run/index.tsx": { + "react/set-state-in-effect": { + "count": 2 + } + }, + "web/app/components/workflow/run/iteration-log/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx": { + "unicorn/prefer-number-properties": { + "count": 1 + } + }, + "web/app/components/workflow/run/loop-log/__tests__/loop-log-trigger.spec.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/run/loop-log/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/workflow/run/loop-log/loop-log-trigger.tsx": { + "unicorn/prefer-number-properties": { + "count": 1 + } + }, + "web/app/components/workflow/run/node.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/components/workflow/run/output-panel.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/run/result-panel.tsx": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/app/components/workflow/run/result-text.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/run/retry-log/index.tsx": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/workflow/run/utils/format-log/agent/index.ts": { + "ts/no-explicit-any": { + "count": 11 + } + }, + "web/app/components/workflow/run/utils/format-log/graph-to-log-struct.ts": { + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/app/components/workflow/run/utils/format-log/index.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/run/utils/format-log/iteration/index.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/run/utils/format-log/loop/index.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/run/utils/format-log/parallel/index.ts": { + "no-console": { + "count": 4 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/store/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/app/components/workflow/store/workflow/debug/inspect-vars-slice.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/store/workflow/workflow-draft-slice.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/store/workflow/workflow-slice.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/types.ts": { + "erasable-syntax-only/enums": { + "count": 16 + }, + "ts/no-empty-object-type": { + "count": 3 + }, + "ts/no-explicit-any": { + "count": 8 + } + }, + "web/app/components/workflow/update-dsl-modal.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/utils/__tests__/node-navigation.spec.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/utils/data-source.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/utils/debug.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/utils/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 10 + } + }, + "web/app/components/workflow/utils/node-navigation.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/utils/node.ts": { + "regexp/no-super-linear-backtracking": { + "count": 1 + } + }, + "web/app/components/workflow/utils/tool.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/utils/workflow-init.ts": { + "ts/no-explicit-any": { + "count": 12 + } + }, + "web/app/components/workflow/utils/workflow.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/variable-inspect/display-content.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/variable-inspect/group.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/variable-inspect/left.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/variable-inspect/listening.tsx": { + "no-restricted-imports": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/variable-inspect/panel.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/variable-inspect/right.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/app/components/workflow/variable-inspect/trigger.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/variable-inspect/utils.tsx": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/app/components/workflow/variable-inspect/value-content.tsx": { + "react/set-state-in-effect": { + "count": 5 + }, + "regexp/no-super-linear-backtracking": { + "count": 1 + }, + "regexp/no-unused-capturing-group": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/components/workflow/workflow-preview/components/nodes/base.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/workflow-preview/components/nodes/constants.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/components/workflow/workflow-preview/components/nodes/iteration-start/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/workflow-preview/components/nodes/loop-start/index.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/components/workflow/workflow-preview/components/zoom-in-out.tsx": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/app/education-apply/expire-notice-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, + "web/app/education-apply/hooks.ts": { + "react/set-state-in-effect": { + "count": 5 + } + }, + "web/app/education-apply/verify-state-modal.tsx": { + "react/set-state-in-effect": { + "count": 1 + } + }, + "web/app/forgot-password/ForgotPasswordForm.spec.tsx": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/app/init/InitPasswordPopup.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/install/installForm.spec.tsx": { + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/app/layout.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/app/reset-password/layout.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/signin/components/mail-and-password-auth.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/signin/layout.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/signin/one-more-step.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/app/signup/layout.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/context/external-api-panel-context.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/context/external-knowledge-api-context.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/context/hooks/use-trigger-events-limit-modal.ts": { + "react/set-state-in-effect": { + "count": 3 + } + }, + "web/context/provider-context-provider.tsx": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/context/web-app-context.tsx": { + "react-refresh/only-export-components": { + "count": 1 + } + }, + "web/hooks/use-async-window-open.spec.ts": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/hooks/use-format-time-from-now.spec.ts": { + "regexp/no-dupe-disjunctions": { + "count": 5 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/hooks/use-metadata.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/hooks/use-mitt.ts": { + "react/component-hook-factories": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/hooks/use-oauth.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/hooks/use-pay.tsx": { + "react/set-state-in-effect": { + "count": 4 + } + }, + "web/i18n-config/index.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/i18n-config/lib.client.ts": { + "no-barrel-files/no-barrel-files": { + "count": 1 + } + }, + "web/i18n/de-DE/billing.json": { + "no-irregular-whitespace": { + "count": 1 + } + }, + "web/i18n/en-US/app-debug.json": { + "no-irregular-whitespace": { + "count": 1 + } + }, + "web/i18n/fr-FR/app-debug.json": { + "no-irregular-whitespace": { + "count": 1 + } + }, + "web/i18n/fr-FR/app.json": { + "no-irregular-whitespace": { + "count": 1 + } + }, + "web/i18n/fr-FR/plugin-trigger.json": { + "no-irregular-whitespace": { + "count": 1 + } + }, + "web/i18n/fr-FR/tools.json": { + "no-irregular-whitespace": { + "count": 1 + } + }, + "web/i18n/fr-FR/workflow.json": { + "no-irregular-whitespace": { + "count": 2 + } + }, + "web/i18n/pt-BR/common.json": { + "no-irregular-whitespace": { + "count": 1 + } + }, + "web/i18n/ru-RU/common.json": { + "no-irregular-whitespace": { + "count": 2 + } + }, + "web/i18n/uk-UA/app-debug.json": { + "no-irregular-whitespace": { + "count": 1 + } + }, + "web/models/access-control.ts": { + "erasable-syntax-only/enums": { + "count": 2 + } + }, + "web/models/app.ts": { + "erasable-syntax-only/enums": { + "count": 2 + } + }, + "web/models/common.ts": { + "erasable-syntax-only/enums": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/models/datasets.ts": { + "erasable-syntax-only/enums": { + "count": 8 + }, + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/models/debug.ts": { + "erasable-syntax-only/enums": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/models/log.ts": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/models/pipeline.ts": { + "erasable-syntax-only/enums": { + "count": 3 + }, + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/models/share.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/plugins/dev-proxy/server.spec.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/scripts/component-analyzer.js": { + "regexp/no-unused-capturing-group": { + "count": 6 + } + }, + "web/service/access-control.ts": { + "@tanstack/query/exhaustive-deps": { + "count": 1 + } + }, + "web/service/annotation.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/service/apps.ts": { + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/service/base.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/service/client.spec.ts": { + "next/no-assign-module-variable": { + "count": 1 + } + }, + "web/service/common.ts": { + "ts/no-explicit-any": { + "count": 29 + } + }, + "web/service/datasets.ts": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/service/debug.ts": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/service/fetch.ts": { + "regexp/no-unused-capturing-group": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/service/knowledge/use-dataset.ts": { + "@tanstack/query/exhaustive-deps": { + "count": 1 + } + }, + "web/service/share.ts": { + "erasable-syntax-only/enums": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/service/try-app.ts": { + "no-barrel-files/no-barrel-files": { + "count": 2 + } + }, + "web/service/use-apps.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/service/use-common.ts": { + "ts/no-empty-object-type": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/service/use-endpoints.ts": { + "ts/no-explicit-any": { + "count": 7 + } + }, + "web/service/use-flow.ts": { + "react/no-unnecessary-use-prefix": { + "count": 1 + } + }, + "web/service/use-pipeline.ts": { + "@tanstack/query/exhaustive-deps": { + "count": 1 + } + }, + "web/service/use-plugins-auth.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/service/use-plugins.ts": { + "react/set-state-in-effect": { + "count": 1 + }, + "regexp/no-unused-capturing-group": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + }, + "ts/no-non-null-asserted-optional-chain": { + "count": 1 + } + }, + "web/service/use-tools.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/service/use-workflow.ts": { + "@tanstack/query/exhaustive-deps": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/service/utils.spec.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/types/app.ts": { + "erasable-syntax-only/enums": { + "count": 9 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/types/assets.d.ts": { + "ts/no-explicit-any": { + "count": 5 + } + }, + "web/types/common.ts": { + "erasable-syntax-only/enums": { + "count": 1 + } + }, + "web/types/feature.ts": { + "erasable-syntax-only/enums": { + "count": 3 + } + }, + "web/types/lamejs.d.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/types/pipeline.tsx": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/types/react-18-input-autosize.d.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/types/workflow.ts": { + "ts/no-explicit-any": { + "count": 17 + } + }, + "web/utils/clipboard.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/utils/completion-params.spec.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/utils/completion-params.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/utils/context.ts": { + "react/component-hook-factories": { + "count": 1 + } + }, + "web/utils/error-parser.ts": { + "no-console": { + "count": 1 + }, + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/utils/get-icon.spec.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/utils/gtag.ts": { + "ts/no-explicit-any": { + "count": 2 + } + }, + "web/utils/index.spec.ts": { + "test/no-identical-title": { + "count": 2 + }, + "ts/no-explicit-any": { + "count": 8 + } + }, + "web/utils/index.ts": { + "ts/no-explicit-any": { + "count": 3 + } + }, + "web/utils/mcp.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/utils/model-config.spec.ts": { + "ts/no-explicit-any": { + "count": 13 + } + }, + "web/utils/model-config.ts": { + "ts/no-explicit-any": { + "count": 6 + } + }, + "web/utils/navigation.spec.ts": { + "ts/no-explicit-any": { + "count": 4 + } + }, + "web/utils/tool-call.spec.ts": { + "ts/no-explicit-any": { + "count": 1 + } + }, + "web/utils/validators.ts": { + "ts/no-explicit-any": { + "count": 2 + } + } +} \ No newline at end of file diff --git a/web/__tests__/billing/billing-integration.test.tsx b/web/__tests__/billing/billing-integration.test.tsx index 90589ae1e4..3113e36751 100644 --- a/web/__tests__/billing/billing-integration.test.tsx +++ b/web/__tests__/billing/billing-integration.test.tsx @@ -9,7 +9,7 @@ import Billing from '@/app/components/billing/billing-page' import { defaultPlan, NUM_INFINITE } from '@/app/components/billing/config' import HeaderBillingBtn from '@/app/components/billing/header-billing-btn' import PlanComp from '@/app/components/billing/plan' -import PlanUpgradeModal from '@/app/components/billing/plan-upgrade-modal' +import { PlanUpgradeModal } from '@/app/components/billing/plan-upgrade-modal' import PriorityLabel from '@/app/components/billing/priority-label' import TriggerEventsLimitModal from '@/app/components/billing/trigger-events-limit-modal' import { Plan } from '@/app/components/billing/type' diff --git a/web/app/components/apps/list.tsx b/web/app/components/apps/list.tsx index 388f15815c..a0dfd69bb7 100644 --- a/web/app/components/apps/list.tsx +++ b/web/app/components/apps/list.tsx @@ -232,15 +232,16 @@ const List: FC = ({ ? t('newApp.noAppsFound', { ns: 'app' }) : t('tabs.noSnippetsFound', { ns: 'workflow' }) const pages = useMemo(() => data?.pages ?? [], [data?.pages]) - const appIds = useMemo(() => { - const ids = new Set() - pages.forEach((page) => { - page.data?.forEach((app) => { - if (app.id) - ids.add(app.id) + + const workflowOnlineUserAppIds = useMemo(() => { + const appIds = new Set() + pages.forEach(({ data: apps }) => { + apps.forEach((app) => { + if (app.mode === AppModeEnum.WORKFLOW || app.mode === AppModeEnum.ADVANCED_CHAT) + appIds.add(app.id) }) }) - return Array.from(ids) + return Array.from(appIds) }, [pages]) const refreshWorkflowOnlineUsers = useCallback(async () => { @@ -249,19 +250,19 @@ const List: FC = ({ return } - if (!appIds.length) { + if (!workflowOnlineUserAppIds.length) { setWorkflowOnlineUsersMap({}) return } try { - const onlineUsersMap = await fetchWorkflowOnlineUsers({ appIds }) + const onlineUsersMap = await fetchWorkflowOnlineUsers({ appIds: workflowOnlineUserAppIds }) setWorkflowOnlineUsersMap(onlineUsersMap) } catch { setWorkflowOnlineUsersMap({}) } - }, [appIds, systemFeatures.enable_collaboration_mode]) + }, [systemFeatures.enable_collaboration_mode, workflowOnlineUserAppIds]) useEffect(() => { void refreshWorkflowOnlineUsers() diff --git a/web/app/components/base/upgrade-modal/index.tsx b/web/app/components/base/upgrade-modal/index.tsx new file mode 100644 index 0000000000..cfae72eaf7 --- /dev/null +++ b/web/app/components/base/upgrade-modal/index.tsx @@ -0,0 +1,75 @@ +'use client' + +import type { ComponentType, ReactNode } from 'react' +import { cn } from '@langgenius/dify-ui/cn' +import { Dialog, DialogContent, DialogDescription, DialogTitle } from '@langgenius/dify-ui/dialog' +import styles from './style.module.css' + +type UpgradeModalClassNames = { + content?: string + heroOverlay?: string + body?: string + icon?: string + copy?: string + title?: string + description?: string + footer?: string +} + +type UpgradeModalProps = { + open: boolean + onOpenChange?: (open: boolean) => void + Icon?: ComponentType<{ className?: string }> + title: ReactNode + description: ReactNode + extraInfo?: ReactNode + footer: ReactNode + classNames?: UpgradeModalClassNames +} + +export function UpgradeModal({ + open, + onOpenChange, + Icon, + title, + description, + extraInfo, + footer, + classNames, +}: UpgradeModalProps) { + return ( + + +
+
+
+ {Icon && ( +
+ +
+ )} +
+ + {title} + + + {description} + +
+ {extraInfo} +
+
+ +
+ {footer} +
+ +
+ ) +} diff --git a/web/app/components/billing/plan-upgrade-modal/style.module.css b/web/app/components/base/upgrade-modal/style.module.css similarity index 100% rename from web/app/components/billing/plan-upgrade-modal/style.module.css rename to web/app/components/base/upgrade-modal/style.module.css diff --git a/web/app/components/billing/plan-upgrade-modal/__tests__/index.spec.tsx b/web/app/components/billing/plan-upgrade-modal/__tests__/index.spec.tsx index b28ffffa53..2e8b7777ee 100644 --- a/web/app/components/billing/plan-upgrade-modal/__tests__/index.spec.tsx +++ b/web/app/components/billing/plan-upgrade-modal/__tests__/index.spec.tsx @@ -1,19 +1,9 @@ import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import * as React from 'react' -import PlanUpgradeModal from '../index' +import { PlanUpgradeModal } from '../index' const mockSetShowPricingModal = vi.fn() -vi.mock('@/app/components/base/modal', () => { - const MockModal = ({ isShow, children }: { isShow: boolean, children: React.ReactNode }) => ( - isShow ?
{children}
: null - ) - return { - default: MockModal, - } -}) - vi.mock('@/context/modal-context', () => ({ useModalContext: () => ({ setShowPricingModal: mockSetShowPricingModal, @@ -70,6 +60,16 @@ describe('PlanUpgradeModal', () => { expect(onClose).toHaveBeenCalledTimes(1) }) + it('should call onClose when dialog requests close', async () => { + const user = userEvent.setup() + const onClose = vi.fn() + renderComponent({ onClose }) + + await user.keyboard('{Escape}') + + expect(onClose).toHaveBeenCalledTimes(1) + }) + // Upgrade path uses provided callback over pricing modal it('should call onUpgrade and onClose when upgrade button is clicked with onUpgrade provided', async () => { const user = userEvent.setup() diff --git a/web/app/components/billing/plan-upgrade-modal/index.tsx b/web/app/components/billing/plan-upgrade-modal/index.tsx index da599dc36c..0d40edcfad 100644 --- a/web/app/components/billing/plan-upgrade-modal/index.tsx +++ b/web/app/components/billing/plan-upgrade-modal/index.tsx @@ -1,26 +1,24 @@ 'use client' -import type { FC } from 'react' +import type { ComponentType, ReactNode } from 'react' import { Button } from '@langgenius/dify-ui/button' -import * as React from 'react' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import Modal from '@/app/components/base/modal' +import { UpgradeModal } from '@/app/components/base/upgrade-modal' import UpgradeBtn from '@/app/components/billing/upgrade-btn' import { useModalContext } from '@/context/modal-context' import { SquareChecklist } from '../../base/icons/src/vender/other' -import styles from './style.module.css' type Props = { - Icon?: React.ComponentType> + Icon?: ComponentType<{ className?: string }> title: string description: string - extraInfo?: React.ReactNode + extraInfo?: ReactNode show: boolean onClose: () => void onUpgrade?: () => void } -const PlanUpgradeModal: FC = ({ +export function PlanUpgradeModal({ Icon = SquareChecklist, title, description, @@ -28,7 +26,7 @@ const PlanUpgradeModal: FC = ({ show, onClose, onUpgrade, -}) => { +}: Props) { const { t } = useTranslation() const { setShowPricingModal } = useModalContext() @@ -41,51 +39,30 @@ const PlanUpgradeModal: FC = ({ }, [onClose, onUpgrade, setShowPricingModal]) return ( - -
-
-
-
- -
-
-
- {title} -
-
- {description} -
-
- {extraInfo} -
-
- -
- - -
- + !open && onClose()} + Icon={Icon} + title={title} + description={description} + extraInfo={extraInfo} + footer={( + <> + + + + )} + /> ) } - -export default React.memo(PlanUpgradeModal) diff --git a/web/app/components/billing/trigger-events-limit-modal/__tests__/index.spec.tsx b/web/app/components/billing/trigger-events-limit-modal/__tests__/index.spec.tsx index 47577a5b48..a8dfcd63e6 100644 --- a/web/app/components/billing/trigger-events-limit-modal/__tests__/index.spec.tsx +++ b/web/app/components/billing/trigger-events-limit-modal/__tests__/index.spec.tsx @@ -4,21 +4,6 @@ import TriggerEventsLimitModal from '../index' const mockOnClose = vi.fn() const mockOnUpgrade = vi.fn() -const planUpgradeModalMock = vi.fn((props: { show: boolean, title: string, description: string, extraInfo?: React.ReactNode, onClose: () => void, onUpgrade: () => void }) => ( -
- {props.extraInfo} -
-)) - -vi.mock('@/app/components/billing/plan-upgrade-modal', () => ({ - default: (props: { show: boolean, title: string, description: string, extraInfo?: React.ReactNode, onClose: () => void, onUpgrade: () => void }) => planUpgradeModalMock(props), -})) - describe('TriggerEventsLimitModal', () => { beforeEach(() => { vi.clearAllMocks() @@ -36,16 +21,9 @@ describe('TriggerEventsLimitModal', () => { />, ) - const modal = screen.getByTestId('plan-upgrade-modal') - expect(modal.getAttribute('data-show')).toBe('true') - expect(modal.getAttribute('data-title')).toContain('billing.triggerLimitModal.title') - expect(modal.getAttribute('data-description')).toContain('billing.triggerLimitModal.description') - expect(planUpgradeModalMock).toHaveBeenCalled() - - const passedProps = planUpgradeModalMock.mock.calls[0]![0] - expect(passedProps.onClose).toBe(mockOnClose) - expect(passedProps.onUpgrade).toBe(mockOnUpgrade) - + expect(screen.getByRole('dialog')).toBeInTheDocument() + expect(screen.getByText('billing.triggerLimitModal.title')).toBeInTheDocument() + expect(screen.getByText('billing.triggerLimitModal.description')).toBeInTheDocument() expect(screen.getByText('billing.triggerLimitModal.usageTitle'))!.toBeInTheDocument() expect(screen.getByText('12'))!.toBeInTheDocument() expect(screen.getByText('20'))!.toBeInTheDocument() @@ -62,8 +40,7 @@ describe('TriggerEventsLimitModal', () => { />, ) - expect(planUpgradeModalMock).toHaveBeenCalled() - expect(screen.getByTestId('plan-upgrade-modal').getAttribute('data-show')).toBe('false') + expect(screen.queryByRole('dialog')).not.toBeInTheDocument() }) it('renders reset info when resetInDays is provided', () => { @@ -94,9 +71,8 @@ describe('TriggerEventsLimitModal', () => { />, ) - const modal = screen.getByTestId('plan-upgrade-modal') - expect(modal.getAttribute('data-title')).toBe('billing.triggerLimitModal.title') - expect(modal.getAttribute('data-description')).toBe('billing.triggerLimitModal.description') + expect(screen.getByText('billing.triggerLimitModal.title')).toBeInTheDocument() + expect(screen.getByText('billing.triggerLimitModal.description')).toBeInTheDocument() }) it('passes onClose and onUpgrade callbacks to PlanUpgradeModal', () => { @@ -110,8 +86,10 @@ describe('TriggerEventsLimitModal', () => { />, ) - const passedProps = planUpgradeModalMock.mock.calls[0]![0] - expect(passedProps.onClose).toBe(mockOnClose) - expect(passedProps.onUpgrade).toBe(mockOnUpgrade) + screen.getByText('billing.triggerLimitModal.dismiss').click() + expect(mockOnClose).toHaveBeenCalledTimes(1) + + screen.getByText('billing.triggerLimitModal.upgrade').click() + expect(mockOnUpgrade).toHaveBeenCalledTimes(1) }) }) diff --git a/web/app/components/billing/trigger-events-limit-modal/index.tsx b/web/app/components/billing/trigger-events-limit-modal/index.tsx index 5debb5cb57..9312f772b5 100644 --- a/web/app/components/billing/trigger-events-limit-modal/index.tsx +++ b/web/app/components/billing/trigger-events-limit-modal/index.tsx @@ -1,9 +1,7 @@ 'use client' -import type { FC } from 'react' -import * as React from 'react' import { useTranslation } from 'react-i18next' import { TriggerAll } from '@/app/components/base/icons/src/vender/workflow' -import PlanUpgradeModal from '@/app/components/billing/plan-upgrade-modal' +import { PlanUpgradeModal } from '@/app/components/billing/plan-upgrade-modal' import UsageInfo from '@/app/components/billing/usage-info' type Props = { @@ -15,14 +13,14 @@ type Props = { resetInDays?: number } -const TriggerEventsLimitModal: FC = ({ +export default function TriggerEventsLimitModal({ show, onClose, onUpgrade, usage, total, resetInDays, -}) => { +}: Props) { const { t } = useTranslation() return ( @@ -30,7 +28,7 @@ const TriggerEventsLimitModal: FC = ({ show={show} onClose={onClose} onUpgrade={onUpgrade} - Icon={TriggerAll as React.ComponentType>} + Icon={TriggerAll} title={t('triggerLimitModal.title', { ns: 'billing' })} description={t('triggerLimitModal.description', { ns: 'billing' })} extraInfo={( @@ -47,5 +45,3 @@ const TriggerEventsLimitModal: FC = ({ /> ) } - -export default React.memo(TriggerEventsLimitModal) diff --git a/web/app/components/datasets/create/step-one/__tests__/index.spec.tsx b/web/app/components/datasets/create/step-one/__tests__/index.spec.tsx index f00ff121cc..6c6c60d808 100644 --- a/web/app/components/datasets/create/step-one/__tests__/index.spec.tsx +++ b/web/app/components/datasets/create/step-one/__tests__/index.spec.tsx @@ -92,18 +92,6 @@ vi.mock('@/app/components/billing/vector-space-full', () => ({ default: () =>
Vector Space Full
, })) -vi.mock('@/app/components/billing/plan-upgrade-modal', () => ({ - default: ({ show, onClose }: { show: boolean, onClose: () => void }) => ( - show - ? ( -
- -
- ) - : null - ), -})) - vi.mock('../../file-preview', () => ({ default: ({ file, hidePreview }: { file: File, hidePreview: () => void }) => (
@@ -388,7 +376,7 @@ describe('StepOne', () => { fireEvent.click(screen.getByRole('button', { name: /datasetCreation.stepOne.button/i })) - expect(screen.getByTestId('plan-upgrade-modal')).toBeInTheDocument() + expect(screen.getByRole('dialog')).toBeInTheDocument() }) it('should show upgrade card when in sandbox plan with files', () => { diff --git a/web/app/components/datasets/create/step-one/components/__tests__/preview-panel.spec.tsx b/web/app/components/datasets/create/step-one/components/__tests__/preview-panel.spec.tsx index f495dd9f3f..a807412008 100644 --- a/web/app/components/datasets/create/step-one/components/__tests__/preview-panel.spec.tsx +++ b/web/app/components/datasets/create/step-one/components/__tests__/preview-panel.spec.tsx @@ -31,17 +31,6 @@ vi.mock('../../../website/preview', () => ({ ), })) -vi.mock('@/app/components/billing/plan-upgrade-modal', () => ({ - default: ({ show, onClose, title }: { show: boolean, onClose: () => void, title: string }) => show - ? ( -
- {title} - -
- ) - : null, -})) - const { default: PreviewPanel } = await import('../preview-panel') describe('PreviewPanel', () => { @@ -87,7 +76,7 @@ describe('PreviewPanel', () => { it('should render plan upgrade modal when isShowPlanUpgradeModal is true', () => { render() - expect(screen.getByTestId('plan-upgrade-modal')).toBeInTheDocument() + expect(screen.getByRole('dialog')).toBeInTheDocument() }) }) @@ -100,7 +89,7 @@ describe('PreviewPanel', () => { it('should call hidePlanUpgradeModal when modal close clicked', () => { render() - fireEvent.click(screen.getByTestId('close-modal')) + fireEvent.click(screen.getByRole('button', { name: 'billing.triggerLimitModal.dismiss' })) expect(defaultProps.hidePlanUpgradeModal).toHaveBeenCalledOnce() }) diff --git a/web/app/components/datasets/create/step-one/components/preview-panel.tsx b/web/app/components/datasets/create/step-one/components/preview-panel.tsx index 8ae0b7df55..51b6568154 100644 --- a/web/app/components/datasets/create/step-one/components/preview-panel.tsx +++ b/web/app/components/datasets/create/step-one/components/preview-panel.tsx @@ -3,7 +3,7 @@ import type { NotionPage } from '@/models/common' import type { CrawlResultItem } from '@/models/datasets' import { useTranslation } from 'react-i18next' -import PlanUpgradeModal from '@/app/components/billing/plan-upgrade-modal' +import { PlanUpgradeModal } from '@/app/components/billing/plan-upgrade-modal' import FilePreview from '../../file-preview' import NotionPagePreview from '../../notion-page-preview' import WebsitePreview from '../../website/preview' diff --git a/web/app/components/datasets/documents/create-from-pipeline/__tests__/index.spec.tsx b/web/app/components/datasets/documents/create-from-pipeline/__tests__/index.spec.tsx index 7daff43a8b..18241f2139 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/__tests__/index.spec.tsx @@ -112,18 +112,6 @@ vi.mock('@/app/components/billing/vector-space-full', () => ({ default: () =>
Vector Space Full
, })) -vi.mock('@/app/components/billing/plan-upgrade-modal', () => ({ - default: ({ show, onClose }: { show: boolean, onClose: () => void }) => ( - show - ? ( -
- -
- ) - : null - ), -})) - vi.mock('@/app/components/datasets/create/step-one/upgrade-card', () => ({ default: () =>
Upgrade Card
, })) diff --git a/web/app/components/datasets/documents/create-from-pipeline/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/index.tsx index 8b8fad5885..799f24fa2a 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/index.tsx @@ -8,7 +8,7 @@ import { useBoolean } from 'ahooks' import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import Loading from '@/app/components/base/loading' -import PlanUpgradeModal from '@/app/components/billing/plan-upgrade-modal' +import { PlanUpgradeModal } from '@/app/components/billing/plan-upgrade-modal' import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail' import { useProviderContextSelector } from '@/context/provider-context' import { DatasourceType } from '@/models/pipeline' diff --git a/web/app/components/datasets/documents/detail/segment-add/__tests__/index.spec.tsx b/web/app/components/datasets/documents/detail/segment-add/__tests__/index.spec.tsx index 35b62915da..b9d967a692 100644 --- a/web/app/components/datasets/documents/detail/segment-add/__tests__/index.spec.tsx +++ b/web/app/components/datasets/documents/detail/segment-add/__tests__/index.spec.tsx @@ -14,21 +14,6 @@ vi.mock('@/context/provider-context', () => ({ }), })) -// Mock PlanUpgradeModal -vi.mock('@/app/components/billing/plan-upgrade-modal', () => ({ - default: ({ show, onClose, title, description }: { show: boolean, onClose: () => void, title?: string, description?: string }) => ( - show - ? ( -
- {title} - {description} - -
- ) - : null - ), -})) - describe('SegmentAdd', () => { beforeEach(() => { vi.clearAllMocks() @@ -189,7 +174,7 @@ describe('SegmentAdd', () => { fireEvent.click(screen.getByText(/list\.action\.addButton/i)) - expect(screen.getByTestId('plan-upgrade-modal')).toBeInTheDocument() + expect(screen.getByRole('dialog')).toBeInTheDocument() }) it('should not call showNewSegmentModal for sandbox users', () => { @@ -219,11 +204,11 @@ describe('SegmentAdd', () => { // Show modal fireEvent.click(screen.getByText(/list\.action\.addButton/i)) - expect(screen.getByTestId('plan-upgrade-modal')).toBeInTheDocument() + expect(screen.getByRole('dialog')).toBeInTheDocument() - fireEvent.click(screen.getByTestId('close-modal')) + fireEvent.click(screen.getByRole('button', { name: 'billing.triggerLimitModal.dismiss' })) - expect(screen.queryByTestId('plan-upgrade-modal')).not.toBeInTheDocument() + expect(screen.queryByRole('dialog')).not.toBeInTheDocument() }) }) diff --git a/web/app/components/datasets/documents/detail/segment-add/index.tsx b/web/app/components/datasets/documents/detail/segment-add/index.tsx index de9735f62f..5ee0a2bcb3 100644 --- a/web/app/components/datasets/documents/detail/segment-add/index.tsx +++ b/web/app/components/datasets/documents/detail/segment-add/index.tsx @@ -11,7 +11,7 @@ import { useBoolean } from 'ahooks' import * as React from 'react' import { useCallback, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' -import PlanUpgradeModal from '@/app/components/billing/plan-upgrade-modal' +import { PlanUpgradeModal } from '@/app/components/billing/plan-upgrade-modal' import { Plan } from '@/app/components/billing/type' import { useProviderContext } from '@/context/provider-context' diff --git a/web/app/components/evaluation/__tests__/index.spec.tsx b/web/app/components/evaluation/__tests__/index.spec.tsx index 1568b60be7..286d4cac68 100644 --- a/web/app/components/evaluation/__tests__/index.spec.tsx +++ b/web/app/components/evaluation/__tests__/index.spec.tsx @@ -11,6 +11,7 @@ const mockUseDefaultEvaluationMetrics = vi.hoisted(() => vi.fn()) const mockUseEvaluationConfig = vi.hoisted(() => vi.fn()) const mockUseSaveEvaluationConfigMutation = vi.hoisted(() => vi.fn()) const mockUseStartEvaluationRunMutation = vi.hoisted(() => vi.fn()) +const mockUseEvaluationTemplateColumns = vi.hoisted(() => vi.fn()) const mockUsePublishedPipelineInfo = vi.hoisted(() => vi.fn()) const mockUseSnippetPublishedWorkflow = vi.hoisted(() => vi.fn()) @@ -55,6 +56,7 @@ vi.mock('@/service/use-evaluation', () => ({ useDefaultEvaluationMetrics: (...args: unknown[]) => mockUseDefaultEvaluationMetrics(...args), useSaveEvaluationConfigMutation: (...args: unknown[]) => mockUseSaveEvaluationConfigMutation(...args), useStartEvaluationRunMutation: (...args: unknown[]) => mockUseStartEvaluationRunMutation(...args), + useEvaluationTemplateColumns: (...args: unknown[]) => mockUseEvaluationTemplateColumns(...args), })) vi.mock('@/service/use-pipeline', () => ({ @@ -170,6 +172,18 @@ describe('Evaluation', () => { isPending: false, mutate: vi.fn(), }) + mockUseEvaluationTemplateColumns.mockReturnValue({ + data: { + columns: [ + { name: 'index', type: 'number' }, + { name: 'query', type: 'string' }, + { name: 'expected_output', type: 'string' }, + ], + }, + isError: false, + isFetching: false, + isPending: false, + }) mockUsePublishedPipelineInfo.mockReturnValue({ data: { graph: { @@ -326,72 +340,61 @@ describe('Evaluation', () => { expect(screen.queryByText('evaluation.batch.noticeDescription')).not.toBeInTheDocument() }) - it('should use published snippet input fields for snippet batch templates', () => { - mockUseSnippetPublishedWorkflow.mockReturnValue({ + it('should use template columns for snippet batch templates', () => { + const store = useEvaluationStore.getState() + act(() => { + store.ensureResource('snippets', 'snippet-fields') + store.setJudgeModel('snippets', 'snippet-fields', 'openai::gpt-4o-mini') + store.addBuiltinMetric('snippets', 'snippet-fields', 'answer-correctness', [ + { node_id: 'node-answer', title: 'Answer Node', type: 'llm' }, + ]) + }) + mockUseEvaluationTemplateColumns.mockReturnValue({ data: { - graph: { - nodes: [{ - id: 'start', - data: { - type: 'start', - variables: [{ - variable: 'graph_only', - type: 'text-input', - }], - }, - }], - }, - input_fields: [ - { - label: 'Snippet Topic', - variable: 'snippet_topic', - type: 'text-input', - required: true, - }, - { - label: 'Need Summary', - variable: 'need_summary', - type: 'checkbox', - required: false, - }, + columns: [ + { name: 'index', type: 'number' }, + { name: 'snippet_topic', type: 'string' }, + { name: 'need_summary', type: 'boolean' }, ], }, - isLoading: false, + isError: false, + isFetching: false, + isPending: false, }) renderWithQueryClient() - expect(mockUseSnippetPublishedWorkflow).toHaveBeenCalledWith('snippet-fields') + expect(mockUseEvaluationTemplateColumns).toHaveBeenCalledWith( + 'snippets', + 'snippet-fields', + expect.any(Object), + true, + ) expect(screen.getByText('snippet_topic')).toBeInTheDocument() expect(screen.getByText('need_summary')).toBeInTheDocument() - expect(screen.queryByText('graph_only')).not.toBeInTheDocument() }) - it('should show snippet-specific empty input fields copy', () => { - mockUseSnippetPublishedWorkflow.mockReturnValue({ + it('should show empty template columns copy', () => { + const store = useEvaluationStore.getState() + act(() => { + store.ensureResource('snippets', 'snippet-empty-fields') + store.setJudgeModel('snippets', 'snippet-empty-fields', 'openai::gpt-4o-mini') + store.addBuiltinMetric('snippets', 'snippet-empty-fields', 'answer-correctness', [ + { node_id: 'node-answer', title: 'Answer Node', type: 'llm' }, + ]) + }) + mockUseEvaluationTemplateColumns.mockReturnValue({ data: { - graph: { - nodes: [{ - id: 'start', - data: { - type: 'start', - variables: [{ - variable: 'graph_only', - type: 'text-input', - }], - }, - }], - }, - input_fields: [], + columns: [], }, - isLoading: false, + isError: false, + isFetching: false, + isPending: false, }) renderWithQueryClient() - expect(screen.getByText('evaluation.batch.noSnippetInputFields')).toBeInTheDocument() - expect(screen.queryByText('evaluation.batch.noInputFields')).not.toBeInTheDocument() - expect(screen.queryByText('graph_only')).not.toBeInTheDocument() + expect(screen.getByText('evaluation.batch.noTemplateColumns')).toBeInTheDocument() }) it('should hide the value row for empty operators', () => { @@ -624,6 +627,18 @@ describe('Evaluation', () => { it('should download the fixed pipeline template columns', () => { const createElement = document.createElement.bind(document) + mockUseEvaluationTemplateColumns.mockReturnValue({ + data: { + columns: [ + { name: 'index', type: 'number' }, + { name: 'query', type: 'string' }, + { name: 'expected_output', type: 'string' }, + ], + }, + isError: false, + isFetching: false, + isPending: false, + }) let downloadLink: HTMLAnchorElement | undefined const createElementSpy = vi.spyOn(document, 'createElement').mockImplementation((tagName, options) => { const element = createElement(tagName, options) @@ -645,6 +660,15 @@ describe('Evaluation', () => { const templateContent = decodeURIComponent(downloadLink?.href ?? '').replace('data:text/csv;charset=utf-8,', '') expect(downloadLink?.download).toBe('pipeline-evaluation-template.csv') expect(templateContent.trim().split(',')).toEqual(['index', 'query', 'expected_output']) + expect(mockUseEvaluationTemplateColumns).toHaveBeenLastCalledWith( + 'datasets', + 'dataset-template', + expect.objectContaining({ + evaluation_model: 'gpt-4o-mini', + evaluation_model_provider: 'openai', + }), + true, + ) createElementSpy.mockRestore() }) diff --git a/web/app/components/evaluation/components/batch-test-panel/input-fields-tab.tsx b/web/app/components/evaluation/components/batch-test-panel/input-fields-tab.tsx index a305be4561..e00e078263 100644 --- a/web/app/components/evaluation/components/batch-test-panel/input-fields-tab.tsx +++ b/web/app/components/evaluation/components/batch-test-panel/input-fields-tab.tsx @@ -5,7 +5,6 @@ import { EVALUATION_TEMPLATE_FILE_NAMES } from '../../store-utils' import InputFieldsRequirements from './input-fields/input-fields-requirements' import UploadRunPopover from './input-fields/upload-run-popover' import { useInputFieldsActions } from './input-fields/use-input-fields-actions' -import { usePublishedInputFields } from './input-fields/use-published-input-fields' type InputFieldsTabProps = EvaluationResourceProps & { isPanelReady: boolean @@ -19,12 +18,9 @@ const InputFieldsTab = ({ isRunnable, }: InputFieldsTabProps) => { const { t } = useTranslation('evaluation') - const { inputFields, isInputFieldsLoading } = usePublishedInputFields(resourceType, resourceId) const actions = useInputFieldsActions({ resourceType, resourceId, - inputFields, - isInputFieldsLoading, isPanelReady, isRunnable, templateFileName: EVALUATION_TEMPLATE_FILE_NAMES[resourceType], @@ -33,9 +29,8 @@ const InputFieldsTab = ({ return (
- ), -})) - describe('DeliveryMethodForm', () => { const onChange = vi.fn() const mockHandleSyncWorkflowDraft = vi.fn() @@ -132,7 +118,7 @@ describe('DeliveryMethodForm', () => { expect(mockHandleSyncWorkflowDraft).toHaveBeenCalledWith(true, true) }) - it('should open and close the upgrade modal', () => { + it('should open and close the upgrade modal', async () => { render( { ) fireEvent.click(screen.getByText('show-upgrade')) - expect(screen.getByText('upgrade-modal')).toBeInTheDocument() + expect(screen.getByRole('dialog')).toBeInTheDocument() - fireEvent.click(screen.getByText('upgrade-modal')) - expect(screen.queryByText('upgrade-modal')).not.toBeInTheDocument() + fireEvent.click(screen.getByRole('button', { name: 'nodes.humanInput.deliveryMethod.upgradeTipHide' })) + await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()) }) }) diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/method-item.spec.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/method-item.spec.tsx index 5f11552c70..8ad330e3f2 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/method-item.spec.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/method-item.spec.tsx @@ -6,15 +6,15 @@ import { DeliveryMethodType } from '../../../types' import DeliveryMethodItem from '../method-item' type EmailConfigureModalProps = { - isShow: boolean + open: boolean config?: EmailConfig - onClose: () => void + onOpenChange: (open: boolean) => void onConfirm: (data: EmailConfig) => void } type TestEmailSenderProps = { - isShow: boolean - onClose: () => void + open: boolean + onOpenChange: (open: boolean) => void jumpToEmailConfigModal: () => void } @@ -30,7 +30,7 @@ vi.mock('@/context/app-context', () => ({ vi.mock('../email-configure-modal', () => ({ default: (props: EmailConfigureModalProps) => { mockEmailConfigureModal(props) - return props.isShow + return props.open ? (
- +
) : null @@ -54,11 +54,11 @@ vi.mock('../email-configure-modal', () => ({ vi.mock('../test-email-sender', () => ({ default: (props: TestEmailSenderProps) => { mockTestEmailSender(props) - return props.isShow + return props.open ? (
- +
) : null @@ -140,14 +140,14 @@ describe('human-input/delivery-method/method-item', () => { const row = getMethodRow('webapp') const actionButtons = within(row).getAllByRole('button') - const deleteButtonWrapper = actionButtons[0]!.parentElement as HTMLDivElement + const deleteButton = actionButtons[0]! - fireEvent.mouseEnter(deleteButtonWrapper) + fireEvent.mouseEnter(deleteButton) expect(row)!.toHaveClass('border-state-destructive-border') - fireEvent.mouseLeave(deleteButtonWrapper) + fireEvent.mouseLeave(deleteButton) expect(row).not.toHaveClass('border-state-destructive-border') - fireEvent.click(actionButtons[0]!) + fireEvent.click(deleteButton) expect(handleDelete).toHaveBeenCalledWith(DeliveryMethodType.WebApp) }) diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/method-selector.spec.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/method-selector.spec.tsx index e1008d7457..9cbcd1c189 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/method-selector.spec.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/method-selector.spec.tsx @@ -67,7 +67,7 @@ describe('human-input/delivery-method/method-selector', () => { }) expect(handleShowUpgradeTip).not.toHaveBeenCalled() expect(screen.getByText('workflow.nodes.humanInput.deliveryMethod.contactTip1')).toBeInTheDocument() - expect(screen.getByRole('tooltip')).toHaveTextContent('nodes.humanInput.deliveryMethod.contactTip2') + expect(screen.getByText('nodes.humanInput.deliveryMethod.contactTip2')).toBeInTheDocument() }) it('should disable webapp in trigger mode and show added states without creating duplicates', () => { diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/test-email-sender.spec.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/test-email-sender.spec.tsx new file mode 100644 index 0000000000..7c62b20b8a --- /dev/null +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/test-email-sender.spec.tsx @@ -0,0 +1,298 @@ +import type { ReactNode } from 'react' +import type { EmailConfig, FormInputItem } from '../../../types' +import type { App, AppSSO } from '@/types/app' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { render, screen, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { useStore as useAppStore } from '@/app/components/app/store' +import { HooksStoreContext } from '@/app/components/workflow/hooks-store/provider' +import { createHooksStore } from '@/app/components/workflow/hooks-store/store' +import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types' +import { AppContext, initialLangGeniusVersionInfo, initialWorkspaceInfo, userProfilePlaceholder } from '@/context/app-context' +import EmailSenderModal from '../test-email-sender' + +type RecordedRequest = { + url: string + method: string + body?: unknown +} + +const createQueryClient = () => new QueryClient({ + defaultOptions: { + queries: { + retry: false, + }, + mutations: { + retry: false, + }, + }, +}) + +const renderWithProviders = (ui: ReactNode) => { + const queryClient = createQueryClient() + const hooksStore = createHooksStore({}) + + return render( + + selector({ + userProfile: { + ...userProfilePlaceholder, + id: 'user-1', + email: 'owner@example.com', + name: 'Owner', + }, + currentWorkspace: { + ...initialWorkspaceInfo, + id: 'workspace-1', + name: 'Product Team', + }, + isCurrentWorkspaceManager: true, + isCurrentWorkspaceOwner: true, + isCurrentWorkspaceEditor: true, + isCurrentWorkspaceDatasetOperator: true, + mutateUserProfile: vi.fn(), + mutateCurrentWorkspace: vi.fn(), + langGeniusVersionInfo: initialLangGeniusVersionInfo, + useSelector: vi.fn(), + isLoadingCurrentWorkspace: false, + isValidatingCurrentWorkspace: false, + }), + isLoadingCurrentWorkspace: false, + isValidatingCurrentWorkspace: false, + }} + > + + {ui} + + + , + ) +} + +const setupFetch = () => { + const requests: RecordedRequest[] = [] + const fetchSpy = vi.spyOn(globalThis, 'fetch').mockImplementation(async (resource: RequestInfo | URL, options?: RequestInit) => { + const request = resource instanceof Request ? resource : new Request(resource, options) + const body = request.method === 'GET' ? undefined : await request.clone().json() + requests.push({ + url: request.url, + method: request.method, + body, + }) + + if (request.url.includes('/workspaces/current/members')) { + return new Response(JSON.stringify({ + accounts: [ + { + id: 'member-1', + email: 'member@example.com', + name: 'Member One', + avatar: '', + avatar_url: '', + status: 'active', + role: 'normal', + created_at: '', + last_active_at: '', + last_login_at: '', + }, + ], + }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }) + } + + return new Response(JSON.stringify({ result: 'success' }), { + status: 200, + headers: { 'Content-Type': 'application/json' }, + }) + }) + + return { + fetchSpy, + requests, + } +} + +const createConfig = (overrides: Partial = {}): EmailConfig => ({ + recipients: { + whole_workspace: true, + items: [], + }, + subject: 'Review request', + body: 'Please review {{#start.score#}}', + debug_mode: false, + ...overrides, +}) + +const createFormInput = (overrides: Partial = {}): FormInputItem => ({ + type: InputVarType.textInput, + output_variable_name: 'user_name', + default: { + type: 'variable', + selector: ['start', 'user_name'], + value: '', + }, + ...overrides, +}) + +describe('human-input/delivery-method/test-email-sender', () => { + beforeEach(() => { + vi.clearAllMocks() + useAppStore.setState({ + appDetail: { + id: 'app-1', + name: 'Workflow App', + } as App & Partial, + }) + }) + + afterEach(() => { + vi.restoreAllMocks() + }) + + it('should submit generated variable inputs and show the success state', async () => { + const user = userEvent.setup() + const { requests } = setupFetch() + const handleOpenChange = vi.fn() + + renderWithProviders( + , + ) + + const sendButton = screen.getByRole('button', { name: 'workflow.nodes.humanInput.deliveryMethod.emailSender.send' }) + expect(sendButton).toBeDisabled() + + await user.type(screen.getByPlaceholderText('user_name'), 'Ada') + await user.type(screen.getByPlaceholderText('score'), '42') + expect(sendButton).toBeEnabled() + + await user.click(sendButton) + + await waitFor(() => expect(screen.getByText('workflow.nodes.humanInput.deliveryMethod.emailSender.done')).toBeInTheDocument()) + expect(requests).toContainEqual(expect.objectContaining({ + url: 'http://localhost:5001/console/api/apps/app-1/workflows/draft/human-input/nodes/human-node/delivery-test', + method: 'POST', + body: { + delivery_method_id: 'delivery-1', + inputs: { + '#start.user_name#': 'Ada', + '#start.score#': '42', + }, + }, + })) + + await user.click(screen.getByRole('button', { name: 'common.operation.ok' })) + + expect(handleOpenChange).toHaveBeenCalledWith(false) + }) + + it('should render fallback variable inputs and allow cancelling', async () => { + const user = userEvent.setup() + setupFetch() + const handleOpenChange = vi.fn() + + renderWithProviders( + , + ) + + expect(screen.getByPlaceholderText('message')).toBeInTheDocument() + + await user.click(screen.getByText('workflow.nodes.humanInput.deliveryMethod.emailSender.vars')) + + expect(screen.queryByPlaceholderText('message')).not.toBeInTheDocument() + + await user.click(screen.getByRole('button', { name: 'common.operation.cancel' })) + + expect(handleOpenChange).toHaveBeenCalledWith(false) + }) + + it('should show selected recipients with the email configuration tip', () => { + setupFetch() + + renderWithProviders( + , + ) + + expect(screen.getByText('external@example.com')).toBeInTheDocument() + expect(screen.getByText('nodes.humanInput.deliveryMethod.emailSender.tip')).toBeInTheDocument() + }) +}) diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/upgrade-modal.spec.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/upgrade-modal.spec.tsx index 537fa351e1..0abae16f9a 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/upgrade-modal.spec.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/upgrade-modal.spec.tsx @@ -1,5 +1,5 @@ import { fireEvent, render, screen } from '@testing-library/react' -import UpgradeModal from '../upgrade-modal' +import { UpgradeModal } from '../upgrade-modal' const mockUseModalContextSelector = vi.hoisted(() => vi.fn()) @@ -32,8 +32,8 @@ describe('human-input/delivery-method/upgrade-modal', () => { render( , ) @@ -41,7 +41,7 @@ describe('human-input/delivery-method/upgrade-modal', () => { expect(screen.getByText('workflow.nodes.humanInput.deliveryMethod.upgradeTipContent')).toBeInTheDocument() fireEvent.click(screen.getByRole('button', { name: 'workflow.nodes.humanInput.deliveryMethod.upgradeTipHide' })) - expect(handleClose).toHaveBeenCalledTimes(1) + expect(handleClose).toHaveBeenCalledWith(false) fireEvent.click(screen.getByRole('button', { name: /billing.upgradeBtn.encourageShort/i })) expect(handleShowPricingModal).toHaveBeenCalledTimes(1) diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx index de38564d95..046320ab37 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx @@ -4,15 +4,13 @@ import type { NodeOutPutVar, } from '@/app/components/workflow/types' import { Button } from '@langgenius/dify-ui/button' +import { Dialog, DialogCloseButton, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog' import { Switch } from '@langgenius/dify-ui/switch' import { toast } from '@langgenius/dify-ui/toast' -import { RiBugLine, RiCloseLine } from '@remixicon/react' -import { noop } from 'es-toolkit/compat' +import { RiBugLine } from '@remixicon/react' import { memo, useCallback, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' -import Divider from '@/app/components/base/divider' import Input from '@/app/components/base/input' -import Modal from '@/app/components/base/modal' import { useSelector as useAppContextWithSelector } from '@/context/app-context' import MailBodyInput from './mail-body-input' import Recipient from './recipient' @@ -20,8 +18,8 @@ import Recipient from './recipient' const i18nPrefix = 'nodes.humanInput' type EmailConfigureModalProps = { - isShow: boolean - onClose: () => void + open: boolean + onOpenChange: (open: boolean) => void onConfirm: (data: EmailConfig) => void config?: EmailConfig nodesOutputVars?: NodeOutPutVar[] @@ -29,8 +27,8 @@ type EmailConfigureModalProps = { } const EmailConfigureModal = ({ - isShow, - onClose, + open, + onOpenChange, onConfirm, config, nodesOutputVars = [], @@ -78,89 +76,87 @@ const EmailConfigureModal = ({ }, [checkValidConfig, onConfirm, recipients, subject, body, debugMode]) return ( - -
- -
-
-
{t(`${i18nPrefix}.deliveryMethod.emailConfigure.title`, { ns: 'workflow' })}
-
{t(`${i18nPrefix}.deliveryMethod.emailConfigure.description`, { ns: 'workflow' })}
-
-
-
-
- {t(`${i18nPrefix}.deliveryMethod.emailConfigure.subject`, { ns: 'workflow' })} -
- setSubject(e.target.value)} - placeholder={t(`${i18nPrefix}.deliveryMethod.emailConfigure.subjectPlaceholder`, { ns: 'workflow' })} - /> + + +
+ {t(`${i18nPrefix}.deliveryMethod.emailConfigure.title`, { ns: 'workflow' })} +
{t(`${i18nPrefix}.deliveryMethod.emailConfigure.description`, { ns: 'workflow' })}
-
-
- {t(`${i18nPrefix}.deliveryMethod.emailConfigure.body`, { ns: 'workflow' })} -
- -
-
-
- {t(`${i18nPrefix}.deliveryMethod.emailConfigure.recipient`, { ns: 'workflow' })} -
- -
- -
-
- -
-
-
{t(`${i18nPrefix}.deliveryMethod.emailConfigure.debugMode`, { ns: 'workflow' })}
-
- {email} }} - values={{ email }} - /> -
{t(`${i18nPrefix}.deliveryMethod.emailConfigure.debugModeTip2`, { ns: 'workflow' })}
+
+
+
+ {t(`${i18nPrefix}.deliveryMethod.emailConfigure.subject`, { ns: 'workflow' })}
+ setSubject(e.target.value)} + placeholder={t(`${i18nPrefix}.deliveryMethod.emailConfigure.subjectPlaceholder`, { ns: 'workflow' })} + /> +
+
+
+ {t(`${i18nPrefix}.deliveryMethod.emailConfigure.body`, { ns: 'workflow' })} +
+ +
+
+
+ {t(`${i18nPrefix}.deliveryMethod.emailConfigure.recipient`, { ns: 'workflow' })} +
+ +
+
+
+ +
+
+
{t(`${i18nPrefix}.deliveryMethod.emailConfigure.debugMode`, { ns: 'workflow' })}
+
+ {email} }} + values={{ email }} + /> +
{t(`${i18nPrefix}.deliveryMethod.emailConfigure.debugModeTip2`, { ns: 'workflow' })}
+
+
+ setDebugMode(checked)} + />
- setDebugMode(checked)} - />
-
-
- - -
- +
+ + +
+ + ) } diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/index.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/index.tsx index cdfda74aeb..50c2bf333a 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/index.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/index.tsx @@ -3,14 +3,14 @@ import type { Node, NodeOutPutVar, } from '@/app/components/workflow/types' -import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { produce } from 'immer' import * as React from 'react' import { useTranslation } from 'react-i18next' +import { Infotip } from '@/app/components/base/infotip' import { useNodesSyncDraft } from '@/app/components/workflow/hooks' import MethodItem from './method-item' import MethodSelector from './method-selector' -import UpgradeModal from './upgrade-modal' +import { UpgradeModal } from './upgrade-modal' const i18nPrefix = 'nodes.humanInput' @@ -62,27 +62,15 @@ const DeliveryMethodForm: React.FC = ({ const handleShowUpgradeModal = () => { setShowUpgradeModal(true) } - const handleCloseUpgradeModal = () => { - setShowUpgradeModal(false) - } return (
{t(`${i18nPrefix}.deliveryMethod.title`, { ns: 'workflow' })}
- - - - - )} - /> - - {t(`${i18nPrefix}.deliveryMethod.tooltip`, { ns: 'workflow' })} - - + + {t(`${i18nPrefix}.deliveryMethod.tooltip`, { ns: 'workflow' })} +
{!readonly && (
@@ -115,12 +103,10 @@ const DeliveryMethodForm: React.FC = ({ ))}
)} - {showUpgradeModal && ( - - )} +
) } diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx index 9cd63e96dd..8abace95f8 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx @@ -7,6 +7,7 @@ import type { import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { Switch } from '@langgenius/dify-ui/switch' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { RiDeleteBinLine, RiEqualizer2Line, @@ -18,7 +19,6 @@ import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' import Badge from '@/app/components/base/badge/index' -import Tooltip from '@/app/components/base/tooltip' import Indicator from '@/app/components/header/indicator' import { useSelector as useAppContextWithSelector } from '@/context/app-context' import { DeliveryMethodType } from '../../types' @@ -79,6 +79,8 @@ const DeliveryMethodItem: FC = ({ } return t(`${i18nPrefix}.deliveryMethod.emailSender.testSendTip`, { ns: 'workflow' }) }, [method.type, method.config?.debug_mode, t, email]) + const configureLabel = t('common.configure', { ns: 'workflow' }) + const removeLabel = t('operation.remove', { ns: 'common' }) const jumpToEmailConfigModal = useCallback(() => { setShowTestEmailModal(false) @@ -114,47 +116,49 @@ const DeliveryMethodItem: FC = ({
{method.type === DeliveryMethodType.Email && method.config && ( <> - - { - setShowTestEmailModal(true) - }} - > - - + + setShowTestEmailModal(true)} + > + + + )} + /> + {emailSenderTooltipContent} - - setShowEmailModal(true)}> - - - + + setShowEmailModal(true)} + > + + + )} + /> + {configureLabel} )} - -
setIsHovering(true)} - onMouseLeave={() => setIsHovering(false)} - > - onDelete(method.type)} - > - - -
+ + setIsHovering(true)} + onMouseLeave={() => setIsHovering(false)} + onClick={() => onDelete(method.type)} + > + + + )} + /> + {removeLabel}
)} @@ -178,33 +182,29 @@ const DeliveryMethodItem: FC = ({ )}
- {showEmailModal && ( - setShowEmailModal(false)} - onConfirm={(data) => { - handleConfigChange(data) - setShowEmailModal(false) - }} - /> - )} - {showTestEmailModal && ( - setShowTestEmailModal(false)} - jumpToEmailConfigModal={jumpToEmailConfigModal} - /> - )} + { + handleConfigChange(data) + setShowEmailModal(false) + }} + /> + ) } diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/method-selector.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/method-selector.tsx index 16c2345549..9ef75fd639 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/method-selector.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/method-selector.tsx @@ -2,6 +2,11 @@ import type { FC } from 'react' import type { DeliveryMethod } from '../../types' import { cn } from '@langgenius/dify-ui/cn' +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@langgenius/dify-ui/popover' import { RiAddLine, RiDiscordFill, @@ -9,17 +14,12 @@ import { RiMailSendFill, RiRobot2Fill, } from '@remixicon/react' -import { memo, useCallback, useMemo, useRef, useState } from 'react' +import { memo, useMemo, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import { v4 as uuid4 } from 'uuid' import ActionButton from '@/app/components/base/action-button' import Badge from '@/app/components/base/badge' import { Slack, Teams } from '@/app/components/base/icons/src/public/other' -import { - PortalToFollowElem, - PortalToFollowElemContent, - PortalToFollowElemTrigger, -} from '@/app/components/base/portal-to-follow-elem' import useWorkflowNodes from '@/app/components/workflow/store/workflow/use-nodes' import { isTriggerWorkflow } from '@/app/components/workflow/utils/workflow-entry' import { IS_CE_EDITION } from '@/config' @@ -40,20 +40,10 @@ const MethodSelector: FC = ({ onShowUpgradeTip, }) => { const { t } = useTranslation() - const [open, doSetOpen] = useState(false) + const [open, setOpen] = useState(false) const humanInputEmailDeliveryEnabled = useProviderContextSelector(s => s.humanInputEmailDeliveryEnabled) - const openRef = useRef(open) const nodes = useWorkflowNodes() - const setOpen = useCallback((v: boolean) => { - doSetOpen(v) - openRef.current = v - }, [doSetOpen]) - - const handleTrigger = useCallback(() => { - setOpen(!openRef.current) - }, [setOpen]) - const webAppDeliveryInfo = useMemo(() => { const isTriggerMode = isTriggerWorkflow(nodes) return { @@ -71,23 +61,25 @@ const MethodSelector: FC = ({ }, [data, humanInputEmailDeliveryEnabled]) return ( - - -
- + -
-
- + )} + /> +
= ({
)} - - + + ) } export default memo(MethodSelector) diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx index 9ad52fa13e..b88ec6cef6 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx @@ -3,16 +3,17 @@ import type { Node, NodeOutPutVar, ValueSelector, + Var, } from '@/app/components/workflow/types' import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' -import { RiArrowRightSFill, RiCloseLine } from '@remixicon/react' +import { Dialog, DialogCloseButton, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog' +import { RiArrowRightSFill } from '@remixicon/react' import { noop, unionBy } from 'es-toolkit/compat' import { memo, useCallback, useMemo, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import { useStore as useAppStore } from '@/app/components/app/store' import Divider from '@/app/components/base/divider' -import Modal from '@/app/components/base/modal' import { getInputVars as doGetInputVars } from '@/app/components/base/prompt-editor/constants' import FormItem from '@/app/components/workflow/nodes/_base/components/before-run-form/form-item' import { @@ -30,11 +31,11 @@ import EmailInput from './recipient/email-input' const i18nPrefix = 'nodes.humanInput' -type EmailConfigureModalProps = { +type EmailSenderModalProps = { nodeId: string deliveryId: string - isShow: boolean - onClose: () => void + open: boolean + onOpenChange: (open: boolean) => void jumpToEmailConfigModal: () => void config?: EmailConfig formContent?: string @@ -48,18 +49,22 @@ const getOriginVar = (valueSelector: string[], list: NodeOutPutVar[]) => { if (!targetVar) return undefined - let curr: any = targetVar.vars + let curr: Var[] | undefined = targetVar.vars for (let i = 1; i < valueSelector.length; i++) { const key = valueSelector[i] const isLast = i === valueSelector.length - 1 + const currentVar: Var | undefined = curr?.find(v => v.variable.replace('conversation.', '') === key) - if (Array.isArray(curr)) - curr = curr.find((v: any) => v.variable.replace('conversation.', '') === key) + if (!currentVar) + return undefined if (isLast) - return curr - else if (curr?.type === VarType.object || curr?.type === VarType.file) - curr = curr.children + return currentVar + + if ((currentVar.type === VarType.object || currentVar.type === VarType.file) && Array.isArray(currentVar.children)) + curr = currentVar.children + else + return undefined } return undefined @@ -68,15 +73,15 @@ const getOriginVar = (valueSelector: string[], list: NodeOutPutVar[]) => { const EmailSenderModal = ({ nodeId, deliveryId, - isShow, - onClose, + open, + onOpenChange, jumpToEmailConfigModal, config, formContent, formInputs, nodesOutputVars = [], availableNodes = [], -}: EmailConfigureModalProps) => { +}: EmailSenderModalProps) => { const { t } = useTranslation() const { userProfile, currentWorkspace } = useAppContext() const appDetail = useAppStore(state => state.appDetail) @@ -104,7 +109,7 @@ const EmailSenderModal = ({ return { label: { nodeType: varInfo?.type, - nodeName: varInfo?.title || availableNodes[0]?.data.title!, // default start node title + nodeName: varInfo?.title || availableNodes[0]?.data.title || '', variable: isSystemVar(item) ? item.join('.') : item[item.length - 1]!, isChatVar: isConversationVar(item), }, @@ -178,194 +183,194 @@ const EmailSenderModal = ({ if (done) { return ( - -
-
{t(`${i18nPrefix}.deliveryMethod.emailSender.done`, { ns: 'workflow' })}
- {debugEnabled && ( -
- }} - values={{ email: userProfile.email }} + +
+ {t(`${i18nPrefix}.deliveryMethod.emailSender.done`, { ns: 'workflow' })} + {debugEnabled && ( +
+ }} + values={{ email: userProfile.email }} + /> +
+ )} + {!debugEnabled && onlyWholeTeam && ( +
+ }} + values={{ team: currentWorkspace.name.replace(/'/g, '’') }} + /> +
+ )} + {!debugEnabled && onlySpecificUsers && ( +
{t(`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamDone3`, { ns: 'workflow' })}
+ )} + {!debugEnabled && combinedRecipients && ( +
+ }} + values={{ team: currentWorkspace.name.replace(/'/g, '’') }} + /> +
+ )} +
+ {(onlySpecificUsers || combinedRecipients) && !debugEnabled && ( +
+
)} +
+ +
+
+ + ) + } + + return ( + + + +
+ {t(`${i18nPrefix}.deliveryMethod.emailSender.title`, { ns: 'workflow' })} + {debugEnabled && ( + <> +
{t(`${i18nPrefix}.deliveryMethod.emailSender.debugModeTip`, { ns: 'workflow' })}
+
+ }} + values={{ email: userProfile.email }} + /> +
+ + )} {!debugEnabled && onlyWholeTeam && ( -
+
}} + components={{ team: }} values={{ team: currentWorkspace.name.replace(/'/g, '’') }} />
)} {!debugEnabled && onlySpecificUsers && ( -
{t(`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamDone3`, { ns: 'workflow' })}
+
{t(`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamTip3`, { ns: 'workflow' })}
)} {!debugEnabled && combinedRecipients && ( -
+
}} + components={{ team: }} values={{ team: currentWorkspace.name.replace(/'/g, '’') }} />
)}
{(onlySpecificUsers || combinedRecipients) && !debugEnabled && ( -
- -
- )} -
- -
- - ) - } - - return ( - -
- -
-
-
{t(`${i18nPrefix}.deliveryMethod.emailSender.title`, { ns: 'workflow' })}
- {debugEnabled && ( <> -
{t(`${i18nPrefix}.deliveryMethod.emailSender.debugModeTip`, { ns: 'workflow' })}
-
+
+ +
+
}} - values={{ email: userProfile.email }} + components={{ + strong: , + }} />
)} - {!debugEnabled && onlyWholeTeam && ( -
- }} - values={{ team: currentWorkspace.name.replace(/'/g, '’') }} - /> -
- )} - {!debugEnabled && onlySpecificUsers && ( -
{t(`${i18nPrefix}.deliveryMethod.emailSender.wholeTeamTip3`, { ns: 'workflow' })}
- )} - {!debugEnabled && combinedRecipients && ( -
- }} - values={{ team: currentWorkspace.name.replace(/'/g, '’') }} - /> -
- )} -
- {(onlySpecificUsers || combinedRecipients) && !debugEnabled && ( - <> -
- -
-
- , - }} - /> -
- - )} - {/* vars */} - {generatedInputs.length > 0 && ( - <> -
- -
-
-
setCollapsed(!collapsed)}> -
{t(`${i18nPrefix}.deliveryMethod.emailSender.vars`, { ns: 'workflow' })}
- + {/* vars */} + {generatedInputs.length > 0 && ( + <> +
+
-
{t(`${i18nPrefix}.deliveryMethod.emailSender.varsTip`, { ns: 'workflow' })}
- {!collapsed && ( -
- {generatedInputs.map((variable, index) => ( -
- handleValueChange(variable.variable, v)} - /> -
- ))} +
+
setCollapsed(!collapsed)}> +
{t(`${i18nPrefix}.deliveryMethod.emailSender.vars`, { ns: 'workflow' })}
+
- )} -
- - )} -
- - -
- +
{t(`${i18nPrefix}.deliveryMethod.emailSender.varsTip`, { ns: 'workflow' })}
+ {!collapsed && ( +
+ {generatedInputs.map((variable, index) => ( +
+ handleValueChange(variable.variable, v)} + /> +
+ ))} +
+ )} +
+ + )} +
+ + +
+ +
) } diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/upgrade-modal.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/upgrade-modal.tsx index 060ec2428c..18a6e90796 100644 --- a/web/app/components/workflow/nodes/human-input/components/delivery-method/upgrade-modal.tsx +++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/upgrade-modal.tsx @@ -1,76 +1,60 @@ import { Button } from '@langgenius/dify-ui/button' -import { cn } from '@langgenius/dify-ui/cn' -import { - RiMailSendFill, -} from '@remixicon/react' -import { noop } from 'es-toolkit/compat' +import { RiMailSendFill } from '@remixicon/react' import { useTranslation } from 'react-i18next' import { SparklesSoft } from '@/app/components/base/icons/src/public/common' -import Modal from '@/app/components/base/modal' import PremiumBadge from '@/app/components/base/premium-badge' +import { UpgradeModal as BaseUpgradeModal } from '@/app/components/base/upgrade-modal' import { useModalContextSelector } from '@/context/modal-context' type UpgradeModalProps = { - isShow: boolean - onClose: () => void + open: boolean + onOpenChange: (open: boolean) => void } -const UpgradeModal: React.FC = ({ - isShow, - onClose, -}) => { +export function UpgradeModal({ + open, + onOpenChange, +}: UpgradeModalProps) { const { t } = useTranslation() const setShowPricingModal = useModalContextSelector(s => s.setShowPricingModal) + const handleUpgrade = () => { + setShowPricingModal() + } return ( - -
-
- -
-

- {t('nodes.humanInput.deliveryMethod.upgradeTip', { ns: 'workflow' })} -

-

- {t('nodes.humanInput.deliveryMethod.upgradeTipContent', { ns: 'workflow' })} -

-
-
- - { - setShowPricingModal() - }} - > - -
- - {t('upgradeBtn.encourageShort', { ns: 'billing' })} - -
-
-
-
+ + + + +
+ + {t('upgradeBtn.encourageShort', { ns: 'billing' })} + +
+
+ + )} + /> ) } - -export default UpgradeModal diff --git a/web/app/components/workflow/nodes/human-input/panel.tsx b/web/app/components/workflow/nodes/human-input/panel.tsx index b7b65e7de8..fa0914c098 100644 --- a/web/app/components/workflow/nodes/human-input/panel.tsx +++ b/web/app/components/workflow/nodes/human-input/panel.tsx @@ -18,7 +18,7 @@ import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import Divider from '@/app/components/base/divider' -import Tooltip from '@/app/components/base/tooltip' +import { Infotip } from '@/app/components/base/infotip' import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' import Split from '@/app/components/workflow/nodes/_base/components/split' import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list' @@ -108,9 +108,9 @@ const Panel: FC> = ({
{t(`${i18nPrefix}.formContent.title`, { ns: 'workflow' })}
- + + {t(`${i18nPrefix}.formContent.tooltip`, { ns: 'workflow' })} +
{!readOnly && (
@@ -164,9 +164,9 @@ const Panel: FC> = ({
{t(`${i18nPrefix}.userActions.title`, { ns: 'workflow' })}
- + + {t(`${i18nPrefix}.userActions.tooltip`, { ns: 'workflow' })} +
{!readOnly && (
diff --git a/web/app/components/workflow/panel-contextmenu.tsx b/web/app/components/workflow/panel-contextmenu.tsx index 5bac2e2364..3c2207adab 100644 --- a/web/app/components/workflow/panel-contextmenu.tsx +++ b/web/app/components/workflow/panel-contextmenu.tsx @@ -1,13 +1,20 @@ import { cn } from '@langgenius/dify-ui/cn' -import { useClickAway } from 'ahooks' +import { + ContextMenu, + ContextMenuContent, + ContextMenuGroup, + ContextMenuItem, + ContextMenuSeparator, +} from '@langgenius/dify-ui/context-menu' import { memo, - useRef, + useCallback, + useMemo, } from 'react' import { useTranslation } from 'react-i18next' -import Divider from '../base/divider' import { useDSL, + useIsChatMode, useNodesInteractions, usePanelInteractions, useWorkflowMoveMode, @@ -20,7 +27,6 @@ import { useStore } from './store' const PanelContextmenu = () => { const { t } = useTranslation() - const ref = useRef(null) const panelMenu = useStore(s => s.panelMenu) const clipboardElements = useStore(s => s.clipboardElements) const setShowImportDSLModal = useStore(s => s.setShowImportDSLModal) @@ -29,127 +35,147 @@ const PanelContextmenu = () => { const setCommentQuickAdd = useStore(s => s.setCommentQuickAdd) const { handleNodesPaste } = useNodesInteractions() const { handlePaneContextmenuCancel } = usePanelInteractions() - const { handleStartWorkflowRun } = useWorkflowStartRun() + const { + handleStartWorkflowRun, + handleWorkflowStartRunInChatflow, + } = useWorkflowStartRun() const { handleAddNote } = useOperator() const { isCommentModeAvailable } = useWorkflowMoveMode() const { exportCheck } = useDSL() + const isChatMode = useIsChatMode() + const panelMenuClientX = panelMenu?.clientX + const panelMenuClientY = panelMenu?.clientY - useClickAway(() => { - handlePaneContextmenuCancel() - }, ref) + const anchor = useMemo(() => { + if (panelMenuClientX === undefined || panelMenuClientY === undefined) + return null - const renderTrigger = () => { + return { + getBoundingClientRect: () => DOMRect.fromRect({ + width: 0, + height: 0, + x: panelMenuClientX, + y: panelMenuClientY, + }), + } + }, [panelMenuClientX, panelMenuClientY]) + + const renderAddBlockTrigger = useCallback(() => { return ( ) - } + }, [t]) - if (!panelMenu) + const handleRunAction = useCallback(() => { + if (isChatMode) + handleWorkflowStartRunInChatflow() + else + handleStartWorkflowRun() + + handlePaneContextmenuCancel() + }, [isChatMode, handleWorkflowStartRunInChatflow, handleStartWorkflowRun, handlePaneContextmenuCancel]) + + if (!panelMenu || !anchor) return null return ( -
!open && handlePaneContextmenuCancel()} > -
- - - {isCommentModeAvailable && ( - - )} - -
- -
- -
- -
- - -
-
+ + {isChatMode ? t('common.debugAndPreview', { ns: 'workflow' }) : t('common.run', { ns: 'workflow' })} + {!isChatMode && } + + + + + { + if (clipboardElements.length) { + handleNodesPaste() + handlePaneContextmenuCancel() + } + }} + > + {t('common.pasteHere', { ns: 'workflow' })} + + + + + + exportCheck?.()} + > + {t('export', { ns: 'app' })} + + setShowImportDSLModal(true)} + > + {t('importApp', { ns: 'app' })} + + + + ) } diff --git a/web/app/components/workflow/panel/version-history-panel/__tests__/index.spec.tsx b/web/app/components/workflow/panel/version-history-panel/__tests__/index.spec.tsx index 3d73d5a61c..3c9f7dba0e 100644 --- a/web/app/components/workflow/panel/version-history-panel/__tests__/index.spec.tsx +++ b/web/app/components/workflow/panel/version-history-panel/__tests__/index.spec.tsx @@ -49,7 +49,7 @@ type MockRestoreConfirmModalProps = { type MockVersionHistoryItemProps = { item: VersionHistory onClick: (item: VersionHistory) => void - handleClickMenuItem: (operation: VersionHistoryContextMenuOptions) => void + handleClickActionMenuItem: (operation: VersionHistoryContextMenuOptions) => void } vi.mock('@/context/app-context', () => ({ @@ -148,7 +148,7 @@ vi.mock('@/app/components/app/app-publisher/version-info-modal', () => ({ vi.mock('../version-history-item', () => ({ default: (props: MockVersionHistoryItemProps) => { const MockVersionHistoryItem = () => { - const { item, onClick, handleClickMenuItem } = props + const { item, onClick, handleClickActionMenuItem } = props useEffect(() => { if (item.version === WorkflowVersion.Draft) @@ -159,7 +159,7 @@ vi.mock('../version-history-item', () => ({
{item.version !== WorkflowVersion.Draft && ( - )} diff --git a/web/app/components/workflow/panel/version-history-panel/__tests__/version-history-item.spec.tsx b/web/app/components/workflow/panel/version-history-panel/__tests__/version-history-item.spec.tsx index 2518b06c5c..545becd1cf 100644 --- a/web/app/components/workflow/panel/version-history-panel/__tests__/version-history-item.spec.tsx +++ b/web/app/components/workflow/panel/version-history-panel/__tests__/version-history-item.spec.tsx @@ -60,7 +60,7 @@ describe('VersionHistoryItem', () => { currentVersion={null} latestVersionId="latest-version" onClick={onClick} - handleClickMenuItem={vi.fn()} + handleClickActionMenuItem={vi.fn()} isLast={false} />, ) @@ -81,7 +81,7 @@ describe('VersionHistoryItem', () => { describe('Published Items', () => { it('should open the context menu for a latest named version and forward restore', async () => { const user = userEvent.setup() - const handleClickMenuItem = vi.fn() + const handleClickActionMenuItem = vi.fn() const onClick = vi.fn() render( @@ -90,7 +90,7 @@ describe('VersionHistoryItem', () => { currentVersion={null} latestVersionId="version-1" onClick={onClick} - handleClickMenuItem={handleClickMenuItem} + handleClickActionMenuItem={handleClickActionMenuItem} isLast={false} />, ) @@ -120,8 +120,8 @@ describe('VersionHistoryItem', () => { fireEvent.click(restoreItem) - expect(handleClickMenuItem).toHaveBeenCalledTimes(1) - expect(handleClickMenuItem).toHaveBeenCalledWith( + expect(handleClickActionMenuItem).toHaveBeenCalledTimes(1) + expect(handleClickActionMenuItem).toHaveBeenCalledWith( VersionHistoryContextMenuOptions.restore, VersionHistoryContextMenuOptions.restore, ) @@ -138,7 +138,7 @@ describe('VersionHistoryItem', () => { currentVersion={item} latestVersionId="other-version" onClick={onClick} - handleClickMenuItem={vi.fn()} + handleClickActionMenuItem={vi.fn()} isLast />, ) diff --git a/web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/menu-item.spec.tsx b/web/app/components/workflow/panel/version-history-panel/action-menu/__tests__/action-menu-item.spec.tsx similarity index 89% rename from web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/menu-item.spec.tsx rename to web/app/components/workflow/panel/version-history-panel/action-menu/__tests__/action-menu-item.spec.tsx index 7dfc362a90..8001f74840 100644 --- a/web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/menu-item.spec.tsx +++ b/web/app/components/workflow/panel/version-history-panel/action-menu/__tests__/action-menu-item.spec.tsx @@ -2,9 +2,9 @@ import { DropdownMenu, DropdownMenuContent } from '@langgenius/dify-ui/dropdown- import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { VersionHistoryContextMenuOptions } from '../../../../types' -import MenuItem from '../menu-item' +import ActionMenuItem from '../action-menu-item' -describe('MenuItem', () => { +describe('ActionMenuItem', () => { it('forwards the selected operation and supports destructive styling', async () => { const user = userEvent.setup() const onClick = vi.fn() @@ -12,7 +12,7 @@ describe('MenuItem', () => { render( - { +describe('ActionMenu', () => { it('toggles the trigger and forwards menu clicks', async () => { const user = userEvent.setup() const setOpen = vi.fn() - const handleClickMenuItem = vi.fn() + const handleClickActionMenuItem = vi.fn() renderWorkflowComponent( - , ) @@ -25,11 +25,11 @@ describe('ContextMenu', () => { await user.click(screen.getByText('common.operation.delete')) expect(setOpen).toHaveBeenCalled() - expect(handleClickMenuItem).toHaveBeenCalledWith( + expect(handleClickActionMenuItem).toHaveBeenCalledWith( VersionHistoryContextMenuOptions.restore, VersionHistoryContextMenuOptions.restore, ) - expect(handleClickMenuItem).toHaveBeenCalledWith( + expect(handleClickActionMenuItem).toHaveBeenCalledWith( VersionHistoryContextMenuOptions.delete, VersionHistoryContextMenuOptions.delete, ) diff --git a/web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/use-context-menu.spec.tsx b/web/app/components/workflow/panel/version-history-panel/action-menu/__tests__/use-action-menu.spec.tsx similarity index 83% rename from web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/use-context-menu.spec.tsx rename to web/app/components/workflow/panel/version-history-panel/action-menu/__tests__/use-action-menu.spec.tsx index 084016d74e..e08bc787d7 100644 --- a/web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/use-context-menu.spec.tsx +++ b/web/app/components/workflow/panel/version-history-panel/action-menu/__tests__/use-action-menu.spec.tsx @@ -1,15 +1,15 @@ import { renderWorkflowHook } from '../../../../__tests__/workflow-test-env' import { VersionHistoryContextMenuOptions } from '../../../../types' -import useContextMenu from '../use-context-menu' +import useActionMenu from '../use-action-menu' -describe('useContextMenu', () => { +describe('useActionMenu', () => { it('returns restore, edit, export, copy and delete operations for app workflows', () => { - const { result } = renderWorkflowHook(() => useContextMenu({ + const { result } = renderWorkflowHook(() => useActionMenu({ isNamedVersion: true, isShowDelete: false, open: false, setOpen: vi.fn(), - handleClickMenuItem: vi.fn(), + handleClickActionMenuItem: vi.fn(), })) expect(result.current.deleteOperation).toEqual({ @@ -25,12 +25,12 @@ describe('useContextMenu', () => { }) it('omits export for pipelines and renames the edit action for unnamed versions', () => { - const { result } = renderWorkflowHook(() => useContextMenu({ + const { result } = renderWorkflowHook(() => useActionMenu({ isNamedVersion: false, isShowDelete: true, open: false, setOpen: vi.fn(), - handleClickMenuItem: vi.fn(), + handleClickActionMenuItem: vi.fn(), }), { initialStoreState: { pipelineId: 'pipeline-1', diff --git a/web/app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx b/web/app/components/workflow/panel/version-history-panel/action-menu/action-menu-item.tsx similarity index 89% rename from web/app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx rename to web/app/components/workflow/panel/version-history-panel/action-menu/action-menu-item.tsx index 0c0096ab25..7d24e81217 100644 --- a/web/app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx +++ b/web/app/components/workflow/panel/version-history-panel/action-menu/action-menu-item.tsx @@ -4,7 +4,7 @@ import { cn } from '@langgenius/dify-ui/cn' import { DropdownMenuItem } from '@langgenius/dify-ui/dropdown-menu' import * as React from 'react' -type MenuItemProps = { +type ActionMenuItemProps = { item: { key: VersionHistoryContextMenuOptions name: string @@ -13,7 +13,7 @@ type MenuItemProps = { isDestructive?: boolean } -const MenuItem: FC = ({ +const ActionMenuItem: FC = ({ item, onClick, isDestructive = false, @@ -41,4 +41,4 @@ const MenuItem: FC = ({ ) } -export default React.memo(MenuItem) +export default React.memo(ActionMenuItem) diff --git a/web/app/components/workflow/panel/version-history-panel/context-menu/index.tsx b/web/app/components/workflow/panel/version-history-panel/action-menu/index.tsx similarity index 68% rename from web/app/components/workflow/panel/version-history-panel/context-menu/index.tsx rename to web/app/components/workflow/panel/version-history-panel/action-menu/index.tsx index 1b90166f65..8299ff2b30 100644 --- a/web/app/components/workflow/panel/version-history-panel/context-menu/index.tsx +++ b/web/app/components/workflow/panel/version-history-panel/action-menu/index.tsx @@ -9,23 +9,23 @@ import { import { RiMoreFill } from '@remixicon/react' import * as React from 'react' import { VersionHistoryContextMenuOptions } from '../../../types' -import MenuItem from './menu-item' -import useContextMenu from './use-context-menu' +import ActionMenuItem from './action-menu-item' +import useActionMenu from './use-action-menu' -export type ContextMenuProps = { +export type ActionMenuProps = { isShowDelete: boolean isNamedVersion: boolean open: boolean setOpen: React.Dispatch> - handleClickMenuItem: (operation: VersionHistoryContextMenuOptions) => void + handleClickActionMenuItem: (operation: VersionHistoryContextMenuOptions) => void } -const ContextMenu: FC = (props: ContextMenuProps) => { - const { isShowDelete, handleClickMenuItem, open, setOpen } = props +const ActionMenu: FC = (props: ActionMenuProps) => { + const { isShowDelete, handleClickActionMenuItem, open, setOpen } = props const { deleteOperation, options, - } = useContextMenu(props) + } = useActionMenu(props) return ( = (props: ContextMenuProps) => { > { options.map(option => ( - )) } @@ -55,10 +55,10 @@ const ContextMenu: FC = (props: ContextMenuProps) => { isShowDelete && ( <> - ) @@ -68,4 +68,4 @@ const ContextMenu: FC = (props: ContextMenuProps) => { ) } -export default React.memo(ContextMenu) +export default React.memo(ActionMenu) diff --git a/web/app/components/workflow/panel/version-history-panel/context-menu/use-context-menu.ts b/web/app/components/workflow/panel/version-history-panel/action-menu/use-action-menu.ts similarity index 89% rename from web/app/components/workflow/panel/version-history-panel/context-menu/use-context-menu.ts rename to web/app/components/workflow/panel/version-history-panel/action-menu/use-action-menu.ts index 92d6ee6869..4a81809aeb 100644 --- a/web/app/components/workflow/panel/version-history-panel/context-menu/use-context-menu.ts +++ b/web/app/components/workflow/panel/version-history-panel/action-menu/use-action-menu.ts @@ -1,10 +1,10 @@ -import type { ContextMenuProps } from './index' +import type { ActionMenuProps } from './index' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { useStore } from '@/app/components/workflow/store' import { VersionHistoryContextMenuOptions } from '../../../types' -const useContextMenu = (props: ContextMenuProps) => { +const useActionMenu = (props: ActionMenuProps) => { const { isNamedVersion, } = props @@ -43,7 +43,7 @@ const useContextMenu = (props: ContextMenuProps) => { name: t('versionHistory.copyId', { ns: 'workflow' }), }, ] - }, [isNamedVersion, t]) + }, [isNamedVersion, pipelineId, t]) return { deleteOperation, @@ -51,4 +51,4 @@ const useContextMenu = (props: ContextMenuProps) => { } } -export default useContextMenu +export default useActionMenu diff --git a/web/app/components/workflow/panel/version-history-panel/index.tsx b/web/app/components/workflow/panel/version-history-panel/index.tsx index eb1f5c962e..851490fc5a 100644 --- a/web/app/components/workflow/panel/version-history-panel/index.tsx +++ b/web/app/components/workflow/panel/version-history-panel/index.tsx @@ -107,7 +107,7 @@ export const VersionHistoryPanel = ({ setIsOnlyShowNamedVersions(false) }, []) - const handleClickMenuItem = useCallback((item: VersionHistory, operation: VersionHistoryContextMenuOptions) => { + const handleClickActionMenuItem = useCallback((item: VersionHistory, operation: VersionHistoryContextMenuOptions) => { setOperatedItem(item) switch (operation) { case VersionHistoryContextMenuOptions.restore: @@ -292,7 +292,7 @@ export const VersionHistoryPanel = ({ currentVersion={currentVersion} latestVersionId={latestVersionId || ''} onClick={handleVersionClick} - handleClickMenuItem={handleClickMenuItem.bind(null, item)} + handleClickActionMenuItem={handleClickActionMenuItem.bind(null, item)} isLast={isLast} /> ) diff --git a/web/app/components/workflow/panel/version-history-panel/version-history-item.tsx b/web/app/components/workflow/panel/version-history-panel/version-history-item.tsx index bd0a967c49..0984b2654f 100644 --- a/web/app/components/workflow/panel/version-history-panel/version-history-item.tsx +++ b/web/app/components/workflow/panel/version-history-panel/version-history-item.tsx @@ -6,14 +6,14 @@ import * as React from 'react' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { WorkflowVersion } from '../../types' -import ContextMenu from './context-menu' +import ActionMenu from './action-menu' type VersionHistoryItemProps = { item: VersionHistory currentVersion: VersionHistory | null latestVersionId: string onClick: (item: VersionHistory) => void - handleClickMenuItem: (operation: VersionHistoryContextMenuOptions) => void + handleClickActionMenuItem: (operation: VersionHistoryContextMenuOptions) => void isLast: boolean } @@ -41,7 +41,7 @@ const VersionHistoryItem: React.FC = ({ currentVersion, latestVersionId, onClick, - handleClickMenuItem, + handleClickActionMenuItem, isLast, }) => { const { t } = useTranslation() @@ -122,15 +122,15 @@ const VersionHistoryItem: React.FC = ({ ) }
- {/* Context Menu */} + {/* Action Menu */} {!isDraft && isHovering && (
-
)} diff --git a/web/app/components/workflow/selection-contextmenu.tsx b/web/app/components/workflow/selection-contextmenu.tsx index b142f6e700..e4e1fd08ce 100644 --- a/web/app/components/workflow/selection-contextmenu.tsx +++ b/web/app/components/workflow/selection-contextmenu.tsx @@ -1,4 +1,5 @@ import type { CreateSnippetDialogPayload } from './create-snippet-dialog' +import type { WorkflowShortcutId } from './shortcuts/definitions' import type { Edge, Node } from './types' import type { SnippetCanvasData } from '@/models/snippet' import { cn } from '@langgenius/dify-ui/cn' @@ -28,7 +29,6 @@ import CreateSnippetDialog from './create-snippet-dialog' import { useNodesInteractions, useNodesReadOnly, useNodesSyncDraft } from './hooks' import { useSelectionInteractions } from './hooks/use-selection-interactions' import { useWorkflowHistory, WorkflowHistoryEvent } from './hooks/use-workflow-history' -import type { WorkflowShortcutId } from './shortcuts/definitions' import { ShortcutKbd } from './shortcuts/shortcut-kbd' import { useStore, useWorkflowStore } from './store' import { BlockEnum, TRIGGER_NODE_TYPES } from './types' @@ -65,6 +65,7 @@ type ActionMenuItem = { disabled?: boolean shortcut?: WorkflowShortcutId translationKey: string + workflowShortcutId?: WorkflowShortcutId } const DEFAULT_SNIPPET_VIEWPORT: SnippetCanvasData['viewport'] = { x: 0, y: 0, zoom: 1 } diff --git a/web/app/components/workflow/store/__tests__/workflow-store.spec.ts b/web/app/components/workflow/store/__tests__/workflow-store.spec.ts index 47819c049f..4b5dc0b302 100644 --- a/web/app/components/workflow/store/__tests__/workflow-store.spec.ts +++ b/web/app/components/workflow/store/__tests__/workflow-store.spec.ts @@ -108,7 +108,7 @@ describe('createWorkflowStore', () => { ['showWorkflowVersionHistoryPanel', 'setShowWorkflowVersionHistoryPanel', true], ['showInputsPanel', 'setShowInputsPanel', true], ['showDebugAndPreviewPanel', 'setShowDebugAndPreviewPanel', true], - ['panelMenu', 'setPanelMenu', { top: 10, left: 20 }], + ['panelMenu', 'setPanelMenu', { clientX: 20, clientY: 10 }], ['selectionMenu', 'setSelectionMenu', { clientX: 50, clientY: 60 }], ['edgeMenu', 'setEdgeMenu', { clientX: 320, clientY: 180, edgeId: 'e1' }], ['showVariableInspectPanel', 'setShowVariableInspectPanel', true], diff --git a/web/app/components/workflow/store/workflow/__tests__/panel-slice.spec.ts b/web/app/components/workflow/store/workflow/__tests__/panel-slice.spec.ts index 0c7f55850f..1f30a2b7cf 100644 --- a/web/app/components/workflow/store/workflow/__tests__/panel-slice.spec.ts +++ b/web/app/components/workflow/store/workflow/__tests__/panel-slice.spec.ts @@ -19,12 +19,12 @@ describe('createPanelSlice', () => { store.getState().setShowFeaturesPanel(true) store.getState().setShowDebugAndPreviewPanel(true) - store.getState().setPanelMenu({ top: 24, left: 48 }) + store.getState().setPanelMenu({ clientX: 48, clientY: 24 }) store.getState().setEdgeMenu({ clientX: 80, clientY: 120, edgeId: 'edge-1' }) expect(store.getState().showFeaturesPanel).toBe(true) expect(store.getState().showDebugAndPreviewPanel).toBe(true) - expect(store.getState().panelMenu).toEqual({ top: 24, left: 48 }) + expect(store.getState().panelMenu).toEqual({ clientX: 48, clientY: 24 }) expect(store.getState().edgeMenu).toEqual({ clientX: 80, clientY: 120, edgeId: 'edge-1' }) }) }) diff --git a/web/app/components/workflow/store/workflow/panel-slice.ts b/web/app/components/workflow/store/workflow/panel-slice.ts index e84d33a1eb..2f4264fc78 100644 --- a/web/app/components/workflow/store/workflow/panel-slice.ts +++ b/web/app/components/workflow/store/workflow/panel-slice.ts @@ -17,8 +17,8 @@ export type PanelSliceShape = { showUserCursors: boolean setShowUserCursors: (showUserCursors: boolean) => void panelMenu?: { - top: number - left: number + clientX: number + clientY: number } setPanelMenu: (panelMenu: PanelSliceShape['panelMenu']) => void selectionMenu?: { diff --git a/web/context/modal-context-provider.tsx b/web/context/modal-context-provider.tsx index fcc37a1030..c51d422aad 100644 --- a/web/context/modal-context-provider.tsx +++ b/web/context/modal-context-provider.tsx @@ -169,13 +169,13 @@ export const ModalContextProvider = ({ showModelModal.onCancelCallback() }, [showModelModal]) - const handleSaveModelModal = useCallback((formValues?: Record) => { + const handleSaveModelModal = useCallback((formValues?: Record) => { if (showModelModal?.onSaveCallback) showModelModal.onSaveCallback(showModelModal.payload, formValues) setShowModelModal(null) }, [showModelModal]) - const handleRemoveModelModal = useCallback((formValues?: Record) => { + const handleRemoveModelModal = useCallback((formValues?: Record) => { if (showModelModal?.onRemoveCallback) showModelModal.onRemoveCallback(showModelModal.payload, formValues) setShowModelModal(null) @@ -369,7 +369,7 @@ export const ModalContextProvider = ({ }} onSave={() => { setShowUpdatePluginModal(null) - showUpdatePluginModal.onSaveCallback?.({} as any) + showUpdatePluginModal.onSaveCallback?.() }} /> ) diff --git a/web/context/modal-context.test.tsx b/web/context/modal-context.test.tsx index ce5efdb8d9..7434130c31 100644 --- a/web/context/modal-context.test.tsx +++ b/web/context/modal-context.test.tsx @@ -1,4 +1,5 @@ -import { act, screen, waitFor } from '@testing-library/react' +import { screen, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' import * as React from 'react' import { defaultPlan } from '@/app/components/billing/config' import { Plan } from '@/app/components/billing/type' @@ -27,21 +28,6 @@ vi.mock('@/context/app-context', () => ({ useAppContext: () => mockUseAppContext(), })) -let latestTriggerEventsModalProps: any = null -const triggerEventsLimitModalMock = vi.fn((props: any) => { - latestTriggerEventsModalProps = props - return ( -
- - -
- ) -}) - -vi.mock('@/app/components/billing/trigger-events-limit-modal', () => ({ - default: (props: any) => triggerEventsLimitModalMock(props), -})) - type DefaultPlanShape = typeof defaultPlan type ResetShape = { apiRateLimit: number | null @@ -79,8 +65,6 @@ const renderProvider = () => renderWithNuqs( describe('ModalContextProvider trigger events limit modal', () => { beforeEach(() => { - latestTriggerEventsModalProps = null - triggerEventsLimitModalMock.mockClear() mockUseAppContext.mockReset() mockUseProviderContext.mockReset() window.localStorage.clear() @@ -109,25 +93,20 @@ describe('ModalContextProvider trigger events limit modal', () => { // Note: vitest.setup.ts replaces localStorage with a mock object that has vi.fn() methods // We need to spy on the mock's setItem, not Storage.prototype.setItem const setItemSpy = vi.spyOn(localStorage, 'setItem') + const user = userEvent.setup() renderProvider() - await waitFor(() => expect(screen.getByTestId('trigger-limit-modal'))!.toBeInTheDocument()) - expect(latestTriggerEventsModalProps).toMatchObject({ - usage: 3000, - total: 3000, - resetInDays: 5, - }) + await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument()) + expect(screen.getAllByText('3000')).toHaveLength(2) - act(() => { - latestTriggerEventsModalProps.onClose() - }) + await user.click(screen.getByRole('button', { name: 'billing.triggerLimitModal.dismiss' })) - await waitFor(() => expect(screen.queryByTestId('trigger-limit-modal')).not.toBeInTheDocument()) + await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()) await waitFor(() => { expect(setItemSpy.mock.calls.length).toBeGreaterThan(0) }) - const [key, value] = (setItemSpy.mock.calls[0] ?? []) as [any, any] + const [key, value] = (setItemSpy.mock.calls[0] ?? []) as [string, string] expect(key).toContain('trigger-events-limit-dismissed-workspace-1-professional-3000-') expect(value).toBe('1') }) @@ -147,18 +126,16 @@ describe('ModalContextProvider trigger events limit modal', () => { throw new Error('Storage disabled') }) const setItemSpy = vi.spyOn(localStorage, 'setItem') + const user = userEvent.setup() renderProvider() - await waitFor(() => expect(screen.getByTestId('trigger-limit-modal'))!.toBeInTheDocument()) + await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument()) - act(() => { - latestTriggerEventsModalProps.onClose() - }) + await user.click(screen.getByRole('button', { name: 'billing.triggerLimitModal.dismiss' })) - await waitFor(() => expect(screen.queryByTestId('trigger-limit-modal')).not.toBeInTheDocument()) + await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()) expect(setItemSpy).not.toHaveBeenCalled() - await waitFor(() => expect(triggerEventsLimitModalMock).toHaveBeenCalledTimes(1)) }) it('falls back to the in-memory guard when localStorage.setItem fails', async () => { @@ -175,16 +152,37 @@ describe('ModalContextProvider trigger events limit modal', () => { vi.spyOn(localStorage, 'setItem').mockImplementation(() => { throw new Error('Quota exceeded') }) + const user = userEvent.setup() renderProvider() - await waitFor(() => expect(screen.getByTestId('trigger-limit-modal'))!.toBeInTheDocument()) + await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument()) - act(() => { - latestTriggerEventsModalProps.onClose() + await user.click(screen.getByRole('button', { name: 'billing.triggerLimitModal.dismiss' })) + + await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()) + }) + + it('closes the trigger events limit modal and opens pricing when upgrading', async () => { + const plan = createPlan({ + type: Plan.professional, + usage: { triggerEvents: 400 }, + total: { triggerEvents: 400 }, + reset: { triggerEvents: 6 }, }) + mockUseProviderContext.mockReturnValue({ + plan, + isFetchedPlan: true, + }) + const user = userEvent.setup() - await waitFor(() => expect(screen.queryByTestId('trigger-limit-modal')).not.toBeInTheDocument()) - await waitFor(() => expect(triggerEventsLimitModalMock).toHaveBeenCalledTimes(1)) + renderProvider() + + await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument()) + + await user.click(screen.getByText('billing.triggerLimitModal.upgrade')) + + await waitFor(() => expect(screen.getByText('billing.plansCommon.mostPopular')).toBeInTheDocument()) + expect(screen.queryByText('400')).not.toBeInTheDocument() }) }) diff --git a/web/context/modal-context.ts b/web/context/modal-context.ts index d019093955..a8f1597cdb 100644 --- a/web/context/modal-context.ts +++ b/web/context/modal-context.ts @@ -28,8 +28,8 @@ import { createContext, useContext, useContextSelector } from 'use-context-selec export type ModalState = { payload: T onCancelCallback?: () => void - onSaveCallback?: (newPayload?: T, formValues?: Record) => void - onRemoveCallback?: (newPayload?: T, formValues?: Record) => void + onSaveCallback?: (newPayload?: T, formValues?: Record) => void + onRemoveCallback?: (newPayload?: T, formValues?: Record) => void onEditCallback?: (newPayload: T) => void onValidateBeforeSaveCallback?: (newPayload: T) => boolean isEditMode?: boolean diff --git a/web/contract/console/apps.ts b/web/contract/console/apps.ts index 54db29db19..077c719671 100644 --- a/web/contract/console/apps.ts +++ b/web/contract/console/apps.ts @@ -32,11 +32,11 @@ export const appWorkflowTypeConvertContract = base export const workflowOnlineUsersContract = base .route({ path: '/apps/workflows/online-users', - method: 'GET', + method: 'POST', }) .input(type<{ - query: { - app_ids: string + body: { + app_ids: string[] } }>()) .output(type()) diff --git a/web/contract/console/evaluation.ts b/web/contract/console/evaluation.ts index ac961732f8..1ac8d51ef7 100644 --- a/web/contract/console/evaluation.ts +++ b/web/contract/console/evaluation.ts @@ -14,6 +14,7 @@ import type { EvaluationRunDetailResponse, EvaluationRunRequest, EvaluationTargetType, + EvaluationTemplateColumnsResponse, EvaluationVersionDetailResponse, EvaluationWorkflowAssociatedTargetsResponse, } from '@/types/evaluation' @@ -154,6 +155,20 @@ export const evaluationTemplateDownloadContract = base }>()) .output(type()) +export const evaluationTemplateColumnsContract = base + .route({ + path: '/{targetType}/{targetId}/evaluation/template-columns', + method: 'POST', + }) + .input(type<{ + params: { + targetType: EvaluationTargetType + targetId: string + } + body: EvaluationConfigData + }>()) + .output(type()) + export const evaluationConfigContract = base .route({ path: '/{targetType}/{targetId}/evaluation', diff --git a/web/contract/router.ts b/web/contract/router.ts index 555d49e233..8812ad5b1c 100644 --- a/web/contract/router.ts +++ b/web/contract/router.ts @@ -20,6 +20,7 @@ import { evaluationMetricsContract, evaluationNodeInfoContract, evaluationRunDetailContract, + evaluationTemplateColumnsContract, evaluationTemplateDownloadContract, evaluationVersionDetailContract, evaluationWorkflowAssociatedTargetsContract, @@ -140,6 +141,7 @@ export const consoleRouterContract = { }, evaluation: { templateDownload: evaluationTemplateDownloadContract, + templateColumns: evaluationTemplateColumnsContract, config: evaluationConfigContract, saveConfig: saveEvaluationConfigContract, logs: evaluationLogsContract, diff --git a/web/i18n/en-US/evaluation.json b/web/i18n/en-US/evaluation.json index ac463df967..cd021d2d88 100644 --- a/web/i18n/en-US/evaluation.json +++ b/web/i18n/en-US/evaluation.json @@ -4,9 +4,10 @@ "batch.emptyHistory": "No test history yet.", "batch.example": "Example:", "batch.fileRequired": "Upload an evaluation dataset file before running the test.", - "batch.loadingInputFields": "Loading input fields...", + "batch.loadingInputFields": "Loading template columns...", "batch.noInputFields": "No published start node input fields found.", "batch.noSnippetInputFields": "No published snippet input fields found.", + "batch.noTemplateColumns": "No template columns found.", "batch.noticeDescription": "Configuration incomplete. Select the Judge Model and Metrics on the left to generate your batch test template.", "batch.noticeTitle": "Quick start", "batch.removeUploadedFile": "Remove uploaded file", @@ -20,6 +21,7 @@ "batch.status.success": "Success", "batch.tabs.history": "Test History", "batch.tabs.input-fields": "Input Fields", + "batch.templateColumnsError": "Failed to generate the CSV template.", "batch.title": "Batch Test", "batch.uploadAndRun": "Upload & Run Test", "batch.uploadDropzoneEmphasis": "filled", diff --git a/web/i18n/zh-Hans/evaluation.json b/web/i18n/zh-Hans/evaluation.json index aacec3f271..5c2d06ea9f 100644 --- a/web/i18n/zh-Hans/evaluation.json +++ b/web/i18n/zh-Hans/evaluation.json @@ -4,9 +4,10 @@ "batch.emptyHistory": "还没有测试历史。", "batch.example": "示例:", "batch.fileRequired": "请先上传评估数据集文件,再运行测试。", - "batch.loadingInputFields": "正在加载输入字段...", + "batch.loadingInputFields": "正在加载模板列...", "batch.noInputFields": "未找到已发布 Start 节点的输入字段。", "batch.noSnippetInputFields": "未找到已发布的片段输入字段。", + "batch.noTemplateColumns": "未找到模板列。", "batch.noticeDescription": "配置尚未完成。请先在左侧选择判定模型和指标,以生成批量测试模板。", "batch.noticeTitle": "快速开始", "batch.removeUploadedFile": "移除已上传文件", @@ -20,6 +21,7 @@ "batch.status.success": "成功", "batch.tabs.history": "测试历史", "batch.tabs.input-fields": "输入字段", + "batch.templateColumnsError": "生成 CSV 模板失败。", "batch.title": "批量测试", "batch.uploadAndRun": "上传并运行测试", "batch.uploadDropzoneEmphasis": "已填写的", diff --git a/web/service/apps.ts b/web/service/apps.ts index d2c6593a34..221e83cf39 100644 --- a/web/service/apps.ts +++ b/web/service/apps.ts @@ -14,7 +14,7 @@ export const fetchWorkflowOnlineUsers = async ({ appIds }: { appIds: string[] }) return {} const response = await consoleClient.apps.workflowOnlineUsers({ - query: { app_ids: appIds.join(',') }, + body: { app_ids: appIds }, }) if (!response?.data) diff --git a/web/service/use-evaluation.ts b/web/service/use-evaluation.ts index 84750417c3..b196505847 100644 --- a/web/service/use-evaluation.ts +++ b/web/service/use-evaluation.ts @@ -1,5 +1,5 @@ import type { EvaluationResourceType, NonPipelineEvaluationResourceType } from '@/app/components/evaluation/types' -import type { AvailableEvaluationWorkflowsResponse, EvaluationConfig } from '@/types/evaluation' +import type { AvailableEvaluationWorkflowsResponse, EvaluationConfig, EvaluationConfigData } from '@/types/evaluation' import { keepPreviousData, skipToken, @@ -131,6 +131,27 @@ export const useStartEvaluationRunMutation = () => { })) } +export const useEvaluationTemplateColumns = ( + resourceType: EvaluationResourceType, + resourceId: string, + configPayload: EvaluationConfigData | null, + enabled = true, +) => { + return useQuery(consoleQuery.evaluation.templateColumns.queryOptions({ + input: resourceId && configPayload + ? { + params: { + targetType: resourceType, + targetId: resourceId, + }, + body: configPayload, + } + : skipToken, + enabled: !!resourceId && !!configPayload && enabled, + refetchOnWindowFocus: false, + })) +} + export const useAvailableEvaluationWorkflows = ( params: AvailableEvaluationWorkflowsParams = {}, options?: { enabled?: boolean }, diff --git a/web/types/evaluation.ts b/web/types/evaluation.ts index a02df57ac8..805dc7ff31 100644 --- a/web/types/evaluation.ts +++ b/web/types/evaluation.ts @@ -59,6 +59,15 @@ export type EvaluationRunRequest = EvaluationConfigData & { file_id: string } +export type EvaluationTemplateColumn = { + name: string + type: string +} + +export type EvaluationTemplateColumnsResponse = { + columns: EvaluationTemplateColumn[] +} + export type EvaluationRunStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled' export type EvaluationJudgmentMetricsSummary = {