diff --git a/autogpt_platform/backend/backend/api/rest_api.py b/autogpt_platform/backend/backend/api/rest_api.py index 147f62e781..e9556e992f 100644 --- a/autogpt_platform/backend/backend/api/rest_api.py +++ b/autogpt_platform/backend/backend/api/rest_api.py @@ -39,7 +39,7 @@ import backend.data.user import backend.integrations.webhooks.utils import backend.util.service import backend.util.settings -from backend.blocks.llm import LlmModel +from backend.blocks.llm import DEFAULT_LLM_MODEL from backend.data.model import Credentials from backend.integrations.providers import ProviderName from backend.monitoring.instrumentation import instrument_fastapi @@ -113,7 +113,7 @@ async def lifespan_context(app: fastapi.FastAPI): await backend.data.user.migrate_and_encrypt_user_integrations() await backend.data.graph.fix_llm_provider_credentials() - await backend.data.graph.migrate_llm_models(LlmModel.GPT4O) + await backend.data.graph.migrate_llm_models(DEFAULT_LLM_MODEL) await backend.integrations.webhooks.utils.migrate_legacy_triggered_graphs() with launch_darkly_context(): diff --git a/autogpt_platform/backend/backend/blocks/ai_condition.py b/autogpt_platform/backend/backend/blocks/ai_condition.py index de43c29a90..2a5cdcdeec 100644 --- a/autogpt_platform/backend/backend/blocks/ai_condition.py +++ b/autogpt_platform/backend/backend/blocks/ai_condition.py @@ -1,6 +1,7 @@ from typing import Any from backend.blocks.llm import ( + DEFAULT_LLM_MODEL, TEST_CREDENTIALS, TEST_CREDENTIALS_INPUT, AIBlockBase, @@ -49,7 +50,7 @@ class AIConditionBlock(AIBlockBase): ) model: LlmModel = SchemaField( title="LLM Model", - default=LlmModel.GPT4O, + default=DEFAULT_LLM_MODEL, description="The language model to use for evaluating the condition.", advanced=False, ) @@ -81,7 +82,7 @@ class AIConditionBlock(AIBlockBase): "condition": "the input is an email address", "yes_value": "Valid email", "no_value": "Not an email", - "model": LlmModel.GPT4O, + "model": DEFAULT_LLM_MODEL, "credentials": TEST_CREDENTIALS_INPUT, }, test_credentials=TEST_CREDENTIALS, diff --git a/autogpt_platform/backend/backend/blocks/dataforseo/related_keywords.py b/autogpt_platform/backend/backend/blocks/dataforseo/related_keywords.py index 7a7fbdd11a..0757cb6507 100644 --- a/autogpt_platform/backend/backend/blocks/dataforseo/related_keywords.py +++ b/autogpt_platform/backend/backend/blocks/dataforseo/related_keywords.py @@ -182,13 +182,10 @@ class DataForSeoRelatedKeywordsBlock(Block): if results and len(results) > 0: # results is a list, get the first element first_result = results[0] if isinstance(results, list) else results - items = ( - first_result.get("items", []) - if isinstance(first_result, dict) - else [] - ) - # Ensure items is never None - if items is None: + # Handle missing key, null value, or valid list value + if isinstance(first_result, dict): + items = first_result.get("items") or [] + else: items = [] for item in items: # Extract keyword_data from the item diff --git a/autogpt_platform/backend/backend/blocks/google/docs.py b/autogpt_platform/backend/backend/blocks/google/docs.py new file mode 100644 index 0000000000..7840cbae73 --- /dev/null +++ b/autogpt_platform/backend/backend/blocks/google/docs.py @@ -0,0 +1,2896 @@ +import asyncio +import re +from enum import Enum +from typing import Any + +from google.oauth2.credentials import Credentials +from googleapiclient.discovery import build +from gravitas_md2gdocs import to_requests + +from backend.blocks.google._drive import GoogleDriveFile, GoogleDriveFileField +from backend.data.block import ( + Block, + BlockCategory, + BlockOutput, + BlockSchemaInput, + BlockSchemaOutput, +) +from backend.data.model import SchemaField +from backend.util.settings import Settings + +from ._auth import ( + GOOGLE_OAUTH_IS_CONFIGURED, + TEST_CREDENTIALS, + TEST_CREDENTIALS_INPUT, + GoogleCredentials, + GoogleCredentialsField, + GoogleCredentialsInput, +) + +settings = Settings() +GOOGLE_DOCS_DISABLED = not GOOGLE_OAUTH_IS_CONFIGURED + + +# ============ Enums ============ + + +class PublicAccessRole(str, Enum): + READER = "reader" + COMMENTER = "commenter" + + +class ShareRole(str, Enum): + READER = "reader" + WRITER = "writer" + COMMENTER = "commenter" + + +# ============ Helper Functions ============ + + +def _build_docs_service(credentials: GoogleCredentials): + """Build Google Docs API service.""" + creds = Credentials( + token=( + credentials.access_token.get_secret_value() + if credentials.access_token + else None + ), + refresh_token=( + credentials.refresh_token.get_secret_value() + if credentials.refresh_token + else None + ), + token_uri="https://oauth2.googleapis.com/token", + client_id=settings.secrets.google_client_id, + client_secret=settings.secrets.google_client_secret, + scopes=credentials.scopes, + ) + return build("docs", "v1", credentials=creds, cache_discovery=False) + + +def _build_drive_service(credentials: GoogleCredentials): + """Build Google Drive API service for file operations.""" + creds = Credentials( + token=( + credentials.access_token.get_secret_value() + if credentials.access_token + else None + ), + refresh_token=( + credentials.refresh_token.get_secret_value() + if credentials.refresh_token + else None + ), + token_uri="https://oauth2.googleapis.com/token", + client_id=settings.secrets.google_client_id, + client_secret=settings.secrets.google_client_secret, + scopes=credentials.scopes, + ) + return build("drive", "v3", credentials=creds, cache_discovery=False) + + +def _validate_document_file(file: GoogleDriveFile) -> str | None: + """Validate that a file is a Google Doc.""" + if not file.id: + return "No document ID provided" + if file.mime_type and file.mime_type != "application/vnd.google-apps.document": + return f"File is not a Google Doc (type: {file.mime_type})" + return None + + +def _parse_hex_color_to_rgb_floats(value: str) -> tuple[float, float, float] | None: + """ + Parse a CSS-like hex color string into normalized RGB floats. + + Supports: + - #RGB / RGB (shorthand) + - #RRGGBB / RRGGBB + + Returns None for malformed inputs. + """ + if not value: + return None + + raw = value.strip() + if raw.startswith("#"): + raw = raw[1:] + + if not re.fullmatch(r"[0-9a-fA-F]{3}([0-9a-fA-F]{3})?", raw): + return None + + if len(raw) == 3: + raw = "".join(ch * 2 for ch in raw) + + r = int(raw[0:2], 16) / 255.0 + g = int(raw[2:4], 16) / 255.0 + b = int(raw[4:6], 16) / 255.0 + return (r, g, b) + + +def _get_document_end_index(service, document_id: str) -> int: + """Get the index at the end of the document body.""" + doc = service.documents().get(documentId=document_id).execute() + body = doc.get("body", {}) + content = body.get("content", []) + if content: + last_element = content[-1] + return last_element.get("endIndex", 1) - 1 + return 1 + + +def _extract_text_from_content(content: list[dict]) -> str: + """Extract plain text from document content structure.""" + text_parts = [] + for element in content: + if "paragraph" in element: + for elem in element["paragraph"].get("elements", []): + if "textRun" in elem: + text_parts.append(elem["textRun"].get("content", "")) + elif "table" in element: + for row in element["table"].get("tableRows", []): + for cell in row.get("tableCells", []): + cell_content = cell.get("content", []) + text_parts.append(_extract_text_from_content(cell_content)) + return "".join(text_parts) + + +# ============ Document Output Helper ============ + + +def _make_document_output(file: GoogleDriveFile) -> GoogleDriveFile: + """Create standardized document output for chaining.""" + return GoogleDriveFile( + id=file.id, + name=file.name, + mimeType="application/vnd.google-apps.document", + url=f"https://docs.google.com/document/d/{file.id}/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=file.credentials_id, + ) + + +# ============ Blocks ============ + + +class GoogleDocsReadBlock(Block): + """Read content from a Google Doc.""" + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc to read", + allowed_views=["DOCUMENTS"], + ) + + class Output(BlockSchemaOutput): + text: str = SchemaField(description="Plain text content of the document") + title: str = SchemaField(description="Document title") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if read failed") + + def __init__(self): + super().__init__( + id="420a2b3c-5db2-4bda-82bc-a68a862a3d55", + description="Read text content from a Google Doc", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsReadBlock.Input, + output_schema=GoogleDocsReadBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("text", "Hello World\nThis is a test document.\n"), + ("title", "Test Document"), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_read_document": lambda *args, **kwargs: { + "text": "Hello World\nThis is a test document.\n", + "title": "Test Document", + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._read_document, + service, + input_data.document.id, + ) + yield "text", result["text"] + yield "title", result["title"] + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to read document: {str(e)}" + + def _read_document(self, service, document_id: str) -> dict: + doc = service.documents().get(documentId=document_id).execute() + title = doc.get("title", "") + body = doc.get("body", {}) + content = body.get("content", []) + text = _extract_text_from_content(content) + return {"text": text, "title": title} + + +class GoogleDocsCreateBlock(Block): + """Create a new Google Doc.""" + + class Input(BlockSchemaInput): + credentials: GoogleCredentialsInput = GoogleCredentialsField( + ["https://www.googleapis.com/auth/drive.file"] + ) + title: str = SchemaField(description="Title for the new document") + initial_content: str = SchemaField( + default="", + description="Optional initial text content", + ) + + class Output(BlockSchemaOutput): + document: GoogleDriveFile = SchemaField(description="The created document") + document_id: str = SchemaField(description="ID of the created document") + document_url: str = SchemaField(description="URL to open the document") + error: str = SchemaField(description="Error message if creation failed") + + def __init__(self): + super().__init__( + id="d430d941-cf81-4f84-8b19-2e3f670b2fca", + description="Create a new Google Doc", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsCreateBlock.Input, + output_schema=GoogleDocsCreateBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "credentials": TEST_CREDENTIALS_INPUT, + "title": "My New Document", + "initial_content": "Hello, this is the initial content.", + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ( + "document", + GoogleDriveFile( + id="new_doc_123", + name="My New Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/new_doc_123/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=TEST_CREDENTIALS_INPUT["id"], + ), + ), + ("document_id", "new_doc_123"), + ("document_url", "https://docs.google.com/document/d/new_doc_123/edit"), + ], + test_mock={ + "_create_document": lambda *args, **kwargs: { + "document_id": "new_doc_123", + "document_url": "https://docs.google.com/document/d/new_doc_123/edit", + "title": "My New Document", + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.title: + yield "error", "Document title is required" + return + + try: + drive_service = _build_drive_service(credentials) + docs_service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._create_document, + drive_service, + docs_service, + input_data.title, + input_data.initial_content, + ) + doc_id = result["document_id"] + doc_url = result["document_url"] + yield "document", GoogleDriveFile( + id=doc_id, + name=input_data.title, + mimeType="application/vnd.google-apps.document", + url=doc_url, + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=input_data.credentials.id, + ) + yield "document_id", doc_id + yield "document_url", doc_url + except Exception as e: + yield "error", f"Failed to create document: {str(e)}" + + def _create_document( + self, drive_service, docs_service, title: str, initial_content: str + ) -> dict: + # Create the document + file_metadata = { + "name": title, + "mimeType": "application/vnd.google-apps.document", + } + result = drive_service.files().create(body=file_metadata).execute() + document_id = result.get("id") + document_url = f"https://docs.google.com/document/d/{document_id}/edit" + + # Add initial content if provided + if initial_content: + requests = [ + { + "insertText": { + "location": {"index": 1}, + "text": initial_content, + } + } + ] + docs_service.documents().batchUpdate( + documentId=document_id, body={"requests": requests} + ).execute() + + return { + "document_id": document_id, + "document_url": document_url, + "title": title, + } + + +class GoogleDocsAppendPlainTextBlock(Block): + """Append plain text to the end of a Google Doc (no formatting).""" + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc to append to", + allowed_views=["DOCUMENTS"], + ) + text: str = SchemaField( + description="Plain text to append (no formatting applied)" + ) + add_newline: bool = SchemaField( + default=True, + description="Add a newline before the appended text", + ) + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result of the append operation") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if append failed") + + def __init__(self): + super().__init__( + id="ddc29d9f-78dc-4682-8787-c8a76f00cf38", + description="Append plain text to the end of a Google Doc (no formatting applied)", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsAppendPlainTextBlock.Input, + output_schema=GoogleDocsAppendPlainTextBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "text": "This is appended text.", + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("result", {"success": True, "characters_added": 23}), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_append_text": lambda *args, **kwargs: { + "success": True, + "characters_added": 23, + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._append_text, + service, + input_data.document.id, + input_data.text, + input_data.add_newline, + ) + yield "result", result + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to append text: {str(e)}" + + def _append_text( + self, service, document_id: str, text: str, add_newline: bool + ) -> dict: + end_index = _get_document_end_index(service, document_id) + text_to_insert = ("\n" if add_newline else "") + text + + requests = [ + { + "insertText": { + "location": {"index": end_index}, + "text": text_to_insert, + } + } + ] + + service.documents().batchUpdate( + documentId=document_id, body={"requests": requests} + ).execute() + + return {"success": True, "characters_added": len(text_to_insert)} + + +class GoogleDocsInsertPlainTextBlock(Block): + """Insert plain text at a specific position in a Google Doc (no formatting).""" + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc to insert into", + allowed_views=["DOCUMENTS"], + ) + text: str = SchemaField( + description="Plain text to insert (no formatting applied)" + ) + index: int = SchemaField( + default=1, + description="Position index to insert at (1 = start of document)", + ) + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result of the insert operation") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if insert failed") + + def __init__(self): + super().__init__( + id="0443fdbc-ebb0-49a4-a3ea-6ace9c14da22", + description="Insert plain text at a specific position in a Google Doc (no formatting applied)", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsInsertPlainTextBlock.Input, + output_schema=GoogleDocsInsertPlainTextBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "text": "Inserted text here. ", + "index": 1, + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("result", {"success": True, "characters_inserted": 20}), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_insert_text": lambda *args, **kwargs: { + "success": True, + "characters_inserted": 20, + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._insert_text, + service, + input_data.document.id, + input_data.text, + input_data.index, + ) + yield "result", result + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to insert text: {str(e)}" + + def _insert_text(self, service, document_id: str, text: str, index: int) -> dict: + requests = [ + { + "insertText": { + "location": {"index": max(1, index)}, + "text": text, + } + } + ] + + service.documents().batchUpdate( + documentId=document_id, body={"requests": requests} + ).execute() + + return {"success": True, "characters_inserted": len(text)} + + +class GoogleDocsFindReplacePlainTextBlock(Block): + """Find and replace plain text in a Google Doc (no formatting applied to replacement).""" + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc", + allowed_views=["DOCUMENTS"], + ) + find_text: str = SchemaField(description="Plain text to find") + replace_text: str = SchemaField( + description="Plain text to replace with (no formatting applied)" + ) + match_case: bool = SchemaField( + default=False, + description="Match case when finding text", + ) + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result with replacement count") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if operation failed") + + def __init__(self): + super().__init__( + id="e5046ee2-b094-418e-a25e-c0f90c91721c", + description="Find and replace plain text in a Google Doc (no formatting applied to replacement)", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsFindReplacePlainTextBlock.Input, + output_schema=GoogleDocsFindReplacePlainTextBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "find_text": "old text", + "replace_text": "new text", + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("result", {"success": True, "replacements_made": 3}), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_find_replace": lambda *args, **kwargs: { + "success": True, + "replacements_made": 3, + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._find_replace, + service, + input_data.document.id, + input_data.find_text, + input_data.replace_text, + input_data.match_case, + ) + yield "result", result + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to find/replace: {str(e)}" + + def _find_replace( + self, + service, + document_id: str, + find_text: str, + replace_text: str, + match_case: bool, + ) -> dict: + requests = [ + { + "replaceAllText": { + "containsText": { + "text": find_text, + "matchCase": match_case, + }, + "replaceText": replace_text, + } + } + ] + + response = ( + service.documents() + .batchUpdate(documentId=document_id, body={"requests": requests}) + .execute() + ) + + # Get replacement count from response + replies = response.get("replies", []) + replacements = 0 + if replies and "replaceAllText" in replies[0]: + replacements = replies[0]["replaceAllText"].get("occurrencesChanged", 0) + + return {"success": True, "replacements_made": replacements} + + +class GoogleDocsGetMetadataBlock(Block): + """Get metadata about a Google Doc.""" + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc", + allowed_views=["DOCUMENTS"], + ) + + class Output(BlockSchemaOutput): + title: str = SchemaField(description="Document title") + document_id: str = SchemaField(description="Document ID") + revision_id: str = SchemaField(description="Current revision ID") + document_url: str = SchemaField(description="URL to open the document") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if operation failed") + + def __init__(self): + super().__init__( + id="100bc806-acbf-4dc5-a3a2-998026b96516", + description="Get metadata about a Google Doc", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsGetMetadataBlock.Input, + output_schema=GoogleDocsGetMetadataBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("title", "Test Document"), + ("document_id", "1abc123def456"), + ("revision_id", "rev_123"), + ( + "document_url", + "https://docs.google.com/document/d/1abc123def456/edit", + ), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_get_metadata": lambda *args, **kwargs: { + "title": "Test Document", + "revision_id": "rev_123", + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._get_metadata, + service, + input_data.document.id, + ) + yield "title", result["title"] + yield "document_id", input_data.document.id + yield "revision_id", result["revision_id"] + yield "document_url", f"https://docs.google.com/document/d/{input_data.document.id}/edit" + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to get metadata: {str(e)}" + + def _get_metadata(self, service, document_id: str) -> dict: + doc = service.documents().get(documentId=document_id).execute() + return { + "title": doc.get("title", ""), + "revision_id": doc.get("revisionId", ""), + } + + +class GoogleDocsInsertTableBlock(Block): + """Insert a table into a Google Doc, optionally with content.""" + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc", + allowed_views=["DOCUMENTS"], + ) + rows: int = SchemaField( + default=3, + description="Number of rows (ignored if content provided)", + ) + columns: int = SchemaField( + default=3, + description="Number of columns (ignored if content provided)", + ) + content: list[list[str]] = SchemaField( + default=[], + description="Optional 2D array of cell content, e.g. [['Header1', 'Header2'], ['Row1Col1', 'Row1Col2']]. If provided, rows/columns are derived from this.", + ) + index: int = SchemaField( + default=0, + description="Position to insert table (0 = end of document)", + ) + format_as_markdown: bool = SchemaField( + default=False, + description="Format cell content as Markdown (headers, bold, links, etc.)", + ) + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result of table insertion") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if operation failed") + + def __init__(self): + super().__init__( + id="e104b3ab-dfef-45f9-9702-14e950988f53", + description="Insert a table into a Google Doc, optionally with content and Markdown formatting", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsInsertTableBlock.Input, + output_schema=GoogleDocsInsertTableBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "content": [["Header1", "Header2"], ["Row1Col1", "Row1Col2"]], + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ( + "result", + { + "success": True, + "rows": 2, + "columns": 2, + "cells_populated": 4, + "cells_found": 4, + }, + ), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_insert_table": lambda *args, **kwargs: { + "success": True, + "rows": 2, + "columns": 2, + "cells_populated": 4, + "cells_found": 4, + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + # Determine rows/columns from content if provided + content = input_data.content + + # Check if content is valid: + # 1. Has at least one row with at least one cell (even if empty string) + # 2. Has at least one non-empty cell value + has_valid_structure = bool(content and any(len(row) > 0 for row in content)) + has_content = has_valid_structure and any( + cell for row in content for cell in row + ) + + if has_content: + # Use content dimensions - filter out empty rows for row count, + # use max column count across all rows + rows = len(content) + columns = max(len(row) for row in content) + else: + # No valid content - use explicit rows/columns, clear content + rows = input_data.rows + columns = input_data.columns + content = [] # Clear so we skip population step + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._insert_table, + service, + input_data.document.id, + rows, + columns, + input_data.index, + content, + input_data.format_as_markdown, + ) + yield "result", result + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to insert table: {str(e)}" + + def _insert_table( + self, + service, + document_id: str, + rows: int, + columns: int, + index: int, + content: list[list[str]], + format_as_markdown: bool, + ) -> dict: + # If index is 0, insert at end of document + if index == 0: + index = _get_document_end_index(service, document_id) + + # Insert the empty table structure + requests = [ + { + "insertTable": { + "rows": rows, + "columns": columns, + "location": {"index": index}, + } + } + ] + + service.documents().batchUpdate( + documentId=document_id, body={"requests": requests} + ).execute() + + # If no content provided, we're done + if not content: + return {"success": True, "rows": rows, "columns": columns} + + # Fetch the document to find cell indexes + doc = service.documents().get(documentId=document_id).execute() + body_content = doc.get("body", {}).get("content", []) + + # Find all tables and pick the one we just inserted + # (the one with highest startIndex that's >= our insert point, or the last one if inserted at end) + tables_found = [] + for element in body_content: + if "table" in element: + tables_found.append(element) + + if not tables_found: + return { + "success": True, + "rows": rows, + "columns": columns, + "warning": "Table created but could not find it to populate", + } + + # If we inserted at end (index was high), take the last table + # Otherwise, take the first table at or after our insert index + table_element = None + # Heuristic: rows * columns * 2 estimates the minimum index space a table + # occupies (each cell has at least a start index and structural overhead). + # This helps determine if our insert point was near the document end. + estimated_table_size = rows * columns * 2 + if ( + index + >= _get_document_end_index(service, document_id) - estimated_table_size + ): + # Likely inserted at end - use last table + table_element = tables_found[-1] + else: + for tbl in tables_found: + if tbl.get("startIndex", 0) >= index: + table_element = tbl + break + if not table_element: + table_element = tables_found[-1] + + # Extract cell start indexes from the table structure + # Structure: table -> tableRows -> tableCells -> content[0] -> startIndex + cell_positions: list[tuple[int, int, int]] = [] # (row, col, start_index) + table_data = table_element.get("table", {}) + table_rows_list = table_data.get("tableRows", []) + + for row_idx, table_row in enumerate(table_rows_list): + cells = table_row.get("tableCells", []) + for col_idx, cell in enumerate(cells): + cell_content = cell.get("content", []) + if cell_content: + # Get the start index of the first element in the cell + first_element = cell_content[0] + cell_start = first_element.get("startIndex") + if cell_start is not None: + cell_positions.append((row_idx, col_idx, cell_start)) + + if not cell_positions: + return { + "success": True, + "rows": rows, + "columns": columns, + "warning": f"Table created but could not extract cell positions. Table has {len(table_rows_list)} rows.", + } + + # Sort by index descending so we can insert in reverse order + # (inserting later content first preserves earlier indexes) + cell_positions.sort(key=lambda x: x[2], reverse=True) + + cells_populated = 0 + + if format_as_markdown: + # Markdown formatting: process each cell individually since + # gravitas-md2gdocs requests may have complex interdependencies + for row_idx, col_idx, cell_start in cell_positions: + if row_idx < len(content) and col_idx < len(content[row_idx]): + cell_text = content[row_idx][col_idx] + if not cell_text: + continue + md_requests = to_requests(cell_text, start_index=cell_start) + if md_requests: + service.documents().batchUpdate( + documentId=document_id, body={"requests": md_requests} + ).execute() + cells_populated += 1 + else: + # Plain text: batch all insertions into a single API call + # Cells are sorted by index descending, so earlier requests + # don't affect indices of later ones + all_text_requests = [] + for row_idx, col_idx, cell_start in cell_positions: + if row_idx < len(content) and col_idx < len(content[row_idx]): + cell_text = content[row_idx][col_idx] + if not cell_text: + continue + all_text_requests.append( + { + "insertText": { + "location": {"index": cell_start}, + "text": cell_text, + } + } + ) + cells_populated += 1 + + if all_text_requests: + service.documents().batchUpdate( + documentId=document_id, body={"requests": all_text_requests} + ).execute() + + return { + "success": True, + "rows": rows, + "columns": columns, + "cells_populated": cells_populated, + "cells_found": len(cell_positions), + } + + +class GoogleDocsInsertPageBreakBlock(Block): + """Insert a page break into a Google Doc.""" + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc", + allowed_views=["DOCUMENTS"], + ) + index: int = SchemaField( + default=0, + description="Position to insert page break (0 = end of document)", + ) + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result of page break insertion") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if operation failed") + + def __init__(self): + super().__init__( + id="f199e674-803b-4ee8-8bbf-172e6512190b", + description="Insert a page break into a Google Doc", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsInsertPageBreakBlock.Input, + output_schema=GoogleDocsInsertPageBreakBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("result", {"success": True}), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_insert_page_break": lambda *args, **kwargs: {"success": True}, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._insert_page_break, + service, + input_data.document.id, + input_data.index, + ) + yield "result", result + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to insert page break: {str(e)}" + + def _insert_page_break(self, service, document_id: str, index: int) -> dict: + if index == 0: + index = _get_document_end_index(service, document_id) + + requests = [ + { + "insertPageBreak": { + "location": {"index": index}, + } + } + ] + + service.documents().batchUpdate( + documentId=document_id, body={"requests": requests} + ).execute() + + return {"success": True} + + +class GoogleDocsDeleteContentBlock(Block): + """Delete a range of content from a Google Doc.""" + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc", + allowed_views=["DOCUMENTS"], + ) + start_index: int = SchemaField( + description="Start index of content to delete (must be >= 1, as index 0 is a section break)", + ge=1, + ) + end_index: int = SchemaField(description="End index of content to delete") + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result of delete operation") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if operation failed") + + def __init__(self): + super().__init__( + id="5f9f9fa4-9071-4028-97c2-9d15fb422dc5", + description="Delete a range of content from a Google Doc", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsDeleteContentBlock.Input, + output_schema=GoogleDocsDeleteContentBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "start_index": 10, + "end_index": 50, + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("result", {"success": True, "characters_deleted": 40}), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_delete_content": lambda *args, **kwargs: { + "success": True, + "characters_deleted": 40, + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + if input_data.start_index >= input_data.end_index: + yield "error", "Start index must be less than end index" + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._delete_content, + service, + input_data.document.id, + input_data.start_index, + input_data.end_index, + ) + yield "result", result + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to delete content: {str(e)}" + + def _delete_content( + self, service, document_id: str, start_index: int, end_index: int + ) -> dict: + requests = [ + { + "deleteContentRange": { + "range": { + "startIndex": start_index, + "endIndex": end_index, + } + } + } + ] + + service.documents().batchUpdate( + documentId=document_id, body={"requests": requests} + ).execute() + + return {"success": True, "characters_deleted": end_index - start_index} + + +class ExportFormat(str, Enum): + PDF = "application/pdf" + DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + ODT = "application/vnd.oasis.opendocument.text" + TXT = "text/plain" + HTML = "text/html" + EPUB = "application/epub+zip" + RTF = "application/rtf" + + +class GoogleDocsExportBlock(Block): + """Export a Google Doc to various formats.""" + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc to export", + allowed_views=["DOCUMENTS"], + ) + format: ExportFormat = SchemaField( + default=ExportFormat.PDF, + description="Export format", + ) + + class Output(BlockSchemaOutput): + content: str = SchemaField( + description="Exported content (base64 encoded for binary formats)" + ) + mime_type: str = SchemaField(description="MIME type of exported content") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if export failed") + + def __init__(self): + super().__init__( + id="e32d5642-7b51-458c-bd83-75ff96fec299", + description="Export a Google Doc to PDF, Word, text, or other formats", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsExportBlock.Input, + output_schema=GoogleDocsExportBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "format": ExportFormat.TXT, + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("content", "This is the document content as plain text."), + ("mime_type", "text/plain"), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_export_document": lambda *args, **kwargs: { + "content": "This is the document content as plain text.", + "mime_type": "text/plain", + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + try: + drive_service = _build_drive_service(credentials) + result = await asyncio.to_thread( + self._export_document, + drive_service, + input_data.document.id, + input_data.format.value, + ) + yield "content", result["content"] + yield "mime_type", result["mime_type"] + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to export document: {str(e)}" + + def _export_document(self, service, document_id: str, mime_type: str) -> dict: + import base64 + + response = ( + service.files().export(fileId=document_id, mimeType=mime_type).execute() + ) + + # For text formats, return as string; for binary, base64 encode + if mime_type in ["text/plain", "text/html"]: + content = ( + response.decode("utf-8") if isinstance(response, bytes) else response + ) + else: + content = base64.b64encode(response).decode("utf-8") + + return {"content": content, "mime_type": mime_type} + + +class GoogleDocsFormatTextBlock(Block): + """Apply formatting to text in a Google Doc.""" + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc", + allowed_views=["DOCUMENTS"], + ) + start_index: int = SchemaField( + description="Start index of text to format (must be >= 1, as index 0 is a section break)", + ge=1, + ) + end_index: int = SchemaField(description="End index of text to format") + bold: bool = SchemaField( + default=False, + description="Make text bold", + ) + italic: bool = SchemaField( + default=False, + description="Make text italic", + ) + underline: bool = SchemaField( + default=False, + description="Underline text", + ) + font_size: int = SchemaField( + default=0, + description="Font size in points (0 = no change)", + ) + foreground_color: str = SchemaField( + default="", + description="Text color as hex (e.g., #FF0000 for red)", + ) + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result of format operation") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if operation failed") + + def __init__(self): + super().__init__( + id="04c38a7e-7ee5-4e1a-86c1-9727123577bc", + description="Apply formatting (bold, italic, color, etc.) to text in a Google Doc", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsFormatTextBlock.Input, + output_schema=GoogleDocsFormatTextBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "start_index": 2, + "end_index": 10, + "bold": True, + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("result", {"success": True}), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_format_text": lambda *args, **kwargs: {"success": True}, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + if input_data.start_index >= input_data.end_index: + yield "error", "Start index must be less than end index" + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._format_text, + service, + input_data.document.id, + input_data.start_index, + input_data.end_index, + input_data.bold, + input_data.italic, + input_data.underline, + input_data.font_size, + input_data.foreground_color, + ) + yield "result", result + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to format text: {str(e)}" + + def _format_text( + self, + service, + document_id: str, + start_index: int, + end_index: int, + bold: bool, + italic: bool, + underline: bool, + font_size: int, + foreground_color: str, + ) -> dict: + text_style: dict[str, Any] = {} + fields = [] + + if bold: + text_style["bold"] = True + fields.append("bold") + if italic: + text_style["italic"] = True + fields.append("italic") + if underline: + text_style["underline"] = True + fields.append("underline") + if font_size > 0: + text_style["fontSize"] = {"magnitude": font_size, "unit": "PT"} + fields.append("fontSize") + if foreground_color: + rgb = _parse_hex_color_to_rgb_floats(foreground_color) + if rgb is None: + if not fields: + return { + "success": False, + "message": ( + f"Invalid foreground_color: {foreground_color!r}. " + "Expected hex like #RGB or #RRGGBB." + ), + } + # Ignore invalid color, but still apply other formatting. + # This avoids failing the whole operation due to a single bad value. + warning = ( + f"Ignored invalid foreground_color: {foreground_color!r}. " + "Expected hex like #RGB or #RRGGBB." + ) + else: + r, g, b = rgb + text_style["foregroundColor"] = { + "color": {"rgbColor": {"red": r, "green": g, "blue": b}} + } + fields.append("foregroundColor") + warning = None + else: + warning = None + + if not fields: + return {"success": True, "message": "No formatting options specified"} + + requests = [ + { + "updateTextStyle": { + "range": {"startIndex": start_index, "endIndex": end_index}, + "textStyle": text_style, + "fields": ",".join(fields), + } + } + ] + + service.documents().batchUpdate( + documentId=document_id, body={"requests": requests} + ).execute() + + if warning: + return {"success": True, "warning": warning} + return {"success": True} + + +class GoogleDocsShareBlock(Block): + """Share a Google Doc with specific users.""" + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc to share", + allowed_views=["DOCUMENTS"], + ) + email: str = SchemaField( + default="", + description="Email address to share with. Leave empty for link sharing.", + ) + role: ShareRole = SchemaField( + default=ShareRole.READER, + description="Permission role for the user", + ) + send_notification: bool = SchemaField( + default=True, + description="Send notification email to the user", + ) + message: str = SchemaField( + default="", + description="Optional message to include in notification email", + ) + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result of the share operation") + share_link: str = SchemaField(description="Link to the document") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if share failed") + + def __init__(self): + super().__init__( + id="4e7ec771-4cc8-4eb7-ae3d-46377ecdb5d2", + description="Share a Google Doc with specific users", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsShareBlock.Input, + output_schema=GoogleDocsShareBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "email": "test@example.com", + "role": "reader", + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("result", {"success": True}), + ("share_link", "https://docs.google.com/document/d/1abc123def456/edit"), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_share_document": lambda *args, **kwargs: { + "success": True, + "share_link": "https://docs.google.com/document/d/1abc123def456/edit", + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + try: + service = _build_drive_service(credentials) + result = await asyncio.to_thread( + self._share_document, + service, + input_data.document.id, + input_data.email, + input_data.role, + input_data.send_notification, + input_data.message, + ) + yield "result", {"success": True} + yield "share_link", result["share_link"] + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to share document: {str(e)}" + + def _share_document( + self, + service, + document_id: str, + email: str, + role: ShareRole, + send_notification: bool, + message: str, + ) -> dict: + share_link = f"https://docs.google.com/document/d/{document_id}/edit" + + if email: + # Share with specific user + permission = {"type": "user", "role": role.value, "emailAddress": email} + + kwargs: dict[str, Any] = { + "fileId": document_id, + "body": permission, + "sendNotificationEmail": send_notification, + } + if message: + kwargs["emailMessage"] = message + + service.permissions().create(**kwargs).execute() + else: + # Create "anyone with the link" permission for link sharing + permission = {"type": "anyone", "role": role.value} + service.permissions().create( + fileId=document_id, + body=permission, + ).execute() + + return {"success": True, "share_link": share_link} + + +class GoogleDocsSetPublicAccessBlock(Block): + """Make a Google Doc publicly accessible or private.""" + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc", + allowed_views=["DOCUMENTS"], + ) + public: bool = SchemaField( + default=True, + description="True to make public, False to make private", + ) + role: PublicAccessRole = SchemaField( + default=PublicAccessRole.READER, + description="Permission role for public access", + ) + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result of the operation") + share_link: str = SchemaField(description="Link to the document") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if operation failed") + + def __init__(self): + super().__init__( + id="d104f6e1-80af-4fe9-b5a1-3cab20081b6c", + description="Make a Google Doc public or private", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsSetPublicAccessBlock.Input, + output_schema=GoogleDocsSetPublicAccessBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "public": True, + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("result", {"success": True, "is_public": True}), + ( + "share_link", + "https://docs.google.com/document/d/1abc123def456/edit?usp=sharing", + ), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_set_public_access": lambda *args, **kwargs: { + "success": True, + "is_public": True, + "share_link": "https://docs.google.com/document/d/1abc123def456/edit?usp=sharing", + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + try: + service = _build_drive_service(credentials) + result = await asyncio.to_thread( + self._set_public_access, + service, + input_data.document.id, + input_data.public, + input_data.role, + ) + yield "result", {"success": True, "is_public": result["is_public"]} + yield "share_link", result["share_link"] + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to set public access: {str(e)}" + + def _set_public_access( + self, service, document_id: str, public: bool, role: PublicAccessRole + ) -> dict: + share_link = f"https://docs.google.com/document/d/{document_id}/edit" + + if public: + permission = {"type": "anyone", "role": role.value} + service.permissions().create(fileId=document_id, body=permission).execute() + share_link += "?usp=sharing" + else: + permissions = service.permissions().list(fileId=document_id).execute() + for perm in permissions.get("permissions", []): + if perm.get("type") == "anyone": + service.permissions().delete( + fileId=document_id, permissionId=perm["id"] + ).execute() + + return {"success": True, "is_public": public, "share_link": share_link} + + +# ============ Markdown Blocks ============ + + +class GoogleDocsAppendMarkdownBlock(Block): + """Append Markdown content to the end of a Google Doc. + + Converts Markdown to Google Docs formatting, supporting: + - Headers (H1-H6) + - Bold, italic, strikethrough + - Inline code and code blocks + - Links + - Bulleted and numbered lists + - Blockquotes + + Perfect for AI agents that generate Markdown output. + """ + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc to append to", + allowed_views=["DOCUMENTS"], + ) + markdown: str = SchemaField( + description="Markdown content to append to the document" + ) + add_newline: bool = SchemaField( + default=True, + description="Add a newline before the appended content", + ) + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result of the append operation") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if operation failed") + + def __init__(self): + super().__init__( + id="60854b69-ecbd-4188-bd89-f7966a4d3b38", + description="Append Markdown content to the end of a Google Doc with full formatting - ideal for LLM/AI output", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsAppendMarkdownBlock.Input, + output_schema=GoogleDocsAppendMarkdownBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "markdown": "# Hello World\n\nThis is **bold** and *italic* text.\n\n- Item 1\n- Item 2", + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("result", {"success": True, "requests_count": 5}), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_append_markdown": lambda *args, **kwargs: { + "success": True, + "requests_count": 5, + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + if not input_data.markdown: + yield "error", "No markdown content provided" + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._append_markdown, + service, + input_data.document.id, + input_data.markdown, + input_data.add_newline, + ) + yield "result", result + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to append markdown: {str(e)}" + + def _append_markdown( + self, + service, + document_id: str, + markdown: str, + add_newline: bool, + ) -> dict: + end_index = _get_document_end_index(service, document_id) + + # Optionally add a newline before the content + if add_newline and end_index > 1: + newline_requests = [ + {"insertText": {"location": {"index": end_index}, "text": "\n"}} + ] + service.documents().batchUpdate( + documentId=document_id, body={"requests": newline_requests} + ).execute() + end_index += 1 + + # Convert markdown to Google Docs requests + requests = to_requests(markdown, start_index=end_index) + + if requests: + service.documents().batchUpdate( + documentId=document_id, body={"requests": requests} + ).execute() + + return {"success": True, "requests_count": len(requests)} + + +class GoogleDocsReplaceAllWithMarkdownBlock(Block): + """Replace entire Google Doc content with Markdown. + + Clears the document and inserts formatted Markdown content. + Supports headers, bold, italic, lists, links, code blocks, etc. + """ + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc to replace content in", + allowed_views=["DOCUMENTS"], + ) + markdown: str = SchemaField( + description="Markdown content to replace the document with" + ) + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result of the replace operation") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if operation failed") + + def __init__(self): + super().__init__( + id="b6cfb2de-5f0b-437c-b29d-45aebbda9c00", + description="Replace entire Google Doc content with formatted Markdown - ideal for LLM/AI output", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsReplaceAllWithMarkdownBlock.Input, + output_schema=GoogleDocsReplaceAllWithMarkdownBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "markdown": "# New Document\n\nThis replaces everything.", + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("result", {"success": True, "requests_count": 3}), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_replace_all_with_markdown": lambda *args, **kwargs: { + "success": True, + "requests_count": 3, + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + if not input_data.markdown: + yield "error", "No markdown content provided" + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._replace_all_with_markdown, + service, + input_data.document.id, + input_data.markdown, + ) + yield "result", result + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to replace document with markdown: {str(e)}" + + def _replace_all_with_markdown( + self, + service, + document_id: str, + markdown: str, + ) -> dict: + # Delete all existing content + doc_end = _get_document_end_index(service, document_id) + if doc_end > 1: + delete_requests = [ + { + "deleteContentRange": { + "range": {"startIndex": 1, "endIndex": doc_end} + } + } + ] + service.documents().batchUpdate( + documentId=document_id, body={"requests": delete_requests} + ).execute() + + # Insert markdown at beginning + requests = to_requests(markdown, start_index=1) + + if requests: + service.documents().batchUpdate( + documentId=document_id, body={"requests": requests} + ).execute() + + return {"success": True, "requests_count": len(requests)} + + +class GoogleDocsInsertMarkdownAtBlock(Block): + """Insert Markdown content at a specific position in a Google Doc. + + Converts Markdown to Google Docs formatting and inserts at the specified index. + """ + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc to insert into", + allowed_views=["DOCUMENTS"], + ) + markdown: str = SchemaField(description="Markdown content to insert") + index: int = SchemaField( + default=1, + description="Position index to insert at (1 = start of document)", + ge=1, + ) + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result of the insert operation") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if operation failed") + + def __init__(self): + super().__init__( + id="76e94b04-e02f-4981-8cb8-47ece1be18b4", + description="Insert formatted Markdown at a specific position in a Google Doc - ideal for LLM/AI output", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsInsertMarkdownAtBlock.Input, + output_schema=GoogleDocsInsertMarkdownAtBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "markdown": "## Inserted Section\n\nThis was inserted.", + "index": 1, + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ("result", {"success": True, "requests_count": 3}), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_insert_markdown_at": lambda *args, **kwargs: { + "success": True, + "requests_count": 3, + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + if not input_data.markdown: + yield "error", "No markdown content provided" + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._insert_markdown_at, + service, + input_data.document.id, + input_data.markdown, + input_data.index, + ) + yield "result", result + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to insert markdown: {str(e)}" + + def _insert_markdown_at( + self, + service, + document_id: str, + markdown: str, + index: int, + ) -> dict: + requests = to_requests(markdown, start_index=index) + + if requests: + service.documents().batchUpdate( + documentId=document_id, body={"requests": requests} + ).execute() + + return {"success": True, "requests_count": len(requests)} + + +class GoogleDocsReplaceRangeWithMarkdownBlock(Block): + """Replace a specific range (by index) in a Google Doc with Markdown. + + Deletes content between start and end indices, then inserts formatted Markdown. + """ + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc", + allowed_views=["DOCUMENTS"], + ) + markdown: str = SchemaField( + description="Markdown content to insert in place of the range" + ) + start_index: int = SchemaField( + description="Start index of the range to replace (must be >= 1)", + ge=1, + ) + end_index: int = SchemaField( + description="End index of the range to replace", + ) + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result of the replace operation") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if operation failed") + + def __init__(self): + super().__init__( + id="9e43a905-a918-4da0-8874-dfddd3c46953", + description="Replace a specific index range in a Google Doc with formatted Markdown - ideal for LLM/AI output", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsReplaceRangeWithMarkdownBlock.Input, + output_schema=GoogleDocsReplaceRangeWithMarkdownBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "markdown": "**Replaced content**", + "start_index": 10, + "end_index": 50, + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ( + "result", + {"success": True, "requests_count": 2, "characters_deleted": 40}, + ), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_replace_range_with_markdown": lambda *args, **kwargs: { + "success": True, + "requests_count": 2, + "characters_deleted": 40, + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + if not input_data.markdown: + yield "error", "No markdown content provided" + return + + if input_data.start_index >= input_data.end_index: + yield "error", "Start index must be less than end index" + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._replace_range_with_markdown, + service, + input_data.document.id, + input_data.markdown, + input_data.start_index, + input_data.end_index, + ) + yield "result", result + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to replace range with markdown: {str(e)}" + + def _replace_range_with_markdown( + self, + service, + document_id: str, + markdown: str, + start_index: int, + end_index: int, + ) -> dict: + # Delete the range first + delete_requests = [ + { + "deleteContentRange": { + "range": {"startIndex": start_index, "endIndex": end_index} + } + } + ] + service.documents().batchUpdate( + documentId=document_id, body={"requests": delete_requests} + ).execute() + + # Insert markdown at the start of the deleted range + requests = to_requests(markdown, start_index=start_index) + + if requests: + service.documents().batchUpdate( + documentId=document_id, body={"requests": requests} + ).execute() + + return { + "success": True, + "requests_count": len(requests), + "characters_deleted": end_index - start_index, + } + + +class GoogleDocsReplaceContentWithMarkdownBlock(Block): + """Find text in a Google Doc and replace it with formatted Markdown. + + Perfect for template workflows - use placeholders like {{INTRO}} or {{SUMMARY}} + and replace them with formatted Markdown content. + """ + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc", + allowed_views=["DOCUMENTS"], + ) + find_text: str = SchemaField( + description="Text to find and replace (e.g., '{{PLACEHOLDER}}' or any text)" + ) + markdown: str = SchemaField( + description="Markdown content to replace the found text with" + ) + match_case: bool = SchemaField( + default=False, + description="Match case when finding text", + ) + + class Output(BlockSchemaOutput): + result: dict = SchemaField(description="Result with replacement count") + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if operation failed") + + def __init__(self): + super().__init__( + id="2cc58467-90a9-4ef8-a7a7-700784f93b76", + description="Find text and replace it with formatted Markdown - ideal for LLM/AI output and templates", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsReplaceContentWithMarkdownBlock.Input, + output_schema=GoogleDocsReplaceContentWithMarkdownBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "find_text": "{{PLACEHOLDER}}", + "markdown": "# Replaced Header\n\nThis is the **replacement** content.", + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ( + "result", + {"success": True, "replacements_made": 1, "requests_count": 4}, + ), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_replace_content_with_markdown": lambda *args, **kwargs: { + "success": True, + "replacements_made": 1, + "requests_count": 4, + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + if not input_data.find_text: + yield "error", "No find text provided" + return + + if not input_data.markdown: + yield "error", "No markdown content provided" + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._replace_content_with_markdown, + service, + input_data.document.id, + input_data.find_text, + input_data.markdown, + input_data.match_case, + ) + yield "result", result + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to replace content with markdown: {str(e)}" + + def _find_text_positions( + self, service, document_id: str, find_text: str, match_case: bool + ) -> list[tuple[int, int]]: + """Find all positions of the search text using actual document indices. + + Iterates through document content and uses the real startIndex/endIndex + from text runs, rather than trying to map plain text offsets to indices. + """ + doc = service.documents().get(documentId=document_id).execute() + body = doc.get("body", {}) + content = body.get("content", []) + + positions = [] + search_text = find_text if match_case else find_text.lower() + + def search_in_content(elements: list[dict]) -> None: + """Recursively search through content elements.""" + for element in elements: + if "paragraph" in element: + for text_elem in element["paragraph"].get("elements", []): + if "textRun" in text_elem: + text_run = text_elem["textRun"] + text_content = text_run.get("content", "") + start_index = text_elem.get("startIndex", 0) + + # Search within this text run + text_to_search = ( + text_content if match_case else text_content.lower() + ) + offset = 0 + while True: + pos = text_to_search.find(search_text, offset) + if pos == -1: + break + # Calculate actual document indices + doc_start = start_index + pos + doc_end = doc_start + len(find_text) + positions.append((doc_start, doc_end)) + offset = pos + 1 + + elif "table" in element: + # Search within table cells + for row in element["table"].get("tableRows", []): + for cell in row.get("tableCells", []): + search_in_content(cell.get("content", [])) + + search_in_content(content) + return positions + + def _replace_content_with_markdown( + self, + service, + document_id: str, + find_text: str, + markdown: str, + match_case: bool, + ) -> dict: + # Find all positions of the text + positions = self._find_text_positions( + service, document_id, find_text, match_case + ) + + if not positions: + return {"success": True, "replacements_made": 0, "requests_count": 0} + + total_requests = 0 + replacements_made = 0 + + # Process in reverse order to maintain correct indices + for start_index, end_index in reversed(positions): + # Build combined request: delete first, then insert markdown + # Combining into single batchUpdate reduces API calls by half + combined_requests = [ + { + "deleteContentRange": { + "range": {"startIndex": start_index, "endIndex": end_index} + } + } + ] + + # Get markdown insert requests + md_requests = to_requests(markdown, start_index=start_index) + if md_requests: + combined_requests.extend(md_requests) + + # Execute delete + insert in single API call + service.documents().batchUpdate( + documentId=document_id, body={"requests": combined_requests} + ).execute() + + total_requests += len(combined_requests) + replacements_made += 1 + + return { + "success": True, + "replacements_made": replacements_made, + "requests_count": total_requests, + } + + +class GoogleDocsGetStructureBlock(Block): + """Get document structure with index positions for precise editing operations. + + Returns content segments with their start/end indexes, making it easy to + target specific parts of the document for insertion, deletion, or formatting. + """ + + class Input(BlockSchemaInput): + document: GoogleDriveFile = GoogleDriveFileField( + title="Document", + description="Select a Google Doc to analyze", + allowed_views=["DOCUMENTS"], + ) + detailed: bool = SchemaField( + default=False, + description="Return full hierarchical structure instead of flat segments", + ) + + class Output(BlockSchemaOutput): + segments: list[dict] = SchemaField( + description="Flat list of content segments with indexes (when detailed=False)" + ) + structure: dict = SchemaField( + description="Full hierarchical document structure (when detailed=True)" + ) + document: GoogleDriveFile = SchemaField(description="The document for chaining") + error: str = SchemaField(description="Error message if operation failed") + + def __init__(self): + super().__init__( + id="e0561cc1-2154-4abf-bd06-79509348a18e", + description="Get document structure with index positions for precise editing operations", + categories={BlockCategory.DATA}, + input_schema=GoogleDocsGetStructureBlock.Input, + output_schema=GoogleDocsGetStructureBlock.Output, + disabled=GOOGLE_DOCS_DISABLED, + test_input={ + "document": { + "id": "1abc123def456", + "name": "Test Document", + "mimeType": "application/vnd.google-apps.document", + }, + "detailed": False, + }, + test_credentials=TEST_CREDENTIALS, + test_output=[ + ( + "segments", + [ + { + "type": "paragraph", + "text": "Hello World", + "start_index": 1, + "end_index": 12, + }, + { + "type": "paragraph", + "text": "Second paragraph", + "start_index": 13, + "end_index": 29, + }, + ], + ), + ("structure", {}), + ( + "document", + GoogleDriveFile( + id="1abc123def456", + name="Test Document", + mimeType="application/vnd.google-apps.document", + url="https://docs.google.com/document/d/1abc123def456/edit", + iconUrl="https://www.gstatic.com/images/branding/product/1x/docs_48dp.png", + isFolder=False, + _credentials_id=None, + ), + ), + ], + test_mock={ + "_get_structure": lambda *args, **kwargs: { + "segments": [ + { + "type": "paragraph", + "text": "Hello World", + "start_index": 1, + "end_index": 12, + }, + { + "type": "paragraph", + "text": "Second paragraph", + "start_index": 13, + "end_index": 29, + }, + ], + "structure": {}, + }, + }, + ) + + async def run( + self, input_data: Input, *, credentials: GoogleCredentials, **kwargs + ) -> BlockOutput: + if not input_data.document: + yield "error", "No document selected" + return + + validation_error = _validate_document_file(input_data.document) + if validation_error: + yield "error", validation_error + return + + try: + service = _build_docs_service(credentials) + result = await asyncio.to_thread( + self._get_structure, + service, + input_data.document.id, + input_data.detailed, + ) + yield "segments", result["segments"] + yield "structure", result["structure"] + yield "document", _make_document_output(input_data.document) + except Exception as e: + yield "error", f"Failed to get document structure: {str(e)}" + + def _extract_paragraph_text(self, paragraph: dict) -> str: + """Extract plain text from a paragraph element.""" + text_parts = [] + for elem in paragraph.get("elements", []): + if "textRun" in elem: + text_parts.append(elem["textRun"].get("content", "")) + return "".join(text_parts).rstrip("\n") + + def _get_paragraph_style(self, paragraph: dict) -> dict: + """Get paragraph style information.""" + style = paragraph.get("paragraphStyle", {}) + named_style = style.get("namedStyleType", "NORMAL_TEXT") + + # Map named styles to heading levels + heading_map = { + "HEADING_1": 1, + "HEADING_2": 2, + "HEADING_3": 3, + "HEADING_4": 4, + "HEADING_5": 5, + "HEADING_6": 6, + } + + if named_style in heading_map: + return {"heading_level": heading_map[named_style]} + return {} + + def _process_table_detailed(self, table_element: dict) -> dict: + """Process table for detailed hierarchical output.""" + table = table_element.get("table", {}) + table_rows = table.get("tableRows", []) + + rows_data = [] + for table_row in table_rows: + cells_data = [] + for cell in table_row.get("tableCells", []): + cell_content = cell.get("content", []) + cell_text = "" + cell_start = None + cell_end = None + + for content_elem in cell_content: + if "paragraph" in content_elem: + cell_text += self._extract_paragraph_text( + content_elem["paragraph"] + ) + if cell_start is None: + cell_start = content_elem.get("startIndex") + cell_end = content_elem.get("endIndex") + + cells_data.append( + { + "text": cell_text, + "start_index": cell_start, + "end_index": cell_end, + } + ) + rows_data.append({"cells": cells_data}) + + return { + "type": "table", + "start_index": table_element.get("startIndex"), + "end_index": table_element.get("endIndex"), + "rows": rows_data, + "row_count": len(table_rows), + "column_count": table.get("columns", 0), + } + + def _get_structure(self, service, document_id: str, detailed: bool) -> dict: + doc = service.documents().get(documentId=document_id).execute() + body = doc.get("body", {}) + content = body.get("content", []) + + segments: list[dict] = [] + structure_body: list[dict] = [] + + for element in content: + start_index = element.get("startIndex") + end_index = element.get("endIndex") + + if "paragraph" in element: + paragraph = element["paragraph"] + text = self._extract_paragraph_text(paragraph) + style_info = self._get_paragraph_style(paragraph) + + # Determine segment type + if style_info.get("heading_level"): + seg_type = "heading" + segment = { + "type": seg_type, + "level": style_info["heading_level"], + "text": text, + "start_index": start_index, + "end_index": end_index, + } + else: + seg_type = "paragraph" + segment = { + "type": seg_type, + "text": text, + "start_index": start_index, + "end_index": end_index, + } + + # Skip empty paragraphs (just newlines) + if text.strip(): + segments.append(segment) + + if detailed: + detailed_seg = segment.copy() + detailed_seg["style"] = paragraph.get("paragraphStyle", {}) + structure_body.append(detailed_seg) + + elif "table" in element: + table = element.get("table", {}) + table_rows = table.get("tableRows", []) + + segment = { + "type": "table", + "rows": len(table_rows), + "columns": table.get("columns", 0), + "start_index": start_index, + "end_index": end_index, + } + segments.append(segment) + + if detailed: + structure_body.append(self._process_table_detailed(element)) + + elif "sectionBreak" in element: + # Skip section breaks in simple mode, include in detailed + if detailed: + structure_body.append( + { + "type": "section_break", + "start_index": start_index, + "end_index": end_index, + } + ) + + elif "tableOfContents" in element: + segment = { + "type": "table_of_contents", + "start_index": start_index, + "end_index": end_index, + } + segments.append(segment) + + if detailed: + structure_body.append(segment) + + result = { + "segments": segments, + "structure": {"body": structure_body} if detailed else {}, + } + + return result diff --git a/autogpt_platform/backend/backend/blocks/llm.py b/autogpt_platform/backend/backend/blocks/llm.py index c0e22d1667..5808c4eb11 100644 --- a/autogpt_platform/backend/backend/blocks/llm.py +++ b/autogpt_platform/backend/backend/blocks/llm.py @@ -92,8 +92,9 @@ class LlmModel(str, Enum, metaclass=LlmModelMeta): O1 = "o1" O1_MINI = "o1-mini" # GPT-5 models - GPT5 = "gpt-5-2025-08-07" + GPT5_2 = "gpt-5.2-2025-12-11" GPT5_1 = "gpt-5.1-2025-11-13" + GPT5 = "gpt-5-2025-08-07" GPT5_MINI = "gpt-5-mini-2025-08-07" GPT5_NANO = "gpt-5-nano-2025-08-07" GPT5_CHAT = "gpt-5-chat-latest" @@ -217,8 +218,9 @@ MODEL_METADATA = { LlmModel.O1: ModelMetadata("openai", 200000, 100000), # o1-2024-12-17 LlmModel.O1_MINI: ModelMetadata("openai", 128000, 65536), # o1-mini-2024-09-12 # GPT-5 models - LlmModel.GPT5: ModelMetadata("openai", 400000, 128000), + LlmModel.GPT5_2: ModelMetadata("openai", 400000, 128000), LlmModel.GPT5_1: ModelMetadata("openai", 400000, 128000), + LlmModel.GPT5: ModelMetadata("openai", 400000, 128000), LlmModel.GPT5_MINI: ModelMetadata("openai", 400000, 128000), LlmModel.GPT5_NANO: ModelMetadata("openai", 400000, 128000), LlmModel.GPT5_CHAT: ModelMetadata("openai", 400000, 16384), @@ -326,6 +328,8 @@ MODEL_METADATA = { LlmModel.V0_1_0_MD: ModelMetadata("v0", 128000, 64000), } +DEFAULT_LLM_MODEL = LlmModel.GPT5_2 + for model in LlmModel: if model not in MODEL_METADATA: raise ValueError(f"Missing MODEL_METADATA metadata for model: {model}") @@ -813,7 +817,7 @@ class AIStructuredResponseGeneratorBlock(AIBlockBase): ) model: LlmModel = SchemaField( title="LLM Model", - default=LlmModel.GPT4O, + default=DEFAULT_LLM_MODEL, description="The language model to use for answering the prompt.", advanced=False, ) @@ -878,7 +882,7 @@ class AIStructuredResponseGeneratorBlock(AIBlockBase): input_schema=AIStructuredResponseGeneratorBlock.Input, output_schema=AIStructuredResponseGeneratorBlock.Output, test_input={ - "model": LlmModel.GPT4O, + "model": DEFAULT_LLM_MODEL, "credentials": TEST_CREDENTIALS_INPUT, "expected_format": { "key1": "value1", @@ -1244,7 +1248,7 @@ class AITextGeneratorBlock(AIBlockBase): ) model: LlmModel = SchemaField( title="LLM Model", - default=LlmModel.GPT4O, + default=DEFAULT_LLM_MODEL, description="The language model to use for answering the prompt.", advanced=False, ) @@ -1340,7 +1344,7 @@ class AITextSummarizerBlock(AIBlockBase): ) model: LlmModel = SchemaField( title="LLM Model", - default=LlmModel.GPT4O, + default=DEFAULT_LLM_MODEL, description="The language model to use for summarizing the text.", ) focus: str = SchemaField( @@ -1557,7 +1561,7 @@ class AIConversationBlock(AIBlockBase): ) model: LlmModel = SchemaField( title="LLM Model", - default=LlmModel.GPT4O, + default=DEFAULT_LLM_MODEL, description="The language model to use for the conversation.", ) credentials: AICredentials = AICredentialsField() @@ -1595,7 +1599,7 @@ class AIConversationBlock(AIBlockBase): }, {"role": "user", "content": "Where was it played?"}, ], - "model": LlmModel.GPT4O, + "model": DEFAULT_LLM_MODEL, "credentials": TEST_CREDENTIALS_INPUT, }, test_credentials=TEST_CREDENTIALS, @@ -1658,7 +1662,7 @@ class AIListGeneratorBlock(AIBlockBase): ) model: LlmModel = SchemaField( title="LLM Model", - default=LlmModel.GPT4O, + default=DEFAULT_LLM_MODEL, description="The language model to use for generating the list.", advanced=True, ) @@ -1715,7 +1719,7 @@ class AIListGeneratorBlock(AIBlockBase): "drawing explorers to uncover its mysteries. Each planet showcases the limitless possibilities of " "fictional worlds." ), - "model": LlmModel.GPT4O, + "model": DEFAULT_LLM_MODEL, "credentials": TEST_CREDENTIALS_INPUT, "max_retries": 3, "force_json_output": False, diff --git a/autogpt_platform/backend/backend/blocks/smart_decision_maker.py b/autogpt_platform/backend/backend/blocks/smart_decision_maker.py index e2e5cfa3e4..751f6af37f 100644 --- a/autogpt_platform/backend/backend/blocks/smart_decision_maker.py +++ b/autogpt_platform/backend/backend/blocks/smart_decision_maker.py @@ -226,7 +226,7 @@ class SmartDecisionMakerBlock(Block): ) model: llm.LlmModel = SchemaField( title="LLM Model", - default=llm.LlmModel.GPT4O, + default=llm.DEFAULT_LLM_MODEL, description="The language model to use for answering the prompt.", advanced=False, ) diff --git a/autogpt_platform/backend/backend/blocks/test/test_blocks_dos_vulnerability.py b/autogpt_platform/backend/backend/blocks/test/test_blocks_dos_vulnerability.py index 87bf102099..389bb5c636 100644 --- a/autogpt_platform/backend/backend/blocks/test/test_blocks_dos_vulnerability.py +++ b/autogpt_platform/backend/backend/blocks/test/test_blocks_dos_vulnerability.py @@ -196,6 +196,15 @@ class TestXMLParserBlockSecurity: async for _ in block.run(XMLParserBlock.Input(input_xml=large_xml)): pass + async def test_rejects_text_outside_root(self): + """Ensure parser surfaces readable errors for invalid root text.""" + block = XMLParserBlock() + invalid_xml = "value trailing" + + with pytest.raises(ValueError, match="text outside the root element"): + async for _ in block.run(XMLParserBlock.Input(input_xml=invalid_xml)): + pass + class TestStoreMediaFileSecurity: """Test file storage security limits.""" diff --git a/autogpt_platform/backend/backend/blocks/test/test_llm.py b/autogpt_platform/backend/backend/blocks/test/test_llm.py index 090587767a..ea8760d313 100644 --- a/autogpt_platform/backend/backend/blocks/test/test_llm.py +++ b/autogpt_platform/backend/backend/blocks/test/test_llm.py @@ -28,7 +28,7 @@ class TestLLMStatsTracking: response = await llm.llm_call( credentials=llm.TEST_CREDENTIALS, - llm_model=llm.LlmModel.GPT4O, + llm_model=llm.DEFAULT_LLM_MODEL, prompt=[{"role": "user", "content": "Hello"}], max_tokens=100, ) @@ -65,7 +65,7 @@ class TestLLMStatsTracking: input_data = llm.AIStructuredResponseGeneratorBlock.Input( prompt="Test prompt", expected_format={"key1": "desc1", "key2": "desc2"}, - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, credentials=llm.TEST_CREDENTIALS_INPUT, # type: ignore # type: ignore ) @@ -109,7 +109,7 @@ class TestLLMStatsTracking: # Run the block input_data = llm.AITextGeneratorBlock.Input( prompt="Generate text", - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, credentials=llm.TEST_CREDENTIALS_INPUT, # type: ignore ) @@ -170,7 +170,7 @@ class TestLLMStatsTracking: input_data = llm.AIStructuredResponseGeneratorBlock.Input( prompt="Test prompt", expected_format={"key1": "desc1", "key2": "desc2"}, - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, credentials=llm.TEST_CREDENTIALS_INPUT, # type: ignore retry=2, ) @@ -228,7 +228,7 @@ class TestLLMStatsTracking: input_data = llm.AITextSummarizerBlock.Input( text=long_text, - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, credentials=llm.TEST_CREDENTIALS_INPUT, # type: ignore max_tokens=100, # Small chunks chunk_overlap=10, @@ -299,7 +299,7 @@ class TestLLMStatsTracking: # Test with very short text (should only need 1 chunk + 1 final summary) input_data = llm.AITextSummarizerBlock.Input( text="This is a short text.", - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, credentials=llm.TEST_CREDENTIALS_INPUT, # type: ignore max_tokens=1000, # Large enough to avoid chunking ) @@ -346,7 +346,7 @@ class TestLLMStatsTracking: {"role": "assistant", "content": "Hi there!"}, {"role": "user", "content": "How are you?"}, ], - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, credentials=llm.TEST_CREDENTIALS_INPUT, # type: ignore ) @@ -387,7 +387,7 @@ class TestLLMStatsTracking: # Run the block input_data = llm.AIListGeneratorBlock.Input( focus="test items", - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, credentials=llm.TEST_CREDENTIALS_INPUT, # type: ignore max_retries=3, ) @@ -469,7 +469,7 @@ class TestLLMStatsTracking: input_data = llm.AIStructuredResponseGeneratorBlock.Input( prompt="Test", expected_format={"result": "desc"}, - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, credentials=llm.TEST_CREDENTIALS_INPUT, # type: ignore ) @@ -513,7 +513,7 @@ class TestAITextSummarizerValidation: # Create input data input_data = llm.AITextSummarizerBlock.Input( text="Some text to summarize", - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, credentials=llm.TEST_CREDENTIALS_INPUT, # type: ignore style=llm.SummaryStyle.BULLET_POINTS, ) @@ -558,7 +558,7 @@ class TestAITextSummarizerValidation: # Create input data input_data = llm.AITextSummarizerBlock.Input( text="Some text to summarize", - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, credentials=llm.TEST_CREDENTIALS_INPUT, # type: ignore style=llm.SummaryStyle.BULLET_POINTS, max_tokens=1000, @@ -593,7 +593,7 @@ class TestAITextSummarizerValidation: # Create input data input_data = llm.AITextSummarizerBlock.Input( text="Some text to summarize", - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, credentials=llm.TEST_CREDENTIALS_INPUT, # type: ignore ) @@ -623,7 +623,7 @@ class TestAITextSummarizerValidation: # Create input data input_data = llm.AITextSummarizerBlock.Input( text="Some text to summarize", - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, credentials=llm.TEST_CREDENTIALS_INPUT, # type: ignore max_tokens=1000, ) @@ -654,7 +654,7 @@ class TestAITextSummarizerValidation: # Create input data input_data = llm.AITextSummarizerBlock.Input( text="Some text to summarize", - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, credentials=llm.TEST_CREDENTIALS_INPUT, # type: ignore ) diff --git a/autogpt_platform/backend/backend/blocks/test/test_smart_decision_maker.py b/autogpt_platform/backend/backend/blocks/test/test_smart_decision_maker.py index deff4278f9..c930fab37e 100644 --- a/autogpt_platform/backend/backend/blocks/test/test_smart_decision_maker.py +++ b/autogpt_platform/backend/backend/blocks/test/test_smart_decision_maker.py @@ -233,7 +233,7 @@ async def test_smart_decision_maker_tracks_llm_stats(): # Create test input input_data = SmartDecisionMakerBlock.Input( prompt="Should I continue with this task?", - model=llm_module.LlmModel.GPT4O, + model=llm_module.DEFAULT_LLM_MODEL, credentials=llm_module.TEST_CREDENTIALS_INPUT, # type: ignore agent_mode_max_iterations=0, ) @@ -335,7 +335,7 @@ async def test_smart_decision_maker_parameter_validation(): input_data = SmartDecisionMakerBlock.Input( prompt="Search for keywords", - model=llm_module.LlmModel.GPT4O, + model=llm_module.DEFAULT_LLM_MODEL, credentials=llm_module.TEST_CREDENTIALS_INPUT, # type: ignore retry=2, # Set retry to 2 for testing agent_mode_max_iterations=0, @@ -402,7 +402,7 @@ async def test_smart_decision_maker_parameter_validation(): input_data = SmartDecisionMakerBlock.Input( prompt="Search for keywords", - model=llm_module.LlmModel.GPT4O, + model=llm_module.DEFAULT_LLM_MODEL, credentials=llm_module.TEST_CREDENTIALS_INPUT, # type: ignore agent_mode_max_iterations=0, ) @@ -462,7 +462,7 @@ async def test_smart_decision_maker_parameter_validation(): input_data = SmartDecisionMakerBlock.Input( prompt="Search for keywords", - model=llm_module.LlmModel.GPT4O, + model=llm_module.DEFAULT_LLM_MODEL, credentials=llm_module.TEST_CREDENTIALS_INPUT, # type: ignore agent_mode_max_iterations=0, ) @@ -526,7 +526,7 @@ async def test_smart_decision_maker_parameter_validation(): input_data = SmartDecisionMakerBlock.Input( prompt="Search for keywords", - model=llm_module.LlmModel.GPT4O, + model=llm_module.DEFAULT_LLM_MODEL, credentials=llm_module.TEST_CREDENTIALS_INPUT, # type: ignore agent_mode_max_iterations=0, ) @@ -648,7 +648,7 @@ async def test_smart_decision_maker_raw_response_conversion(): input_data = SmartDecisionMakerBlock.Input( prompt="Test prompt", - model=llm_module.LlmModel.GPT4O, + model=llm_module.DEFAULT_LLM_MODEL, credentials=llm_module.TEST_CREDENTIALS_INPUT, # type: ignore retry=2, agent_mode_max_iterations=0, @@ -722,7 +722,7 @@ async def test_smart_decision_maker_raw_response_conversion(): ): input_data = SmartDecisionMakerBlock.Input( prompt="Simple prompt", - model=llm_module.LlmModel.GPT4O, + model=llm_module.DEFAULT_LLM_MODEL, credentials=llm_module.TEST_CREDENTIALS_INPUT, # type: ignore agent_mode_max_iterations=0, ) @@ -778,7 +778,7 @@ async def test_smart_decision_maker_raw_response_conversion(): ): input_data = SmartDecisionMakerBlock.Input( prompt="Another test", - model=llm_module.LlmModel.GPT4O, + model=llm_module.DEFAULT_LLM_MODEL, credentials=llm_module.TEST_CREDENTIALS_INPUT, # type: ignore agent_mode_max_iterations=0, ) @@ -931,7 +931,7 @@ async def test_smart_decision_maker_agent_mode(): # Test agent mode with max_iterations = 3 input_data = SmartDecisionMakerBlock.Input( prompt="Complete this task using tools", - model=llm_module.LlmModel.GPT4O, + model=llm_module.DEFAULT_LLM_MODEL, credentials=llm_module.TEST_CREDENTIALS_INPUT, # type: ignore agent_mode_max_iterations=3, # Enable agent mode with 3 max iterations ) @@ -1020,7 +1020,7 @@ async def test_smart_decision_maker_traditional_mode_default(): # Test default behavior (traditional mode) input_data = SmartDecisionMakerBlock.Input( prompt="Test prompt", - model=llm_module.LlmModel.GPT4O, + model=llm_module.DEFAULT_LLM_MODEL, credentials=llm_module.TEST_CREDENTIALS_INPUT, # type: ignore agent_mode_max_iterations=0, # Traditional mode ) diff --git a/autogpt_platform/backend/backend/blocks/test/test_smart_decision_maker_dynamic_fields.py b/autogpt_platform/backend/backend/blocks/test/test_smart_decision_maker_dynamic_fields.py index d6a0c0fe39..6ed830e517 100644 --- a/autogpt_platform/backend/backend/blocks/test/test_smart_decision_maker_dynamic_fields.py +++ b/autogpt_platform/backend/backend/blocks/test/test_smart_decision_maker_dynamic_fields.py @@ -373,7 +373,7 @@ async def test_output_yielding_with_dynamic_fields(): input_data = block.input_schema( prompt="Create a user dictionary", credentials=llm.TEST_CREDENTIALS_INPUT, - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, agent_mode_max_iterations=0, # Use traditional mode to test output yielding ) @@ -594,7 +594,7 @@ async def test_validation_errors_dont_pollute_conversation(): input_data = block.input_schema( prompt="Test prompt", credentials=llm.TEST_CREDENTIALS_INPUT, - model=llm.LlmModel.GPT4O, + model=llm.DEFAULT_LLM_MODEL, retry=3, # Allow retries agent_mode_max_iterations=1, ) diff --git a/autogpt_platform/backend/backend/blocks/xml_parser.py b/autogpt_platform/backend/backend/blocks/xml_parser.py index bf35733c15..223f8ea367 100644 --- a/autogpt_platform/backend/backend/blocks/xml_parser.py +++ b/autogpt_platform/backend/backend/blocks/xml_parser.py @@ -1,5 +1,5 @@ from gravitasml.parser import Parser -from gravitasml.token import tokenize +from gravitasml.token import Token, tokenize from backend.data.block import Block, BlockOutput, BlockSchemaInput, BlockSchemaOutput from backend.data.model import SchemaField @@ -25,6 +25,38 @@ class XMLParserBlock(Block): ], ) + @staticmethod + def _validate_tokens(tokens: list[Token]) -> None: + """Ensure the XML has a single root element and no stray text.""" + if not tokens: + raise ValueError("XML input is empty.") + + depth = 0 + root_seen = False + + for token in tokens: + if token.type == "TAG_OPEN": + if depth == 0 and root_seen: + raise ValueError("XML must have a single root element.") + depth += 1 + if depth == 1: + root_seen = True + elif token.type == "TAG_CLOSE": + depth -= 1 + if depth < 0: + raise SyntaxError("Unexpected closing tag in XML input.") + elif token.type in {"TEXT", "ESCAPE"}: + if depth == 0 and token.value: + raise ValueError( + "XML contains text outside the root element; " + "wrap content in a single root tag." + ) + + if depth != 0: + raise SyntaxError("Unclosed tag detected in XML input.") + if not root_seen: + raise ValueError("XML must include a root element.") + async def run(self, input_data: Input, **kwargs) -> BlockOutput: # Security fix: Add size limits to prevent XML bomb attacks MAX_XML_SIZE = 10 * 1024 * 1024 # 10MB limit for XML input @@ -35,7 +67,9 @@ class XMLParserBlock(Block): ) try: - tokens = tokenize(input_data.input_xml) + tokens = list(tokenize(input_data.input_xml)) + self._validate_tokens(tokens) + parser = Parser(tokens) parsed_result = parser.parse() yield "parsed_xml", parsed_result diff --git a/autogpt_platform/backend/backend/blocks/youtube.py b/autogpt_platform/backend/backend/blocks/youtube.py index 322cac35a8..e79be3e99b 100644 --- a/autogpt_platform/backend/backend/blocks/youtube.py +++ b/autogpt_platform/backend/backend/blocks/youtube.py @@ -111,6 +111,8 @@ class TranscribeYoutubeVideoBlock(Block): return parsed_url.path.split("/")[2] if parsed_url.path[:3] == "/v/": return parsed_url.path.split("/")[2] + if parsed_url.path.startswith("/shorts/"): + return parsed_url.path.split("/")[2] raise ValueError(f"Invalid YouTube URL: {url}") def get_transcript( diff --git a/autogpt_platform/backend/backend/data/block_cost_config.py b/autogpt_platform/backend/backend/data/block_cost_config.py index 6bb32d3a47..7f8ee97d52 100644 --- a/autogpt_platform/backend/backend/data/block_cost_config.py +++ b/autogpt_platform/backend/backend/data/block_cost_config.py @@ -59,12 +59,13 @@ from backend.integrations.credentials_store import ( MODEL_COST: dict[LlmModel, int] = { LlmModel.O3: 4, - LlmModel.O3_MINI: 2, # $1.10 / $4.40 - LlmModel.O1: 16, # $15 / $60 + LlmModel.O3_MINI: 2, + LlmModel.O1: 16, LlmModel.O1_MINI: 4, # GPT-5 models - LlmModel.GPT5: 2, + LlmModel.GPT5_2: 6, LlmModel.GPT5_1: 5, + LlmModel.GPT5: 2, LlmModel.GPT5_MINI: 1, LlmModel.GPT5_NANO: 1, LlmModel.GPT5_CHAT: 5, @@ -87,7 +88,7 @@ MODEL_COST: dict[LlmModel, int] = { LlmModel.AIML_API_LLAMA3_3_70B: 1, LlmModel.AIML_API_META_LLAMA_3_1_70B: 1, LlmModel.AIML_API_LLAMA_3_2_3B: 1, - LlmModel.LLAMA3_3_70B: 1, # $0.59 / $0.79 + LlmModel.LLAMA3_3_70B: 1, LlmModel.LLAMA3_1_8B: 1, LlmModel.OLLAMA_LLAMA3_3: 1, LlmModel.OLLAMA_LLAMA3_2: 1, diff --git a/autogpt_platform/backend/backend/data/credit.py b/autogpt_platform/backend/backend/data/credit.py index 95f0b158e1..f3c5365446 100644 --- a/autogpt_platform/backend/backend/data/credit.py +++ b/autogpt_platform/backend/backend/data/credit.py @@ -341,6 +341,19 @@ class UserCreditBase(ABC): if result: # UserBalance is already updated by the CTE + + # Clear insufficient funds notification flags when credits are added + # so user can receive alerts again if they run out in the future. + if transaction.amount > 0 and transaction.type in [ + CreditTransactionType.GRANT, + CreditTransactionType.TOP_UP, + ]: + from backend.executor.manager import ( + clear_insufficient_funds_notifications, + ) + + await clear_insufficient_funds_notifications(user_id) + return result[0]["balance"] async def _add_transaction( @@ -530,6 +543,22 @@ class UserCreditBase(ABC): if result: new_balance, tx_key = result[0]["balance"], result[0]["transactionKey"] # UserBalance is already updated by the CTE + + # Clear insufficient funds notification flags when credits are added + # so user can receive alerts again if they run out in the future. + if ( + amount > 0 + and is_active + and transaction_type + in [CreditTransactionType.GRANT, CreditTransactionType.TOP_UP] + ): + # Lazy import to avoid circular dependency with executor.manager + from backend.executor.manager import ( + clear_insufficient_funds_notifications, + ) + + await clear_insufficient_funds_notifications(user_id) + return new_balance, tx_key # If no result, either user doesn't exist or insufficient balance diff --git a/autogpt_platform/backend/backend/executor/manager.py b/autogpt_platform/backend/backend/executor/manager.py index 161e68b0d6..75459c5a2a 100644 --- a/autogpt_platform/backend/backend/executor/manager.py +++ b/autogpt_platform/backend/backend/executor/manager.py @@ -114,6 +114,40 @@ utilization_gauge = Gauge( "Ratio of active graph runs to max graph workers", ) +# Redis key prefix for tracking insufficient funds Discord notifications. +# We only send one notification per user per agent until they top up credits. +INSUFFICIENT_FUNDS_NOTIFIED_PREFIX = "insufficient_funds_discord_notified" +# TTL for the notification flag (30 days) - acts as a fallback cleanup +INSUFFICIENT_FUNDS_NOTIFIED_TTL_SECONDS = 30 * 24 * 60 * 60 + + +async def clear_insufficient_funds_notifications(user_id: str) -> int: + """ + Clear all insufficient funds notification flags for a user. + + This should be called when a user tops up their credits, allowing + Discord notifications to be sent again if they run out of funds. + + Args: + user_id: The user ID to clear notifications for. + + Returns: + The number of keys that were deleted. + """ + try: + redis_client = await redis.get_redis_async() + pattern = f"{INSUFFICIENT_FUNDS_NOTIFIED_PREFIX}:{user_id}:*" + keys = [key async for key in redis_client.scan_iter(match=pattern)] + if keys: + return await redis_client.delete(*keys) + return 0 + except Exception as e: + logger.warning( + f"Failed to clear insufficient funds notification flags for user " + f"{user_id}: {e}" + ) + return 0 + # Thread-local storage for ExecutionProcessor instances _tls = threading.local() @@ -1261,12 +1295,40 @@ class ExecutionProcessor: graph_id: str, e: InsufficientBalanceError, ): + # Check if we've already sent a notification for this user+agent combo. + # We only send one notification per user per agent until they top up credits. + redis_key = f"{INSUFFICIENT_FUNDS_NOTIFIED_PREFIX}:{user_id}:{graph_id}" + try: + redis_client = redis.get_redis() + # SET NX returns True only if the key was newly set (didn't exist) + is_new_notification = redis_client.set( + redis_key, + "1", + nx=True, + ex=INSUFFICIENT_FUNDS_NOTIFIED_TTL_SECONDS, + ) + if not is_new_notification: + # Already notified for this user+agent, skip all notifications + logger.debug( + f"Skipping duplicate insufficient funds notification for " + f"user={user_id}, graph={graph_id}" + ) + return + except Exception as redis_error: + # If Redis fails, log and continue to send the notification + # (better to occasionally duplicate than to never notify) + logger.warning( + f"Failed to check/set insufficient funds notification flag in Redis: " + f"{redis_error}" + ) + shortfall = abs(e.amount) - e.balance metadata = db_client.get_graph_metadata(graph_id) base_url = ( settings.config.frontend_base_url or settings.config.platform_base_url ) + # Queue user email notification queue_notification( NotificationEventModel( user_id=user_id, @@ -1280,6 +1342,7 @@ class ExecutionProcessor: ) ) + # Send Discord system alert try: user_email = db_client.get_user_email_by_id(user_id) diff --git a/autogpt_platform/backend/backend/executor/manager_insufficient_funds_test.py b/autogpt_platform/backend/backend/executor/manager_insufficient_funds_test.py new file mode 100644 index 0000000000..276c9f4f7a --- /dev/null +++ b/autogpt_platform/backend/backend/executor/manager_insufficient_funds_test.py @@ -0,0 +1,560 @@ +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest +from prisma.enums import NotificationType + +from backend.data.notifications import ZeroBalanceData +from backend.executor.manager import ( + INSUFFICIENT_FUNDS_NOTIFIED_PREFIX, + ExecutionProcessor, + clear_insufficient_funds_notifications, +) +from backend.util.exceptions import InsufficientBalanceError +from backend.util.test import SpinTestServer + + +async def async_iter(items): + """Helper to create an async iterator from a list.""" + for item in items: + yield item + + +@pytest.mark.asyncio(loop_scope="session") +async def test_handle_insufficient_funds_sends_discord_alert_first_time( + server: SpinTestServer, +): + """Test that the first insufficient funds notification sends a Discord alert.""" + + execution_processor = ExecutionProcessor() + user_id = "test-user-123" + graph_id = "test-graph-456" + error = InsufficientBalanceError( + message="Insufficient balance", + user_id=user_id, + balance=72, # $0.72 + amount=-714, # Attempting to spend $7.14 + ) + + with patch( + "backend.executor.manager.queue_notification" + ) as mock_queue_notif, patch( + "backend.executor.manager.get_notification_manager_client" + ) as mock_get_client, patch( + "backend.executor.manager.settings" + ) as mock_settings, patch( + "backend.executor.manager.redis" + ) as mock_redis_module: + + # Setup mocks + mock_client = MagicMock() + mock_get_client.return_value = mock_client + mock_settings.config.frontend_base_url = "https://test.com" + + # Mock Redis to simulate first-time notification (set returns True) + mock_redis_client = MagicMock() + mock_redis_module.get_redis.return_value = mock_redis_client + mock_redis_client.set.return_value = True # Key was newly set + + # Create mock database client + mock_db_client = MagicMock() + mock_graph_metadata = MagicMock() + mock_graph_metadata.name = "Test Agent" + mock_db_client.get_graph_metadata.return_value = mock_graph_metadata + mock_db_client.get_user_email_by_id.return_value = "test@example.com" + + # Test the insufficient funds handler + execution_processor._handle_insufficient_funds_notif( + db_client=mock_db_client, + user_id=user_id, + graph_id=graph_id, + e=error, + ) + + # Verify notification was queued + mock_queue_notif.assert_called_once() + notification_call = mock_queue_notif.call_args[0][0] + assert notification_call.type == NotificationType.ZERO_BALANCE + assert notification_call.user_id == user_id + assert isinstance(notification_call.data, ZeroBalanceData) + assert notification_call.data.current_balance == 72 + + # Verify Redis was checked with correct key pattern + expected_key = f"{INSUFFICIENT_FUNDS_NOTIFIED_PREFIX}:{user_id}:{graph_id}" + mock_redis_client.set.assert_called_once() + call_args = mock_redis_client.set.call_args + assert call_args[0][0] == expected_key + assert call_args[1]["nx"] is True + + # Verify Discord alert was sent + mock_client.discord_system_alert.assert_called_once() + discord_message = mock_client.discord_system_alert.call_args[0][0] + assert "Insufficient Funds Alert" in discord_message + assert "test@example.com" in discord_message + assert "Test Agent" in discord_message + + +@pytest.mark.asyncio(loop_scope="session") +async def test_handle_insufficient_funds_skips_duplicate_notifications( + server: SpinTestServer, +): + """Test that duplicate insufficient funds notifications skip both email and Discord.""" + + execution_processor = ExecutionProcessor() + user_id = "test-user-123" + graph_id = "test-graph-456" + error = InsufficientBalanceError( + message="Insufficient balance", + user_id=user_id, + balance=72, + amount=-714, + ) + + with patch( + "backend.executor.manager.queue_notification" + ) as mock_queue_notif, patch( + "backend.executor.manager.get_notification_manager_client" + ) as mock_get_client, patch( + "backend.executor.manager.settings" + ) as mock_settings, patch( + "backend.executor.manager.redis" + ) as mock_redis_module: + + # Setup mocks + mock_client = MagicMock() + mock_get_client.return_value = mock_client + mock_settings.config.frontend_base_url = "https://test.com" + + # Mock Redis to simulate duplicate notification (set returns False/None) + mock_redis_client = MagicMock() + mock_redis_module.get_redis.return_value = mock_redis_client + mock_redis_client.set.return_value = None # Key already existed + + # Create mock database client + mock_db_client = MagicMock() + mock_db_client.get_graph_metadata.return_value = MagicMock(name="Test Agent") + + # Test the insufficient funds handler + execution_processor._handle_insufficient_funds_notif( + db_client=mock_db_client, + user_id=user_id, + graph_id=graph_id, + e=error, + ) + + # Verify email notification was NOT queued (deduplication worked) + mock_queue_notif.assert_not_called() + + # Verify Discord alert was NOT sent (deduplication worked) + mock_client.discord_system_alert.assert_not_called() + + +@pytest.mark.asyncio(loop_scope="session") +async def test_handle_insufficient_funds_different_agents_get_separate_alerts( + server: SpinTestServer, +): + """Test that different agents for the same user get separate Discord alerts.""" + + execution_processor = ExecutionProcessor() + user_id = "test-user-123" + graph_id_1 = "test-graph-111" + graph_id_2 = "test-graph-222" + + error = InsufficientBalanceError( + message="Insufficient balance", + user_id=user_id, + balance=72, + amount=-714, + ) + + with patch("backend.executor.manager.queue_notification"), patch( + "backend.executor.manager.get_notification_manager_client" + ) as mock_get_client, patch( + "backend.executor.manager.settings" + ) as mock_settings, patch( + "backend.executor.manager.redis" + ) as mock_redis_module: + + mock_client = MagicMock() + mock_get_client.return_value = mock_client + mock_settings.config.frontend_base_url = "https://test.com" + + mock_redis_client = MagicMock() + mock_redis_module.get_redis.return_value = mock_redis_client + # Both calls return True (first time for each agent) + mock_redis_client.set.return_value = True + + mock_db_client = MagicMock() + mock_graph_metadata = MagicMock() + mock_graph_metadata.name = "Test Agent" + mock_db_client.get_graph_metadata.return_value = mock_graph_metadata + mock_db_client.get_user_email_by_id.return_value = "test@example.com" + + # First agent notification + execution_processor._handle_insufficient_funds_notif( + db_client=mock_db_client, + user_id=user_id, + graph_id=graph_id_1, + e=error, + ) + + # Second agent notification + execution_processor._handle_insufficient_funds_notif( + db_client=mock_db_client, + user_id=user_id, + graph_id=graph_id_2, + e=error, + ) + + # Verify Discord alerts were sent for both agents + assert mock_client.discord_system_alert.call_count == 2 + + # Verify Redis was called with different keys + assert mock_redis_client.set.call_count == 2 + calls = mock_redis_client.set.call_args_list + assert ( + calls[0][0][0] + == f"{INSUFFICIENT_FUNDS_NOTIFIED_PREFIX}:{user_id}:{graph_id_1}" + ) + assert ( + calls[1][0][0] + == f"{INSUFFICIENT_FUNDS_NOTIFIED_PREFIX}:{user_id}:{graph_id_2}" + ) + + +@pytest.mark.asyncio(loop_scope="session") +async def test_clear_insufficient_funds_notifications(server: SpinTestServer): + """Test that clearing notifications removes all keys for a user.""" + + user_id = "test-user-123" + + with patch("backend.executor.manager.redis") as mock_redis_module: + + mock_redis_client = MagicMock() + # get_redis_async is an async function, so we need AsyncMock for it + mock_redis_module.get_redis_async = AsyncMock(return_value=mock_redis_client) + + # Mock scan_iter to return some keys as an async iterator + mock_keys = [ + f"{INSUFFICIENT_FUNDS_NOTIFIED_PREFIX}:{user_id}:graph-1", + f"{INSUFFICIENT_FUNDS_NOTIFIED_PREFIX}:{user_id}:graph-2", + f"{INSUFFICIENT_FUNDS_NOTIFIED_PREFIX}:{user_id}:graph-3", + ] + mock_redis_client.scan_iter.return_value = async_iter(mock_keys) + # delete is awaited, so use AsyncMock + mock_redis_client.delete = AsyncMock(return_value=3) + + # Clear notifications + result = await clear_insufficient_funds_notifications(user_id) + + # Verify correct pattern was used + expected_pattern = f"{INSUFFICIENT_FUNDS_NOTIFIED_PREFIX}:{user_id}:*" + mock_redis_client.scan_iter.assert_called_once_with(match=expected_pattern) + + # Verify delete was called with all keys + mock_redis_client.delete.assert_called_once_with(*mock_keys) + + # Verify return value + assert result == 3 + + +@pytest.mark.asyncio(loop_scope="session") +async def test_clear_insufficient_funds_notifications_no_keys(server: SpinTestServer): + """Test clearing notifications when there are no keys to clear.""" + + user_id = "test-user-no-notifications" + + with patch("backend.executor.manager.redis") as mock_redis_module: + + mock_redis_client = MagicMock() + # get_redis_async is an async function, so we need AsyncMock for it + mock_redis_module.get_redis_async = AsyncMock(return_value=mock_redis_client) + + # Mock scan_iter to return no keys as an async iterator + mock_redis_client.scan_iter.return_value = async_iter([]) + + # Clear notifications + result = await clear_insufficient_funds_notifications(user_id) + + # Verify delete was not called + mock_redis_client.delete.assert_not_called() + + # Verify return value + assert result == 0 + + +@pytest.mark.asyncio(loop_scope="session") +async def test_clear_insufficient_funds_notifications_handles_redis_error( + server: SpinTestServer, +): + """Test that clearing notifications handles Redis errors gracefully.""" + + user_id = "test-user-redis-error" + + with patch("backend.executor.manager.redis") as mock_redis_module: + + # Mock get_redis_async to raise an error + mock_redis_module.get_redis_async = AsyncMock( + side_effect=Exception("Redis connection failed") + ) + + # Clear notifications should not raise, just return 0 + result = await clear_insufficient_funds_notifications(user_id) + + # Verify it returned 0 (graceful failure) + assert result == 0 + + +@pytest.mark.asyncio(loop_scope="session") +async def test_handle_insufficient_funds_continues_on_redis_error( + server: SpinTestServer, +): + """Test that both email and Discord notifications are still sent when Redis fails.""" + + execution_processor = ExecutionProcessor() + user_id = "test-user-123" + graph_id = "test-graph-456" + error = InsufficientBalanceError( + message="Insufficient balance", + user_id=user_id, + balance=72, + amount=-714, + ) + + with patch( + "backend.executor.manager.queue_notification" + ) as mock_queue_notif, patch( + "backend.executor.manager.get_notification_manager_client" + ) as mock_get_client, patch( + "backend.executor.manager.settings" + ) as mock_settings, patch( + "backend.executor.manager.redis" + ) as mock_redis_module: + + mock_client = MagicMock() + mock_get_client.return_value = mock_client + mock_settings.config.frontend_base_url = "https://test.com" + + # Mock Redis to raise an error + mock_redis_client = MagicMock() + mock_redis_module.get_redis.return_value = mock_redis_client + mock_redis_client.set.side_effect = Exception("Redis connection error") + + mock_db_client = MagicMock() + mock_graph_metadata = MagicMock() + mock_graph_metadata.name = "Test Agent" + mock_db_client.get_graph_metadata.return_value = mock_graph_metadata + mock_db_client.get_user_email_by_id.return_value = "test@example.com" + + # Test the insufficient funds handler + execution_processor._handle_insufficient_funds_notif( + db_client=mock_db_client, + user_id=user_id, + graph_id=graph_id, + e=error, + ) + + # Verify email notification was still queued despite Redis error + mock_queue_notif.assert_called_once() + + # Verify Discord alert was still sent despite Redis error + mock_client.discord_system_alert.assert_called_once() + + +@pytest.mark.asyncio(loop_scope="session") +async def test_add_transaction_clears_notifications_on_grant(server: SpinTestServer): + """Test that _add_transaction clears notification flags when adding GRANT credits.""" + from prisma.enums import CreditTransactionType + + from backend.data.credit import UserCredit + + user_id = "test-user-grant-clear" + + with patch("backend.data.credit.query_raw_with_schema") as mock_query, patch( + "backend.executor.manager.redis" + ) as mock_redis_module: + + # Mock the query to return a successful transaction + mock_query.return_value = [{"balance": 1000, "transactionKey": "test-tx-key"}] + + # Mock async Redis for notification clearing + mock_redis_client = MagicMock() + mock_redis_module.get_redis_async = AsyncMock(return_value=mock_redis_client) + mock_redis_client.scan_iter.return_value = async_iter( + [f"{INSUFFICIENT_FUNDS_NOTIFIED_PREFIX}:{user_id}:graph-1"] + ) + mock_redis_client.delete = AsyncMock(return_value=1) + + # Create a concrete instance + credit_model = UserCredit() + + # Call _add_transaction with GRANT type (should clear notifications) + await credit_model._add_transaction( + user_id=user_id, + amount=500, # Positive amount + transaction_type=CreditTransactionType.GRANT, + is_active=True, # Active transaction + ) + + # Verify notification clearing was called + mock_redis_module.get_redis_async.assert_called_once() + mock_redis_client.scan_iter.assert_called_once_with( + match=f"{INSUFFICIENT_FUNDS_NOTIFIED_PREFIX}:{user_id}:*" + ) + + +@pytest.mark.asyncio(loop_scope="session") +async def test_add_transaction_clears_notifications_on_top_up(server: SpinTestServer): + """Test that _add_transaction clears notification flags when adding TOP_UP credits.""" + from prisma.enums import CreditTransactionType + + from backend.data.credit import UserCredit + + user_id = "test-user-topup-clear" + + with patch("backend.data.credit.query_raw_with_schema") as mock_query, patch( + "backend.executor.manager.redis" + ) as mock_redis_module: + + # Mock the query to return a successful transaction + mock_query.return_value = [{"balance": 2000, "transactionKey": "test-tx-key-2"}] + + # Mock async Redis for notification clearing + mock_redis_client = MagicMock() + mock_redis_module.get_redis_async = AsyncMock(return_value=mock_redis_client) + mock_redis_client.scan_iter.return_value = async_iter([]) + mock_redis_client.delete = AsyncMock(return_value=0) + + credit_model = UserCredit() + + # Call _add_transaction with TOP_UP type (should clear notifications) + await credit_model._add_transaction( + user_id=user_id, + amount=1000, # Positive amount + transaction_type=CreditTransactionType.TOP_UP, + is_active=True, + ) + + # Verify notification clearing was attempted + mock_redis_module.get_redis_async.assert_called_once() + + +@pytest.mark.asyncio(loop_scope="session") +async def test_add_transaction_skips_clearing_for_inactive_transaction( + server: SpinTestServer, +): + """Test that _add_transaction does NOT clear notifications for inactive transactions.""" + from prisma.enums import CreditTransactionType + + from backend.data.credit import UserCredit + + user_id = "test-user-inactive" + + with patch("backend.data.credit.query_raw_with_schema") as mock_query, patch( + "backend.executor.manager.redis" + ) as mock_redis_module: + + # Mock the query to return a successful transaction + mock_query.return_value = [{"balance": 500, "transactionKey": "test-tx-key-3"}] + + # Mock async Redis + mock_redis_client = MagicMock() + mock_redis_module.get_redis_async = AsyncMock(return_value=mock_redis_client) + + credit_model = UserCredit() + + # Call _add_transaction with is_active=False (should NOT clear notifications) + await credit_model._add_transaction( + user_id=user_id, + amount=500, + transaction_type=CreditTransactionType.TOP_UP, + is_active=False, # Inactive - pending Stripe payment + ) + + # Verify notification clearing was NOT called + mock_redis_module.get_redis_async.assert_not_called() + + +@pytest.mark.asyncio(loop_scope="session") +async def test_add_transaction_skips_clearing_for_usage_transaction( + server: SpinTestServer, +): + """Test that _add_transaction does NOT clear notifications for USAGE transactions.""" + from prisma.enums import CreditTransactionType + + from backend.data.credit import UserCredit + + user_id = "test-user-usage" + + with patch("backend.data.credit.query_raw_with_schema") as mock_query, patch( + "backend.executor.manager.redis" + ) as mock_redis_module: + + # Mock the query to return a successful transaction + mock_query.return_value = [{"balance": 400, "transactionKey": "test-tx-key-4"}] + + # Mock async Redis + mock_redis_client = MagicMock() + mock_redis_module.get_redis_async = AsyncMock(return_value=mock_redis_client) + + credit_model = UserCredit() + + # Call _add_transaction with USAGE type (spending, should NOT clear) + await credit_model._add_transaction( + user_id=user_id, + amount=-100, # Negative - spending credits + transaction_type=CreditTransactionType.USAGE, + is_active=True, + ) + + # Verify notification clearing was NOT called + mock_redis_module.get_redis_async.assert_not_called() + + +@pytest.mark.asyncio(loop_scope="session") +async def test_enable_transaction_clears_notifications(server: SpinTestServer): + """Test that _enable_transaction clears notification flags when enabling a TOP_UP.""" + from prisma.enums import CreditTransactionType + + from backend.data.credit import UserCredit + + user_id = "test-user-enable" + + with patch("backend.data.credit.CreditTransaction") as mock_credit_tx, patch( + "backend.data.credit.query_raw_with_schema" + ) as mock_query, patch("backend.executor.manager.redis") as mock_redis_module: + + # Mock finding the pending transaction + mock_transaction = MagicMock() + mock_transaction.amount = 1000 + mock_transaction.type = CreditTransactionType.TOP_UP + mock_credit_tx.prisma.return_value.find_first = AsyncMock( + return_value=mock_transaction + ) + + # Mock the query to return updated balance + mock_query.return_value = [{"balance": 1500}] + + # Mock async Redis for notification clearing + mock_redis_client = MagicMock() + mock_redis_module.get_redis_async = AsyncMock(return_value=mock_redis_client) + mock_redis_client.scan_iter.return_value = async_iter( + [f"{INSUFFICIENT_FUNDS_NOTIFIED_PREFIX}:{user_id}:graph-1"] + ) + mock_redis_client.delete = AsyncMock(return_value=1) + + credit_model = UserCredit() + + # Call _enable_transaction (simulates Stripe checkout completion) + from backend.util.json import SafeJson + + await credit_model._enable_transaction( + transaction_key="cs_test_123", + user_id=user_id, + metadata=SafeJson({"payment": "completed"}), + ) + + # Verify notification clearing was called + mock_redis_module.get_redis_async.assert_called_once() + mock_redis_client.scan_iter.assert_called_once_with( + match=f"{INSUFFICIENT_FUNDS_NOTIFIED_PREFIX}:{user_id}:*" + ) diff --git a/autogpt_platform/backend/poetry.lock b/autogpt_platform/backend/poetry.lock index 9aa55a6c22..5e3b07a145 100644 --- a/autogpt_platform/backend/poetry.lock +++ b/autogpt_platform/backend/poetry.lock @@ -1906,16 +1906,32 @@ httpx = {version = ">=0.26,<0.29", extras = ["http2"]} pydantic = ">=1.10,<3" pyjwt = ">=2.10.1,<3.0.0" +[[package]] +name = "gravitas-md2gdocs" +version = "0.1.0" +description = "Convert Markdown to Google Docs API requests" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "gravitas_md2gdocs-0.1.0-py3-none-any.whl", hash = "sha256:0cb0627779fdd65c1604818af4142eea1b25d055060183363de1bae4d9e46508"}, + {file = "gravitas_md2gdocs-0.1.0.tar.gz", hash = "sha256:bb3122fe9fa35c528f3f00b785d3f1398d350082d5d03f60f56c895bdcc68033"}, +] + +[package.extras] +dev = ["google-auth-oauthlib (>=1.0.0)", "pytest (>=7.0.0)", "pytest-cov (>=4.0.0)", "python-dotenv (>=1.0.0)", "ruff (>=0.1.0)"] +google = ["google-api-python-client (>=2.0.0)", "google-auth (>=2.0.0)"] + [[package]] name = "gravitasml" -version = "0.1.3" +version = "0.1.4" description = "" optional = false python-versions = "<4.0,>=3.10" groups = ["main"] files = [ - {file = "gravitasml-0.1.3-py3-none-any.whl", hash = "sha256:51ff98b4564b7a61f7796f18d5f2558b919d30b3722579296089645b7bc18b85"}, - {file = "gravitasml-0.1.3.tar.gz", hash = "sha256:04d240b9fa35878252d57a36032130b6516487468847fcdced1022c032a20f57"}, + {file = "gravitasml-0.1.4-py3-none-any.whl", hash = "sha256:671a18b11d3d8a0e270c6a80c72cd058458b18d5ef7560d00010e962ab1bca74"}, + {file = "gravitasml-0.1.4.tar.gz", hash = "sha256:35d0d9fec7431817482d53d9c976e375557c3e041d1eb6928e809324a8c866e3"}, ] [package.dependencies] @@ -7279,4 +7295,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.14" -content-hash = "13b191b2a1989d3321ff713c66ff6f5f4f3b82d15df4d407e0e5dbf87d7522c4" +content-hash = "a93ba0cea3b465cb6ec3e3f258b383b09f84ea352ccfdbfa112902cde5653fc6" diff --git a/autogpt_platform/backend/pyproject.toml b/autogpt_platform/backend/pyproject.toml index fb06b65162..e8b8fd0ba5 100644 --- a/autogpt_platform/backend/pyproject.toml +++ b/autogpt_platform/backend/pyproject.toml @@ -27,7 +27,7 @@ google-api-python-client = "^2.177.0" google-auth-oauthlib = "^1.2.2" google-cloud-storage = "^3.2.0" googlemaps = "^4.10.0" -gravitasml = "^0.1.3" +gravitasml = "^0.1.4" groq = "^0.30.0" html2text = "^2024.2.26" jinja2 = "^3.1.6" @@ -82,6 +82,7 @@ firecrawl-py = "^4.3.6" exa-py = "^1.14.20" croniter = "^6.0.0" stagehand = "^0.5.1" +gravitas-md2gdocs = "^0.1.0" [tool.poetry.group.dev.dependencies] aiohappyeyeballs = "^2.6.1" diff --git a/autogpt_platform/backend/test/blocks/test_google_docs_format_text_color.py b/autogpt_platform/backend/test/blocks/test_google_docs_format_text_color.py new file mode 100644 index 0000000000..6a6e8e07c8 --- /dev/null +++ b/autogpt_platform/backend/test/blocks/test_google_docs_format_text_color.py @@ -0,0 +1,113 @@ +from unittest.mock import Mock + +from backend.blocks.google.docs import GoogleDocsFormatTextBlock + + +def _make_mock_docs_service() -> Mock: + service = Mock() + # Ensure chained call exists: service.documents().batchUpdate(...).execute() + service.documents.return_value.batchUpdate.return_value.execute.return_value = {} + return service + + +def test_format_text_parses_shorthand_hex_color(): + block = GoogleDocsFormatTextBlock() + service = _make_mock_docs_service() + + result = block._format_text( + service, + document_id="doc_1", + start_index=1, + end_index=2, + bold=False, + italic=False, + underline=False, + font_size=0, + foreground_color="#FFF", + ) + + assert result["success"] is True + + # Verify request body contains correct rgbColor for white. + _, kwargs = service.documents.return_value.batchUpdate.call_args + requests = kwargs["body"]["requests"] + rgb = requests[0]["updateTextStyle"]["textStyle"]["foregroundColor"]["color"][ + "rgbColor" + ] + assert rgb == {"red": 1.0, "green": 1.0, "blue": 1.0} + + +def test_format_text_parses_full_hex_color(): + block = GoogleDocsFormatTextBlock() + service = _make_mock_docs_service() + + result = block._format_text( + service, + document_id="doc_1", + start_index=1, + end_index=2, + bold=False, + italic=False, + underline=False, + font_size=0, + foreground_color="#FF0000", + ) + + assert result["success"] is True + + _, kwargs = service.documents.return_value.batchUpdate.call_args + requests = kwargs["body"]["requests"] + rgb = requests[0]["updateTextStyle"]["textStyle"]["foregroundColor"]["color"][ + "rgbColor" + ] + assert rgb == {"red": 1.0, "green": 0.0, "blue": 0.0} + + +def test_format_text_ignores_invalid_color_when_other_fields_present(): + block = GoogleDocsFormatTextBlock() + service = _make_mock_docs_service() + + result = block._format_text( + service, + document_id="doc_1", + start_index=1, + end_index=2, + bold=True, + italic=False, + underline=False, + font_size=0, + foreground_color="#GGG", + ) + + assert result["success"] is True + assert "warning" in result + + # Should still apply bold, but should NOT include foregroundColor in textStyle. + _, kwargs = service.documents.return_value.batchUpdate.call_args + requests = kwargs["body"]["requests"] + text_style = requests[0]["updateTextStyle"]["textStyle"] + fields = requests[0]["updateTextStyle"]["fields"] + + assert text_style == {"bold": True} + assert fields == "bold" + + +def test_format_text_invalid_color_only_does_not_call_api(): + block = GoogleDocsFormatTextBlock() + service = _make_mock_docs_service() + + result = block._format_text( + service, + document_id="doc_1", + start_index=1, + end_index=2, + bold=False, + italic=False, + underline=False, + font_size=0, + foreground_color="#F", + ) + + assert result["success"] is False + assert "Invalid foreground_color" in result["message"] + service.documents.return_value.batchUpdate.assert_not_called() diff --git a/autogpt_platform/backend/test/blocks/test_youtube.py b/autogpt_platform/backend/test/blocks/test_youtube.py index 1af7c31b9b..4d3bd9d800 100644 --- a/autogpt_platform/backend/test/blocks/test_youtube.py +++ b/autogpt_platform/backend/test/blocks/test_youtube.py @@ -37,6 +37,18 @@ class TestTranscribeYoutubeVideoBlock: video_id = self.youtube_block.extract_video_id(url) assert video_id == "dQw4w9WgXcQ" + def test_extract_video_id_shorts_url(self): + """Test extracting video ID from YouTube Shorts URL.""" + url = "https://www.youtube.com/shorts/dtUqwMu3e-g" + video_id = self.youtube_block.extract_video_id(url) + assert video_id == "dtUqwMu3e-g" + + def test_extract_video_id_shorts_url_with_params(self): + """Test extracting video ID from YouTube Shorts URL with query parameters.""" + url = "https://www.youtube.com/shorts/dtUqwMu3e-g?feature=share" + video_id = self.youtube_block.extract_video_id(url) + assert video_id == "dtUqwMu3e-g" + @patch("backend.blocks.youtube.YouTubeTranscriptApi") def test_get_transcript_english_available(self, mock_api_class): """Test getting transcript when English is available.""" diff --git a/autogpt_platform/cloudflare_worker.js b/autogpt_platform/cloudflare_worker.js new file mode 100644 index 0000000000..15e26a301d --- /dev/null +++ b/autogpt_platform/cloudflare_worker.js @@ -0,0 +1,146 @@ +/** + * Cloudflare Workers Script for docs.agpt.co → agpt.co/docs migration + * + * Deploy this script to handle all redirects with a single JavaScript file. + * No rule limits, easy to maintain, handles all edge cases. + */ + +// URL mapping for special cases that don't follow patterns +const SPECIAL_MAPPINGS = { + // Root page + '/': '/docs/platform', + + // Special cases that don't follow standard patterns + '/platform/d_id/': '/docs/integrations/block-integrations/d-id', + '/platform/blocks/blocks/': '/docs/integrations', + '/platform/blocks/decoder_block/': '/docs/integrations/block-integrations/text-decoder', + '/platform/blocks/http': '/docs/integrations/block-integrations/send-web-request', + '/platform/blocks/llm/': '/docs/integrations/block-integrations/ai-and-llm', + '/platform/blocks/time_blocks': '/docs/integrations/block-integrations/time-and-date', + '/platform/blocks/text_to_speech_block': '/docs/integrations/block-integrations/text-to-speech', + '/platform/blocks/ai_shortform_video_block': '/docs/integrations/block-integrations/ai-shortform-video', + '/platform/blocks/replicate_flux_advanced': '/docs/integrations/block-integrations/replicate-flux-advanced', + '/platform/blocks/flux_kontext': '/docs/integrations/block-integrations/flux-kontext', + '/platform/blocks/ai_condition/': '/docs/integrations/block-integrations/ai-condition', + '/platform/blocks/email_block': '/docs/integrations/block-integrations/email', + '/platform/blocks/google_maps': '/docs/integrations/block-integrations/google-maps', + '/platform/blocks/google/gmail': '/docs/integrations/block-integrations/gmail', + '/platform/blocks/github/issues/': '/docs/integrations/block-integrations/github-issues', + '/platform/blocks/github/repo/': '/docs/integrations/block-integrations/github-repo', + '/platform/blocks/github/pull_requests': '/docs/integrations/block-integrations/github-pull-requests', + '/platform/blocks/twitter/twitter': '/docs/integrations/block-integrations/twitter', + '/classic/setup/': '/docs/classic/setup/setting-up-autogpt-classic', + '/code-of-conduct/': '/docs/classic/help-us-improve-autogpt/code-of-conduct', + '/contributing/': '/docs/classic/contributing', + '/contribute/': '/docs/contribute', + '/forge/components/introduction/': '/docs/classic/forge/introduction' +}; + +/** + * Transform path by replacing underscores with hyphens and removing trailing slashes + */ +function transformPath(path) { + return path.replace(/_/g, '-').replace(/\/$/, ''); +} + +/** + * Handle docs.agpt.co redirects + */ +function handleDocsRedirect(url) { + const pathname = url.pathname; + + // Check special mappings first + if (SPECIAL_MAPPINGS[pathname]) { + return `https://agpt.co${SPECIAL_MAPPINGS[pathname]}`; + } + + // Pattern-based redirects + + // Platform blocks: /platform/blocks/* → /docs/integrations/block-integrations/* + if (pathname.startsWith('/platform/blocks/')) { + const blockName = pathname.substring('/platform/blocks/'.length); + const transformedName = transformPath(blockName); + return `https://agpt.co/docs/integrations/block-integrations/${transformedName}`; + } + + // Platform contributing: /platform/contributing/* → /docs/platform/contributing/* + if (pathname.startsWith('/platform/contributing/')) { + const subPath = pathname.substring('/platform/contributing/'.length); + return `https://agpt.co/docs/platform/contributing/${subPath}`; + } + + // Platform general: /platform/* → /docs/platform/* (with underscore→hyphen) + if (pathname.startsWith('/platform/')) { + const subPath = pathname.substring('/platform/'.length); + const transformedPath = transformPath(subPath); + return `https://agpt.co/docs/platform/${transformedPath}`; + } + + // Forge components: /forge/components/* → /docs/classic/forge/introduction/* + if (pathname.startsWith('/forge/components/')) { + const subPath = pathname.substring('/forge/components/'.length); + return `https://agpt.co/docs/classic/forge/introduction/${subPath}`; + } + + // Forge general: /forge/* → /docs/classic/forge/* + if (pathname.startsWith('/forge/')) { + const subPath = pathname.substring('/forge/'.length); + return `https://agpt.co/docs/classic/forge/${subPath}`; + } + + // Classic: /classic/* → /docs/classic/* + if (pathname.startsWith('/classic/')) { + const subPath = pathname.substring('/classic/'.length); + return `https://agpt.co/docs/classic/${subPath}`; + } + + // Default fallback + return 'https://agpt.co/docs/'; +} + +/** + * Main Worker function + */ +export default { + async fetch(request, env, ctx) { + const url = new URL(request.url); + + // Only handle docs.agpt.co requests + if (url.hostname === 'docs.agpt.co') { + const redirectUrl = handleDocsRedirect(url); + + return new Response(null, { + status: 301, + headers: { + 'Location': redirectUrl, + 'Cache-Control': 'max-age=300' // Cache redirects for 5 minutes + } + }); + } + + // For non-docs requests, pass through or return 404 + return new Response('Not Found', { status: 404 }); + } +}; + +// Test function for local development +export function testRedirects() { + const testCases = [ + 'https://docs.agpt.co/', + 'https://docs.agpt.co/platform/getting-started/', + 'https://docs.agpt.co/platform/advanced_setup/', + 'https://docs.agpt.co/platform/blocks/basic/', + 'https://docs.agpt.co/platform/blocks/ai_condition/', + 'https://docs.agpt.co/classic/setup/', + 'https://docs.agpt.co/forge/components/agents/', + 'https://docs.agpt.co/contributing/', + 'https://docs.agpt.co/unknown-page' + ]; + + console.log('Testing redirects:'); + testCases.forEach(testUrl => { + const url = new URL(testUrl); + const result = handleDocsRedirect(url); + console.log(`${testUrl} → ${result}`); + }); +} \ No newline at end of file diff --git a/autogpt_platform/frontend/package.json b/autogpt_platform/frontend/package.json index 4cbd867cd8..fb8856a30f 100644 --- a/autogpt_platform/frontend/package.json +++ b/autogpt_platform/frontend/package.json @@ -46,14 +46,15 @@ "@radix-ui/react-scroll-area": "1.2.10", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-separator": "1.1.7", + "@radix-ui/react-slider": "1.3.6", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-switch": "1.2.6", "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-tooltip": "1.2.8", - "@rjsf/core": "5.24.13", - "@rjsf/utils": "5.24.13", - "@rjsf/validator-ajv8": "5.24.13", + "@rjsf/core": "6.1.2", + "@rjsf/utils": "6.1.2", + "@rjsf/validator-ajv8": "6.1.2", "@sentry/nextjs": "10.27.0", "@supabase/ssr": "0.7.0", "@supabase/supabase-js": "2.78.0", @@ -69,6 +70,7 @@ "cmdk": "1.1.1", "cookie": "1.0.2", "date-fns": "4.1.0", + "dexie": "4.2.1", "dotenv": "17.2.3", "elliptic": "6.6.1", "embla-carousel-react": "8.6.0", diff --git a/autogpt_platform/frontend/pnpm-lock.yaml b/autogpt_platform/frontend/pnpm-lock.yaml index 54843fc589..6b3e5e2ffd 100644 --- a/autogpt_platform/frontend/pnpm-lock.yaml +++ b/autogpt_platform/frontend/pnpm-lock.yaml @@ -16,7 +16,7 @@ importers: version: 5.2.2(react-hook-form@7.66.0(react@18.3.1)) '@next/third-parties': specifier: 15.4.6 - version: 15.4.6(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 15.4.6(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@phosphor-icons/react': specifier: 2.1.10 version: 2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -62,6 +62,9 @@ importers: '@radix-ui/react-separator': specifier: 1.1.7 version: 1.1.7(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slider': + specifier: 1.3.6 + version: 1.3.6(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-slot': specifier: 1.2.3 version: 1.2.3(@types/react@18.3.17)(react@18.3.1) @@ -78,17 +81,17 @@ importers: specifier: 1.2.8 version: 1.2.8(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@rjsf/core': - specifier: 5.24.13 - version: 5.24.13(@rjsf/utils@5.24.13(react@18.3.1))(react@18.3.1) + specifier: 6.1.2 + version: 6.1.2(@rjsf/utils@6.1.2(react@18.3.1))(react@18.3.1) '@rjsf/utils': - specifier: 5.24.13 - version: 5.24.13(react@18.3.1) + specifier: 6.1.2 + version: 6.1.2(react@18.3.1) '@rjsf/validator-ajv8': - specifier: 5.24.13 - version: 5.24.13(@rjsf/utils@5.24.13(react@18.3.1)) + specifier: 6.1.2 + version: 6.1.2(@rjsf/utils@6.1.2(react@18.3.1)) '@sentry/nextjs': specifier: 10.27.0 - version: 10.27.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.101.3(esbuild@0.25.9)) + version: 10.27.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.104.1(esbuild@0.25.12)) '@supabase/ssr': specifier: 0.7.0 version: 0.7.0(@supabase/supabase-js@2.78.0) @@ -106,13 +109,13 @@ importers: version: 0.2.4 '@vercel/analytics': specifier: 1.5.0 - version: 1.5.0(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 1.5.0(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@vercel/speed-insights': specifier: 1.2.0 - version: 1.2.0(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 1.2.0(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@xyflow/react': specifier: 12.9.2 - version: 12.9.2(@types/react@18.3.17)(immer@10.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 12.9.2(@types/react@18.3.17)(immer@11.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) boring-avatars: specifier: 1.11.2 version: 1.11.2 @@ -131,6 +134,9 @@ importers: date-fns: specifier: 4.1.0 version: 4.1.0 + dexie: + specifier: 4.2.1 + version: 4.2.1 dotenv: specifier: 17.2.3 version: 17.2.3 @@ -148,7 +154,7 @@ importers: version: 12.23.24(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) geist: specifier: 1.5.1 - version: 1.5.1(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 1.5.1(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) highlight.js: specifier: 11.11.1 version: 11.11.1 @@ -172,13 +178,13 @@ importers: version: 2.30.1 next: specifier: 15.4.10 - version: 15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-themes: specifier: 0.4.6 version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nuqs: specifier: 2.7.2 - version: 2.7.2(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 2.7.2(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) party-js: specifier: 2.2.0 version: 2.2.0 @@ -262,7 +268,7 @@ importers: version: 3.25.76 zustand: specifier: 5.0.8 - version: 5.0.8(@types/react@18.3.17)(immer@10.1.3)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)) + version: 5.0.8(@types/react@18.3.17)(immer@11.1.3)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)) devDependencies: '@chromatic-com/storybook': specifier: 4.1.2 @@ -284,7 +290,7 @@ importers: version: 9.1.5(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2)) '@storybook/nextjs': specifier: 9.1.5 - version: 9.1.5(esbuild@0.25.9)(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(type-fest@4.41.0)(typescript@5.9.3)(webpack-hot-middleware@2.26.1)(webpack@5.101.3(esbuild@0.25.9)) + version: 9.1.5(esbuild@0.25.12)(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(type-fest@4.41.0)(typescript@5.9.3)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(esbuild@0.25.12)) '@tanstack/eslint-plugin-query': specifier: 5.91.2 version: 5.91.2(eslint@8.57.1)(typescript@5.9.3) @@ -408,16 +414,16 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.4': - resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.4': - resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.3': - resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} '@babel/helper-annotate-as-pure@7.27.3': @@ -428,14 +434,14 @@ packages: resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.28.3': - resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} + '@babel/helper-create-class-features-plugin@7.28.5': + resolution: {integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-create-regexp-features-plugin@7.27.1': - resolution: {integrity: sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==} + '@babel/helper-create-regexp-features-plugin@7.28.5': + resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -449,8 +455,8 @@ packages: resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} - '@babel/helper-member-expression-to-functions@7.27.1': - resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.27.1': @@ -491,8 +497,8 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} '@babel/helper-validator-option@7.27.1': @@ -507,13 +513,13 @@ packages: resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.4': - resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1': - resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==} + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': + resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -612,8 +618,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoping@7.28.4': - resolution: {integrity: sha512-1yxmvN0MJHOhPVmAsmoW5liWwoILobu/d/ShymZmj867bAdxGbehIrew1DuLpw2Ukv+qDSSPQdYW1dLNE7t11A==} + '@babel/plugin-transform-block-scoping@7.28.5': + resolution: {integrity: sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -642,8 +648,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-destructuring@7.28.0': - resolution: {integrity: sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==} + '@babel/plugin-transform-destructuring@7.28.5': + resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -678,8 +684,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-exponentiation-operator@7.27.1': - resolution: {integrity: sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==} + '@babel/plugin-transform-exponentiation-operator@7.28.5': + resolution: {integrity: sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -714,8 +720,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-logical-assignment-operators@7.27.1': - resolution: {integrity: sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==} + '@babel/plugin-transform-logical-assignment-operators@7.28.5': + resolution: {integrity: sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -738,8 +744,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-systemjs@7.27.1': - resolution: {integrity: sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==} + '@babel/plugin-transform-modules-systemjs@7.28.5': + resolution: {integrity: sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -792,8 +798,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-chaining@7.27.1': - resolution: {integrity: sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==} + '@babel/plugin-transform-optional-chaining@7.28.5': + resolution: {integrity: sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -864,8 +870,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-runtime@7.28.3': - resolution: {integrity: sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==} + '@babel/plugin-transform-runtime@7.28.5': + resolution: {integrity: sha512-20NUVgOrinudkIBzQ2bNxP08YpKprUkRTiRSd2/Z5GOdPImJGkoN4Z7IQe1T5AdyKI1i5L6RBmluqdSzvaq9/w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -900,8 +906,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.28.0': - resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} + '@babel/plugin-transform-typescript@7.28.5': + resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -930,8 +936,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.28.3': - resolution: {integrity: sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==} + '@babel/preset-env@7.28.5': + resolution: {integrity: sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -941,14 +947,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - '@babel/preset-react@7.27.1': - resolution: {integrity: sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==} + '@babel/preset-react@7.28.5': + resolution: {integrity: sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/preset-typescript@7.27.1': - resolution: {integrity: sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==} + '@babel/preset-typescript@7.28.5': + resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -961,12 +967,12 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.4': - resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.4': - resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} '@chromatic-com/storybook@4.1.2': @@ -983,14 +989,11 @@ packages: '@date-fns/tz@1.4.1': resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==} - '@emnapi/core@1.7.1': - resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} + '@emnapi/core@1.8.1': + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} - '@emnapi/runtime@1.5.0': - resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} - - '@emnapi/runtime@1.7.1': - resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} + '@emnapi/runtime@1.8.1': + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} @@ -1007,328 +1010,168 @@ packages: '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} - '@esbuild/aix-ppc64@0.25.11': - resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.9': - resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.25.11': - resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.9': - resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.25.11': - resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.9': - resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.25.11': - resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.9': - resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.25.11': - resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.9': - resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.11': - resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.25.9': - resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.25.11': - resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.9': - resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.25.11': - resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.9': - resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.25.11': - resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.9': - resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.25.11': - resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.9': - resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.25.11': - resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.9': - resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.25.11': - resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.9': - resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.25.11': - resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.9': - resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.25.11': - resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.9': - resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.11': - resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.9': - resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.25.11': - resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.25.9': - resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.25.11': - resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.9': - resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.25.11': - resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.25.9': - resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.25.11': - resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.9': - resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.25.11': - resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.25.9': - resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.11': - resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.9': - resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.25.11': - resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.25.9': - resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.25.11': - resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.9': - resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.25.11': - resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.9': - resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.25.11': - resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.9': - resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.25.11': - resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.9': - resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@eslint-community/eslint-utils@4.9.0': - resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint-community/regexpp@4.12.2': resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -1351,11 +1194,11 @@ packages: '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} - '@floating-ui/dom@1.7.3': - resolution: {integrity: sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==} + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} - '@floating-ui/react-dom@2.1.5': - resolution: {integrity: sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==} + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' @@ -1363,8 +1206,8 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} - '@gerrit0/mini-shiki@3.14.0': - resolution: {integrity: sha512-c5X8fwPLOtUS8TVdqhynz9iV0GlOtFUT1ppXYzUUlEXe4kbZ/mvMT8wXoT8kCwUka+zsiloq7sD3pZ3+QVTuNQ==} + '@gerrit0/mini-shiki@3.20.0': + resolution: {integrity: sha512-Wa57i+bMpK6PGJZ1f2myxo3iO+K/kZikcyvH8NIqNNZhQUbDav7V9LQmWOXhf946mz5c1NZ19WMsGYiDKTryzQ==} '@hookform/resolvers@5.2.2': resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==} @@ -1388,138 +1231,153 @@ packages: resolution: {integrity: sha512-AoFbSarOqFBYH+1TZ9Ahkm2IWYSi5v0pBk88fpV+5b3qGJukypX8PwvCWADjuyIccKg48/F73a6hTTkBzDQ2UA==} engines: {node: '>=16.0.0'} - '@ibm-cloud/openapi-ruleset@1.33.3': - resolution: {integrity: sha512-lOxglXIzUZwsw5WsbgZraxxzAYMdXYyiMNOioxYJYTd55ZuN4XEERoPdV5v1oPTdKedHEUSQu5siiSHToENFdA==} + '@ibm-cloud/openapi-ruleset@1.33.5': + resolution: {integrity: sha512-oT8USsTulFAA8FiBN0lA2rJqQI2lIt+HP2pdakGQXo3EviL2vqJTgpSCRwjl6mLJL158f1BVcdQUOEFGxomK3w==} engines: {node: '>=16.0.0'} - '@img/sharp-darwin-arm64@0.34.3': - resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.34.3': - resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==} + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.0': - resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==} + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.0': - resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==} + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.2.0': - resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm@1.2.0': - resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-ppc64@1.2.0': - resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==} + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] - '@img/sharp-libvips-linux-s390x@1.2.0': - resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-x64@1.2.0': - resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.2.0': - resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.2.0': - resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} cpu: [x64] os: [linux] - '@img/sharp-linux-arm64@0.34.3': - resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linux-arm@0.34.3': - resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - '@img/sharp-linux-ppc64@0.34.3': - resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==} + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ppc64] os: [linux] - '@img/sharp-linux-s390x@0.34.3': - resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - '@img/sharp-linux-x64@0.34.3': - resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.34.3': - resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-x64@0.34.3': - resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-wasm32@0.34.3': - resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.3': - resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==} + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.34.3': - resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==} + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.34.3': - resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==} + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] - '@inquirer/ansi@1.0.1': - resolution: {integrity: sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw==} + '@inquirer/ansi@1.0.2': + resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} engines: {node: '>=18'} - '@inquirer/confirm@5.1.19': - resolution: {integrity: sha512-wQNz9cfcxrtEnUyG5PndC8g3gZ7lGDBzmWiXZkX8ot3vfZ+/BLjR8EvyGX4YzQLeVqtAlY/YScZpW7CW8qMoDQ==} + '@inquirer/confirm@5.1.21': + resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -1527,8 +1385,8 @@ packages: '@types/node': optional: true - '@inquirer/core@10.3.0': - resolution: {integrity: sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA==} + '@inquirer/core@10.3.2': + resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -1536,12 +1394,12 @@ packages: '@types/node': optional: true - '@inquirer/figures@1.0.14': - resolution: {integrity: sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ==} + '@inquirer/figures@1.0.15': + resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} engines: {node: '>=18'} - '@inquirer/type@3.0.9': - resolution: {integrity: sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w==} + '@inquirer/type@3.0.10': + resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -1569,8 +1427,8 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.30': - resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@jsep-plugin/assignment@1.3.0': resolution: {integrity: sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==} @@ -1865,8 +1723,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/semantic-conventions@1.37.0': - resolution: {integrity: sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA==} + '@opentelemetry/semantic-conventions@1.38.0': + resolution: {integrity: sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==} engines: {node: '>=14'} '@opentelemetry/sql-common@0.41.2': @@ -2242,6 +2100,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-primitive@2.1.4': + resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-radio-group@1.3.8': resolution: {integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==} peerDependencies: @@ -2307,6 +2178,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-slider@1.3.6': + resolution: {integrity: sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-slot@1.2.3': resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: @@ -2316,6 +2200,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-slot@1.2.4': + resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-switch@1.2.6': resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} peerDependencies: @@ -2465,8 +2358,8 @@ packages: '@radix-ui/rect@1.1.1': resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - '@reduxjs/toolkit@2.9.0': - resolution: {integrity: sha512-fSfQlSRu9Z5yBkvsNhYF2rPS8cGXn/TZVrlwN1948QyZ8xMZ0JvP50S2acZNaf+o63u6aEeMjipFyksjIcWrog==} + '@reduxjs/toolkit@2.11.2': + resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} peerDependencies: react: ^16.9.0 || ^17.0.0 || ^18 || ^19 react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 @@ -2476,24 +2369,24 @@ packages: react-redux: optional: true - '@rjsf/core@5.24.13': - resolution: {integrity: sha512-ONTr14s7LFIjx2VRFLuOpagL76sM/HPy6/OhdBfq6UukINmTIs6+aFN0GgcR0aXQHFDXQ7f/fel0o/SO05Htdg==} - engines: {node: '>=14'} + '@rjsf/core@6.1.2': + resolution: {integrity: sha512-fcEO6kArMcVIzTBoBxNStqxzAL417NDw049nmNx11pIcMwUnU5sAkSW18c8kgZOT6v1xaZhQrY+X5cBzzHy9+g==} + engines: {node: '>=20'} peerDependencies: - '@rjsf/utils': ^5.24.x - react: ^16.14.0 || >=17 + '@rjsf/utils': ^6.x + react: '>=18' - '@rjsf/utils@5.24.13': - resolution: {integrity: sha512-rNF8tDxIwTtXzz5O/U23QU73nlhgQNYJ+Sv5BAwQOIyhIE2Z3S5tUiSVMwZHt0julkv/Ryfwi+qsD4FiE5rOuw==} - engines: {node: '>=14'} + '@rjsf/utils@6.1.2': + resolution: {integrity: sha512-Px3FIkE1KK0745Qng9v88RZ0O7hcLf/1JUu0j00g+r6C8Zyokna42Hz/5TKyyQSKJqgVYcj2Z47YroVLenUM3A==} + engines: {node: '>=20'} peerDependencies: - react: ^16.14.0 || >=17 + react: '>=18' - '@rjsf/validator-ajv8@5.24.13': - resolution: {integrity: sha512-oWHP7YK581M8I5cF1t+UXFavnv+bhcqjtL1a7MG/Kaffi0EwhgcYjODrD8SsnrhncsEYMqSECr4ZOEoirnEUWw==} - engines: {node: '>=14'} + '@rjsf/validator-ajv8@6.1.2': + resolution: {integrity: sha512-9P3np2d+TaZcFTEFLocbj19fqrAWB/bxtY0Y8EjP8Oiz8LL+/wUITaN3Wx9uxzWerJyphfpZXWhUS9XkllDLig==} + engines: {node: '>=20'} peerDependencies: - '@rjsf/utils': ^5.24.x + '@rjsf/utils': ^6.x '@rollup/plugin-commonjs@28.0.1': resolution: {integrity: sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==} @@ -2513,113 +2406,128 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.52.2': - resolution: {integrity: sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ==} + '@rollup/rollup-android-arm-eabi@4.55.1': + resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.52.2': - resolution: {integrity: sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw==} + '@rollup/rollup-android-arm64@4.55.1': + resolution: {integrity: sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.52.2': - resolution: {integrity: sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA==} + '@rollup/rollup-darwin-arm64@4.55.1': + resolution: {integrity: sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.52.2': - resolution: {integrity: sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ==} + '@rollup/rollup-darwin-x64@4.55.1': + resolution: {integrity: sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.52.2': - resolution: {integrity: sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw==} + '@rollup/rollup-freebsd-arm64@4.55.1': + resolution: {integrity: sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.52.2': - resolution: {integrity: sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng==} + '@rollup/rollup-freebsd-x64@4.55.1': + resolution: {integrity: sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.52.2': - resolution: {integrity: sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA==} + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': + resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.52.2': - resolution: {integrity: sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w==} + '@rollup/rollup-linux-arm-musleabihf@4.55.1': + resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.52.2': - resolution: {integrity: sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg==} + '@rollup/rollup-linux-arm64-gnu@4.55.1': + resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.52.2': - resolution: {integrity: sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA==} + '@rollup/rollup-linux-arm64-musl@4.55.1': + resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.52.2': - resolution: {integrity: sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw==} + '@rollup/rollup-linux-loong64-gnu@4.55.1': + resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.52.2': - resolution: {integrity: sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A==} + '@rollup/rollup-linux-loong64-musl@4.55.1': + resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.55.1': + resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.52.2': - resolution: {integrity: sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w==} + '@rollup/rollup-linux-ppc64-musl@4.55.1': + resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.55.1': + resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.52.2': - resolution: {integrity: sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q==} + '@rollup/rollup-linux-riscv64-musl@4.55.1': + resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.52.2': - resolution: {integrity: sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ==} + '@rollup/rollup-linux-s390x-gnu@4.55.1': + resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.52.2': - resolution: {integrity: sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA==} + '@rollup/rollup-linux-x64-gnu@4.55.1': + resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.52.2': - resolution: {integrity: sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ==} + '@rollup/rollup-linux-x64-musl@4.55.1': + resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.52.2': - resolution: {integrity: sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q==} + '@rollup/rollup-openbsd-x64@4.55.1': + resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.55.1': + resolution: {integrity: sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.52.2': - resolution: {integrity: sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA==} + '@rollup/rollup-win32-arm64-msvc@4.55.1': + resolution: {integrity: sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.2': - resolution: {integrity: sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A==} + '@rollup/rollup-win32-ia32-msvc@4.55.1': + resolution: {integrity: sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.52.2': - resolution: {integrity: sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA==} + '@rollup/rollup-win32-x64-gnu@4.55.1': + resolution: {integrity: sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.2': - resolution: {integrity: sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw==} + '@rollup/rollup-win32-x64-msvc@4.55.1': + resolution: {integrity: sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==} cpu: [x64] os: [win32] @@ -2648,10 +2556,6 @@ packages: resolution: {integrity: sha512-tKSzHq1hNzB619Ssrqo25cqdQJ84R3xSSLsUWEnkGO/wcXJvpZy94gwdoS+KmH18BB1iRRRGtnMxZcUkiPSesw==} engines: {node: '>=18'} - '@sentry/babel-plugin-component-annotate@4.3.0': - resolution: {integrity: sha512-OuxqBprXRyhe8Pkfyz/4yHQJc5c3lm+TmYWSSx8u48g5yKewSQDOxkiLU5pAk3WnbLPy8XwU/PN+2BG0YFU9Nw==} - engines: {node: '>= 14'} - '@sentry/babel-plugin-component-annotate@4.6.1': resolution: {integrity: sha512-aSIk0vgBqv7PhX6/Eov+vlI4puCE0bRXzUG5HdCsHBpAfeMkI8Hva6kSOusnzKqs8bf04hU7s3Sf0XxGTj/1AA==} engines: {node: '>= 14'} @@ -2660,115 +2564,59 @@ packages: resolution: {integrity: sha512-G8q362DdKp9y1b5qkQEmhTFzyWTOVB0ps1rflok0N6bVA75IEmSDX1pqJsNuY3qy14VsVHYVwQBJQsNltQLS0g==} engines: {node: '>=18'} - '@sentry/bundler-plugin-core@4.3.0': - resolution: {integrity: sha512-dmR4DJhJ4jqVWGWppuTL2blNFqOZZnt4aLkewbD1myFG3KVfUx8CrMQWEmGjkgPOtj5TO6xH9PyTJjXC6o5tnA==} - engines: {node: '>= 14'} - '@sentry/bundler-plugin-core@4.6.1': resolution: {integrity: sha512-WPeRbnMXm927m4Kr69NTArPfI+p5/34FHftdCRI3LFPMyhZDzz6J3wLy4hzaVUgmMf10eLzmq2HGEMvpQmdynA==} engines: {node: '>= 14'} - '@sentry/cli-darwin@2.55.0': - resolution: {integrity: sha512-jGHE7SHHzqXUmnsmRLgorVH6nmMmTjQQXdPZbSL5tRtH8d3OIYrVNr5D72DSgD26XAPBDMV0ibqOQ9NKoiSpfA==} + '@sentry/cli-darwin@2.58.4': + resolution: {integrity: sha512-kbTD+P4X8O+nsNwPxCywtj3q22ecyRHWff98rdcmtRrvwz8CKi/T4Jxn/fnn2i4VEchy08OWBuZAqaA5Kh2hRQ==} engines: {node: '>=10'} os: [darwin] - '@sentry/cli-darwin@2.58.2': - resolution: {integrity: sha512-MArsb3zLhA2/cbd4rTm09SmTpnEuZCoZOpuZYkrpDw1qzBVJmRFA1W1hGAQ9puzBIk/ubY3EUhhzuU3zN2uD6w==} - engines: {node: '>=10'} - os: [darwin] - - '@sentry/cli-linux-arm64@2.55.0': - resolution: {integrity: sha512-jNB/0/gFcOuDCaY/TqeuEpsy/k52dwyk1SOV3s1ku4DUsln6govTppeAGRewY3T1Rj9B2vgIWTrnB8KVh9+Rgg==} + '@sentry/cli-linux-arm64@2.58.4': + resolution: {integrity: sha512-0g0KwsOozkLtzN8/0+oMZoOuQ0o7W6O+hx+ydVU1bktaMGKEJLMAWxOQNjsh1TcBbNIXVOKM/I8l0ROhaAb8Ig==} engines: {node: '>=10'} cpu: [arm64] os: [linux, freebsd, android] - '@sentry/cli-linux-arm64@2.58.2': - resolution: {integrity: sha512-ay3OeObnbbPrt45cjeUyQjsx5ain1laj1tRszWj37NkKu55NZSp4QCg1gGBZ0gBGhckI9nInEsmKtix00alw2g==} - engines: {node: '>=10'} - cpu: [arm64] - os: [linux, freebsd, android] - - '@sentry/cli-linux-arm@2.55.0': - resolution: {integrity: sha512-ATjU0PsiWADSPLF/kZroLZ7FPKd5W9TDWHVkKNwIUNTei702LFgTjNeRwOIzTgSvG3yTmVEqtwFQfFN/7hnVXQ==} + '@sentry/cli-linux-arm@2.58.4': + resolution: {integrity: sha512-rdQ8beTwnN48hv7iV7e7ZKucPec5NJkRdrrycMJMZlzGBPi56LqnclgsHySJ6Kfq506A2MNuQnKGaf/sBC9REA==} engines: {node: '>=10'} cpu: [arm] os: [linux, freebsd, android] - '@sentry/cli-linux-arm@2.58.2': - resolution: {integrity: sha512-HU9lTCzcHqCz/7Mt5n+cv+nFuJdc1hGD2h35Uo92GgxX3/IujNvOUfF+nMX9j6BXH6hUt73R5c0Ycq9+a3Parg==} - engines: {node: '>=10'} - cpu: [arm] - os: [linux, freebsd, android] - - '@sentry/cli-linux-i686@2.55.0': - resolution: {integrity: sha512-8LZjo6PncTM6bWdaggscNOi5r7F/fqRREsCwvd51dcjGj7Kp1plqo9feEzYQ+jq+KUzVCiWfHrUjddFmYyZJrg==} + '@sentry/cli-linux-i686@2.58.4': + resolution: {integrity: sha512-NseoIQAFtkziHyjZNPTu1Gm1opeQHt7Wm1LbLrGWVIRvUOzlslO9/8i6wETUZ6TjlQxBVRgd3Q0lRBG2A8rFYA==} engines: {node: '>=10'} cpu: [x86, ia32] os: [linux, freebsd, android] - '@sentry/cli-linux-i686@2.58.2': - resolution: {integrity: sha512-CN9p0nfDFsAT1tTGBbzOUGkIllwS3hygOUyTK7LIm9z+UHw5uNgNVqdM/3Vg+02ymjkjISNB3/+mqEM5osGXdA==} - engines: {node: '>=10'} - cpu: [x86, ia32] - os: [linux, freebsd, android] - - '@sentry/cli-linux-x64@2.55.0': - resolution: {integrity: sha512-5LUVvq74Yj2cZZy5g5o/54dcWEaX4rf3myTHy73AKhRj1PABtOkfexOLbF9xSrZy95WXWaXyeH+k5n5z/vtHfA==} + '@sentry/cli-linux-x64@2.58.4': + resolution: {integrity: sha512-d3Arz+OO/wJYTqCYlSN3Ktm+W8rynQ/IMtSZLK8nu0ryh5mJOh+9XlXY6oDXw4YlsM8qCRrNquR8iEI1Y/IH+Q==} engines: {node: '>=10'} cpu: [x64] os: [linux, freebsd, android] - '@sentry/cli-linux-x64@2.58.2': - resolution: {integrity: sha512-oX/LLfvWaJO50oBVOn4ZvG2SDWPq0MN8SV9eg5tt2nviq+Ryltfr7Rtoo+HfV+eyOlx1/ZXhq9Wm7OT3cQuz+A==} - engines: {node: '>=10'} - cpu: [x64] - os: [linux, freebsd, android] - - '@sentry/cli-win32-arm64@2.55.0': - resolution: {integrity: sha512-cWIQdzm1pfLwPARsV6dUb8TVd6Y3V1A2VWxjTons3Ift6GvtVmiAe0OWL8t2Yt95i8v61kTD/6Tq21OAaogqzA==} + '@sentry/cli-win32-arm64@2.58.4': + resolution: {integrity: sha512-bqYrF43+jXdDBh0f8HIJU3tbvlOFtGyRjHB8AoRuMQv9TEDUfENZyCelhdjA+KwDKYl48R1Yasb4EHNzsoO83w==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@sentry/cli-win32-arm64@2.58.2': - resolution: {integrity: sha512-+cl3x2HPVMpoSVGVM1IDWlAEREZrrVQj4xBb0TRKII7g3hUxRsAIcsrr7+tSkie++0FuH4go/b5fGAv51OEF3w==} - engines: {node: '>=10'} - cpu: [arm64] - os: [win32] - - '@sentry/cli-win32-i686@2.55.0': - resolution: {integrity: sha512-ldepCn2t9r4I0wvgk7NRaA7coJyy4rTQAzM66u9j5nTEsUldf66xym6esd5ZZRAaJUjffqvHqUIr/lrieTIrVg==} + '@sentry/cli-win32-i686@2.58.4': + resolution: {integrity: sha512-3triFD6jyvhVcXOmGyttf+deKZcC1tURdhnmDUIBkiDPJKGT/N5xa4qAtHJlAB/h8L9jgYih9bvJnvvFVM7yug==} engines: {node: '>=10'} cpu: [x86, ia32] os: [win32] - '@sentry/cli-win32-i686@2.58.2': - resolution: {integrity: sha512-omFVr0FhzJ8oTJSg1Kf+gjLgzpYklY0XPfLxZ5iiMiYUKwF5uo1RJRdkUOiEAv0IqpUKnmKcmVCLaDxsWclB7Q==} - engines: {node: '>=10'} - cpu: [x86, ia32] - os: [win32] - - '@sentry/cli-win32-x64@2.55.0': - resolution: {integrity: sha512-4hPc/I/9tXx+HLTdTGwlagtAfDSIa2AoTUP30tl32NAYQhx9a6niUbPAemK2qfxesiufJ7D2djX83rCw6WnJVA==} + '@sentry/cli-win32-x64@2.58.4': + resolution: {integrity: sha512-cSzN4PjM1RsCZ4pxMjI0VI7yNCkxiJ5jmWncyiwHXGiXrV1eXYdQ3n1LhUYLZ91CafyprR0OhDcE+RVZ26Qb5w==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@sentry/cli-win32-x64@2.58.2': - resolution: {integrity: sha512-2NAFs9UxVbRztQbgJSP5i8TB9eJQ7xraciwj/93djrSMHSEbJ0vC47TME0iifgvhlHMs5vqETOKJtfbbpQAQFA==} - engines: {node: '>=10'} - cpu: [x64] - os: [win32] - - '@sentry/cli@2.55.0': - resolution: {integrity: sha512-cynvcIM2xL8ddwELyFRSpZQw4UtFZzoM2rId2l9vg7+wDREPDocMJB9lEQpBIo3eqhp9JswqUT037yjO6iJ5Sw==} - engines: {node: '>= 10'} - hasBin: true - - '@sentry/cli@2.58.2': - resolution: {integrity: sha512-U4u62V4vaTWF+o40Mih8aOpQKqKUbZQt9A3LorIJwaE3tO3XFLRI70eWtW2se1Qmy0RZ74zB14nYcFNFl2t4Rw==} + '@sentry/cli@2.58.4': + resolution: {integrity: sha512-ArDrpuS8JtDYEvwGleVE+FgR+qHaOp77IgdGSacz6SZy6Lv90uX0Nu4UrHCQJz8/xwIcNxSqnN22lq0dH4IqTg==} engines: {node: '>= 10'} hasBin: true @@ -2818,23 +2666,23 @@ packages: resolution: {integrity: sha512-uBfpOnzSNSd2ITMTMeX5bV9Jlci9iMyI+iOPuW8c3oc+0dITTN0OpKLyNd6nfm50bM5h/1qFVQrph+oFTrtuGQ==} engines: {node: '>=18'} - '@sentry/webpack-plugin@4.3.0': - resolution: {integrity: sha512-K4nU1SheK/tvyakBws2zfd+MN6hzmpW+wPTbSbDWn1+WL9+g9hsPh8hjFFiVe47AhhUoUZ3YgiH2HyeHXjHflA==} + '@sentry/webpack-plugin@4.6.1': + resolution: {integrity: sha512-CJgT/t2pQWsPsMx9VJ86goU/orCQhL2HhDj5ZYBol6fPPoEGeTqKOPCnv/xsbCAfGSp1uHpyRLTA/Gx96u7VVA==} engines: {node: '>= 14'} peerDependencies: webpack: '>=4.40.0' - '@shikijs/engine-oniguruma@3.14.0': - resolution: {integrity: sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug==} + '@shikijs/engine-oniguruma@3.21.0': + resolution: {integrity: sha512-OYknTCct6qiwpQDqDdf3iedRdzj6hFlOPv5hMvI+hkWfCKs5mlJ4TXziBG9nyabLwGulrUjHiCq3xCspSzErYQ==} - '@shikijs/langs@3.14.0': - resolution: {integrity: sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg==} + '@shikijs/langs@3.21.0': + resolution: {integrity: sha512-g6mn5m+Y6GBJ4wxmBYqalK9Sp0CFkUqfNzUy2pJglUginz6ZpWbaWjDB4fbQ/8SHzFjYbtU6Ddlp1pc+PPNDVA==} - '@shikijs/themes@3.14.0': - resolution: {integrity: sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA==} + '@shikijs/themes@3.21.0': + resolution: {integrity: sha512-BAE4cr9EDiZyYzwIHEk7JTBJ9CzlPuM4PchfcA5ao1dWXb25nv6hYsoDiBq2aZK9E3dlt3WB78uI96UESD+8Mw==} - '@shikijs/types@3.14.0': - resolution: {integrity: sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ==} + '@shikijs/types@3.21.0': + resolution: {integrity: sha512-zGrWOxZ0/+0ovPY7PvBU2gIS9tmhSUUt30jAcNV0Bq0gb2S98gwfjIs1vxlmH5zM7/4YxLamT6ChlqqAJmPPjA==} '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} @@ -2842,6 +2690,9 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} @@ -2964,8 +2815,8 @@ packages: '@storybook/global@5.0.0': resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} - '@storybook/icons@1.4.0': - resolution: {integrity: sha512-Td73IeJxOyalzvjQL+JXx72jlIYHgs+REaHiREOqfpo3A2AYYG71AUbcv+lg7mEDIweKVCxsMQ0UKo634c8XeA==} + '@storybook/icons@1.6.0': + resolution: {integrity: sha512-hcFZIjW8yQz8O8//2WTIXylm5Xsgc+lW9ISLgUk1xGmptIJQRdlhVIXCpSyLrQaaRiyhQRaVg7l3BD9S216BHw==} engines: {node: '>=14.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta @@ -3091,8 +2942,8 @@ packages: resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} - '@testing-library/jest-dom@6.8.0': - resolution: {integrity: sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==} + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} '@testing-library/user-event@14.6.1': @@ -3122,14 +2973,14 @@ packages: '@types/canvas-confetti@1.9.0': resolution: {integrity: sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==} - '@types/chai@5.2.2': - resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/d3-array@3.2.1': - resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} '@types/d3-color@3.1.3': resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} @@ -3242,8 +3093,8 @@ packages: '@types/pg@8.15.6': resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==} - '@types/phoenix@1.6.6': - resolution: {integrity: sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==} + '@types/phoenix@1.6.7': + resolution: {integrity: sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==} '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} @@ -3271,8 +3122,8 @@ packages: '@types/statuses@2.0.6': resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} - '@types/stylis@4.2.5': - resolution: {integrity: sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==} + '@types/stylis@4.2.7': + resolution: {integrity: sha512-VgDNokpBoKF+wrdvhAAfS55OMQpL6QRglwTwNC3kIgBrzZxA4WsFj+2eLfEA/uMUDzBcEhYmjSbwQakn/i3ajA==} '@types/tedious@4.0.14': resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==} @@ -3292,137 +3143,63 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@typescript-eslint/eslint-plugin@8.48.1': - resolution: {integrity: sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA==} + '@typescript-eslint/eslint-plugin@8.52.0': + resolution: {integrity: sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.48.1 + '@typescript-eslint/parser': ^8.52.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.48.1': - resolution: {integrity: sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==} + '@typescript-eslint/parser@8.52.0': + resolution: {integrity: sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.43.0': - resolution: {integrity: sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw==} + '@typescript-eslint/project-service@8.52.0': + resolution: {integrity: sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.46.2': - resolution: {integrity: sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==} + '@typescript-eslint/scope-manager@8.52.0': + resolution: {integrity: sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.52.0': + resolution: {integrity: sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.48.1': - resolution: {integrity: sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/scope-manager@8.43.0': - resolution: {integrity: sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/scope-manager@8.46.2': - resolution: {integrity: sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/scope-manager@8.48.1': - resolution: {integrity: sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/tsconfig-utils@8.43.0': - resolution: {integrity: sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/tsconfig-utils@8.46.2': - resolution: {integrity: sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/tsconfig-utils@8.48.1': - resolution: {integrity: sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/type-utils@8.48.1': - resolution: {integrity: sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg==} + '@typescript-eslint/type-utils@8.52.0': + resolution: {integrity: sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.43.0': - resolution: {integrity: sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw==} + '@typescript-eslint/types@8.52.0': + resolution: {integrity: sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.46.2': - resolution: {integrity: sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/types@8.48.1': - resolution: {integrity: sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@8.43.0': - resolution: {integrity: sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw==} + '@typescript-eslint/typescript-estree@8.52.0': + resolution: {integrity: sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.46.2': - resolution: {integrity: sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/typescript-estree@8.48.1': - resolution: {integrity: sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/utils@8.43.0': - resolution: {integrity: sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g==} + '@typescript-eslint/utils@8.52.0': + resolution: {integrity: sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.46.2': - resolution: {integrity: sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/utils@8.48.1': - resolution: {integrity: sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/visitor-keys@8.43.0': - resolution: {integrity: sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/visitor-keys@8.46.2': - resolution: {integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/visitor-keys@8.48.1': - resolution: {integrity: sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==} + '@typescript-eslint/visitor-keys@8.52.0': + resolution: {integrity: sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -3640,6 +3417,9 @@ packages: '@webassemblyjs/wast-printer@1.14.1': resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + '@x0k/json-schema-merge@1.0.2': + resolution: {integrity: sha512-1734qiJHNX3+cJGDMMw2yz7R+7kpbAtl5NdPs1c/0gO5kYT6s4dMbLXiIfpZNsOYhGZI3aH7FWrj4Zxz7epXNg==} + '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -3848,12 +3628,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.10.3: - resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==} - engines: {node: '>=4'} - - axe-core@4.11.0: - resolution: {integrity: sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==} + axe-core@4.11.1: + resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==} engines: {node: '>=4'} axe-html-reporter@2.2.11: @@ -3902,6 +3678,10 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.9.11: + resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} + hasBin: true + better-opn@3.0.2: resolution: {integrity: sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==} engines: {node: '>=12.0.0'} @@ -3951,15 +3731,15 @@ packages: resolution: {integrity: sha512-YBjSAiTqM04ZVei6sXighu679a3SqWORA3qZTEqZImnlkDIFtKc6pNutpjyZ8RJTjQtuYfeetkxM11GwoYXMIQ==} engines: {node: '>= 0.10'} - browserify-sign@4.2.3: - resolution: {integrity: sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==} - engines: {node: '>= 0.12'} + browserify-sign@4.2.5: + resolution: {integrity: sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==} + engines: {node: '>= 0.10'} browserify-zlib@0.2.0: resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} - browserslist@4.25.4: - resolution: {integrity: sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -4004,8 +3784,8 @@ packages: camelize@1.0.1: resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} - caniuse-lite@1.0.30001741: - resolution: {integrity: sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==} + caniuse-lite@1.0.30001762: + resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==} case-sensitive-paths-webpack-plugin@2.4.0: resolution: {integrity: sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==} @@ -4034,8 +3814,8 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - check-error@2.1.1: - resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} engines: {node: '>= 16'} chokidar@3.6.0: @@ -4119,13 +3899,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - - color@4.2.3: - resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} - engines: {node: '>=12.5.0'} - colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} @@ -4156,12 +3929,6 @@ packages: compare-versions@6.1.1: resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} - compute-gcd@1.2.1: - resolution: {integrity: sha512-TwMbxBNz0l71+8Sc4czv13h4kEqnchV9igQZBi6QUaz09dnz13juGnnaWWJTRsP3brxOoxeB4SA2WELLw1hCtg==} - - compute-lcm@1.1.2: - resolution: {integrity: sha512-OFNPdQAXnQhDSKioX8/XYT6sdUlXwpeMjfd6ApxMJfyZ4GxmLR1xvMERctlYhlHwIiz6CSpBc2+qYKjHGZw4TQ==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -4186,11 +3953,11 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} - core-js-compat@3.45.1: - resolution: {integrity: sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==} + core-js-compat@3.47.0: + resolution: {integrity: sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==} - core-js-pure@3.45.1: - resolution: {integrity: sha512-OHnWFKgTUshEU8MK+lOs1H8kC8GkTi9Z1tvNkxrCcw9wl3MJIO7q2ld77wjWn4/xuGrVu2X+nME1iIIPBSdyEQ==} + core-js-pure@3.47.0: + resolution: {integrity: sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -4264,8 +4031,8 @@ packages: engines: {node: '>=4'} hasBin: true - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} d3-array@3.2.4: resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} @@ -4418,8 +4185,8 @@ packages: des.js@1.1.0: resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} - detect-libc@2.0.4: - resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} detect-node-es@1.1.0: @@ -4428,6 +4195,9 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + dexie@4.2.1: + resolution: {integrity: sha512-Ckej0NS6jxQ4Po3OrSQBFddayRhTCic2DoCAG5zacOfOVB9P2Q5Xc5uL/nVa7ZVs+HdMnvUPzLFCB/JwpB6Csg==} + didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -4493,8 +4263,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.214: - resolution: {integrity: sha512-TpvUNdha+X3ybfU78NoQatKvQEm1oq3lf2QbnmCEdw+Bd9RuIAY+hJTvq1avzHM0f7EJfnH3vbCnbzKzisc/9Q==} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} @@ -4525,8 +4295,8 @@ packages: endent@2.1.0: resolution: {integrity: sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==} - enhanced-resolve@5.18.3: - resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + enhanced-resolve@5.18.4: + resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} engines: {node: '>=10.13.0'} enquirer@2.4.1: @@ -4548,14 +4318,14 @@ packages: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} error-stack-parser@2.1.4: resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} - es-abstract@1.24.0: - resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} es-aggregate-error@1.0.14: @@ -4570,13 +4340,16 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.2.1: - resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + es-iterator-helpers@1.2.2: + resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} engines: {node: '>= 0.4'} es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -4593,8 +4366,8 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} - es-toolkit@1.39.10: - resolution: {integrity: sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==} + es-toolkit@1.43.0: + resolution: {integrity: sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==} es6-promise@3.3.1: resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} @@ -4604,13 +4377,8 @@ packages: peerDependencies: esbuild: '>=0.12 <1' - esbuild@0.25.11: - resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} - engines: {node: '>=18'} - hasBin: true - - esbuild@0.25.9: - resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} hasBin: true @@ -4738,8 +4506,8 @@ packages: engines: {node: '>=4'} hasBin: true - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -4820,11 +4588,11 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - fast-uri@3.0.6: - resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -4924,8 +4692,8 @@ packages: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} - fs-extra@11.3.2: - resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} + fs-extra@11.3.3: + resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} engines: {node: '>=14.14'} fs-monkey@1.1.0: @@ -5008,10 +4776,6 @@ packages: glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true - glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} hasBin: true @@ -5020,10 +4784,6 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported - glob@9.3.5: - resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} - engines: {node: '>=16 || 14 >=14.17'} - globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} @@ -5046,8 +4806,8 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - graphql@16.11.0: - resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} + graphql@16.12.0: + resolution: {integrity: sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} has-bigints@1.1.0: @@ -5152,8 +4912,8 @@ packages: html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} - html-webpack-plugin@5.6.4: - resolution: {integrity: sha512-V/PZeWsqhfpE27nKeX9EO2sbR+D17A+tLf6qU+ht66jdUsN0QLKJN27Z+1+gHrVMKgndBahes0PU6rRihDgHTw==} + html-webpack-plugin@5.6.5: + resolution: {integrity: sha512-4xynFbKNNk+WlzXeQQ+6YYsH2g7mpfPszQZUi3ovKlj+pDmngQ7vRXjrrmGROabmKwyQkcgcX5hqfOwHbFmK5g==} engines: {node: '>=10.13.0'} peerDependencies: '@rspack/core': 0.x || 1.x @@ -5203,8 +4963,11 @@ packages: engines: {node: '>=16.x'} hasBin: true - immer@10.1.3: - resolution: {integrity: sha512-tmjF/k8QDKydUlm3mZU+tjM6zeq9/fFpPqH9SzWmBnVVKsPBg/V66qsMwb3/Bo90cgUN+ghdVBess+hPsxUyRw==} + immer@10.2.0: + resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} + + immer@11.1.3: + resolution: {integrity: sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==} immer@9.0.21: resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} @@ -5213,8 +4976,8 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} - import-in-the-middle@2.0.0: - resolution: {integrity: sha512-yNZhyQYqXpkT0AKq3F3KLasUSK4fHvebNH5hOsKQw2dhGSALvQ4U0BqUc5suziKvydO5u5hgN2hy1RJaho8U5A==} + import-in-the-middle@2.0.1: + resolution: {integrity: sha512-bruMpJ7xz+9jwGzrwEhWgvRrlKRYCRDBrfU+ur3FcasYXLJDxTruJ//8g2Noj+QFyRBeqbpj8Bhn4Fbw6HjvhA==} imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} @@ -5234,8 +4997,8 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - inline-style-parser@0.2.4: - resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} @@ -5262,9 +5025,6 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - is-async-function@2.1.1: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} @@ -5320,10 +5080,6 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-function@1.1.0: - resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} - engines: {node: '>= 0.4'} - is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -5440,8 +5196,8 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - jiti@2.5.1: - resolution: {integrity: sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w==} + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true js-tokens@4.0.0: @@ -5451,15 +5207,14 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + jsep@1.4.0: resolution: {integrity: sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==} engines: {node: '>= 10.16.0'} - jsesc@3.0.2: - resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} - engines: {node: '>=6'} - hasBin: true - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -5471,13 +5226,6 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-compare@0.2.2: - resolution: {integrity: sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==} - - json-schema-merge-allof@0.8.1: - resolution: {integrity: sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w==} - engines: {node: '>=12.0.0'} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -5566,8 +5314,8 @@ packages: linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - loader-runner@4.3.0: - resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==} + loader-runner@4.3.1: + resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} engines: {node: '>=6.11.5'} loader-utils@2.0.4: @@ -5590,8 +5338,8 @@ packages: resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lodash-es@4.17.21: - resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + lodash-es@4.17.22: + resolution: {integrity: sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==} lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} @@ -5664,8 +5412,8 @@ packages: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true - magic-string@0.30.19: - resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} magic-string@0.30.8: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} @@ -5682,11 +5430,14 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - markdown-to-jsx@7.7.13: - resolution: {integrity: sha512-DiueEq2bttFcSxUs85GJcQVrOr0+VVsPfj9AEUPqmExJ3f8P/iQNvZHltV4tm1XVhu1kl0vWBZWT3l99izRMaA==} + markdown-to-jsx@8.0.0: + resolution: {integrity: sha512-hWEaRxeCDjes1CVUQqU+Ov0mCqBqkGhLKjL98KdbwHSgEWZZSJQeGlJQatVfeZ3RaxrfTrZZ3eczl2dhp5c/pA==} engines: {node: '>= 10'} peerDependencies: react: '>= 0.14.0' + peerDependenciesMeta: + react: + optional: true math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} @@ -5734,8 +5485,8 @@ packages: mdast-util-phrasing@4.1.0: resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} - mdast-util-to-hast@13.2.0: - resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} mdast-util-to-markdown@2.1.2: resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} @@ -5884,10 +5635,6 @@ packages: resolution: {integrity: sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==} engines: {node: '>=10'} - minimatch@8.0.4: - resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} - engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -5895,10 +5642,6 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@4.2.8: - resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} - engines: {node: '>=8'} - minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} @@ -5909,11 +5652,11 @@ packages: moment@2.30.1: resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} - motion-dom@12.23.23: - resolution: {integrity: sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==} + motion-dom@12.24.8: + resolution: {integrity: sha512-wX64WITk6gKOhaTqhsFqmIkayLAAx45SVFiMnJIxIrH5uqyrwrxjrfo8WX9Kh8CaUAixjeMn82iH0W0QT9wD5w==} - motion-utils@12.23.6: - resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} + motion-utils@12.23.28: + resolution: {integrity: sha512-0W6cWd5Okoyf8jmessVK3spOmbyE0yTdNKujHctHH9XdAE4QDuZ1/LjSXC68rrhsJU+TkzXURC5OdSWh9ibOwQ==} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -6019,8 +5762,8 @@ packages: node-readfiles@0.2.0: resolution: {integrity: sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==} - node-releases@2.0.20: - resolution: {integrity: sha512-7gK6zSXEH6neM212JgfYFXe+GmZQM+fia5SsusuBIUgnPheLFBmIPhtFoAQRj8/7wASYQnbDlHPVwY0BefoFgA==} + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -6189,8 +5932,8 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-asn1@5.1.7: - resolution: {integrity: sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==} + parse-asn1@5.1.9: + resolution: {integrity: sha512-fIYNuZ/HastSb80baGOuPRo1O9cf4baWw5WsAp7dBuUzeTD/BoaG8sVTdlPFksBE2lF21dN+A1AnrpIjSWqHHg==} engines: {node: '>= 0.10'} parse-entities@4.0.2: @@ -6312,8 +6055,8 @@ packages: peerDependencies: postcss: ^8.0.0 - postcss-js@4.0.1: - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 @@ -6377,8 +6120,8 @@ packages: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} - postcss-selector-parser@7.1.0: - resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + postcss-selector-parser@7.1.1: + resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} postcss-value-parser@4.2.0: @@ -6400,8 +6143,8 @@ packages: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} - postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + postgres-bytea@1.0.1: + resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==} engines: {node: '>=0.10.0'} postgres-date@1.0.7: @@ -6517,8 +6260,8 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + qs@6.14.1: + resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} engines: {node: '>=0.6'} querystring-es3@0.2.1: @@ -6630,8 +6373,8 @@ packages: '@types/react': optional: true - react-remove-scroll@2.7.1: - resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==} + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} engines: {node: '>=10'} peerDependencies: '@types/react': '*' @@ -6721,8 +6464,8 @@ packages: reftools@1.1.9: resolution: {integrity: sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==} - regenerate-unicode-properties@10.2.0: - resolution: {integrity: sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==} + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} engines: {node: '>=4'} regenerate@1.4.2: @@ -6735,15 +6478,15 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} - regexpu-core@6.2.0: - resolution: {integrity: sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==} + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} engines: {node: '>=4'} regjsgen@0.8.0: resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} - regjsparser@0.12.0: - resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} + regjsparser@0.13.0: + resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} hasBin: true rehype-autolink-headings@7.1.0: @@ -6810,11 +6553,6 @@ packages: resolution: {integrity: sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==} engines: {node: '>=12'} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} - engines: {node: '>= 0.4'} - hasBin: true - resolve@1.22.11: resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} @@ -6844,8 +6582,8 @@ packages: resolution: {integrity: sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==} engines: {node: '>= 0.8'} - rollup@4.52.2: - resolution: {integrity: sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==} + rollup@4.55.1: + resolution: {integrity: sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -6876,8 +6614,8 @@ packages: safe-stable-stringify@1.1.1: resolution: {integrity: sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==} - sass-loader@16.0.5: - resolution: {integrity: sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==} + sass-loader@16.0.6: + resolution: {integrity: sha512-sglGzId5gmlfxNs4gK2U3h7HlVRfx278YK6Ono5lwzuvi1jxig80YiuHkaDBVsYIKFhx8wN7XSCI0M2IDS/3qA==} engines: {node: '>= 18.12.0'} peerDependencies: '@rspack/core': 0.x || 1.x @@ -6904,19 +6642,14 @@ packages: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} - schema-utils@4.3.2: - resolution: {integrity: sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==} + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} engines: {node: '>= 10.13.0'} semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} - engines: {node: '>=10'} - hasBin: true - semver@7.7.3: resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} @@ -6948,8 +6681,8 @@ packages: shallowequal@1.1.0: resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} - sharp@0.34.3: - resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==} + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: @@ -7013,9 +6746,6 @@ packages: resolution: {integrity: sha512-LH7FpTAkeD+y5xQC4fzS+tFtaNlvt3Ib1zKzvhjv/Y+cioV4zIuw4IZr2yhRLu67CWL7FR9/6KXKnjRoZTvGGQ==} engines: {node: '>=12'} - simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -7144,8 +6874,8 @@ packages: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} - strip-indent@4.0.0: - resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} + strip-indent@4.1.1: + resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} engines: {node: '>=12'} strip-json-comments@3.1.1: @@ -7158,14 +6888,14 @@ packages: peerDependencies: webpack: ^5.0.0 - style-to-js@1.1.17: - resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==} + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} - style-to-object@1.0.9: - resolution: {integrity: sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==} + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} - styled-components@6.1.19: - resolution: {integrity: sha512-1v/e3Dl1BknC37cXMhwGomhO8AkYmN41CqyX9xhUDxry1ns3BFQy2lLDRQXJRdVVWB9OHemv/53xaStimvWyuA==} + styled-components@6.2.0: + resolution: {integrity: sha512-ryFCkETE++8jlrBmC+BoGPUN96ld1/Yp0s7t5bcXDobrs4XoXroY1tN+JbFi09hV6a5h3MzbcVi8/BGDP0eCgQ==} engines: {node: '>= 16'} peerDependencies: react: '>= 16.8.0' @@ -7197,11 +6927,11 @@ packages: babel-plugin-macros: optional: true - stylis@4.3.2: - resolution: {integrity: sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==} + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true @@ -7240,12 +6970,12 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tapable@2.2.3: - resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==} + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} - terser-webpack-plugin@5.3.14: - resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} + terser-webpack-plugin@5.3.16: + resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -7260,8 +6990,8 @@ packages: uglify-js: optional: true - terser@5.44.0: - resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==} + terser@5.44.1: + resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==} engines: {node: '>=10'} hasBin: true @@ -7293,15 +7023,15 @@ packages: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} - tinyspy@4.0.3: - resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} - tldts-core@7.0.17: - resolution: {integrity: sha512-DieYoGrP78PWKsrXr8MZwtQ7GLCUeLxihtjC1jZsW1DnvSMdKPitJSe8OSYDM2u5H6g3kWJZpePqkp43TfLh0g==} + tldts-core@7.0.19: + resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==} - tldts@7.0.17: - resolution: {integrity: sha512-Y1KQBgDd/NUc+LfOtKS6mNsC9CCaH+m2P1RoIZy7RAPo3C3/t8X45+zgut31cRZtZ3xKPjfn3TkGTrctC2TQIQ==} + tldts@7.0.19: + resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} hasBin: true to-buffer@1.2.2: @@ -7329,8 +7059,8 @@ packages: trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - ts-api-utils@2.1.0: - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -7423,8 +7153,8 @@ packages: peerDependencies: typedoc: 0.28.x - typedoc@0.28.14: - resolution: {integrity: sha512-ftJYPvpVfQvFzpkoSfHLkJybdA/geDJ8BGQt/ZnkkhnBYoYW6lBgPQXu6vqLxO4X75dA55hX8Af847H5KXlEFA==} + typedoc@0.28.15: + resolution: {integrity: sha512-mw2/2vTL7MlT+BVo43lOsufkkd2CJO4zeOSuWQQsiXoV2VuEn7f6IZp2jsUDPmBMABpgR0R5jlcJ2OGEFYmkyg==} engines: {node: '>= 18', pnpm: '>= 10'} hasBin: true peerDependencies: @@ -7453,12 +7183,12 @@ packages: resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} engines: {node: '>=4'} - unicode-match-property-value-ecmascript@2.2.0: - resolution: {integrity: sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==} + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} engines: {node: '>=4'} - unicode-property-aliases-ecmascript@2.1.0: - resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} engines: {node: '>=4'} unicorn-magic@0.1.0: @@ -7471,8 +7201,8 @@ packages: unist-util-find-after@5.0.0: resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} - unist-util-is@6.0.0: - resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} unist-util-position@5.0.0: resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} @@ -7483,8 +7213,8 @@ packages: unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - unist-util-visit-parents@6.0.1: - resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} @@ -7506,8 +7236,8 @@ packages: until-async@3.0.2: resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -7542,8 +7272,8 @@ packages: '@types/react': optional: true - use-sync-external-store@1.5.0: - resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -7572,23 +7302,8 @@ packages: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true - validate.io-array@1.0.6: - resolution: {integrity: sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==} - - validate.io-function@1.0.2: - resolution: {integrity: sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==} - - validate.io-integer-array@1.0.0: - resolution: {integrity: sha512-mTrMk/1ytQHtCY0oNO3dztafHYyGU88KL+jRxWuzfOmQb+4qqnWmI+gykvGp8usKZOM0H7keJHEbRaFiYA0VrA==} - - validate.io-integer@1.0.5: - resolution: {integrity: sha512-22izsYSLojN/P6bppBqhgUDjCkr5RY2jd+N2a3DCAUey8ydvrZ/OkGvFPR7qfOpwR2LC5p4Ngzxz36g5Vgr/hQ==} - - validate.io-number@1.0.3: - resolution: {integrity: sha512-kRAyotcbNaSYoDnXvb4MHg/0a1egJdLwS6oJ38TJY7aw9n93Fl/3blIXdyYvPOp55CNxywooG/3BcrwNrBpcSg==} - - validator@13.15.20: - resolution: {integrity: sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==} + validator@13.15.26: + resolution: {integrity: sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==} engines: {node: '>= 0.10'} vaul@1.1.2: @@ -7615,8 +7330,8 @@ packages: warning@4.0.3: resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} - watchpack@2.4.4: - resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} + watchpack@2.5.0: + resolution: {integrity: sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==} engines: {node: '>=10.13.0'} web-namespaces@2.0.1: @@ -7647,8 +7362,8 @@ packages: webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - webpack@5.101.3: - resolution: {integrity: sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==} + webpack@5.104.1: + resolution: {integrity: sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -7700,8 +7415,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.3: - resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -7731,8 +7446,8 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} - yaml@2.8.1: - resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + yaml@2.8.2: + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} engines: {node: '>= 14.6'} hasBin: true @@ -7748,8 +7463,8 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-queue@1.2.1: - resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} yoctocolors-cjs@2.1.3: @@ -7836,23 +7551,23 @@ snapshots: '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.4': {} + '@babel/compat-data@7.28.5': {} - '@babel/core@7.28.4': + '@babel/core@7.28.5': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 + '@babel/generator': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.4 + '@babel/parser': 7.28.5 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -7862,49 +7577,49 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.28.3': + '@babel/generator@7.28.5': dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.28.4 + '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.25.4 + browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)': + '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.28.4)': + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 - regexpu-core: 6.2.0 + regexpu-core: 6.4.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.28.4)': + '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 debug: 4.4.3 @@ -7915,646 +7630,646 @@ snapshots: '@babel/helper-globals@7.28.0': {} - '@babel/helper-member-expression-to-functions@7.27.1': + '@babel/helper-member-expression-to-functions@7.28.5': dependencies: - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.4)': + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.28.3 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.4)': + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/core': 7.28.5 + '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.28.5': {} '@babel/helper-validator-option@7.27.1': {} '@babel/helper-wrap-function@7.28.3': dependencies: '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helpers@7.28.4': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 - '@babel/parser@7.28.4': + '@babel/parser@7.28.5': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-optional-chaining': 7.28.5(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3(@babel/core@7.28.4)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.4)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.4)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.28.4)': + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.4)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.28.4)': + '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4) - '@babel/traverse': 7.28.4 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4) + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-block-scoping@7.28.4(@babel/core@7.28.4)': + '@babel/plugin-transform-block-scoping@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.28.4)': + '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.28.4(@babel/core@7.28.4)': + '@babel/plugin-transform-classes@7.28.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-globals': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) - '@babel/traverse': 7.28.4 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 '@babel/template': 7.27.2 - '@babel/plugin-transform-destructuring@7.28.0(@babel/core@7.28.4)': + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-explicit-resource-management@7.28.0(@babel/core@7.28.4)': + '@babel/plugin-transform-explicit-resource-management@7.28.0(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-exponentiation-operator@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-logical-assignment-operators@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-modules-systemjs@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.28.4)': + '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4) - '@babel/traverse': 7.28.4 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-optional-chaining@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.4)': + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.28.4)': + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) - '@babel/types': 7.28.4 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regenerator@7.28.4(@babel/core@7.28.4)': + '@babel/plugin-transform-regenerator@7.28.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-runtime@7.28.3(@babel/core@7.28.4)': + '@babel/plugin-transform-runtime@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.4) - babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.4) - babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.4) + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.5) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.5) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.5) semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-spread@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-spread@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.4)': + '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) '@babel/helper-plugin-utils': 7.27.1 - '@babel/preset-env@7.28.3(@babel/core@7.28.4)': + '@babel/preset-env@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/compat-data': 7.28.4 - '@babel/core': 7.28.4 + '@babel/compat-data': 7.28.5 + '@babel/core': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.3(@babel/core@7.28.4) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.4) - '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.4) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.4) - '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-block-scoping': 7.28.4(@babel/core@7.28.4) - '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.28.4) - '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.4) - '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) - '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-explicit-resource-management': 7.28.0(@babel/core@7.28.4) - '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.4) - '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4) - '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-regenerator': 7.28.4(@babel/core@7.28.4) - '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.28.4) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.4) - babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.4) - babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.4) - babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.4) - core-js-compat: 3.45.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.3(@babel/core@7.28.5) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.5) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.5) + '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-block-scoping': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.28.5) + '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-explicit-resource-management': 7.28.0(@babel/core@7.28.5) + '@babel/plugin-transform-exponentiation-operator': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-logical-assignment-operators': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-systemjs': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-optional-chaining': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-regenerator': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.28.5) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.5) + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.5) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.5) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.5) + core-js-compat: 3.47.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.4)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 esutils: 2.0.3 - '@babel/preset-react@7.27.1(@babel/core@7.28.4)': + '@babel/preset-react@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.4) - '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.28.5) transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.27.1(@babel/core@7.28.4)': + '@babel/preset-typescript@7.28.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) transitivePeerDependencies: - supports-color @@ -8563,25 +8278,25 @@ snapshots: '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 - '@babel/traverse@7.28.4': + '@babel/traverse@7.28.5': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 + '@babel/generator': 7.28.5 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.4 + '@babel/parser': 7.28.5 '@babel/template': 7.27.2 - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.28.4': + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 '@chromatic-com/storybook@4.1.2(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))': dependencies: @@ -8601,18 +8316,13 @@ snapshots: '@date-fns/tz@1.4.1': {} - '@emnapi/core@1.7.1': + '@emnapi/core@1.8.1': dependencies: '@emnapi/wasi-threads': 1.1.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.5.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@emnapi/runtime@1.7.1': + '@emnapi/runtime@1.8.1': dependencies: tslib: 2.8.1 optional: true @@ -8632,169 +8342,89 @@ snapshots: '@epic-web/invariant@1.0.0': {} - '@esbuild/aix-ppc64@0.25.11': + '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/aix-ppc64@0.25.9': + '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-arm64@0.25.11': + '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/android-arm64@0.25.9': + '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/android-arm@0.25.11': + '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/android-arm@0.25.9': + '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/android-x64@0.25.11': + '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/android-x64@0.25.9': + '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.25.11': + '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.25.9': + '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/darwin-x64@0.25.11': + '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/darwin-x64@0.25.9': + '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.25.11': + '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.25.9': + '@esbuild/linux-ppc64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.25.11': + '@esbuild/linux-riscv64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.25.9': + '@esbuild/linux-s390x@0.25.12': optional: true - '@esbuild/linux-arm64@0.25.11': + '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/linux-arm64@0.25.9': + '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/linux-arm@0.25.11': + '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/linux-arm@0.25.9': + '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/linux-ia32@0.25.11': + '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/linux-ia32@0.25.9': + '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/linux-loong64@0.25.11': + '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/linux-loong64@0.25.9': + '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/linux-mips64el@0.25.11': + '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/linux-mips64el@0.25.9': + '@esbuild/win32-x64@0.25.12': optional: true - '@esbuild/linux-ppc64@0.25.11': - optional: true - - '@esbuild/linux-ppc64@0.25.9': - optional: true - - '@esbuild/linux-riscv64@0.25.11': - optional: true - - '@esbuild/linux-riscv64@0.25.9': - optional: true - - '@esbuild/linux-s390x@0.25.11': - optional: true - - '@esbuild/linux-s390x@0.25.9': - optional: true - - '@esbuild/linux-x64@0.25.11': - optional: true - - '@esbuild/linux-x64@0.25.9': - optional: true - - '@esbuild/netbsd-arm64@0.25.11': - optional: true - - '@esbuild/netbsd-arm64@0.25.9': - optional: true - - '@esbuild/netbsd-x64@0.25.11': - optional: true - - '@esbuild/netbsd-x64@0.25.9': - optional: true - - '@esbuild/openbsd-arm64@0.25.11': - optional: true - - '@esbuild/openbsd-arm64@0.25.9': - optional: true - - '@esbuild/openbsd-x64@0.25.11': - optional: true - - '@esbuild/openbsd-x64@0.25.9': - optional: true - - '@esbuild/openharmony-arm64@0.25.11': - optional: true - - '@esbuild/openharmony-arm64@0.25.9': - optional: true - - '@esbuild/sunos-x64@0.25.11': - optional: true - - '@esbuild/sunos-x64@0.25.9': - optional: true - - '@esbuild/win32-arm64@0.25.11': - optional: true - - '@esbuild/win32-arm64@0.25.9': - optional: true - - '@esbuild/win32-ia32@0.25.11': - optional: true - - '@esbuild/win32-ia32@0.25.9': - optional: true - - '@esbuild/win32-x64@0.25.11': - optional: true - - '@esbuild/win32-x64@0.25.9': - optional: true - - '@eslint-community/eslint-utils@4.9.0(eslint@8.57.1)': + '@eslint-community/eslint-utils@4.9.1(eslint@8.57.1)': dependencies: eslint: 8.57.1 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.12.1': {} - '@eslint-community/regexpp@4.12.2': {} '@eslint/eslintrc@2.1.4': @@ -8805,7 +8435,7 @@ snapshots: globals: 13.24.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.0 + js-yaml: 4.1.1 minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: @@ -8821,25 +8451,25 @@ snapshots: dependencies: '@floating-ui/utils': 0.2.10 - '@floating-ui/dom@1.7.3': + '@floating-ui/dom@1.7.4': dependencies: '@floating-ui/core': 1.7.3 '@floating-ui/utils': 0.2.10 - '@floating-ui/react-dom@2.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@floating-ui/react-dom@2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@floating-ui/dom': 1.7.3 + '@floating-ui/dom': 1.7.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) '@floating-ui/utils@0.2.10': {} - '@gerrit0/mini-shiki@3.14.0': + '@gerrit0/mini-shiki@3.20.0': dependencies: - '@shikijs/engine-oniguruma': 3.14.0 - '@shikijs/langs': 3.14.0 - '@shikijs/themes': 3.14.0 - '@shikijs/types': 3.14.0 + '@shikijs/engine-oniguruma': 3.21.0 + '@shikijs/langs': 3.21.0 + '@shikijs/themes': 3.21.0 + '@shikijs/types': 3.21.0 '@shikijs/vscode-textmate': 10.0.2 '@hookform/resolvers@5.2.2(react-hook-form@7.66.0(react@18.3.1))': @@ -8861,7 +8491,7 @@ snapshots: '@ibm-cloud/openapi-ruleset-utilities@1.9.0': {} - '@ibm-cloud/openapi-ruleset@1.33.3': + '@ibm-cloud/openapi-ruleset@1.33.5': dependencies: '@ibm-cloud/openapi-ruleset-utilities': 1.9.0 '@stoplight/spectral-formats': 1.8.2 @@ -8874,110 +8504,121 @@ snapshots: loglevel: 1.9.2 loglevel-plugin-prefix: 0.8.4 minimatch: 6.2.0 - validator: 13.15.20 + validator: 13.15.26 transitivePeerDependencies: - encoding - '@img/sharp-darwin-arm64@0.34.3': + '@img/colour@1.0.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.0 + '@img/sharp-libvips-darwin-arm64': 1.2.4 optional: true - '@img/sharp-darwin-x64@0.34.3': + '@img/sharp-darwin-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.0 + '@img/sharp-libvips-darwin-x64': 1.2.4 optional: true - '@img/sharp-libvips-darwin-arm64@1.2.0': + '@img/sharp-libvips-darwin-arm64@1.2.4': optional: true - '@img/sharp-libvips-darwin-x64@1.2.0': + '@img/sharp-libvips-darwin-x64@1.2.4': optional: true - '@img/sharp-libvips-linux-arm64@1.2.0': + '@img/sharp-libvips-linux-arm64@1.2.4': optional: true - '@img/sharp-libvips-linux-arm@1.2.0': + '@img/sharp-libvips-linux-arm@1.2.4': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.0': + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true - '@img/sharp-libvips-linux-s390x@1.2.0': + '@img/sharp-libvips-linux-riscv64@1.2.4': optional: true - '@img/sharp-libvips-linux-x64@1.2.0': + '@img/sharp-libvips-linux-s390x@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + '@img/sharp-libvips-linux-x64@1.2.4': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.0': + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': optional: true - '@img/sharp-linux-arm64@0.34.3': + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.0 + '@img/sharp-libvips-linux-arm64': 1.2.4 optional: true - '@img/sharp-linux-arm@0.34.3': + '@img/sharp-linux-arm@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.0 + '@img/sharp-libvips-linux-arm': 1.2.4 optional: true - '@img/sharp-linux-ppc64@0.34.3': + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.0 + '@img/sharp-libvips-linux-ppc64': 1.2.4 optional: true - '@img/sharp-linux-s390x@0.34.3': + '@img/sharp-linux-riscv64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.0 + '@img/sharp-libvips-linux-riscv64': 1.2.4 optional: true - '@img/sharp-linux-x64@0.34.3': + '@img/sharp-linux-s390x@0.34.5': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.0 + '@img/sharp-libvips-linux-s390x': 1.2.4 optional: true - '@img/sharp-linuxmusl-arm64@0.34.3': + '@img/sharp-linux-x64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + '@img/sharp-libvips-linux-x64': 1.2.4 optional: true - '@img/sharp-linuxmusl-x64@0.34.3': + '@img/sharp-linuxmusl-arm64@0.34.5': optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 optional: true - '@img/sharp-wasm32@0.34.3': + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.5.0 + '@emnapi/runtime': 1.8.1 optional: true - '@img/sharp-win32-arm64@0.34.3': + '@img/sharp-win32-arm64@0.34.5': optional: true - '@img/sharp-win32-ia32@0.34.3': + '@img/sharp-win32-ia32@0.34.5': optional: true - '@img/sharp-win32-x64@0.34.3': + '@img/sharp-win32-x64@0.34.5': optional: true - '@inquirer/ansi@1.0.1': {} + '@inquirer/ansi@1.0.2': {} - '@inquirer/confirm@5.1.19(@types/node@24.10.0)': + '@inquirer/confirm@5.1.21(@types/node@24.10.0)': dependencies: - '@inquirer/core': 10.3.0(@types/node@24.10.0) - '@inquirer/type': 3.0.9(@types/node@24.10.0) + '@inquirer/core': 10.3.2(@types/node@24.10.0) + '@inquirer/type': 3.0.10(@types/node@24.10.0) optionalDependencies: '@types/node': 24.10.0 - '@inquirer/core@10.3.0(@types/node@24.10.0)': + '@inquirer/core@10.3.2(@types/node@24.10.0)': dependencies: - '@inquirer/ansi': 1.0.1 - '@inquirer/figures': 1.0.14 - '@inquirer/type': 3.0.9(@types/node@24.10.0) + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@24.10.0) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 @@ -8986,9 +8627,9 @@ snapshots: optionalDependencies: '@types/node': 24.10.0 - '@inquirer/figures@1.0.14': {} + '@inquirer/figures@1.0.15': {} - '@inquirer/type@3.0.9(@types/node@24.10.0)': + '@inquirer/type@3.0.10(@types/node@24.10.0)': optionalDependencies: '@types/node': 24.10.0 @@ -9004,23 +8645,23 @@ snapshots: '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/remapping@2.3.5': dependencies: '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/source-map@0.3.11': dependencies: '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.30': + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 @@ -9054,8 +8695,8 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.7.1 - '@emnapi/runtime': 1.7.1 + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 '@tybys/wasm-util': 0.10.1 optional: true @@ -9091,9 +8732,9 @@ snapshots: '@next/swc-win32-x64-msvc@15.4.8': optional: true - '@next/third-parties@15.4.6(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@next/third-parties@15.4.6(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: - next: 15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 third-party-capital: 1.0.20 @@ -9107,7 +8748,7 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + fastq: 1.20.1 '@nolyfill/is-core-module@1.0.39': {} @@ -9133,7 +8774,7 @@ snapshots: '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 '@opentelemetry/instrumentation-amqplib@0.55.0(@opentelemetry/api@1.9.0)': dependencies: @@ -9148,7 +8789,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 '@types/connect': 3.4.38 transitivePeerDependencies: - supports-color @@ -9165,7 +8806,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -9196,7 +8837,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -9205,7 +8846,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 forwarded-parse: 2.1.2 transitivePeerDependencies: - supports-color @@ -9222,7 +8863,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -9230,7 +8871,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -9239,7 +8880,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -9269,7 +8910,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -9287,7 +8928,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) '@types/pg': 8.15.6 '@types/pg-pool': 2.0.6 @@ -9299,7 +8940,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.38.2 - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -9316,7 +8957,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -9324,7 +8965,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.208.0 - import-in-the-middle: 2.0.0 + import-in-the-middle: 2.0.1 require-in-the-middle: 8.0.1 transitivePeerDependencies: - supports-color @@ -9335,16 +8976,16 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 '@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 - '@opentelemetry/semantic-conventions@1.37.0': {} + '@opentelemetry/semantic-conventions@1.38.0': {} '@opentelemetry/sql-common@0.41.2(@opentelemetry/api@1.9.0)': dependencies: @@ -9372,15 +9013,15 @@ snapshots: '@orval/core@7.13.0(openapi-types@12.1.3)(typescript@5.9.3)': dependencies: '@apidevtools/swagger-parser': 12.1.0(openapi-types@12.1.3) - '@ibm-cloud/openapi-ruleset': 1.33.3 + '@ibm-cloud/openapi-ruleset': 1.33.5 '@stoplight/spectral-core': 1.20.0 acorn: 8.15.0 chalk: 4.1.2 compare-versions: 6.1.1 debug: 4.4.3 - esbuild: 0.25.11 + esbuild: 0.25.12 esutils: 2.0.3 - fs-extra: 11.3.2 + fs-extra: 11.3.3 globby: 11.1.0 lodash.isempty: 4.4.0 lodash.uniq: 4.5.0 @@ -9389,7 +9030,7 @@ snapshots: micromatch: 4.0.8 openapi3-ts: 4.5.0 swagger2openapi: 7.0.8 - typedoc: 0.28.14(typescript@5.9.3) + typedoc: 0.28.15(typescript@5.9.3) transitivePeerDependencies: - encoding - openapi-types @@ -9410,7 +9051,7 @@ snapshots: dependencies: '@orval/core': 7.13.0(openapi-types@12.1.3)(typescript@5.9.3) '@orval/zod': 7.13.0(openapi-types@12.1.3)(typescript@5.9.3) - fs-extra: 11.3.2 + fs-extra: 11.3.3 lodash.uniq: 4.5.0 openapi3-ts: 4.5.0 transitivePeerDependencies: @@ -9486,17 +9127,17 @@ snapshots: dependencies: playwright: 1.56.1 - '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.101.3(esbuild@0.25.9))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(esbuild@0.25.12))': dependencies: ansi-html: 0.0.9 - core-js-pure: 3.45.1 + core-js-pure: 3.47.0 error-stack-parser: 2.1.4 html-entities: 2.6.0 loader-utils: 2.0.4 react-refresh: 0.14.2 - schema-utils: 4.3.2 + schema-utils: 4.3.3 source-map: 0.7.6 - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.104.1(esbuild@0.25.12) optionalDependencies: type-fest: 4.41.0 webpack-hot-middleware: 2.26.1 @@ -9635,7 +9276,7 @@ snapshots: aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.7.1(@types/react@18.3.17)(react@18.3.1) + react-remove-scroll: 2.7.2(@types/react@18.3.17)(react@18.3.1) optionalDependencies: '@types/react': 18.3.17 '@types/react-dom': 18.3.5(@types/react@18.3.17) @@ -9732,7 +9373,7 @@ snapshots: aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.7.1(@types/react@18.3.17)(react@18.3.1) + react-remove-scroll: 2.7.2(@types/react@18.3.17)(react@18.3.1) optionalDependencies: '@types/react': 18.3.17 '@types/react-dom': 18.3.5(@types/react@18.3.17) @@ -9755,14 +9396,14 @@ snapshots: aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.7.1(@types/react@18.3.17)(react@18.3.1) + react-remove-scroll: 2.7.2(@types/react@18.3.17)(react@18.3.1) optionalDependencies: '@types/react': 18.3.17 '@types/react-dom': 18.3.5(@types/react@18.3.17) '@radix-ui/react-popper@1.2.8(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@floating-ui/react-dom': 2.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-arrow': 1.1.7(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.17)(react@18.3.1) '@radix-ui/react-context': 1.1.2(@types/react@18.3.17)(react@18.3.1) @@ -9807,6 +9448,15 @@ snapshots: '@types/react': 18.3.17 '@types/react-dom': 18.3.5(@types/react@18.3.17) + '@radix-ui/react-primitive@2.1.4(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.2.4(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.17 + '@types/react-dom': 18.3.5(@types/react@18.3.17) + '@radix-ui/react-radio-group@1.3.8(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -9883,7 +9533,7 @@ snapshots: aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.7.1(@types/react@18.3.17)(react@18.3.1) + react-remove-scroll: 2.7.2(@types/react@18.3.17)(react@18.3.1) optionalDependencies: '@types/react': 18.3.17 '@types/react-dom': 18.3.5(@types/react@18.3.17) @@ -9897,6 +9547,25 @@ snapshots: '@types/react': 18.3.17 '@types/react-dom': 18.3.5(@types/react@18.3.17) + '@radix-ui/react-slider@1.3.6(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.17)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.17 + '@types/react-dom': 18.3.5(@types/react@18.3.17) + '@radix-ui/react-slot@1.2.3(@types/react@18.3.17)(react@18.3.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.17)(react@18.3.1) @@ -9904,6 +9573,13 @@ snapshots: optionalDependencies: '@types/react': 18.3.17 + '@radix-ui/react-slot@1.2.4(@types/react@18.3.17)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.17)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + '@radix-ui/react-switch@1.2.6(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -10006,7 +9682,7 @@ snapshots: '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@18.3.17)(react@18.3.1)': dependencies: react: 18.3.1 - use-sync-external-store: 1.5.0(react@18.3.1) + use-sync-external-store: 1.6.0(react@18.3.1) optionalDependencies: '@types/react': 18.3.17 @@ -10047,11 +9723,11 @@ snapshots: '@radix-ui/rect@1.1.1': {} - '@reduxjs/toolkit@2.9.0(react-redux@9.2.0(@types/react@18.3.17)(react@18.3.1)(redux@5.0.1))(react@18.3.1)': + '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@18.3.17)(react@18.3.1)(redux@5.0.1))(react@18.3.1)': dependencies: - '@standard-schema/spec': 1.0.0 + '@standard-schema/spec': 1.1.0 '@standard-schema/utils': 0.3.0 - immer: 10.1.3 + immer: 11.1.3 redux: 5.0.1 redux-thunk: 3.1.0(redux@5.0.1) reselect: 5.1.1 @@ -10059,116 +9735,126 @@ snapshots: react: 18.3.1 react-redux: 9.2.0(@types/react@18.3.17)(react@18.3.1)(redux@5.0.1) - '@rjsf/core@5.24.13(@rjsf/utils@5.24.13(react@18.3.1))(react@18.3.1)': + '@rjsf/core@6.1.2(@rjsf/utils@6.1.2(react@18.3.1))(react@18.3.1)': dependencies: - '@rjsf/utils': 5.24.13(react@18.3.1) + '@rjsf/utils': 6.1.2(react@18.3.1) lodash: 4.17.21 - lodash-es: 4.17.21 - markdown-to-jsx: 7.7.13(react@18.3.1) + lodash-es: 4.17.22 + markdown-to-jsx: 8.0.0(react@18.3.1) prop-types: 15.8.1 react: 18.3.1 - '@rjsf/utils@5.24.13(react@18.3.1)': + '@rjsf/utils@6.1.2(react@18.3.1)': dependencies: - json-schema-merge-allof: 0.8.1 + '@x0k/json-schema-merge': 1.0.2 + fast-uri: 3.1.0 jsonpointer: 5.0.1 lodash: 4.17.21 - lodash-es: 4.17.21 + lodash-es: 4.17.22 react: 18.3.1 react-is: 18.3.1 - '@rjsf/validator-ajv8@5.24.13(@rjsf/utils@5.24.13(react@18.3.1))': + '@rjsf/validator-ajv8@6.1.2(@rjsf/utils@6.1.2(react@18.3.1))': dependencies: - '@rjsf/utils': 5.24.13(react@18.3.1) + '@rjsf/utils': 6.1.2(react@18.3.1) ajv: 8.17.1 ajv-formats: 2.1.1(ajv@8.17.1) lodash: 4.17.21 - lodash-es: 4.17.21 + lodash-es: 4.17.22 - '@rollup/plugin-commonjs@28.0.1(rollup@4.52.2)': + '@rollup/plugin-commonjs@28.0.1(rollup@4.55.1)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.2) + '@rollup/pluginutils': 5.3.0(rollup@4.55.1) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.3) is-reference: 1.2.1 - magic-string: 0.30.19 + magic-string: 0.30.21 picomatch: 4.0.3 optionalDependencies: - rollup: 4.52.2 + rollup: 4.55.1 - '@rollup/pluginutils@5.3.0(rollup@4.52.2)': + '@rollup/pluginutils@5.3.0(rollup@4.55.1)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.52.2 + rollup: 4.55.1 - '@rollup/rollup-android-arm-eabi@4.52.2': + '@rollup/rollup-android-arm-eabi@4.55.1': optional: true - '@rollup/rollup-android-arm64@4.52.2': + '@rollup/rollup-android-arm64@4.55.1': optional: true - '@rollup/rollup-darwin-arm64@4.52.2': + '@rollup/rollup-darwin-arm64@4.55.1': optional: true - '@rollup/rollup-darwin-x64@4.52.2': + '@rollup/rollup-darwin-x64@4.55.1': optional: true - '@rollup/rollup-freebsd-arm64@4.52.2': + '@rollup/rollup-freebsd-arm64@4.55.1': optional: true - '@rollup/rollup-freebsd-x64@4.52.2': + '@rollup/rollup-freebsd-x64@4.55.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.2': + '@rollup/rollup-linux-arm-gnueabihf@4.55.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.2': + '@rollup/rollup-linux-arm-musleabihf@4.55.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.2': + '@rollup/rollup-linux-arm64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.2': + '@rollup/rollup-linux-arm64-musl@4.55.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.2': + '@rollup/rollup-linux-loong64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.2': + '@rollup/rollup-linux-loong64-musl@4.55.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.52.2': + '@rollup/rollup-linux-ppc64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.2': + '@rollup/rollup-linux-ppc64-musl@4.55.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.2': + '@rollup/rollup-linux-riscv64-gnu@4.55.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.2': + '@rollup/rollup-linux-riscv64-musl@4.55.1': optional: true - '@rollup/rollup-linux-x64-musl@4.52.2': + '@rollup/rollup-linux-s390x-gnu@4.55.1': optional: true - '@rollup/rollup-openharmony-arm64@4.52.2': + '@rollup/rollup-linux-x64-gnu@4.55.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.2': + '@rollup/rollup-linux-x64-musl@4.55.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.2': + '@rollup/rollup-openbsd-x64@4.55.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.2': + '@rollup/rollup-openharmony-arm64@4.55.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.2': + '@rollup/rollup-win32-arm64-msvc@4.55.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.55.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.55.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.55.1': optional: true '@rtsao/scc@1.1.0': {} @@ -10195,8 +9881,6 @@ snapshots: '@sentry-internal/browser-utils': 10.27.0 '@sentry/core': 10.27.0 - '@sentry/babel-plugin-component-annotate@4.3.0': {} - '@sentry/babel-plugin-component-annotate@4.6.1': {} '@sentry/browser@10.27.0': @@ -10207,25 +9891,11 @@ snapshots: '@sentry-internal/replay-canvas': 10.27.0 '@sentry/core': 10.27.0 - '@sentry/bundler-plugin-core@4.3.0': - dependencies: - '@babel/core': 7.28.4 - '@sentry/babel-plugin-component-annotate': 4.3.0 - '@sentry/cli': 2.55.0 - dotenv: 16.6.1 - find-up: 5.0.0 - glob: 9.3.5 - magic-string: 0.30.8 - unplugin: 1.0.1 - transitivePeerDependencies: - - encoding - - supports-color - '@sentry/bundler-plugin-core@4.6.1': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@sentry/babel-plugin-component-annotate': 4.6.1 - '@sentry/cli': 2.58.2 + '@sentry/cli': 2.58.4 dotenv: 16.6.1 find-up: 5.0.0 glob: 10.5.0 @@ -10235,55 +9905,31 @@ snapshots: - encoding - supports-color - '@sentry/cli-darwin@2.55.0': + '@sentry/cli-darwin@2.58.4': optional: true - '@sentry/cli-darwin@2.58.2': + '@sentry/cli-linux-arm64@2.58.4': optional: true - '@sentry/cli-linux-arm64@2.55.0': + '@sentry/cli-linux-arm@2.58.4': optional: true - '@sentry/cli-linux-arm64@2.58.2': + '@sentry/cli-linux-i686@2.58.4': optional: true - '@sentry/cli-linux-arm@2.55.0': + '@sentry/cli-linux-x64@2.58.4': optional: true - '@sentry/cli-linux-arm@2.58.2': + '@sentry/cli-win32-arm64@2.58.4': optional: true - '@sentry/cli-linux-i686@2.55.0': + '@sentry/cli-win32-i686@2.58.4': optional: true - '@sentry/cli-linux-i686@2.58.2': + '@sentry/cli-win32-x64@2.58.4': optional: true - '@sentry/cli-linux-x64@2.55.0': - optional: true - - '@sentry/cli-linux-x64@2.58.2': - optional: true - - '@sentry/cli-win32-arm64@2.55.0': - optional: true - - '@sentry/cli-win32-arm64@2.58.2': - optional: true - - '@sentry/cli-win32-i686@2.55.0': - optional: true - - '@sentry/cli-win32-i686@2.58.2': - optional: true - - '@sentry/cli-win32-x64@2.55.0': - optional: true - - '@sentry/cli-win32-x64@2.58.2': - optional: true - - '@sentry/cli@2.55.0': + '@sentry/cli@2.58.4': dependencies: https-proxy-agent: 5.0.1 node-fetch: 2.7.0 @@ -10291,56 +9937,36 @@ snapshots: proxy-from-env: 1.1.0 which: 2.0.2 optionalDependencies: - '@sentry/cli-darwin': 2.55.0 - '@sentry/cli-linux-arm': 2.55.0 - '@sentry/cli-linux-arm64': 2.55.0 - '@sentry/cli-linux-i686': 2.55.0 - '@sentry/cli-linux-x64': 2.55.0 - '@sentry/cli-win32-arm64': 2.55.0 - '@sentry/cli-win32-i686': 2.55.0 - '@sentry/cli-win32-x64': 2.55.0 - transitivePeerDependencies: - - encoding - - supports-color - - '@sentry/cli@2.58.2': - dependencies: - https-proxy-agent: 5.0.1 - node-fetch: 2.7.0 - progress: 2.0.3 - proxy-from-env: 1.1.0 - which: 2.0.2 - optionalDependencies: - '@sentry/cli-darwin': 2.58.2 - '@sentry/cli-linux-arm': 2.58.2 - '@sentry/cli-linux-arm64': 2.58.2 - '@sentry/cli-linux-i686': 2.58.2 - '@sentry/cli-linux-x64': 2.58.2 - '@sentry/cli-win32-arm64': 2.58.2 - '@sentry/cli-win32-i686': 2.58.2 - '@sentry/cli-win32-x64': 2.58.2 + '@sentry/cli-darwin': 2.58.4 + '@sentry/cli-linux-arm': 2.58.4 + '@sentry/cli-linux-arm64': 2.58.4 + '@sentry/cli-linux-i686': 2.58.4 + '@sentry/cli-linux-x64': 2.58.4 + '@sentry/cli-win32-arm64': 2.58.4 + '@sentry/cli-win32-i686': 2.58.4 + '@sentry/cli-win32-x64': 2.58.4 transitivePeerDependencies: - encoding - supports-color '@sentry/core@10.27.0': {} - '@sentry/nextjs@10.27.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.101.3(esbuild@0.25.9))': + '@sentry/nextjs@10.27.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.104.1(esbuild@0.25.12))': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.37.0 - '@rollup/plugin-commonjs': 28.0.1(rollup@4.52.2) + '@opentelemetry/semantic-conventions': 1.38.0 + '@rollup/plugin-commonjs': 28.0.1(rollup@4.55.1) '@sentry-internal/browser-utils': 10.27.0 '@sentry/bundler-plugin-core': 4.6.1 '@sentry/core': 10.27.0 '@sentry/node': 10.27.0 - '@sentry/opentelemetry': 10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0) + '@sentry/opentelemetry': 10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) '@sentry/react': 10.27.0(react@18.3.1) '@sentry/vercel-edge': 10.27.0 - '@sentry/webpack-plugin': 4.3.0(webpack@5.101.3(esbuild@0.25.9)) - next: 15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@sentry/webpack-plugin': 4.6.1(webpack@5.104.1(esbuild@0.25.12)) + next: 15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) resolve: 1.22.8 - rollup: 4.52.2 + rollup: 4.55.1 stacktrace-parser: 0.1.11 transitivePeerDependencies: - '@opentelemetry/context-async-hooks' @@ -10351,7 +9977,7 @@ snapshots: - supports-color - webpack - '@sentry/node-core@10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)': + '@sentry/node-core@10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)': dependencies: '@apm-js-collab/tracing-hooks': 0.3.1 '@opentelemetry/api': 1.9.0 @@ -10360,10 +9986,10 @@ snapshots: '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 '@sentry/core': 10.27.0 - '@sentry/opentelemetry': 10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0) - import-in-the-middle: 2.0.0 + '@sentry/opentelemetry': 10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) + import-in-the-middle: 2.0.1 transitivePeerDependencies: - supports-color @@ -10397,23 +10023,23 @@ snapshots: '@opentelemetry/instrumentation-undici': 0.19.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 '@prisma/instrumentation': 6.19.0(@opentelemetry/api@1.9.0) '@sentry/core': 10.27.0 - '@sentry/node-core': 10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0) - '@sentry/opentelemetry': 10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0) - import-in-the-middle: 2.0.0 + '@sentry/node-core': 10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) + '@sentry/opentelemetry': 10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) + import-in-the-middle: 2.0.1 minimatch: 9.0.5 transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.37.0)': + '@sentry/opentelemetry@10.27.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 '@sentry/core': 10.27.0 '@sentry/react@10.27.0(react@18.3.1)': @@ -10429,30 +10055,30 @@ snapshots: '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) '@sentry/core': 10.27.0 - '@sentry/webpack-plugin@4.3.0(webpack@5.101.3(esbuild@0.25.9))': + '@sentry/webpack-plugin@4.6.1(webpack@5.104.1(esbuild@0.25.12))': dependencies: - '@sentry/bundler-plugin-core': 4.3.0 + '@sentry/bundler-plugin-core': 4.6.1 unplugin: 1.0.1 uuid: 9.0.1 - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.104.1(esbuild@0.25.12) transitivePeerDependencies: - encoding - supports-color - '@shikijs/engine-oniguruma@3.14.0': + '@shikijs/engine-oniguruma@3.21.0': dependencies: - '@shikijs/types': 3.14.0 + '@shikijs/types': 3.21.0 '@shikijs/vscode-textmate': 10.0.2 - '@shikijs/langs@3.14.0': + '@shikijs/langs@3.21.0': dependencies: - '@shikijs/types': 3.14.0 + '@shikijs/types': 3.21.0 - '@shikijs/themes@3.14.0': + '@shikijs/themes@3.21.0': dependencies: - '@shikijs/types': 3.14.0 + '@shikijs/types': 3.21.0 - '@shikijs/types@3.14.0': + '@shikijs/types@3.21.0': dependencies: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 @@ -10461,6 +10087,8 @@ snapshots: '@standard-schema/spec@1.0.0': {} + '@standard-schema/spec@1.1.0': {} + '@standard-schema/utils@0.3.0': {} '@stoplight/better-ajv-errors@1.0.3(ajv@8.17.1)': @@ -10480,7 +10108,7 @@ snapshots: dependencies: '@stoplight/json': 3.21.7 '@stoplight/path': 1.3.2 - '@stoplight/types': 13.20.0 + '@stoplight/types': 13.6.0 '@types/urijs': 1.19.26 dependency-graph: 0.11.0 fast-memoize: 2.5.2 @@ -10493,7 +10121,7 @@ snapshots: dependencies: '@stoplight/ordered-object-literal': 1.0.5 '@stoplight/path': 1.3.2 - '@stoplight/types': 13.20.0 + '@stoplight/types': 13.6.0 jsonc-parser: 2.2.1 lodash: 4.17.21 safe-stable-stringify: 1.1.1 @@ -10594,7 +10222,7 @@ snapshots: dependencies: '@stoplight/json': 3.21.7 '@stoplight/path': 1.3.2 - '@stoplight/types': 13.20.0 + '@stoplight/types': 13.6.0 abort-controller: 3.0.0 lodash: 4.17.21 node-fetch: 2.7.0 @@ -10629,14 +10257,14 @@ snapshots: '@storybook/addon-a11y@9.1.5(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))': dependencies: '@storybook/global': 5.0.0 - axe-core: 4.10.3 + axe-core: 4.11.1 storybook: 9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2) '@storybook/addon-docs@9.1.5(@types/react@18.3.17)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))': dependencies: '@mdx-js/react': 3.1.1(@types/react@18.3.17)(react@18.3.1) '@storybook/csf-plugin': 9.1.5(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2)) - '@storybook/icons': 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/icons': 1.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/react-dom-shim': 9.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2)) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -10656,22 +10284,22 @@ snapshots: dependencies: storybook: 9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2) - '@storybook/builder-webpack5@9.1.5(esbuild@0.25.9)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(typescript@5.9.3)': + '@storybook/builder-webpack5@9.1.5(esbuild@0.25.12)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(typescript@5.9.3)': dependencies: '@storybook/core-webpack': 9.1.5(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2)) case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.4.3 - css-loader: 6.11.0(webpack@5.101.3(esbuild@0.25.9)) + css-loader: 6.11.0(webpack@5.104.1(esbuild@0.25.12)) es-module-lexer: 1.7.0 - fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.9.3)(webpack@5.101.3(esbuild@0.25.9)) - html-webpack-plugin: 5.6.4(webpack@5.101.3(esbuild@0.25.9)) - magic-string: 0.30.19 + fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.9.3)(webpack@5.104.1(esbuild@0.25.12)) + html-webpack-plugin: 5.6.5(webpack@5.104.1(esbuild@0.25.12)) + magic-string: 0.30.21 storybook: 9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2) - style-loader: 3.3.4(webpack@5.101.3(esbuild@0.25.9)) - terser-webpack-plugin: 5.3.14(esbuild@0.25.9)(webpack@5.101.3(esbuild@0.25.9)) + style-loader: 3.3.4(webpack@5.104.1(esbuild@0.25.12)) + terser-webpack-plugin: 5.3.16(esbuild@0.25.12)(webpack@5.104.1(esbuild@0.25.12)) ts-dedent: 2.2.0 - webpack: 5.101.3(esbuild@0.25.9) - webpack-dev-middleware: 6.1.3(webpack@5.101.3(esbuild@0.25.9)) + webpack: 5.104.1(esbuild@0.25.12) + webpack-dev-middleware: 6.1.3(webpack@5.104.1(esbuild@0.25.12)) webpack-hot-middleware: 2.26.1 webpack-virtual-modules: 0.6.2 optionalDependencies: @@ -10695,53 +10323,53 @@ snapshots: '@storybook/global@5.0.0': {} - '@storybook/icons@1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@storybook/icons@1.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@storybook/nextjs@9.1.5(esbuild@0.25.9)(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(type-fest@4.41.0)(typescript@5.9.3)(webpack-hot-middleware@2.26.1)(webpack@5.101.3(esbuild@0.25.9))': + '@storybook/nextjs@9.1.5(esbuild@0.25.12)(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(type-fest@4.41.0)(typescript@5.9.3)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(esbuild@0.25.12))': dependencies: - '@babel/core': 7.28.4 - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.28.4) - '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.4) - '@babel/plugin-transform-runtime': 7.28.3(@babel/core@7.28.4) - '@babel/preset-env': 7.28.3(@babel/core@7.28.4) - '@babel/preset-react': 7.27.1(@babel/core@7.28.4) - '@babel/preset-typescript': 7.27.1(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-runtime': 7.28.5(@babel/core@7.28.5) + '@babel/preset-env': 7.28.5(@babel/core@7.28.5) + '@babel/preset-react': 7.28.5(@babel/core@7.28.5) + '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) '@babel/runtime': 7.28.4 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.17(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.101.3(esbuild@0.25.9)) - '@storybook/builder-webpack5': 9.1.5(esbuild@0.25.9)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(typescript@5.9.3) - '@storybook/preset-react-webpack': 9.1.5(esbuild@0.25.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(typescript@5.9.3) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.17(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.104.1(esbuild@0.25.12)) + '@storybook/builder-webpack5': 9.1.5(esbuild@0.25.12)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(typescript@5.9.3) + '@storybook/preset-react-webpack': 9.1.5(esbuild@0.25.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(typescript@5.9.3) '@storybook/react': 9.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(typescript@5.9.3) '@types/semver': 7.7.1 - babel-loader: 9.2.1(@babel/core@7.28.4)(webpack@5.101.3(esbuild@0.25.9)) - css-loader: 6.11.0(webpack@5.101.3(esbuild@0.25.9)) + babel-loader: 9.2.1(@babel/core@7.28.5)(webpack@5.104.1(esbuild@0.25.12)) + css-loader: 6.11.0(webpack@5.104.1(esbuild@0.25.12)) image-size: 2.0.2 loader-utils: 3.3.1 - next: 15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - node-polyfill-webpack-plugin: 2.0.1(webpack@5.101.3(esbuild@0.25.9)) + next: 15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + node-polyfill-webpack-plugin: 2.0.1(webpack@5.104.1(esbuild@0.25.12)) postcss: 8.5.6 - postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.101.3(esbuild@0.25.9)) + postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.104.1(esbuild@0.25.12)) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-refresh: 0.14.2 resolve-url-loader: 5.0.0 - sass-loader: 16.0.5(webpack@5.101.3(esbuild@0.25.9)) - semver: 7.7.2 + sass-loader: 16.0.6(webpack@5.104.1(esbuild@0.25.12)) + semver: 7.7.3 storybook: 9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2) - style-loader: 3.3.4(webpack@5.101.3(esbuild@0.25.9)) - styled-jsx: 5.1.7(@babel/core@7.28.4)(react@18.3.1) + style-loader: 3.3.4(webpack@5.104.1(esbuild@0.25.12)) + styled-jsx: 5.1.7(@babel/core@7.28.5)(react@18.3.1) tsconfig-paths: 4.2.0 tsconfig-paths-webpack-plugin: 4.2.0 optionalDependencies: typescript: 5.9.3 - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.104.1(esbuild@0.25.12) transitivePeerDependencies: - '@rspack/core' - '@swc/core' @@ -10760,21 +10388,21 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/preset-react-webpack@9.1.5(esbuild@0.25.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(typescript@5.9.3)': + '@storybook/preset-react-webpack@9.1.5(esbuild@0.25.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(typescript@5.9.3)': dependencies: '@storybook/core-webpack': 9.1.5(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2)) - '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.9.3)(webpack@5.101.3(esbuild@0.25.9)) + '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.9.3)(webpack@5.104.1(esbuild@0.25.12)) '@types/semver': 7.7.1 find-up: 7.0.0 - magic-string: 0.30.19 + magic-string: 0.30.21 react: 18.3.1 react-docgen: 7.1.1 react-dom: 18.3.1(react@18.3.1) - resolve: 1.22.10 - semver: 7.7.2 + resolve: 1.22.11 + semver: 7.7.3 storybook: 9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2) tsconfig-paths: 4.2.0 - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.104.1(esbuild@0.25.12) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -10784,7 +10412,7 @@ snapshots: - uglify-js - webpack-cli - '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.9.3)(webpack@5.101.3(esbuild@0.25.9))': + '@storybook/react-docgen-typescript-plugin@1.0.6--canary.9.0c3f3b7.0(typescript@5.9.3)(webpack@5.104.1(esbuild@0.25.12))': dependencies: debug: 4.4.3 endent: 2.1.0 @@ -10794,7 +10422,7 @@ snapshots: react-docgen-typescript: 2.4.0(typescript@5.9.3) tslib: 2.8.1 typescript: 5.9.3 - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.104.1(esbuild@0.25.12) transitivePeerDependencies: - supports-color @@ -10836,10 +10464,10 @@ snapshots: '@supabase/realtime-js@2.78.0': dependencies: '@supabase/node-fetch': 2.6.15 - '@types/phoenix': 1.6.6 + '@types/phoenix': 1.6.7 '@types/ws': 8.18.1 tslib: 2.8.1 - ws: 8.18.3 + ws: 8.19.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -10872,7 +10500,7 @@ snapshots: '@tanstack/eslint-plugin-query@5.91.2(eslint@8.57.1)(typescript@5.9.3)': dependencies: - '@typescript-eslint/utils': 8.46.2(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 transitivePeerDependencies: - supports-color @@ -10912,7 +10540,7 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.8.0': + '@testing-library/jest-dom@6.9.1': dependencies: '@adobe/css-tools': 4.4.4 aria-query: 5.3.2 @@ -10934,36 +10562,37 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@types/canvas-confetti@1.9.0': {} - '@types/chai@5.2.2': + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 '@types/connect@3.4.38': dependencies: '@types/node': 24.10.0 - '@types/d3-array@3.2.1': {} + '@types/d3-array@3.2.2': {} '@types/d3-color@3.1.3': {} @@ -11078,7 +10707,7 @@ snapshots: pg-protocol: 1.10.3 pg-types: 2.2.0 - '@types/phoenix@1.6.6': {} + '@types/phoenix@1.6.7': {} '@types/prop-types@15.7.15': {} @@ -11097,7 +10726,7 @@ snapshots: '@types/react@18.3.17': dependencies: '@types/prop-types': 15.7.15 - csstype: 3.1.3 + csstype: 3.2.3 '@types/resolve@1.20.6': {} @@ -11105,7 +10734,7 @@ snapshots: '@types/statuses@2.0.6': {} - '@types/stylis@4.2.5': {} + '@types/stylis@4.2.7': {} '@types/tedious@4.0.14': dependencies: @@ -11123,200 +10752,95 @@ snapshots: dependencies: '@types/node': 24.10.0 - '@typescript-eslint/eslint-plugin@8.48.1(@typescript-eslint/parser@8.48.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.48.1(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.48.1 - '@typescript-eslint/type-utils': 8.48.1(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.1(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.48.1 + '@typescript-eslint/parser': 8.52.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/type-utils': 8.52.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.52.0 eslint: 8.57.1 - graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.48.1(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.48.1 - '@typescript-eslint/types': 8.48.1 - '@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.48.1 + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.52.0 debug: 4.4.3 eslint: 8.57.1 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.43.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.52.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.9.3) - '@typescript-eslint/types': 8.48.1 + '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) + '@typescript-eslint/types': 8.52.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.46.2(typescript@5.9.3)': + '@typescript-eslint/scope-manager@8.52.0': dependencies: - '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) - '@typescript-eslint/types': 8.48.1 - debug: 4.4.3 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/visitor-keys': 8.52.0 - '@typescript-eslint/project-service@8.48.1(typescript@5.9.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.48.1(typescript@5.9.3) - '@typescript-eslint/types': 8.48.1 - debug: 4.4.3 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@8.43.0': - dependencies: - '@typescript-eslint/types': 8.43.0 - '@typescript-eslint/visitor-keys': 8.43.0 - - '@typescript-eslint/scope-manager@8.46.2': - dependencies: - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/visitor-keys': 8.46.2 - - '@typescript-eslint/scope-manager@8.48.1': - dependencies: - '@typescript-eslint/types': 8.48.1 - '@typescript-eslint/visitor-keys': 8.48.1 - - '@typescript-eslint/tsconfig-utils@8.43.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.52.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.46.2(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.52.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: - typescript: 5.9.3 - - '@typescript-eslint/tsconfig-utils@8.48.1(typescript@5.9.3)': - dependencies: - typescript: 5.9.3 - - '@typescript-eslint/type-utils@8.48.1(eslint@8.57.1)(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 8.48.1 - '@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@8.57.1)(typescript@5.9.3) debug: 4.4.3 eslint: 8.57.1 - ts-api-utils: 2.1.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.43.0': {} + '@typescript-eslint/types@8.52.0': {} - '@typescript-eslint/types@8.46.2': {} - - '@typescript-eslint/types@8.48.1': {} - - '@typescript-eslint/typescript-estree@8.43.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.52.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.43.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.43.0(typescript@5.9.3) - '@typescript-eslint/types': 8.43.0 - '@typescript-eslint/visitor-keys': 8.43.0 - debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.3 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/typescript-estree@8.46.2(typescript@5.9.3)': - dependencies: - '@typescript-eslint/project-service': 8.46.2(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/visitor-keys': 8.46.2 - debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.3 - ts-api-utils: 2.1.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/typescript-estree@8.48.1(typescript@5.9.3)': - dependencies: - '@typescript-eslint/project-service': 8.48.1(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.48.1(typescript@5.9.3) - '@typescript-eslint/types': 8.48.1 - '@typescript-eslint/visitor-keys': 8.48.1 + '@typescript-eslint/project-service': 8.52.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3) + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/visitor-keys': 8.52.0 debug: 4.4.3 minimatch: 9.0.5 semver: 7.7.3 tinyglobby: 0.2.15 - ts-api-utils: 2.1.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.43.0(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/utils@8.52.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) - '@typescript-eslint/scope-manager': 8.43.0 - '@typescript-eslint/types': 8.43.0 - '@typescript-eslint/typescript-estree': 8.43.0(typescript@5.9.3) + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@typescript-eslint/scope-manager': 8.52.0 + '@typescript-eslint/types': 8.52.0 + '@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3) eslint: 8.57.1 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.46.2(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/visitor-keys@8.52.0': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) - '@typescript-eslint/scope-manager': 8.46.2 - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) - eslint: 8.57.1 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.48.1(eslint@8.57.1)(typescript@5.9.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) - '@typescript-eslint/scope-manager': 8.48.1 - '@typescript-eslint/types': 8.48.1 - '@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) - eslint: 8.57.1 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/visitor-keys@8.43.0': - dependencies: - '@typescript-eslint/types': 8.43.0 - eslint-visitor-keys: 4.2.1 - - '@typescript-eslint/visitor-keys@8.46.2': - dependencies: - '@typescript-eslint/types': 8.46.2 - eslint-visitor-keys: 4.2.1 - - '@typescript-eslint/visitor-keys@8.48.1': - dependencies: - '@typescript-eslint/types': 8.48.1 + '@typescript-eslint/types': 8.52.0 eslint-visitor-keys: 4.2.1 '@ungap/structured-clone@1.3.0': {} @@ -11380,19 +10904,19 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vercel/analytics@1.5.0(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@vercel/analytics@1.5.0(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': optionalDependencies: - next: 15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 - '@vercel/speed-insights@1.2.0(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@vercel/speed-insights@1.2.0(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': optionalDependencies: - next: 15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 '@vitest/expect@3.2.4': dependencies: - '@types/chai': 5.2.2 + '@types/chai': 5.2.3 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 chai: 5.3.3 @@ -11402,7 +10926,7 @@ snapshots: dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 - magic-string: 0.30.19 + magic-string: 0.30.21 optionalDependencies: msw: 2.11.6(@types/node@24.10.0)(typescript@5.9.3) @@ -11412,7 +10936,7 @@ snapshots: '@vitest/spy@3.2.4': dependencies: - tinyspy: 4.0.3 + tinyspy: 4.0.4 '@vitest/utils@3.2.4': dependencies: @@ -11496,17 +11020,21 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 + '@x0k/json-schema-merge@1.0.2': + dependencies: + '@types/json-schema': 7.0.15 + '@xtuc/ieee754@1.2.0': {} '@xtuc/long@4.2.2': {} - '@xyflow/react@12.9.2(@types/react@18.3.17)(immer@10.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@xyflow/react@12.9.2(@types/react@18.3.17)(immer@11.1.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@xyflow/system': 0.0.72 classcat: 5.0.5 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - zustand: 4.5.7(@types/react@18.3.17)(immer@10.1.3)(react@18.3.1) + zustand: 4.5.7(@types/react@18.3.17)(immer@11.1.3)(react@18.3.1) transitivePeerDependencies: - '@types/react' - immer @@ -11583,7 +11111,7 @@ snapshots: ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.0.6 + fast-uri: 3.1.0 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -11636,7 +11164,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 is-string: 1.1.1 @@ -11648,7 +11176,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 @@ -11658,7 +11186,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 @@ -11667,21 +11195,21 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-shim-unscopables: 1.1.0 @@ -11690,7 +11218,7 @@ snapshots: array-buffer-byte-length: 1.0.2 call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 @@ -11725,54 +11253,52 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axe-core@4.10.3: {} + axe-core@4.11.1: {} - axe-core@4.11.0: {} - - axe-html-reporter@2.2.11(axe-core@4.11.0): + axe-html-reporter@2.2.11(axe-core@4.11.1): dependencies: - axe-core: 4.11.0 + axe-core: 4.11.1 mustache: 4.2.0 axe-playwright@2.2.2(playwright@1.56.1): dependencies: '@types/junit-report-builder': 3.0.2 - axe-core: 4.11.0 - axe-html-reporter: 2.2.11(axe-core@4.11.0) + axe-core: 4.11.1 + axe-html-reporter: 2.2.11(axe-core@4.11.1) junit-report-builder: 5.1.1 picocolors: 1.1.1 playwright: 1.56.1 axobject-query@4.1.0: {} - babel-loader@9.2.1(@babel/core@7.28.4)(webpack@5.101.3(esbuild@0.25.9)): + babel-loader@9.2.1(@babel/core@7.28.5)(webpack@5.104.1(esbuild@0.25.12)): dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 find-cache-dir: 4.0.0 - schema-utils: 4.3.2 - webpack: 5.101.3(esbuild@0.25.9) + schema-utils: 4.3.3 + webpack: 5.104.1(esbuild@0.25.12) - babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.4): + babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.5): dependencies: - '@babel/compat-data': 7.28.4 - '@babel/core': 7.28.4 - '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) + '@babel/compat-data': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.4): + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.5): dependencies: - '@babel/core': 7.28.4 - '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) - core-js-compat: 3.45.1 + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) + core-js-compat: 3.47.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.28.4): + babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.28.5): dependencies: - '@babel/core': 7.28.4 - '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) transitivePeerDependencies: - supports-color @@ -11782,6 +11308,8 @@ snapshots: base64-js@1.5.1: {} + baseline-browser-mapping@2.9.11: {} + better-opn@3.0.2: dependencies: open: 8.4.2 @@ -11841,16 +11369,15 @@ snapshots: randombytes: 2.1.0 safe-buffer: 5.2.1 - browserify-sign@4.2.3: + browserify-sign@4.2.5: dependencies: bn.js: 5.2.2 browserify-rsa: 4.1.1 create-hash: 1.2.0 create-hmac: 1.1.7 elliptic: 6.6.1 - hash-base: 3.0.5 inherits: 2.0.4 - parse-asn1: 5.1.7 + parse-asn1: 5.1.9 readable-stream: 2.3.8 safe-buffer: 5.2.1 @@ -11858,12 +11385,13 @@ snapshots: dependencies: pako: 1.0.11 - browserslist@4.25.4: + browserslist@4.28.1: dependencies: - caniuse-lite: 1.0.30001741 - electron-to-chromium: 1.5.214 - node-releases: 2.0.20 - update-browserslist-db: 1.1.3(browserslist@4.25.4) + baseline-browser-mapping: 2.9.11 + caniuse-lite: 1.0.30001762 + electron-to-chromium: 1.5.267 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) buffer-from@1.1.2: {} @@ -11906,7 +11434,7 @@ snapshots: camelize@1.0.1: {} - caniuse-lite@1.0.30001741: {} + caniuse-lite@1.0.30001762: {} case-sensitive-paths-webpack-plugin@2.4.0: {} @@ -11915,7 +11443,7 @@ snapshots: chai@5.3.3: dependencies: assertion-error: 2.0.1 - check-error: 2.1.1 + check-error: 2.1.3 deep-eql: 5.0.2 loupe: 3.2.1 pathval: 2.0.1 @@ -11933,7 +11461,7 @@ snapshots: character-reference-invalid@2.0.1: {} - check-error@2.1.1: {} + check-error@2.1.3: {} chokidar@3.6.0: dependencies: @@ -11992,7 +11520,7 @@ snapshots: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.17)(react@18.3.1) '@radix-ui/react-dialog': 1.1.15(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-id': 1.1.1(@types/react@18.3.17)(react@18.3.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.4(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: @@ -12005,18 +11533,6 @@ snapshots: color-name@1.1.4: {} - color-string@1.9.1: - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.2 - optional: true - - color@4.2.3: - dependencies: - color-convert: 2.0.1 - color-string: 1.9.1 - optional: true - colorette@2.0.20: {} comma-separated-tokens@2.0.3: {} @@ -12035,19 +11551,6 @@ snapshots: compare-versions@6.1.1: {} - compute-gcd@1.2.1: - dependencies: - validate.io-array: 1.0.6 - validate.io-function: 1.0.2 - validate.io-integer-array: 1.0.0 - - compute-lcm@1.1.2: - dependencies: - compute-gcd: 1.2.1 - validate.io-array: 1.0.6 - validate.io-function: 1.0.2 - validate.io-integer-array: 1.0.0 - concat-map@0.0.1: {} concurrently@9.2.1: @@ -12069,11 +11572,11 @@ snapshots: cookie@1.0.2: {} - core-js-compat@3.45.1: + core-js-compat@3.47.0: dependencies: - browserslist: 4.25.4 + browserslist: 4.28.1 - core-js-pure@3.45.1: {} + core-js-pure@3.47.0: {} core-util-is@1.0.3: {} @@ -12089,7 +11592,7 @@ snapshots: dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 - js-yaml: 4.1.0 + js-yaml: 4.1.1 parse-json: 5.2.0 optionalDependencies: typescript: 5.9.3 @@ -12130,7 +11633,7 @@ snapshots: crypto-browserify@3.12.1: dependencies: browserify-cipher: 1.0.1 - browserify-sign: 4.2.3 + browserify-sign: 4.2.5 create-ecdh: 4.0.4 create-hash: 1.2.0 create-hmac: 1.1.7 @@ -12144,7 +11647,7 @@ snapshots: css-color-keywords@1.0.0: {} - css-loader@6.11.0(webpack@5.101.3(esbuild@0.25.9)): + css-loader@6.11.0(webpack@5.104.1(esbuild@0.25.12)): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -12153,9 +11656,9 @@ snapshots: postcss-modules-scope: 3.2.1(postcss@8.5.6) postcss-modules-values: 4.0.0(postcss@8.5.6) postcss-value-parser: 4.2.0 - semver: 7.7.2 + semver: 7.7.3 optionalDependencies: - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.104.1(esbuild@0.25.12) css-select@4.3.0: dependencies: @@ -12177,7 +11680,7 @@ snapshots: cssesc@3.0.0: {} - csstype@3.1.3: {} + csstype@3.2.3: {} d3-array@3.2.4: dependencies: @@ -12314,7 +11817,7 @@ snapshots: inherits: 2.0.4 minimalistic-assert: 1.0.1 - detect-libc@2.0.4: + detect-libc@2.1.2: optional: true detect-node-es@1.1.0: {} @@ -12323,6 +11826,8 @@ snapshots: dependencies: dequal: 2.0.3 + dexie@4.2.1: {} + didyoumean@1.2.2: {} diffie-hellman@5.0.3: @@ -12390,7 +11895,7 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.214: {} + electron-to-chromium@1.5.267: {} elliptic@6.6.1: dependencies: @@ -12426,10 +11931,10 @@ snapshots: fast-json-parse: 1.0.3 objectorarray: 1.0.5 - enhanced-resolve@5.18.3: + enhanced-resolve@5.18.4: dependencies: graceful-fs: 4.2.11 - tapable: 2.2.3 + tapable: 2.3.0 enquirer@2.4.1: dependencies: @@ -12444,7 +11949,7 @@ snapshots: env-paths@2.2.1: {} - error-ex@1.3.2: + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -12452,7 +11957,7 @@ snapshots: dependencies: stackframe: 1.3.4 - es-abstract@1.24.0: + es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 @@ -12513,7 +12018,7 @@ snapshots: dependencies: define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 function-bind: 1.1.2 globalthis: 1.0.4 @@ -12524,12 +12029,12 @@ snapshots: es-errors@1.3.0: {} - es-iterator-helpers@1.2.1: + es-iterator-helpers@1.2.2: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-set-tostringtag: 2.1.0 function-bind: 1.1.2 @@ -12545,6 +12050,8 @@ snapshots: es-module-lexer@1.7.0: {} + es-module-lexer@2.0.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -12566,74 +12073,45 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 - es-toolkit@1.39.10: {} + es-toolkit@1.43.0: {} es6-promise@3.3.1: {} - esbuild-register@3.6.0(esbuild@0.25.9): + esbuild-register@3.6.0(esbuild@0.25.12): dependencies: debug: 4.4.3 - esbuild: 0.25.9 + esbuild: 0.25.12 transitivePeerDependencies: - supports-color - esbuild@0.25.11: + esbuild@0.25.12: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.11 - '@esbuild/android-arm': 0.25.11 - '@esbuild/android-arm64': 0.25.11 - '@esbuild/android-x64': 0.25.11 - '@esbuild/darwin-arm64': 0.25.11 - '@esbuild/darwin-x64': 0.25.11 - '@esbuild/freebsd-arm64': 0.25.11 - '@esbuild/freebsd-x64': 0.25.11 - '@esbuild/linux-arm': 0.25.11 - '@esbuild/linux-arm64': 0.25.11 - '@esbuild/linux-ia32': 0.25.11 - '@esbuild/linux-loong64': 0.25.11 - '@esbuild/linux-mips64el': 0.25.11 - '@esbuild/linux-ppc64': 0.25.11 - '@esbuild/linux-riscv64': 0.25.11 - '@esbuild/linux-s390x': 0.25.11 - '@esbuild/linux-x64': 0.25.11 - '@esbuild/netbsd-arm64': 0.25.11 - '@esbuild/netbsd-x64': 0.25.11 - '@esbuild/openbsd-arm64': 0.25.11 - '@esbuild/openbsd-x64': 0.25.11 - '@esbuild/openharmony-arm64': 0.25.11 - '@esbuild/sunos-x64': 0.25.11 - '@esbuild/win32-arm64': 0.25.11 - '@esbuild/win32-ia32': 0.25.11 - '@esbuild/win32-x64': 0.25.11 - - esbuild@0.25.9: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.9 - '@esbuild/android-arm': 0.25.9 - '@esbuild/android-arm64': 0.25.9 - '@esbuild/android-x64': 0.25.9 - '@esbuild/darwin-arm64': 0.25.9 - '@esbuild/darwin-x64': 0.25.9 - '@esbuild/freebsd-arm64': 0.25.9 - '@esbuild/freebsd-x64': 0.25.9 - '@esbuild/linux-arm': 0.25.9 - '@esbuild/linux-arm64': 0.25.9 - '@esbuild/linux-ia32': 0.25.9 - '@esbuild/linux-loong64': 0.25.9 - '@esbuild/linux-mips64el': 0.25.9 - '@esbuild/linux-ppc64': 0.25.9 - '@esbuild/linux-riscv64': 0.25.9 - '@esbuild/linux-s390x': 0.25.9 - '@esbuild/linux-x64': 0.25.9 - '@esbuild/netbsd-arm64': 0.25.9 - '@esbuild/netbsd-x64': 0.25.9 - '@esbuild/openbsd-arm64': 0.25.9 - '@esbuild/openbsd-x64': 0.25.9 - '@esbuild/openharmony-arm64': 0.25.9 - '@esbuild/sunos-x64': 0.25.9 - '@esbuild/win32-arm64': 0.25.9 - '@esbuild/win32-ia32': 0.25.9 - '@esbuild/win32-x64': 0.25.9 + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 escalade@3.2.0: {} @@ -12645,12 +12123,12 @@ snapshots: dependencies: '@next/eslint-plugin-next': 15.5.7 '@rushstack/eslint-patch': 1.15.0 - '@typescript-eslint/eslint-plugin': 8.48.1(@typescript-eslint/parser@8.48.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/parser': 8.48.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.52.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.52.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) eslint-plugin-react-hooks: 5.2.0(eslint@8.57.1) @@ -12669,7 +12147,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -12680,22 +12158,22 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.48.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.48.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.52.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -12706,7 +12184,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.48.1(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.52.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -12718,7 +12196,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.48.1(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.52.0(eslint@8.57.1)(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -12730,7 +12208,7 @@ snapshots: array-includes: 3.1.9 array.prototype.flatmap: 1.3.3 ast-types-flow: 0.0.8 - axe-core: 4.11.0 + axe-core: 4.11.1 axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 @@ -12754,7 +12232,7 @@ snapshots: array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 + es-iterator-helpers: 1.2.2 eslint: 8.57.1 estraverse: 5.3.0 hasown: 2.0.2 @@ -12771,7 +12249,7 @@ snapshots: eslint-plugin-storybook@9.1.5(eslint@8.57.1)(storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2))(typescript@5.9.3): dependencies: - '@typescript-eslint/utils': 8.43.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.52.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 storybook: 9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2) transitivePeerDependencies: @@ -12794,8 +12272,8 @@ snapshots: eslint@8.57.1: dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.1) - '@eslint-community/regexpp': 4.12.1 + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.2 '@eslint/eslintrc': 2.1.4 '@eslint/js': 8.57.1 '@humanwhocodes/config-array': 0.13.0 @@ -12811,7 +12289,7 @@ snapshots: eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - esquery: 1.6.0 + esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 6.0.1 @@ -12823,7 +12301,7 @@ snapshots: imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 - js-yaml: 4.1.0 + js-yaml: 4.1.1 json-stable-stringify-without-jsonify: 1.0.1 levn: 0.4.1 lodash.merge: 4.6.2 @@ -12843,7 +12321,7 @@ snapshots: esprima@4.0.1: {} - esquery@1.6.0: + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -12922,9 +12400,9 @@ snapshots: fast-safe-stringify@2.1.1: {} - fast-uri@3.0.6: {} + fast-uri@3.1.0: {} - fastq@1.19.1: + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -12999,7 +12477,7 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - fork-ts-checker-webpack-plugin@8.0.0(typescript@5.9.3)(webpack@5.101.3(esbuild@0.25.9)): + fork-ts-checker-webpack-plugin@8.0.0(typescript@5.9.3)(webpack@5.104.1(esbuild@0.25.12)): dependencies: '@babel/code-frame': 7.27.1 chalk: 4.1.2 @@ -13011,17 +12489,17 @@ snapshots: minimatch: 3.1.2 node-abort-controller: 3.1.1 schema-utils: 3.3.0 - semver: 7.7.2 - tapable: 2.2.3 + semver: 7.7.3 + tapable: 2.3.0 typescript: 5.9.3 - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.104.1(esbuild@0.25.12) forwarded-parse@2.1.2: {} framer-motion@12.23.24(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - motion-dom: 12.23.23 - motion-utils: 12.23.6 + motion-dom: 12.24.8 + motion-utils: 12.23.28 tslib: 2.8.1 optionalDependencies: '@emotion/is-prop-valid': 1.2.2 @@ -13034,7 +12512,7 @@ snapshots: jsonfile: 6.2.0 universalify: 2.0.1 - fs-extra@11.3.2: + fs-extra@11.3.3: dependencies: graceful-fs: 4.2.11 jsonfile: 6.2.0 @@ -13063,9 +12541,9 @@ snapshots: functions-have-names@1.2.3: {} - geist@1.5.1(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): + geist@1.5.1(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): dependencies: - next: 15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) generator-function@2.0.1: {} @@ -13117,15 +12595,6 @@ snapshots: glob-to-regexp@0.4.1: {} - glob@10.4.5: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - glob@10.5.0: dependencies: foreground-child: 3.3.1 @@ -13144,13 +12613,6 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 - glob@9.3.5: - dependencies: - fs.realpath: 1.0.0 - minimatch: 8.0.4 - minipass: 4.2.8 - path-scurry: 1.11.1 - globals@13.24.0: dependencies: type-fest: 0.20.2 @@ -13175,7 +12637,7 @@ snapshots: graphemer@1.4.0: {} - graphql@16.11.0: {} + graphql@16.12.0: {} has-bigints@1.1.0: {} @@ -13275,7 +12737,7 @@ snapshots: mdast-util-mdxjs-esm: 2.0.1 property-information: 7.1.0 space-separated-tokens: 2.0.2 - style-to-js: 1.1.17 + style-to-js: 1.1.21 unist-util-position: 5.0.0 vfile-message: 4.0.3 transitivePeerDependencies: @@ -13330,19 +12792,19 @@ snapshots: he: 1.2.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.44.0 + terser: 5.44.1 html-url-attributes@3.0.1: {} - html-webpack-plugin@5.6.4(webpack@5.101.3(esbuild@0.25.9)): + html-webpack-plugin@5.6.5(webpack@5.104.1(esbuild@0.25.12)): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 lodash: 4.17.21 pretty-error: 4.0.0 - tapable: 2.2.3 + tapable: 2.3.0 optionalDependencies: - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.104.1(esbuild@0.25.12) htmlparser2@6.1.0: dependencies: @@ -13376,7 +12838,9 @@ snapshots: image-size@2.0.2: {} - immer@10.1.3: {} + immer@10.2.0: {} + + immer@11.1.3: {} immer@9.0.21: {} @@ -13385,7 +12849,7 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - import-in-the-middle@2.0.0: + import-in-the-middle@2.0.1: dependencies: acorn: 8.15.0 acorn-import-attributes: 1.9.5(acorn@8.15.0) @@ -13405,7 +12869,7 @@ snapshots: inherits@2.0.4: {} - inline-style-parser@0.2.4: {} + inline-style-parser@0.2.7: {} internal-slot@1.1.0: dependencies: @@ -13435,9 +12899,6 @@ snapshots: is-arrayish@0.2.1: {} - is-arrayish@0.3.2: - optional: true - is-async-function@2.1.1: dependencies: async-function: 1.0.0 @@ -13492,13 +12953,6 @@ snapshots: is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.1.0: - dependencies: - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -13615,7 +13069,7 @@ snapshots: jiti@1.21.7: {} - jiti@2.5.1: {} + jiti@2.6.1: {} js-tokens@4.0.0: {} @@ -13623,9 +13077,11 @@ snapshots: dependencies: argparse: 2.0.1 - jsep@1.4.0: {} + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 - jsesc@3.0.2: {} + jsep@1.4.0: {} jsesc@3.1.0: {} @@ -13633,16 +13089,6 @@ snapshots: json-parse-even-better-errors@2.3.1: {} - json-schema-compare@0.2.2: - dependencies: - lodash: 4.17.21 - - json-schema-merge-allof@0.8.1: - dependencies: - compute-lcm: 1.1.2 - json-schema-compare: 0.2.2 - lodash: 4.17.21 - json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -13734,7 +13180,7 @@ snapshots: dependencies: uc.micro: 2.1.0 - loader-runner@4.3.0: {} + loader-runner@4.3.1: {} loader-utils@2.0.4: dependencies: @@ -13756,7 +13202,7 @@ snapshots: dependencies: p-locate: 6.0.0 - lodash-es@4.17.21: {} + lodash-es@4.17.22: {} lodash.camelcase@4.3.0: {} @@ -13814,7 +13260,7 @@ snapshots: lz-string@1.5.0: {} - magic-string@0.30.19: + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -13837,8 +13283,8 @@ snapshots: markdown-table@3.0.4: {} - markdown-to-jsx@7.7.13(react@18.3.1): - dependencies: + markdown-to-jsx@8.0.0(react@18.3.1): + optionalDependencies: react: 18.3.1 math-intrinsics@1.1.0: {} @@ -13853,8 +13299,8 @@ snapshots: dependencies: '@types/mdast': 4.0.4 escape-string-regexp: 5.0.0 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 mdast-util-from-markdown@2.0.2: dependencies: @@ -13984,9 +13430,9 @@ snapshots: mdast-util-phrasing@4.1.0: dependencies: '@types/mdast': 4.0.4 - unist-util-is: 6.0.0 + unist-util-is: 6.0.1 - mdast-util-to-hast@13.2.0: + mdast-util-to-hast@13.2.1: dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 @@ -14259,29 +13705,23 @@ snapshots: dependencies: brace-expansion: 2.0.2 - minimatch@8.0.4: - dependencies: - brace-expansion: 2.0.2 - minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 minimist@1.2.8: {} - minipass@4.2.8: {} - minipass@7.1.2: {} module-details-from-path@1.0.4: {} moment@2.30.1: {} - motion-dom@12.23.23: + motion-dom@12.24.8: dependencies: - motion-utils: 12.23.6 + motion-utils: 12.23.28 - motion-utils@12.23.6: {} + motion-utils@12.23.28: {} ms@2.1.3: {} @@ -14292,12 +13732,12 @@ snapshots: msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3): dependencies: - '@inquirer/confirm': 5.1.19(@types/node@24.10.0) + '@inquirer/confirm': 5.1.21(@types/node@24.10.0) '@mswjs/interceptors': 0.40.0 '@open-draft/deferred-promise': 2.2.0 '@types/statuses': 2.0.6 cookie: 1.0.2 - graphql: 16.11.0 + graphql: 16.12.0 headers-polyfill: 4.0.3 is-node-process: 1.2.0 outvariant: 1.4.3 @@ -14338,15 +13778,15 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@next/env': 15.4.10 '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001741 + caniuse-lite: 1.0.30001762 postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.6(@babel/core@7.28.4)(react@18.3.1) + styled-jsx: 5.1.6(@babel/core@7.28.5)(react@18.3.1) optionalDependencies: '@next/swc-darwin-arm64': 15.4.8 '@next/swc-darwin-x64': 15.4.8 @@ -14358,7 +13798,7 @@ snapshots: '@next/swc-win32-x64-msvc': 15.4.8 '@opentelemetry/api': 1.9.0 '@playwright/test': 1.56.1 - sharp: 0.34.3 + sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -14388,7 +13828,7 @@ snapshots: dependencies: whatwg-url: 5.0.0 - node-polyfill-webpack-plugin@2.0.1(webpack@5.101.3(esbuild@0.25.9)): + node-polyfill-webpack-plugin@2.0.1(webpack@5.104.1(esbuild@0.25.12)): dependencies: assert: 2.1.0 browserify-zlib: 0.2.0 @@ -14415,13 +13855,13 @@ snapshots: url: 0.11.4 util: 0.12.5 vm-browserify: 1.1.2 - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.104.1(esbuild@0.25.12) node-readfiles@0.2.0: dependencies: es6-promise: 3.3.1 - node-releases@2.0.20: {} + node-releases@2.0.27: {} normalize-path@3.0.0: {} @@ -14433,12 +13873,12 @@ snapshots: dependencies: boolbase: 1.0.0 - nuqs@2.7.2(next@15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): + nuqs@2.7.2(next@15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): dependencies: '@standard-schema/spec': 1.0.0 react: 18.3.1 optionalDependencies: - next: 15.4.10(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 15.4.10(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) oas-kit-common@1.0.8: dependencies: @@ -14504,14 +13944,14 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 object.groupby@1.0.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 object.values@1.2.1: dependencies: @@ -14540,7 +13980,7 @@ snapshots: openapi3-ts@4.5.0: dependencies: - yaml: 2.8.1 + yaml: 2.8.2 optionator@0.9.4: dependencies: @@ -14571,15 +14011,15 @@ snapshots: enquirer: 2.4.1 execa: 5.1.1 find-up: 5.0.0 - fs-extra: 11.3.2 + fs-extra: 11.3.3 js-yaml: 4.1.0 lodash.uniq: 4.5.0 openapi3-ts: 4.5.0 string-argv: 0.3.2 tsconfck: 2.1.2(typescript@5.9.3) - typedoc: 0.28.14(typescript@5.9.3) - typedoc-plugin-coverage: 4.0.2(typedoc@0.28.14(typescript@5.9.3)) - typedoc-plugin-markdown: 4.9.0(typedoc@0.28.14(typescript@5.9.3)) + typedoc: 0.28.15(typescript@5.9.3) + typedoc-plugin-coverage: 4.0.2(typedoc@0.28.15(typescript@5.9.3)) + typedoc-plugin-markdown: 4.9.0(typedoc@0.28.15(typescript@5.9.3)) transitivePeerDependencies: - encoding - openapi-types @@ -14606,7 +14046,7 @@ snapshots: p-limit@4.0.0: dependencies: - yocto-queue: 1.2.1 + yocto-queue: 1.2.2 p-locate@4.1.0: dependencies: @@ -14635,12 +14075,11 @@ snapshots: dependencies: callsites: 3.1.0 - parse-asn1@5.1.7: + parse-asn1@5.1.9: dependencies: asn1.js: 4.10.1 browserify-aes: 1.2.0 evp_bytestokey: 1.0.3 - hash-base: 3.0.5 pbkdf2: 3.1.5 safe-buffer: 5.2.1 @@ -14657,7 +14096,7 @@ snapshots: parse-json@5.2.0: dependencies: '@babel/code-frame': 7.27.1 - error-ex: 1.3.2 + error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -14712,7 +14151,7 @@ snapshots: dependencies: pg-int8: 1.0.1 postgres-array: 2.0.0 - postgres-bytea: 1.0.0 + postgres-bytea: 1.0.1 postgres-date: 1.0.7 postgres-interval: 1.2.0 @@ -14751,9 +14190,9 @@ snapshots: postcss: 8.5.6 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.10 + resolve: 1.22.11 - postcss-js@4.0.1(postcss@8.5.6): + postcss-js@4.1.0(postcss@8.5.6): dependencies: camelcase-css: 2.0.1 postcss: 8.5.6 @@ -14761,18 +14200,18 @@ snapshots: postcss-load-config@4.0.2(postcss@8.5.6): dependencies: lilconfig: 3.1.3 - yaml: 2.8.1 + yaml: 2.8.2 optionalDependencies: postcss: 8.5.6 - postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.101.3(esbuild@0.25.9)): + postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.104.1(esbuild@0.25.12)): dependencies: cosmiconfig: 9.0.0(typescript@5.9.3) - jiti: 2.5.1 + jiti: 2.6.1 postcss: 8.5.6 - semver: 7.7.2 + semver: 7.7.3 optionalDependencies: - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.104.1(esbuild@0.25.12) transitivePeerDependencies: - typescript @@ -14784,13 +14223,13 @@ snapshots: dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 - postcss-selector-parser: 7.1.0 + postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 postcss-modules-scope@3.2.1(postcss@8.5.6): dependencies: postcss: 8.5.6 - postcss-selector-parser: 7.1.0 + postcss-selector-parser: 7.1.1 postcss-modules-values@4.0.0(postcss@8.5.6): dependencies: @@ -14807,7 +14246,7 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-selector-parser@7.1.0: + postcss-selector-parser@7.1.1: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 @@ -14834,7 +14273,7 @@ snapshots: postgres-array@2.0.0: {} - postgres-bytea@1.0.0: {} + postgres-bytea@1.0.1: {} postgres-date@1.0.7: {} @@ -14882,7 +14321,7 @@ snapshots: bn.js: 4.12.2 browserify-rsa: 4.1.1 create-hash: 1.2.0 - parse-asn1: 5.1.7 + parse-asn1: 5.1.9 randombytes: 2.1.0 safe-buffer: 5.2.1 @@ -14892,7 +14331,7 @@ snapshots: punycode@2.3.1: {} - qs@6.14.0: + qs@6.14.1: dependencies: side-channel: 1.1.0 @@ -14928,16 +14367,16 @@ snapshots: react-docgen@7.1.1: dependencies: - '@babel/core': 7.28.4 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/core': 7.28.5 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.28.0 '@types/doctrine': 0.0.9 '@types/resolve': 1.20.6 doctrine: 3.0.0 - resolve: 1.22.10 - strip-indent: 4.0.0 + resolve: 1.22.11 + strip-indent: 4.1.1 transitivePeerDependencies: - supports-color @@ -14952,7 +14391,7 @@ snapshots: prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-components: 6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + styled-components: 6.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-hook-form@7.66.0(react@18.3.1): dependencies: @@ -14977,7 +14416,7 @@ snapshots: devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 - mdast-util-to-hast: 13.2.0 + mdast-util-to-hast: 13.2.1 react: 18.3.1 remark-parse: 11.0.0 remark-rehype: 11.1.2 @@ -15000,7 +14439,7 @@ snapshots: dependencies: '@types/use-sync-external-store': 0.0.6 react: 18.3.1 - use-sync-external-store: 1.5.0(react@18.3.1) + use-sync-external-store: 1.6.0(react@18.3.1) optionalDependencies: '@types/react': 18.3.17 redux: 5.0.1 @@ -15015,7 +14454,7 @@ snapshots: optionalDependencies: '@types/react': 18.3.17 - react-remove-scroll@2.7.1(@types/react@18.3.17)(react@18.3.1): + react-remove-scroll@2.7.2(@types/react@18.3.17)(react@18.3.1): dependencies: react: 18.3.1 react-remove-scroll-bar: 2.3.8(@types/react@18.3.17)(react@18.3.1) @@ -15096,19 +14535,19 @@ snapshots: recharts@3.3.0(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(redux@5.0.1): dependencies: - '@reduxjs/toolkit': 2.9.0(react-redux@9.2.0(@types/react@18.3.17)(react@18.3.1)(redux@5.0.1))(react@18.3.1) + '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@18.3.17)(react@18.3.1)(redux@5.0.1))(react@18.3.1) clsx: 2.1.1 decimal.js-light: 2.5.1 - es-toolkit: 1.39.10 + es-toolkit: 1.43.0 eventemitter3: 5.0.1 - immer: 10.1.3 + immer: 10.2.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-is: 18.3.1 react-redux: 9.2.0(@types/react@18.3.17)(react@18.3.1)(redux@5.0.1) reselect: 5.1.1 tiny-invariant: 1.3.3 - use-sync-external-store: 1.5.0(react@18.3.1) + use-sync-external-store: 1.6.0(react@18.3.1) victory-vendor: 37.3.6 transitivePeerDependencies: - '@types/react' @@ -15129,7 +14568,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -15138,7 +14577,7 @@ snapshots: reftools@1.1.9: {} - regenerate-unicode-properties@10.2.0: + regenerate-unicode-properties@10.2.2: dependencies: regenerate: 1.4.2 @@ -15155,20 +14594,20 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 - regexpu-core@6.2.0: + regexpu-core@6.4.0: dependencies: regenerate: 1.4.2 - regenerate-unicode-properties: 10.2.0 + regenerate-unicode-properties: 10.2.2 regjsgen: 0.8.0 - regjsparser: 0.12.0 + regjsparser: 0.13.0 unicode-match-property-ecmascript: 2.0.0 - unicode-match-property-value-ecmascript: 2.2.0 + unicode-match-property-value-ecmascript: 2.2.1 regjsgen@0.8.0: {} - regjsparser@0.12.0: + regjsparser@0.13.0: dependencies: - jsesc: 3.0.2 + jsesc: 3.1.0 rehype-autolink-headings@7.1.0: dependencies: @@ -15194,7 +14633,7 @@ snapshots: hast-util-from-html-isomorphic: 2.0.0 hast-util-to-text: 4.0.2 katex: 0.16.25 - unist-util-visit-parents: 6.0.1 + unist-util-visit-parents: 6.0.2 vfile: 6.0.3 rehype-slug@6.0.0: @@ -15240,7 +14679,7 @@ snapshots: dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - mdast-util-to-hast: 13.2.0 + mdast-util-to-hast: 13.2.1 unified: 11.0.5 vfile: 6.0.3 @@ -15266,7 +14705,7 @@ snapshots: dependencies: debug: 4.4.3 module-details-from-path: 1.0.4 - resolve: 1.22.10 + resolve: 1.22.11 transitivePeerDependencies: - supports-color @@ -15291,12 +14730,6 @@ snapshots: postcss: 8.5.6 source-map: 0.6.1 - resolve@1.22.10: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - resolve@1.22.11: dependencies: is-core-module: 2.16.1 @@ -15328,32 +14761,35 @@ snapshots: hash-base: 3.1.2 inherits: 2.0.4 - rollup@4.52.2: + rollup@4.55.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.52.2 - '@rollup/rollup-android-arm64': 4.52.2 - '@rollup/rollup-darwin-arm64': 4.52.2 - '@rollup/rollup-darwin-x64': 4.52.2 - '@rollup/rollup-freebsd-arm64': 4.52.2 - '@rollup/rollup-freebsd-x64': 4.52.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.2 - '@rollup/rollup-linux-arm-musleabihf': 4.52.2 - '@rollup/rollup-linux-arm64-gnu': 4.52.2 - '@rollup/rollup-linux-arm64-musl': 4.52.2 - '@rollup/rollup-linux-loong64-gnu': 4.52.2 - '@rollup/rollup-linux-ppc64-gnu': 4.52.2 - '@rollup/rollup-linux-riscv64-gnu': 4.52.2 - '@rollup/rollup-linux-riscv64-musl': 4.52.2 - '@rollup/rollup-linux-s390x-gnu': 4.52.2 - '@rollup/rollup-linux-x64-gnu': 4.52.2 - '@rollup/rollup-linux-x64-musl': 4.52.2 - '@rollup/rollup-openharmony-arm64': 4.52.2 - '@rollup/rollup-win32-arm64-msvc': 4.52.2 - '@rollup/rollup-win32-ia32-msvc': 4.52.2 - '@rollup/rollup-win32-x64-gnu': 4.52.2 - '@rollup/rollup-win32-x64-msvc': 4.52.2 + '@rollup/rollup-android-arm-eabi': 4.55.1 + '@rollup/rollup-android-arm64': 4.55.1 + '@rollup/rollup-darwin-arm64': 4.55.1 + '@rollup/rollup-darwin-x64': 4.55.1 + '@rollup/rollup-freebsd-arm64': 4.55.1 + '@rollup/rollup-freebsd-x64': 4.55.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.55.1 + '@rollup/rollup-linux-arm-musleabihf': 4.55.1 + '@rollup/rollup-linux-arm64-gnu': 4.55.1 + '@rollup/rollup-linux-arm64-musl': 4.55.1 + '@rollup/rollup-linux-loong64-gnu': 4.55.1 + '@rollup/rollup-linux-loong64-musl': 4.55.1 + '@rollup/rollup-linux-ppc64-gnu': 4.55.1 + '@rollup/rollup-linux-ppc64-musl': 4.55.1 + '@rollup/rollup-linux-riscv64-gnu': 4.55.1 + '@rollup/rollup-linux-riscv64-musl': 4.55.1 + '@rollup/rollup-linux-s390x-gnu': 4.55.1 + '@rollup/rollup-linux-x64-gnu': 4.55.1 + '@rollup/rollup-linux-x64-musl': 4.55.1 + '@rollup/rollup-openbsd-x64': 4.55.1 + '@rollup/rollup-openharmony-arm64': 4.55.1 + '@rollup/rollup-win32-arm64-msvc': 4.55.1 + '@rollup/rollup-win32-ia32-msvc': 4.55.1 + '@rollup/rollup-win32-x64-gnu': 4.55.1 + '@rollup/rollup-win32-x64-msvc': 4.55.1 fsevents: 2.3.3 run-parallel@1.2.0: @@ -15389,11 +14825,11 @@ snapshots: safe-stable-stringify@1.1.1: {} - sass-loader@16.0.5(webpack@5.101.3(esbuild@0.25.9)): + sass-loader@16.0.6(webpack@5.104.1(esbuild@0.25.12)): dependencies: neo-async: 2.6.2 optionalDependencies: - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.104.1(esbuild@0.25.12) scheduler@0.23.2: dependencies: @@ -15405,7 +14841,7 @@ snapshots: ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) - schema-utils@4.3.2: + schema-utils@4.3.3: dependencies: '@types/json-schema': 7.0.15 ajv: 8.17.1 @@ -15414,8 +14850,6 @@ snapshots: semver@6.3.1: {} - semver@7.7.2: {} - semver@7.7.3: {} serialize-javascript@6.0.2: @@ -15454,34 +14888,36 @@ snapshots: shallowequal@1.1.0: {} - sharp@0.34.3: + sharp@0.34.5: dependencies: - color: 4.2.3 - detect-libc: 2.0.4 + '@img/colour': 1.0.0 + detect-libc: 2.1.2 semver: 7.7.3 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.3 - '@img/sharp-darwin-x64': 0.34.3 - '@img/sharp-libvips-darwin-arm64': 1.2.0 - '@img/sharp-libvips-darwin-x64': 1.2.0 - '@img/sharp-libvips-linux-arm': 1.2.0 - '@img/sharp-libvips-linux-arm64': 1.2.0 - '@img/sharp-libvips-linux-ppc64': 1.2.0 - '@img/sharp-libvips-linux-s390x': 1.2.0 - '@img/sharp-libvips-linux-x64': 1.2.0 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 - '@img/sharp-libvips-linuxmusl-x64': 1.2.0 - '@img/sharp-linux-arm': 0.34.3 - '@img/sharp-linux-arm64': 0.34.3 - '@img/sharp-linux-ppc64': 0.34.3 - '@img/sharp-linux-s390x': 0.34.3 - '@img/sharp-linux-x64': 0.34.3 - '@img/sharp-linuxmusl-arm64': 0.34.3 - '@img/sharp-linuxmusl-x64': 0.34.3 - '@img/sharp-wasm32': 0.34.3 - '@img/sharp-win32-arm64': 0.34.3 - '@img/sharp-win32-ia32': 0.34.3 - '@img/sharp-win32-x64': 0.34.3 + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 optional: true shebang-command@2.0.0: @@ -15494,7 +14930,7 @@ snapshots: shepherd.js@14.5.1: dependencies: - '@floating-ui/dom': 1.7.3 + '@floating-ui/dom': 1.7.4 '@scarf/scarf': 1.4.0 deepmerge-ts: 7.1.5 @@ -15560,11 +14996,6 @@ snapshots: dependencies: jsep: 1.4.0 - simple-swizzle@0.2.2: - dependencies: - is-arrayish: 0.3.2 - optional: true - slash@3.0.0: {} sonner@2.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -15603,17 +15034,17 @@ snapshots: storybook@9.1.5(@testing-library/dom@10.4.1)(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3))(prettier@3.6.2): dependencies: '@storybook/global': 5.0.0 - '@testing-library/jest-dom': 6.8.0 + '@testing-library/jest-dom': 6.9.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) '@vitest/expect': 3.2.4 '@vitest/mocker': 3.2.4(msw@2.11.6(@types/node@24.10.0)(typescript@5.9.3)) '@vitest/spy': 3.2.4 better-opn: 3.0.2 - esbuild: 0.25.9 - esbuild-register: 3.6.0(esbuild@0.25.9) + esbuild: 0.25.12 + esbuild-register: 3.6.0(esbuild@0.25.12) recast: 0.23.11 - semver: 7.7.2 - ws: 8.18.3 + semver: 7.7.3 + ws: 8.19.0 optionalDependencies: prettier: 3.6.2 transitivePeerDependencies: @@ -15656,14 +15087,14 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -15677,7 +15108,7 @@ snapshots: string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 string.prototype.trim@1.2.10: dependencies: @@ -15685,7 +15116,7 @@ snapshots: call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 @@ -15731,62 +15162,60 @@ snapshots: dependencies: min-indent: 1.0.1 - strip-indent@4.0.0: - dependencies: - min-indent: 1.0.1 + strip-indent@4.1.1: {} strip-json-comments@3.1.1: {} - style-loader@3.3.4(webpack@5.101.3(esbuild@0.25.9)): + style-loader@3.3.4(webpack@5.104.1(esbuild@0.25.12)): dependencies: - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.104.1(esbuild@0.25.12) - style-to-js@1.1.17: + style-to-js@1.1.21: dependencies: - style-to-object: 1.0.9 + style-to-object: 1.0.14 - style-to-object@1.0.9: + style-to-object@1.0.14: dependencies: - inline-style-parser: 0.2.4 + inline-style-parser: 0.2.7 - styled-components@6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + styled-components@6.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@emotion/is-prop-valid': 1.2.2 '@emotion/unitless': 0.8.1 - '@types/stylis': 4.2.5 + '@types/stylis': 4.2.7 css-to-react-native: 3.2.0 - csstype: 3.1.3 + csstype: 3.2.3 postcss: 8.4.49 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) shallowequal: 1.1.0 - stylis: 4.3.2 + stylis: 4.3.6 tslib: 2.6.2 - styled-jsx@5.1.6(@babel/core@7.28.4)(react@18.3.1): + styled-jsx@5.1.6(@babel/core@7.28.5)(react@18.3.1): dependencies: client-only: 0.0.1 react: 18.3.1 optionalDependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 - styled-jsx@5.1.7(@babel/core@7.28.4)(react@18.3.1): + styled-jsx@5.1.7(@babel/core@7.28.5)(react@18.3.1): dependencies: client-only: 0.0.1 react: 18.3.1 optionalDependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 - stylis@4.3.2: {} + stylis@4.3.6: {} - sucrase@3.35.0: + sucrase@3.35.1: dependencies: '@jridgewell/gen-mapping': 0.3.13 commander: 4.1.1 - glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.7 + tinyglobby: 0.2.15 ts-interface-checker: 0.1.13 supports-color@7.2.0: @@ -15843,29 +15272,29 @@ snapshots: picocolors: 1.1.1 postcss: 8.5.6 postcss-import: 15.1.0(postcss@8.5.6) - postcss-js: 4.0.1(postcss@8.5.6) + postcss-js: 4.1.0(postcss@8.5.6) postcss-load-config: 4.0.2(postcss@8.5.6) postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 - resolve: 1.22.10 - sucrase: 3.35.0 + resolve: 1.22.11 + sucrase: 3.35.1 transitivePeerDependencies: - ts-node - tapable@2.2.3: {} + tapable@2.3.0: {} - terser-webpack-plugin@5.3.14(esbuild@0.25.9)(webpack@5.101.3(esbuild@0.25.9)): + terser-webpack-plugin@5.3.16(esbuild@0.25.12)(webpack@5.104.1(esbuild@0.25.12)): dependencies: - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 - schema-utils: 4.3.2 + schema-utils: 4.3.3 serialize-javascript: 6.0.2 - terser: 5.44.0 - webpack: 5.101.3(esbuild@0.25.9) + terser: 5.44.1 + webpack: 5.104.1(esbuild@0.25.12) optionalDependencies: - esbuild: 0.25.9 + esbuild: 0.25.12 - terser@5.44.0: + terser@5.44.1: dependencies: '@jridgewell/source-map': 0.3.11 acorn: 8.15.0 @@ -15897,13 +15326,13 @@ snapshots: tinyrainbow@2.0.0: {} - tinyspy@4.0.3: {} + tinyspy@4.0.4: {} - tldts-core@7.0.17: {} + tldts-core@7.0.19: {} - tldts@7.0.17: + tldts@7.0.19: dependencies: - tldts-core: 7.0.17 + tldts-core: 7.0.19 to-buffer@1.2.2: dependencies: @@ -15917,7 +15346,7 @@ snapshots: tough-cookie@6.0.0: dependencies: - tldts: 7.0.17 + tldts: 7.0.19 tr46@0.0.3: {} @@ -15927,7 +15356,7 @@ snapshots: trough@2.2.0: {} - ts-api-utils@2.1.0(typescript@5.9.3): + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -15942,8 +15371,8 @@ snapshots: tsconfig-paths-webpack-plugin@4.2.0: dependencies: chalk: 4.1.2 - enhanced-resolve: 5.18.3 - tapable: 2.2.3 + enhanced-resolve: 5.18.4 + tapable: 2.3.0 tsconfig-paths: 4.2.0 tsconfig-paths@3.15.0: @@ -16012,22 +15441,22 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typedoc-plugin-coverage@4.0.2(typedoc@0.28.14(typescript@5.9.3)): + typedoc-plugin-coverage@4.0.2(typedoc@0.28.15(typescript@5.9.3)): dependencies: - typedoc: 0.28.14(typescript@5.9.3) + typedoc: 0.28.15(typescript@5.9.3) - typedoc-plugin-markdown@4.9.0(typedoc@0.28.14(typescript@5.9.3)): + typedoc-plugin-markdown@4.9.0(typedoc@0.28.15(typescript@5.9.3)): dependencies: - typedoc: 0.28.14(typescript@5.9.3) + typedoc: 0.28.15(typescript@5.9.3) - typedoc@0.28.14(typescript@5.9.3): + typedoc@0.28.15(typescript@5.9.3): dependencies: - '@gerrit0/mini-shiki': 3.14.0 + '@gerrit0/mini-shiki': 3.20.0 lunr: 2.3.9 markdown-it: 14.1.0 minimatch: 9.0.5 typescript: 5.9.3 - yaml: 2.8.1 + yaml: 2.8.2 typescript@5.9.3: {} @@ -16047,11 +15476,11 @@ snapshots: unicode-match-property-ecmascript@2.0.0: dependencies: unicode-canonical-property-names-ecmascript: 2.0.1 - unicode-property-aliases-ecmascript: 2.1.0 + unicode-property-aliases-ecmascript: 2.2.0 - unicode-match-property-value-ecmascript@2.2.0: {} + unicode-match-property-value-ecmascript@2.2.1: {} - unicode-property-aliases-ecmascript@2.1.0: {} + unicode-property-aliases-ecmascript@2.2.0: {} unicorn-magic@0.1.0: {} @@ -16068,9 +15497,9 @@ snapshots: unist-util-find-after@5.0.0: dependencies: '@types/unist': 3.0.3 - unist-util-is: 6.0.0 + unist-util-is: 6.0.1 - unist-util-is@6.0.0: + unist-util-is@6.0.1: dependencies: '@types/unist': 3.0.3 @@ -16087,16 +15516,16 @@ snapshots: dependencies: '@types/unist': 3.0.3 - unist-util-visit-parents@6.0.1: + unist-util-visit-parents@6.0.2: dependencies: '@types/unist': 3.0.3 - unist-util-is: 6.0.0 + unist-util-is: 6.0.1 unist-util-visit@5.0.0: dependencies: '@types/unist': 3.0.3 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 universalify@2.0.1: {} @@ -16138,9 +15567,9 @@ snapshots: until-async@3.0.2: {} - update-browserslist-db@1.1.3(browserslist@4.25.4): + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: - browserslist: 4.25.4 + browserslist: 4.28.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -16153,7 +15582,7 @@ snapshots: url@0.11.4: dependencies: punycode: 1.4.1 - qs: 6.14.0 + qs: 6.14.1 use-callback-ref@1.3.3(@types/react@18.3.17)(react@18.3.1): dependencies: @@ -16170,7 +15599,7 @@ snapshots: optionalDependencies: '@types/react': 18.3.17 - use-sync-external-store@1.5.0(react@18.3.1): + use-sync-external-store@1.6.0(react@18.3.1): dependencies: react: 18.3.1 @@ -16180,7 +15609,7 @@ snapshots: dependencies: inherits: 2.0.4 is-arguments: 1.2.0 - is-generator-function: 1.1.0 + is-generator-function: 1.1.2 is-typed-array: 1.1.15 which-typed-array: 1.1.19 @@ -16194,22 +15623,7 @@ snapshots: uuid@9.0.1: {} - validate.io-array@1.0.6: {} - - validate.io-function@1.0.2: {} - - validate.io-integer-array@1.0.0: - dependencies: - validate.io-array: 1.0.6 - validate.io-integer: 1.0.5 - - validate.io-integer@1.0.5: - dependencies: - validate.io-number: 1.0.3 - - validate.io-number@1.0.3: {} - - validator@13.15.20: {} + validator@13.15.26: {} vaul@1.1.2(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: @@ -16237,7 +15651,7 @@ snapshots: victory-vendor@37.3.6: dependencies: - '@types/d3-array': 3.2.1 + '@types/d3-array': 3.2.2 '@types/d3-ease': 3.0.2 '@types/d3-interpolate': 3.0.4 '@types/d3-scale': 4.0.9 @@ -16258,7 +15672,7 @@ snapshots: dependencies: loose-envify: 1.4.0 - watchpack@2.4.4: + watchpack@2.5.0: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 @@ -16267,15 +15681,15 @@ snapshots: webidl-conversions@3.0.1: {} - webpack-dev-middleware@6.1.3(webpack@5.101.3(esbuild@0.25.9)): + webpack-dev-middleware@6.1.3(webpack@5.104.1(esbuild@0.25.12)): dependencies: colorette: 2.0.20 memfs: 3.5.3 mime-types: 2.1.35 range-parser: 1.2.1 - schema-utils: 4.3.2 + schema-utils: 4.3.3 optionalDependencies: - webpack: 5.101.3(esbuild@0.25.9) + webpack: 5.104.1(esbuild@0.25.12) webpack-hot-middleware@2.26.1: dependencies: @@ -16289,7 +15703,7 @@ snapshots: webpack-virtual-modules@0.6.2: {} - webpack@5.101.3(esbuild@0.25.9): + webpack@5.104.1(esbuild@0.25.12): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -16299,22 +15713,22 @@ snapshots: '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 acorn-import-phases: 1.0.4(acorn@8.15.0) - browserslist: 4.25.4 + browserslist: 4.28.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.3 - es-module-lexer: 1.7.0 + enhanced-resolve: 5.18.4 + es-module-lexer: 2.0.0 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.0 + loader-runner: 4.3.1 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 4.3.2 - tapable: 2.2.3 - terser-webpack-plugin: 5.3.14(esbuild@0.25.9)(webpack@5.101.3(esbuild@0.25.9)) - watchpack: 2.4.4 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.16(esbuild@0.25.12)(webpack@5.104.1(esbuild@0.25.12)) + watchpack: 2.5.0 webpack-sources: 3.3.3 transitivePeerDependencies: - '@swc/core' @@ -16393,7 +15807,7 @@ snapshots: wrappy@1.0.2: {} - ws@8.18.3: {} + ws@8.19.0: {} xmlbuilder@15.1.1: {} @@ -16405,7 +15819,7 @@ snapshots: yaml@1.10.2: {} - yaml@2.8.1: {} + yaml@2.8.2: {} yargs-parser@21.1.1: {} @@ -16421,25 +15835,25 @@ snapshots: yocto-queue@0.1.0: {} - yocto-queue@1.2.1: {} + yocto-queue@1.2.2: {} yoctocolors-cjs@2.1.3: {} zod@3.25.76: {} - zustand@4.5.7(@types/react@18.3.17)(immer@10.1.3)(react@18.3.1): + zustand@4.5.7(@types/react@18.3.17)(immer@11.1.3)(react@18.3.1): dependencies: - use-sync-external-store: 1.5.0(react@18.3.1) + use-sync-external-store: 1.6.0(react@18.3.1) optionalDependencies: '@types/react': 18.3.17 - immer: 10.1.3 + immer: 11.1.3 react: 18.3.1 - zustand@5.0.8(@types/react@18.3.17)(immer@10.1.3)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)): + zustand@5.0.8(@types/react@18.3.17)(immer@11.1.3)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)): optionalDependencies: '@types/react': 18.3.17 - immer: 10.1.3 + immer: 11.1.3 react: 18.3.1 - use-sync-external-store: 1.5.0(react@18.3.1) + use-sync-external-store: 1.6.0(react@18.3.1) zwitch@2.0.4: {} diff --git a/autogpt_platform/frontend/src/app/(platform)/auth/integrations/oauth_callback/route.ts b/autogpt_platform/frontend/src/app/(platform)/auth/integrations/oauth_callback/route.ts index df1de26300..41d05a9afb 100644 --- a/autogpt_platform/frontend/src/app/(platform)/auth/integrations/oauth_callback/route.ts +++ b/autogpt_platform/frontend/src/app/(platform)/auth/integrations/oauth_callback/route.ts @@ -1,4 +1,4 @@ -import { OAuthPopupResultMessage } from "@/components/renderers/input-renderer/fields/CredentialField/models/OAuthCredentialModal/useOAuthCredentialModal"; +import { OAuthPopupResultMessage } from "./types"; import { NextResponse } from "next/server"; // This route is intended to be used as the callback for integration OAuth flows, diff --git a/autogpt_platform/frontend/src/app/(platform)/auth/integrations/oauth_callback/types.ts b/autogpt_platform/frontend/src/app/(platform)/auth/integrations/oauth_callback/types.ts new file mode 100644 index 0000000000..9000adf392 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/auth/integrations/oauth_callback/types.ts @@ -0,0 +1,11 @@ +export type OAuthPopupResultMessage = { message_type: "oauth_popup_result" } & ( + | { + success: true; + code: string; + state: string; + } + | { + success: false; + message: string; + } +); diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/AgentOutputs/AgentOutputs.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/AgentOutputs/AgentOutputs.tsx index 237bea2ab0..de56bb46b8 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/AgentOutputs/AgentOutputs.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/AgentOutputs/AgentOutputs.tsx @@ -16,6 +16,7 @@ import { SheetTitle, SheetTrigger, } from "@/components/__legacy__/ui/sheet"; +import { Button } from "@/components/atoms/Button/Button"; import { Tooltip, TooltipContent, @@ -25,7 +26,6 @@ import { import { BookOpenIcon } from "@phosphor-icons/react"; import { useMemo } from "react"; import { useShallow } from "zustand/react/shallow"; -import { BuilderActionButton } from "../BuilderActionButton"; export const AgentOutputs = ({ flowID }: { flowID: string | null }) => { const hasOutputs = useGraphStore(useShallow((state) => state.hasOutputs)); @@ -76,9 +76,13 @@ export const AgentOutputs = ({ flowID }: { flowID: string | null }) => { - - - + diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/BuilderActionButton.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/BuilderActionButton.tsx deleted file mode 100644 index f8b3f1051e..0000000000 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/BuilderActionButton.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Button } from "@/components/atoms/Button/Button"; -import { ButtonProps } from "@/components/atoms/Button/helpers"; -import { cn } from "@/lib/utils"; -import { CircleNotchIcon } from "@phosphor-icons/react"; - -export const BuilderActionButton = ({ - children, - className, - isLoading, - ...props -}: ButtonProps & { isLoading?: boolean }) => { - return ( - - ); -}; diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/PublishToMarketplace/PublishToMarketplace.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/PublishToMarketplace/PublishToMarketplace.tsx index 1e6545dfbd..e7381b7d52 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/PublishToMarketplace/PublishToMarketplace.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/PublishToMarketplace/PublishToMarketplace.tsx @@ -1,12 +1,12 @@ -import { ShareIcon } from "@phosphor-icons/react"; -import { BuilderActionButton } from "../BuilderActionButton"; +import { Button } from "@/components/atoms/Button/Button"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/atoms/Tooltip/BaseTooltip"; -import { usePublishToMarketplace } from "./usePublishToMarketplace"; import { PublishAgentModal } from "@/components/contextual/PublishAgentModal/PublishAgentModal"; +import { ShareIcon } from "@phosphor-icons/react"; +import { usePublishToMarketplace } from "./usePublishToMarketplace"; export const PublishToMarketplace = ({ flowID }: { flowID: string | null }) => { const { handlePublishToMarketplace, publishState, handleStateChange } = @@ -16,12 +16,14 @@ export const PublishToMarketplace = ({ flowID }: { flowID: string | null }) => { <> - - - + + Publish to Marketplace @@ -30,6 +32,7 @@ export const PublishToMarketplace = ({ flowID }: { flowID: string | null }) => { targetState={publishState} onStateChange={handleStateChange} preSelectedAgentId={flowID || undefined} + showTrigger={false} /> ); diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunGraph/RunGraph.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunGraph/RunGraph.tsx index f4c1a7331f..7ee00ec285 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunGraph/RunGraph.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunGraph/RunGraph.tsx @@ -1,15 +1,14 @@ -import { useRunGraph } from "./useRunGraph"; import { useGraphStore } from "@/app/(platform)/build/stores/graphStore"; -import { useShallow } from "zustand/react/shallow"; -import { PlayIcon, StopIcon } from "@phosphor-icons/react"; -import { cn } from "@/lib/utils"; -import { RunInputDialog } from "../RunInputDialog/RunInputDialog"; +import { Button } from "@/components/atoms/Button/Button"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/atoms/Tooltip/BaseTooltip"; -import { BuilderActionButton } from "../BuilderActionButton"; +import { PlayIcon, StopIcon } from "@phosphor-icons/react"; +import { useShallow } from "zustand/react/shallow"; +import { RunInputDialog } from "../RunInputDialog/RunInputDialog"; +import { useRunGraph } from "./useRunGraph"; export const RunGraph = ({ flowID }: { flowID: string | null }) => { const { @@ -29,21 +28,19 @@ export const RunGraph = ({ flowID }: { flowID: string | null }) => { <> - {!isGraphRunning ? ( - + ) : ( - + )} - + {isGraphRunning ? "Stop agent" : "Run agent"} diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunInputDialog/RunInputDialog.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunInputDialog/RunInputDialog.tsx index 2d9f51c8bf..431feeaade 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunInputDialog/RunInputDialog.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunInputDialog/RunInputDialog.tsx @@ -5,7 +5,7 @@ import { useGraphStore } from "@/app/(platform)/build/stores/graphStore"; import { Button } from "@/components/atoms/Button/Button"; import { ClockIcon, PlayIcon } from "@phosphor-icons/react"; import { Text } from "@/components/atoms/Text/Text"; -import { FormRenderer } from "@/components/renderers/input-renderer/FormRenderer"; +import { FormRenderer } from "@/components/renderers/InputRenderer/FormRenderer"; import { useRunInputDialog } from "./useRunInputDialog"; import { CronSchedulerDialog } from "../CronSchedulerDialog/CronSchedulerDialog"; diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunInputDialog/useRunInputDialog.ts b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunInputDialog/useRunInputDialog.ts index f0bb3b1c98..a71ad0bd07 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunInputDialog/useRunInputDialog.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/RunInputDialog/useRunInputDialog.ts @@ -8,7 +8,7 @@ import { import { parseAsInteger, parseAsString, useQueryStates } from "nuqs"; import { useMemo, useState } from "react"; import { uiSchema } from "../../../FlowEditor/nodes/uiSchema"; -import { isCredentialFieldSchema } from "@/components/renderers/input-renderer/fields/CredentialField/helpers"; +import { isCredentialFieldSchema } from "@/components/renderers/InputRenderer/custom/CredentialField/helpers"; export const useRunInputDialog = ({ setIsOpen, diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/ScheduleGraph/ScheduleGraph.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/ScheduleGraph/ScheduleGraph.tsx index be588fa9e7..5cc8538de1 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/ScheduleGraph/ScheduleGraph.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderActions/components/ScheduleGraph/ScheduleGraph.tsx @@ -1,14 +1,14 @@ -import { ClockIcon } from "@phosphor-icons/react"; -import { RunInputDialog } from "../RunInputDialog/RunInputDialog"; -import { useScheduleGraph } from "./useScheduleGraph"; +import { Button } from "@/components/atoms/Button/Button"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/atoms/Tooltip/BaseTooltip"; +import { ClockIcon } from "@phosphor-icons/react"; import { CronSchedulerDialog } from "../CronSchedulerDialog/CronSchedulerDialog"; -import { BuilderActionButton } from "../BuilderActionButton"; +import { RunInputDialog } from "../RunInputDialog/RunInputDialog"; +import { useScheduleGraph } from "./useScheduleGraph"; export const ScheduleGraph = ({ flowID }: { flowID: string | null }) => { const { @@ -23,12 +23,14 @@ export const ScheduleGraph = ({ flowID }: { flowID: string | null }) => { - - - + +

Schedule Graph

diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/DraftRecoveryDialog/DraftRecoveryPopup.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/DraftRecoveryDialog/DraftRecoveryPopup.tsx new file mode 100644 index 0000000000..905d1d4680 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/DraftRecoveryDialog/DraftRecoveryPopup.tsx @@ -0,0 +1,160 @@ +"use client"; + +import { Button } from "@/components/atoms/Button/Button"; +import { ClockCounterClockwiseIcon, XIcon } from "@phosphor-icons/react"; +import { cn } from "@/lib/utils"; +import { formatTimeAgo } from "@/lib/utils/time"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/atoms/Tooltip/BaseTooltip"; +import { useDraftRecoveryPopup } from "./useDraftRecoveryPopup"; +import { Text } from "@/components/atoms/Text/Text"; +import { AnimatePresence, motion } from "framer-motion"; +import { DraftDiff } from "@/lib/dexie/draft-utils"; + +interface DraftRecoveryPopupProps { + isInitialLoadComplete: boolean; +} + +function formatDiffSummary(diff: DraftDiff | null): string { + if (!diff) return ""; + + const parts: string[] = []; + + // Node changes + const nodeChanges: string[] = []; + if (diff.nodes.added > 0) nodeChanges.push(`+${diff.nodes.added}`); + if (diff.nodes.removed > 0) nodeChanges.push(`-${diff.nodes.removed}`); + if (diff.nodes.modified > 0) nodeChanges.push(`~${diff.nodes.modified}`); + + if (nodeChanges.length > 0) { + parts.push( + `${nodeChanges.join("/")} block${diff.nodes.added + diff.nodes.removed + diff.nodes.modified !== 1 ? "s" : ""}`, + ); + } + + // Edge changes + const edgeChanges: string[] = []; + if (diff.edges.added > 0) edgeChanges.push(`+${diff.edges.added}`); + if (diff.edges.removed > 0) edgeChanges.push(`-${diff.edges.removed}`); + if (diff.edges.modified > 0) edgeChanges.push(`~${diff.edges.modified}`); + + if (edgeChanges.length > 0) { + parts.push( + `${edgeChanges.join("/")} connection${diff.edges.added + diff.edges.removed + diff.edges.modified !== 1 ? "s" : ""}`, + ); + } + + return parts.join(", "); +} + +export function DraftRecoveryPopup({ + isInitialLoadComplete, +}: DraftRecoveryPopupProps) { + const { + isOpen, + popupRef, + nodeCount, + edgeCount, + diff, + savedAt, + onLoad, + onDiscard, + } = useDraftRecoveryPopup(isInitialLoadComplete); + + const diffSummary = formatDiffSummary(diff); + + return ( + + {isOpen && ( + +
+
+ +
+ +
+ + Unsaved changes found + + + {diffSummary || + `${nodeCount} block${nodeCount !== 1 ? "s" : ""}, ${edgeCount} connection${edgeCount !== 1 ? "s" : ""}`}{" "} + • {formatTimeAgo(new Date(savedAt).toISOString())} + +
+ +
+ + + + + Restore changes + + + + + + Discard changes + +
+
+
+ )} +
+ ); +} diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/DraftRecoveryDialog/useDraftRecoveryPopup.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/DraftRecoveryDialog/useDraftRecoveryPopup.tsx new file mode 100644 index 0000000000..7a77f7b4cc --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/DraftRecoveryDialog/useDraftRecoveryPopup.tsx @@ -0,0 +1,63 @@ +import { useEffect, useRef } from "react"; +import { useDraftManager } from "../FlowEditor/Flow/useDraftManager"; + +export const useDraftRecoveryPopup = (isInitialLoadComplete: boolean) => { + const popupRef = useRef(null); + + const { + isRecoveryOpen: isOpen, + savedAt, + nodeCount, + edgeCount, + diff, + loadDraft: onLoad, + discardDraft: onDiscard, + } = useDraftManager(isInitialLoadComplete); + + useEffect(() => { + if (!isOpen) return; + + const handleClickOutside = (event: MouseEvent) => { + if ( + popupRef.current && + !popupRef.current.contains(event.target as Node) + ) { + onDiscard(); + } + }; + + const timeoutId = setTimeout(() => { + document.addEventListener("mousedown", handleClickOutside); + }, 100); + + return () => { + clearTimeout(timeoutId); + document.removeEventListener("mousedown", handleClickOutside); + }; + }, [isOpen, onDiscard]); + + useEffect(() => { + if (!isOpen) return; + + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === "Escape") { + onDiscard(); + } + }; + + document.addEventListener("keydown", handleKeyDown); + return () => { + document.removeEventListener("keydown", handleKeyDown); + }; + }, [isOpen, onDiscard]); + return { + popupRef, + isOpen, + nodeCount, + edgeCount, + diff, + savedAt, + onLoad, + onDiscard, + }; +}; diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/Flow.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/Flow.tsx index c9cf5296c6..faaebb6b35 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/Flow.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/Flow.tsx @@ -1,26 +1,27 @@ -import { ReactFlow, Background } from "@xyflow/react"; -import NewControlPanel from "../../NewControlPanel/NewControlPanel"; -import CustomEdge from "../edges/CustomEdge"; -import { useFlow } from "./useFlow"; -import { useShallow } from "zustand/react/shallow"; -import { useNodeStore } from "../../../stores/nodeStore"; -import { useMemo, useEffect, useCallback } from "react"; -import { CustomNode } from "../nodes/CustomNode/CustomNode"; -import { useCustomEdge } from "../edges/useCustomEdge"; -import { useFlowRealtime } from "./useFlowRealtime"; -import { GraphLoadingBox } from "./components/GraphLoadingBox"; -import { BuilderActions } from "../../BuilderActions/BuilderActions"; -import { RunningBackground } from "./components/RunningBackground"; -import { useGraphStore } from "../../../stores/graphStore"; -import { useCopyPaste } from "./useCopyPaste"; -import { FloatingReviewsPanel } from "@/components/organisms/FloatingReviewsPanel/FloatingReviewsPanel"; -import { parseAsString, useQueryStates } from "nuqs"; -import { CustomControls } from "./components/CustomControl"; import { useGetV1GetSpecificGraph } from "@/app/api/__generated__/endpoints/graphs/graphs"; import { okData } from "@/app/api/helpers"; +import { FloatingReviewsPanel } from "@/components/organisms/FloatingReviewsPanel/FloatingReviewsPanel"; +import { Background, ReactFlow } from "@xyflow/react"; +import { parseAsString, useQueryStates } from "nuqs"; +import { useCallback, useMemo } from "react"; +import { useShallow } from "zustand/react/shallow"; +import { useGraphStore } from "../../../stores/graphStore"; +import { useNodeStore } from "../../../stores/nodeStore"; +import { BuilderActions } from "../../BuilderActions/BuilderActions"; +import { DraftRecoveryPopup } from "../../DraftRecoveryDialog/DraftRecoveryPopup"; +import { FloatingSafeModeToggle } from "../../FloatingSafeModeToogle"; +import NewControlPanel from "../../NewControlPanel/NewControlPanel"; +import CustomEdge from "../edges/CustomEdge"; +import { useCustomEdge } from "../edges/useCustomEdge"; +import { CustomNode } from "../nodes/CustomNode/CustomNode"; +import { CustomControls } from "./components/CustomControl"; +import { GraphLoadingBox } from "./components/GraphLoadingBox"; +import { RunningBackground } from "./components/RunningBackground"; import { TriggerAgentBanner } from "./components/TriggerAgentBanner"; import { resolveCollisions } from "./helpers/resolve-collision"; -import { FloatingSafeModeToggle } from "../../FloatingSafeModeToogle"; +import { useCopyPaste } from "./useCopyPaste"; +import { useFlow } from "./useFlow"; +import { useFlowRealtime } from "./useFlowRealtime"; export const Flow = () => { const [{ flowID, flowExecutionID }] = useQueryStates({ @@ -41,14 +42,18 @@ export const Flow = () => { const nodes = useNodeStore(useShallow((state) => state.nodes)); const setNodes = useNodeStore(useShallow((state) => state.setNodes)); + const onNodesChange = useNodeStore( useShallow((state) => state.onNodesChange), ); + const hasWebhookNodes = useNodeStore( useShallow((state) => state.hasWebhookNodes()), ); + const nodeTypes = useMemo(() => ({ custom: CustomNode }), []); const edgeTypes = useMemo(() => ({ custom: CustomEdge }), []); + const onNodeDragStop = useCallback(() => { setNodes( resolveCollisions(nodes, { @@ -60,29 +65,26 @@ export const Flow = () => { }, [setNodes, nodes]); const { edges, onConnect, onEdgesChange } = useCustomEdge(); - // We use this hook to load the graph and convert them into custom nodes and edges. - const { onDragOver, onDrop, isFlowContentLoading, isLocked, setIsLocked } = - useFlow(); + // for loading purpose + const { + onDragOver, + onDrop, + isFlowContentLoading, + isInitialLoadComplete, + isLocked, + setIsLocked, + } = useFlow(); // This hook is used for websocket realtime updates. useFlowRealtime(); // Copy/paste functionality - const handleCopyPaste = useCopyPaste(); + useCopyPaste(); - useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - handleCopyPaste(event); - }; - - window.addEventListener("keydown", handleKeyDown); - return () => { - window.removeEventListener("keydown", handleKeyDown); - }; - }, [handleCopyPaste]); const isGraphRunning = useGraphStore( useShallow((state) => state.isGraphRunning), ); + return (
@@ -102,6 +104,7 @@ export const Flow = () => { nodesDraggable={!isLocked} nodesConnectable={!isLocked} elementsSelectable={!isLocked} + deleteKeyCode={["Backspace", "Delete"]} > @@ -115,6 +118,7 @@ export const Flow = () => { className="right-2 top-32 p-2" /> )} +
{/* TODO: Need to update it in future - also do not send executionId as prop - rather use useQueryState inside the component */} diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/helpers/resolve-collision.ts b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/helpers/resolve-collision.ts index c05f00b5fb..890d1982c8 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/helpers/resolve-collision.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/helpers/resolve-collision.ts @@ -48,8 +48,6 @@ export const resolveCollisions: CollisionAlgorithm = ( const width = (node.width ?? node.measured?.width ?? 0) + margin * 2; const height = (node.height ?? node.measured?.height ?? 0) + margin * 2; - console.log("width", width); - console.log("height", height); const x = node.position.x - margin; const y = node.position.y - margin; diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useCopyPaste.ts b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useCopyPaste.ts index 7a8213da22..c6c54006d4 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useCopyPaste.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useCopyPaste.ts @@ -1,4 +1,4 @@ -import { useCallback } from "react"; +import { useCallback, useEffect } from "react"; import { useReactFlow } from "@xyflow/react"; import { v4 as uuidv4 } from "uuid"; import { useNodeStore } from "../../../stores/nodeStore"; @@ -151,5 +151,16 @@ export function useCopyPaste() { [getViewport, toast], ); + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + handleCopyPaste(event); + }; + + window.addEventListener("keydown", handleKeyDown); + return () => { + window.removeEventListener("keydown", handleKeyDown); + }; + }, [handleCopyPaste]); + return handleCopyPaste; } diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useDraftManager.ts b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useDraftManager.ts new file mode 100644 index 0000000000..a38def74f6 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useDraftManager.ts @@ -0,0 +1,319 @@ +import { useState, useCallback, useEffect, useRef } from "react"; +import { parseAsString, parseAsInteger, useQueryStates } from "nuqs"; +import { + draftService, + getTempFlowId, + getOrCreateTempFlowId, + DraftData, +} from "@/services/builder-draft/draft-service"; +import { BuilderDraft } from "@/lib/dexie/db"; +import { + cleanNodes, + cleanEdges, + calculateDraftDiff, + DraftDiff, +} from "@/lib/dexie/draft-utils"; +import { useNodeStore } from "../../../stores/nodeStore"; +import { useEdgeStore } from "../../../stores/edgeStore"; +import { useGraphStore } from "../../../stores/graphStore"; +import { useHistoryStore } from "../../../stores/historyStore"; +import isEqual from "lodash/isEqual"; + +const AUTO_SAVE_INTERVAL_MS = 15000; // 15 seconds + +interface DraftRecoveryState { + isOpen: boolean; + draft: BuilderDraft | null; + diff: DraftDiff | null; +} + +/** + * Consolidated hook for draft persistence and recovery + * - Auto-saves builder state every 15 seconds + * - Saves on beforeunload event + * - Checks for and manages unsaved drafts on load + */ +export function useDraftManager(isInitialLoadComplete: boolean) { + const [state, setState] = useState({ + isOpen: false, + draft: null, + diff: null, + }); + + const [{ flowID, flowVersion }] = useQueryStates({ + flowID: parseAsString, + flowVersion: parseAsInteger, + }); + + const lastSavedStateRef = useRef(null); + const saveTimeoutRef = useRef(null); + const isDirtyRef = useRef(false); + const hasCheckedForDraft = useRef(false); + + const getEffectiveFlowId = useCallback((): string => { + return flowID || getOrCreateTempFlowId(); + }, [flowID]); + + const getCurrentState = useCallback((): DraftData => { + const nodes = useNodeStore.getState().nodes; + const edges = useEdgeStore.getState().edges; + const nodeCounter = useNodeStore.getState().nodeCounter; + const graphStore = useGraphStore.getState(); + + return { + nodes, + edges, + graphSchemas: { + input: graphStore.inputSchema, + credentials: graphStore.credentialsInputSchema, + output: graphStore.outputSchema, + }, + nodeCounter, + flowVersion: flowVersion ?? undefined, + }; + }, [flowVersion]); + + const cleanStateForComparison = useCallback((stateData: DraftData) => { + return { + nodes: cleanNodes(stateData.nodes), + edges: cleanEdges(stateData.edges), + }; + }, []); + + const hasChanges = useCallback((): boolean => { + const currentState = getCurrentState(); + + if (!lastSavedStateRef.current) { + return currentState.nodes.length > 0; + } + + const currentClean = cleanStateForComparison(currentState); + const lastClean = cleanStateForComparison(lastSavedStateRef.current); + + return !isEqual(currentClean, lastClean); + }, [getCurrentState, cleanStateForComparison]); + + const saveDraft = useCallback(async () => { + const effectiveFlowId = getEffectiveFlowId(); + const currentState = getCurrentState(); + + if (currentState.nodes.length === 0 && currentState.edges.length === 0) { + return; + } + + if (!hasChanges()) { + return; + } + + try { + await draftService.saveDraft(effectiveFlowId, currentState); + lastSavedStateRef.current = currentState; + isDirtyRef.current = false; + } catch (error) { + console.error("[DraftPersistence] Failed to save draft:", error); + } + }, [getEffectiveFlowId, getCurrentState, hasChanges]); + + const scheduleSave = useCallback(() => { + isDirtyRef.current = true; + + if (saveTimeoutRef.current) { + clearTimeout(saveTimeoutRef.current); + } + + saveTimeoutRef.current = setTimeout(() => { + saveDraft(); + }, AUTO_SAVE_INTERVAL_MS); + }, [saveDraft]); + + useEffect(() => { + const unsubscribeNodes = useNodeStore.subscribe((storeState, prevState) => { + if (storeState.nodes !== prevState.nodes) { + scheduleSave(); + } + }); + + const unsubscribeEdges = useEdgeStore.subscribe((storeState, prevState) => { + if (storeState.edges !== prevState.edges) { + scheduleSave(); + } + }); + + return () => { + unsubscribeNodes(); + unsubscribeEdges(); + }; + }, [scheduleSave]); + + useEffect(() => { + const handleBeforeUnload = () => { + if (isDirtyRef.current) { + const effectiveFlowId = getEffectiveFlowId(); + const currentState = getCurrentState(); + + if ( + currentState.nodes.length === 0 && + currentState.edges.length === 0 + ) { + return; + } + + draftService.saveDraft(effectiveFlowId, currentState).catch(() => { + // Ignore errors on unload + }); + } + }; + + window.addEventListener("beforeunload", handleBeforeUnload); + return () => { + window.removeEventListener("beforeunload", handleBeforeUnload); + }; + }, [getEffectiveFlowId, getCurrentState]); + + useEffect(() => { + return () => { + if (saveTimeoutRef.current) { + clearTimeout(saveTimeoutRef.current); + } + if (isDirtyRef.current) { + saveDraft(); + } + }; + }, [saveDraft]); + + useEffect(() => { + draftService.cleanupExpired().catch((error) => { + console.error( + "[DraftPersistence] Failed to cleanup expired drafts:", + error, + ); + }); + }, []); + + const checkForDraft = useCallback(async () => { + const effectiveFlowId = flowID || getTempFlowId(); + + if (!effectiveFlowId) { + return; + } + + try { + const draft = await draftService.loadDraft(effectiveFlowId); + + if (!draft) { + return; + } + + const currentNodes = useNodeStore.getState().nodes; + const currentEdges = useEdgeStore.getState().edges; + + const isDifferent = draftService.isDraftDifferent( + draft, + currentNodes, + currentEdges, + ); + + if (isDifferent && (draft.nodes.length > 0 || draft.edges.length > 0)) { + const diff = calculateDraftDiff( + draft.nodes, + draft.edges, + currentNodes, + currentEdges, + ); + setState({ + isOpen: true, + draft, + diff, + }); + } else { + await draftService.deleteDraft(effectiveFlowId); + } + } catch (error) { + console.error("[DraftRecovery] Failed to check for draft:", error); + } + }, [flowID]); + + useEffect(() => { + if (isInitialLoadComplete && !hasCheckedForDraft.current) { + hasCheckedForDraft.current = true; + checkForDraft(); + } + }, [isInitialLoadComplete, checkForDraft]); + + useEffect(() => { + hasCheckedForDraft.current = false; + setState({ + isOpen: false, + draft: null, + diff: null, + }); + }, [flowID]); + + const loadDraft = useCallback(async () => { + if (!state.draft) return; + + const { draft } = state; + + try { + useNodeStore.getState().setNodes(draft.nodes); + useEdgeStore.getState().setEdges(draft.edges); + draft.nodes.forEach((node) => { + useNodeStore.getState().syncHardcodedValuesWithHandleIds(node.id); + }); + + if (draft.nodeCounter !== undefined) { + useNodeStore.setState({ nodeCounter: draft.nodeCounter }); + } + + if (draft.graphSchemas) { + useGraphStore + .getState() + .setGraphSchemas( + draft.graphSchemas.input as Record | null, + draft.graphSchemas.credentials as Record | null, + draft.graphSchemas.output as Record | null, + ); + } + + setTimeout(() => { + useHistoryStore.getState().initializeHistory(); + }, 100); + + await draftService.deleteDraft(draft.id); + + setState({ + isOpen: false, + draft: null, + diff: null, + }); + } catch (error) { + console.error("[DraftRecovery] Failed to load draft:", error); + } + }, [state.draft]); + + const discardDraft = useCallback(async () => { + if (!state.draft) { + setState({ isOpen: false, draft: null, diff: null }); + return; + } + + try { + await draftService.deleteDraft(state.draft.id); + } catch (error) { + console.error("[DraftRecovery] Failed to discard draft:", error); + } + + setState({ isOpen: false, draft: null, diff: null }); + }, [state.draft]); + + return { + // Recovery popup props + isRecoveryOpen: state.isOpen, + savedAt: state.draft?.savedAt ?? 0, + nodeCount: state.draft?.nodes.length ?? 0, + edgeCount: state.draft?.edges.length ?? 0, + diff: state.diff, + loadDraft, + discardDraft, + }; +} diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts index be76c4ec2b..407482073f 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/Flow/useFlow.ts @@ -21,6 +21,7 @@ import { AgentExecutionStatus } from "@/app/api/__generated__/models/agentExecut export const useFlow = () => { const [isLocked, setIsLocked] = useState(false); const [hasAutoFramed, setHasAutoFramed] = useState(false); + const [isInitialLoadComplete, setIsInitialLoadComplete] = useState(false); const addNodes = useNodeStore(useShallow((state) => state.addNodes)); const addLinks = useEdgeStore(useShallow((state) => state.addLinks)); const updateNodeStatus = useNodeStore( @@ -120,6 +121,14 @@ export const useFlow = () => { if (customNodes.length > 0) { useNodeStore.getState().setNodes([]); addNodes(customNodes); + + // Sync hardcoded values with handle IDs. + // If a key–value field has a key without a value, the backend omits it from hardcoded values. + // But if a handleId exists for that key, it causes inconsistency. + // This ensures hardcoded values stay in sync with handle IDs. + customNodes.forEach((node) => { + useNodeStore.getState().syncHardcodedValuesWithHandleIds(node.id); + }); } }, [customNodes, addNodes]); @@ -174,11 +183,23 @@ export const useFlow = () => { if (customNodes.length > 0 && graph?.links) { const timer = setTimeout(() => { useHistoryStore.getState().initializeHistory(); + // Mark initial load as complete after history is initialized + setIsInitialLoadComplete(true); }, 100); return () => clearTimeout(timer); } }, [customNodes, graph?.links]); + // Also mark as complete for new flows (no flowID) after a short delay + useEffect(() => { + if (!flowID && !isGraphLoading && !isBlocksLoading) { + const timer = setTimeout(() => { + setIsInitialLoadComplete(true); + }, 200); + return () => clearTimeout(timer); + } + }, [flowID, isGraphLoading, isBlocksLoading]); + useEffect(() => { return () => { useNodeStore.getState().setNodes([]); @@ -217,6 +238,7 @@ export const useFlow = () => { useEffect(() => { setHasAutoFramed(false); + setIsInitialLoadComplete(false); }, [flowID, flowVersion]); // Drag and drop block from block menu @@ -253,6 +275,7 @@ export const useFlow = () => { return { isFlowContentLoading: isGraphLoading || isBlocksLoading, + isInitialLoadComplete, onDragOver, onDrop, isLocked, diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/edges/useCustomEdge.ts b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/edges/useCustomEdge.ts index 8d27f346ef..bf4ba3a418 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/edges/useCustomEdge.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/edges/useCustomEdge.ts @@ -1,12 +1,17 @@ -import { Connection as RFConnection, EdgeChange } from "@xyflow/react"; +import { + Connection as RFConnection, + EdgeChange, + applyEdgeChanges, +} from "@xyflow/react"; import { useEdgeStore } from "@/app/(platform)/build/stores/edgeStore"; import { useCallback } from "react"; import { useNodeStore } from "../../../stores/nodeStore"; +import { CustomEdge } from "./CustomEdge"; export const useCustomEdge = () => { const edges = useEdgeStore((s) => s.edges); const addEdge = useEdgeStore((s) => s.addEdge); - const removeEdge = useEdgeStore((s) => s.removeEdge); + const setEdges = useEdgeStore((s) => s.setEdges); const onConnect = useCallback( (conn: RFConnection) => { @@ -45,14 +50,10 @@ export const useCustomEdge = () => { ); const onEdgesChange = useCallback( - (changes: EdgeChange[]) => { - changes.forEach((change) => { - if (change.type === "remove") { - removeEdge(change.id); - } - }); + (changes: EdgeChange[]) => { + setEdges(applyEdgeChanges(changes, edges)); }, - [removeEdge], + [edges, setEdges], ); return { edges, onConnect, onEdgesChange }; diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/handlers/NodeHandle.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/handlers/NodeHandle.tsx index 4eb2437b65..99edb00c45 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/handlers/NodeHandle.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/handlers/NodeHandle.tsx @@ -1,26 +1,32 @@ import { CircleIcon } from "@phosphor-icons/react"; import { Handle, Position } from "@xyflow/react"; +import { useEdgeStore } from "../../../stores/edgeStore"; +import { cleanUpHandleId } from "@/components/renderers/InputRenderer/helpers"; +import { cn } from "@/lib/utils"; -const NodeHandle = ({ +const InputNodeHandle = ({ handleId, - isConnected, - side, + nodeId, }: { handleId: string; - isConnected: boolean; - side: "left" | "right"; + nodeId: string; }) => { + const cleanedHandleId = cleanUpHandleId(handleId); + const isInputConnected = useEdgeStore((state) => + state.isInputConnected(nodeId ?? "", cleanedHandleId), + ); + return (
@@ -28,4 +34,35 @@ const NodeHandle = ({ ); }; -export default NodeHandle; +const OutputNodeHandle = ({ + field_name, + nodeId, + hexColor, +}: { + field_name: string; + nodeId: string; + hexColor: string; +}) => { + const isOutputConnected = useEdgeStore((state) => + state.isOutputConnected(nodeId, field_name), + ); + return ( + +
+ +
+
+ ); +}; + +export { InputNodeHandle, OutputNodeHandle }; diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/handlers/helpers.ts b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/handlers/helpers.ts index ecacc83146..afaa85a38a 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/handlers/helpers.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/handlers/helpers.ts @@ -1,31 +1,4 @@ -/** - * Handle ID Types for different input structures - * - * Examples: - * SIMPLE: "message" - * NESTED: "config.api_key" - * ARRAY: "items_$_0", "items_$_1" - * KEY_VALUE: "headers_#_Authorization", "params_#_limit" - * - * Note: All handle IDs are sanitized to remove spaces and special characters. - * Spaces become underscores, and special characters are removed. - * Example: "user name" becomes "user_name", "email@domain.com" becomes "emaildomaincom" - */ -export enum HandleIdType { - SIMPLE = "SIMPLE", - NESTED = "NESTED", - ARRAY = "ARRAY", - KEY_VALUE = "KEY_VALUE", -} - -const fromRjsfId = (id: string): string => { - if (!id) return ""; - const parts = id.split("_"); - const filtered = parts.filter( - (p) => p !== "root" && p !== "properties" && p.length > 0, - ); - return filtered.join("_") || ""; -}; +// Here we are handling single level of nesting, if need more in future then i will update it const sanitizeForHandleId = (str: string): string => { if (!str) return ""; @@ -38,51 +11,53 @@ const sanitizeForHandleId = (str: string): string => { .replace(/^_|_$/g, ""); // Remove leading/trailing underscores }; -export const generateHandleId = ( +const cleanTitleId = (id: string): string => { + if (!id) return ""; + + if (id.endsWith("_title")) { + id = id.slice(0, -6); + } + const parts = id.split("_"); + const filtered = parts.filter( + (p) => p !== "root" && p !== "properties" && p.length > 0, + ); + const filtered_id = filtered.join("_") || ""; + return filtered_id; +}; + +export const generateHandleIdFromTitleId = ( fieldKey: string, - nestedValues: string[] = [], - type: HandleIdType = HandleIdType.SIMPLE, + { + isObjectProperty, + isAdditionalProperty, + isArrayItem, + }: { + isArrayItem?: boolean; + isObjectProperty?: boolean; + isAdditionalProperty?: boolean; + } = { + isArrayItem: false, + isObjectProperty: false, + isAdditionalProperty: false, + }, ): string => { if (!fieldKey) return ""; - fieldKey = fromRjsfId(fieldKey); - fieldKey = sanitizeForHandleId(fieldKey); + const filteredKey = cleanTitleId(fieldKey); + if (isAdditionalProperty || isArrayItem) { + return filteredKey; + } + const cleanedKey = sanitizeForHandleId(filteredKey); - if (type === HandleIdType.SIMPLE || nestedValues.length === 0) { - return fieldKey; + if (isObjectProperty) { + // "config_api_key" -> "config.api_key" + const parts = cleanedKey.split("_"); + if (parts.length >= 2) { + const baseName = parts[0]; + const propertyName = parts.slice(1).join("_"); + return `${baseName}.${propertyName}`; + } } - const sanitizedNestedValues = nestedValues.map((value) => - sanitizeForHandleId(value), - ); - - switch (type) { - case HandleIdType.NESTED: - return [fieldKey, ...sanitizedNestedValues].join("."); - - case HandleIdType.ARRAY: - return [fieldKey, ...sanitizedNestedValues].join("_$_"); - - case HandleIdType.KEY_VALUE: - return [fieldKey, ...sanitizedNestedValues].join("_#_"); - - default: - return fieldKey; - } -}; - -export const parseKeyValueHandleId = ( - handleId: string, - type: HandleIdType, -): string => { - if (type === HandleIdType.KEY_VALUE) { - return handleId.split("_#_")[1]; - } else if (type === HandleIdType.ARRAY) { - return handleId.split("_$_")[1]; - } else if (type === HandleIdType.NESTED) { - return handleId.split(".")[1]; - } else if (type === HandleIdType.SIMPLE) { - return handleId.split("_")[1]; - } - return ""; + return cleanedKey; }; diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/CustomNode.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/CustomNode.tsx index 52068f3acb..3523079b71 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/CustomNode.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/CustomNode.tsx @@ -10,7 +10,7 @@ import { NodeExecutionResult } from "@/app/api/__generated__/models/nodeExecutio import { NodeContainer } from "./components/NodeContainer"; import { NodeHeader } from "./components/NodeHeader"; import { FormCreator } from "../FormCreator"; -import { preprocessInputSchema } from "@/components/renderers/input-renderer/utils/input-schema-pre-processor"; +import { preprocessInputSchema } from "@/components/renderers/InputRenderer/utils/input-schema-pre-processor"; import { OutputHandler } from "../OutputHandler"; import { NodeAdvancedToggle } from "./components/NodeAdvancedToggle"; import { NodeDataRenderer } from "./components/NodeOutput/NodeOutput"; @@ -99,7 +99,7 @@ export const CustomNode: React.FC> = React.memo( nodeId={nodeId} uiType={data.uiType} className={cn( - "bg-white pr-6", + "bg-white px-4", isWebhook && "pointer-events-none opacity-50", )} showHandles={showHandles} diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeAdvancedToggle.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeAdvancedToggle.tsx index 4903f2e020..950db1657f 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeAdvancedToggle.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeAdvancedToggle.tsx @@ -8,7 +8,7 @@ export const NodeAdvancedToggle = ({ nodeId }: { nodeId: string }) => { ); const setShowAdvanced = useNodeStore((state) => state.setShowAdvanced); return ( -
+
Advanced diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeContainer.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeContainer.tsx index f8d5b2e089..da9c13335f 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeContainer.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeContainer.tsx @@ -22,7 +22,7 @@ export const NodeContainer = ({ return (
state.updateNodeData); const title = (data.metadata?.customized_name as string) || data.title; const [isEditingTitle, setIsEditingTitle] = useState(false); - const [editedTitle, setEditedTitle] = useState(title); + const [editedTitle, setEditedTitle] = useState( + beautifyString(title).replace("Block", "").trim(), + ); const handleTitleEdit = () => { updateNodeData(nodeId, { @@ -41,7 +43,7 @@ export const NodeHeader = ({ }; return ( -
+
{/* Title row with context menu */}
@@ -68,12 +70,12 @@ export const NodeHeader = ({
- {beautifyString(title)} + {beautifyString(title).replace("Block", "").trim()}
-

{beautifyString(title)}

+

{beautifyString(title).replace("Block", "").trim()}

diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeOutput/NodeOutput.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeOutput/NodeOutput.tsx index a4b53e3ac3..3f0ae6e350 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeOutput/NodeOutput.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/NodeOutput/NodeOutput.tsx @@ -23,7 +23,7 @@ export const NodeDataRenderer = ({ nodeId }: { nodeId: string }) => { } return ( -
+
Node Output diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/StickyNoteBlock.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/StickyNoteBlock.tsx index 5d57c6c5b6..f900b1633f 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/StickyNoteBlock.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/CustomNode/components/StickyNoteBlock.tsx @@ -1,6 +1,6 @@ import { useMemo } from "react"; import { FormCreator } from "../../FormCreator"; -import { preprocessInputSchema } from "@/components/renderers/input-renderer/utils/input-schema-pre-processor"; +import { preprocessInputSchema } from "@/components/renderers/InputRenderer/utils/input-schema-pre-processor"; import { CustomNodeData } from "../CustomNode"; import { Text } from "@/components/atoms/Text/Text"; import { cn } from "@/lib/utils"; diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/FormCreator.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/FormCreator.tsx index cfee0bf89f..28d1bcc0ab 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/FormCreator.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/FormCreator.tsx @@ -3,7 +3,7 @@ import React from "react"; import { uiSchema } from "./uiSchema"; import { useNodeStore } from "../../../stores/nodeStore"; import { BlockUIType } from "../../types"; -import { FormRenderer } from "@/components/renderers/input-renderer/FormRenderer"; +import { FormRenderer } from "@/components/renderers/InputRenderer/FormRenderer"; export const FormCreator = React.memo( ({ diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/OutputHandler.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/OutputHandler.tsx index ab3b648ba9..b70a8e239b 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/OutputHandler.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/OutputHandler.tsx @@ -4,7 +4,7 @@ import { CaretDownIcon, InfoIcon } from "@phosphor-icons/react"; import { RJSFSchema } from "@rjsf/utils"; import { useState } from "react"; -import NodeHandle from "../handlers/NodeHandle"; +import { OutputNodeHandle } from "../handlers/NodeHandle"; import { Tooltip, TooltipContent, @@ -13,7 +13,6 @@ import { } from "@/components/atoms/Tooltip/BaseTooltip"; import { useEdgeStore } from "@/app/(platform)/build/stores/edgeStore"; import { getTypeDisplayInfo } from "./helpers"; -import { generateHandleId } from "../handlers/helpers"; import { BlockUIType } from "../../types"; export const OutputHandler = ({ @@ -29,8 +28,73 @@ export const OutputHandler = ({ const properties = outputSchema?.properties || {}; const [isOutputVisible, setIsOutputVisible] = useState(true); + const showHandles = uiType !== BlockUIType.OUTPUT; + + const renderOutputHandles = ( + schema: RJSFSchema, + keyPrefix: string = "", + titlePrefix: string = "", + ): React.ReactNode[] => { + return Object.entries(schema).map( + ([key, fieldSchema]: [string, RJSFSchema]) => { + const fullKey = keyPrefix ? `${keyPrefix}_#_${key}` : key; + const fieldTitle = titlePrefix + (fieldSchema?.title || key); + + const isConnected = isOutputConnected(nodeId, fullKey); + const shouldShow = isConnected || isOutputVisible; + const { displayType, colorClass, hexColor } = + getTypeDisplayInfo(fieldSchema); + + return shouldShow ? ( +
+
+ {fieldSchema?.description && ( + + + + + + + + {fieldSchema?.description} + + + )} + + {fieldTitle} + + + ({displayType}) + + + {showHandles && ( + + )} +
+ + {/* Recursively render nested properties */} + {fieldSchema?.properties && + renderOutputHandles( + fieldSchema.properties, + fullKey, + `${fieldTitle}.`, + )} +
+ ) : null; + }, + ); + }; + return ( -
+
- { -
- {Object.entries(properties).map(([key, property]: [string, any]) => { - const isConnected = isOutputConnected(nodeId, key); - const shouldShow = isConnected || isOutputVisible; - const { displayType, colorClass } = getTypeDisplayInfo(property); - - return shouldShow ? ( -
- {property?.description && ( - - - - - - - - {property?.description} - - - )} - - {property?.title || key}{" "} - - - ({displayType}) - - - -
- ) : null; - })} -
- } +
+ {renderOutputHandles(properties)} +
); }; diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/helpers.ts b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/helpers.ts index 5572426dc7..39384485f5 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/helpers.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/FlowEditor/nodes/helpers.ts @@ -92,14 +92,38 @@ export const getTypeDisplayInfo = (schema: any) => { if (schema?.type === "string" && schema?.format) { const formatMap: Record< string, - { displayType: string; colorClass: string } + { displayType: string; colorClass: string; hexColor: string } > = { - file: { displayType: "file", colorClass: "!text-green-500" }, - date: { displayType: "date", colorClass: "!text-blue-500" }, - time: { displayType: "time", colorClass: "!text-blue-500" }, - "date-time": { displayType: "datetime", colorClass: "!text-blue-500" }, - "long-text": { displayType: "text", colorClass: "!text-green-500" }, - "short-text": { displayType: "text", colorClass: "!text-green-500" }, + file: { + displayType: "file", + colorClass: "!text-green-500", + hexColor: "#22c55e", + }, + date: { + displayType: "date", + colorClass: "!text-blue-500", + hexColor: "#3b82f6", + }, + time: { + displayType: "time", + colorClass: "!text-blue-500", + hexColor: "#3b82f6", + }, + "date-time": { + displayType: "datetime", + colorClass: "!text-blue-500", + hexColor: "#3b82f6", + }, + "long-text": { + displayType: "text", + colorClass: "!text-green-500", + hexColor: "#22c55e", + }, + "short-text": { + displayType: "text", + colorClass: "!text-green-500", + hexColor: "#22c55e", + }, }; const formatInfo = formatMap[schema.format]; @@ -131,10 +155,23 @@ export const getTypeDisplayInfo = (schema: any) => { any: "!text-gray-500", }; + const hexColorMap: Record = { + string: "#22c55e", + number: "#3b82f6", + integer: "#3b82f6", + boolean: "#eab308", + object: "#a855f7", + array: "#6366f1", + null: "#6b7280", + any: "#6b7280", + }; + const colorClass = colorMap[schema?.type] || "!text-gray-500"; + const hexColor = hexColorMap[schema?.type] || "#6b7280"; return { displayType, colorClass, + hexColor, }; }; diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/ControlPanelButton.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/ControlPanelButton.tsx index b176a002a7..36834becf6 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/ControlPanelButton.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/ControlPanelButton.tsx @@ -24,7 +24,7 @@ export const ControlPanelButton: React.FC = ({ role={as === "div" ? "button" : undefined} disabled={as === "button" ? disabled : undefined} className={cn( - "flex h-[4.25rem] w-[4.25rem] items-center justify-center whitespace-normal bg-white p-[1.38rem] text-zinc-800 shadow-none hover:cursor-pointer hover:bg-zinc-100 hover:text-zinc-950 focus:ring-0", + "flex w-auto items-center justify-center whitespace-normal bg-white px-4 py-4 text-zinc-800 shadow-none hover:cursor-pointer hover:bg-zinc-100 hover:text-zinc-950 focus:ring-0", selected && "bg-violet-50 text-violet-700 hover:cursor-default hover:bg-violet-50 hover:text-violet-700 active:bg-violet-50 active:text-violet-700", disabled && "cursor-not-allowed opacity-50 hover:cursor-not-allowed", diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewBlockMenu/BlockMenu/BlockMenu.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewBlockMenu/BlockMenu/BlockMenu.tsx index bbaf410296..7533f501df 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewBlockMenu/BlockMenu/BlockMenu.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewBlockMenu/BlockMenu/BlockMenu.tsx @@ -1,18 +1,17 @@ -import React from "react"; +import { useControlPanelStore } from "@/app/(platform)/build/stores/controlPanelStore"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/__legacy__/ui/popover"; -import { BlockMenuContent } from "../BlockMenuContent/BlockMenuContent"; -import { ControlPanelButton } from "../../ControlPanelButton"; -import { LegoIcon } from "@phosphor-icons/react"; -import { useControlPanelStore } from "@/app/(platform)/build/stores/controlPanelStore"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/atoms/Tooltip/BaseTooltip"; +import { LegoIcon } from "@phosphor-icons/react"; +import { ControlPanelButton } from "../../ControlPanelButton"; +import { BlockMenuContent } from "../BlockMenuContent/BlockMenuContent"; export const BlockMenu = () => { const { blockMenuOpen, setBlockMenuOpen } = useControlPanelStore(); @@ -28,7 +27,7 @@ export const BlockMenu = () => { selected={blockMenuOpen} className="rounded-none" > - + diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewControlPanel.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewControlPanel.tsx index 4828a73af4..8c8bbf1842 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewControlPanel.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewControlPanel.tsx @@ -7,10 +7,10 @@ import { useNewControlPanel } from "./useNewControlPanel"; import { GraphExecutionID } from "@/lib/autogpt-server-api"; // import { ControlPanelButton } from "../ControlPanelButton"; // import { GraphSearchMenu } from "../GraphMenu/GraphMenu"; -import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag"; import { Separator } from "@/components/__legacy__/ui/separator"; -import { NewSaveControl } from "./NewSaveControl/NewSaveControl"; +import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag"; import { CustomNode } from "../FlowEditor/nodes/CustomNode/CustomNode"; +import { NewSaveControl } from "./NewSaveControl/NewSaveControl"; import { UndoRedoButtons } from "./UndoRedoButtons"; export type Control = { @@ -56,7 +56,7 @@ export const NewControlPanel = memo( return (
diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewSaveControl/NewSaveControl.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewSaveControl/NewSaveControl.tsx index beae5c1705..cbf06af1c6 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewSaveControl/NewSaveControl.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewSaveControl/NewSaveControl.tsx @@ -1,22 +1,21 @@ -import React from "react"; +import { Card, CardContent, CardFooter } from "@/components/__legacy__/ui/card"; +import { Form, FormField } from "@/components/__legacy__/ui/form"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/__legacy__/ui/popover"; -import { Card, CardContent, CardFooter } from "@/components/__legacy__/ui/card"; +import { Button } from "@/components/atoms/Button/Button"; +import { Input } from "@/components/atoms/Input/Input"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/atoms/Tooltip/BaseTooltip"; -import { useNewSaveControl } from "./useNewSaveControl"; -import { Form, FormField } from "@/components/__legacy__/ui/form"; -import { ControlPanelButton } from "../ControlPanelButton"; -import { useControlPanelStore } from "../../../stores/controlPanelStore"; import { FloppyDiskIcon } from "@phosphor-icons/react"; -import { Input } from "@/components/atoms/Input/Input"; -import { Button } from "@/components/atoms/Button/Button"; +import { useControlPanelStore } from "../../../stores/controlPanelStore"; +import { ControlPanelButton } from "../ControlPanelButton"; +import { useNewSaveControl } from "./useNewSaveControl"; export const NewSaveControl = () => { const { form, isSaving, graphVersion, handleSave } = useNewSaveControl(); @@ -33,7 +32,7 @@ export const NewSaveControl = () => { selected={saveControlOpen} className="rounded-none" > - + diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewSearchGraph/GraphMenu/GraphMenu.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewSearchGraph/GraphMenu/GraphMenu.tsx index c886919642..8ff96a598b 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewSearchGraph/GraphMenu/GraphMenu.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/NewSearchGraph/GraphMenu/GraphMenu.tsx @@ -1,13 +1,13 @@ -import React from "react"; +import { CustomNode } from "@/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/__legacy__/ui/popover"; import { MagnifyingGlassIcon } from "@phosphor-icons/react"; -import { GraphSearchContent } from "../GraphMenuContent/GraphContent"; +import React from "react"; import { ControlPanelButton } from "../../ControlPanelButton"; -import { CustomNode } from "@/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode"; +import { GraphSearchContent } from "../GraphMenuContent/GraphContent"; import { useGraphMenu } from "./useGraphMenu"; interface GraphSearchMenuProps { @@ -50,7 +50,7 @@ export const GraphSearchMenu: React.FC = ({ selected={blockMenuSelected === "search"} className="rounded-none" > - + diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/UndoRedoButtons.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/UndoRedoButtons.tsx index 6f134056c8..5510335104 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/UndoRedoButtons.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/NewControlPanel/UndoRedoButtons.tsx @@ -1,12 +1,12 @@ import { Separator } from "@/components/__legacy__/ui/separator"; -import { ControlPanelButton } from "./ControlPanelButton"; -import { ArrowUUpLeftIcon, ArrowUUpRightIcon } from "@phosphor-icons/react"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/atoms/Tooltip/BaseTooltip"; +import { ArrowUUpLeftIcon, ArrowUUpRightIcon } from "@phosphor-icons/react"; import { useHistoryStore } from "../../stores/historyStore"; +import { ControlPanelButton } from "./ControlPanelButton"; import { useEffect } from "react"; @@ -43,7 +43,7 @@ export const UndoRedoButtons = () => { - + Undo @@ -52,7 +52,7 @@ export const UndoRedoButtons = () => { - + Redo diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/ExpandableOutputDialog.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/ExpandableOutputDialog.tsx index 0050c6cf64..98edbca2fb 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/ExpandableOutputDialog.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/ExpandableOutputDialog.tsx @@ -4,19 +4,12 @@ import { OutputActions, OutputItem, } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/OutputRenderers"; +import { Dialog } from "@/components/molecules/Dialog/Dialog"; import { beautifyString } from "@/lib/utils"; import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag"; import { Clipboard, Maximize2 } from "lucide-react"; import React, { FC, useMemo, useState } from "react"; import { Button } from "../../../../../components/__legacy__/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "../../../../../components/__legacy__/ui/dialog"; import { ContentRenderer } from "../../../../../components/__legacy__/ui/render"; import { ScrollArea } from "../../../../../components/__legacy__/ui/scroll-area"; import { Separator } from "../../../../../components/__legacy__/ui/separator"; @@ -120,138 +113,155 @@ const ExpandableOutputDialog: FC = ({ }; return ( - - - - -
- - Full Output Preview -
- {enableEnhancedOutputHandling && ( -
- - -
- )} -
- - Execution ID: {execId} -
- Pin:{" "} - {beautifyString(pinName)} -
-
- -
- {useEnhancedRenderer && outputItems.length > 0 && ( -
- ({ - value: item.value, - metadata: item.metadata, - renderer: item.renderer, - }))} + +
+ + Full Output Preview +
+ {enableEnhancedOutputHandling && ( +
+ +
)} - -
- {data.length > 0 ? ( - useEnhancedRenderer ? ( -
- {outputItems.map((item) => ( - - ))} -
- ) : ( -
- {data.map((item, index) => ( -
-
- - Item {index + 1} of {data.length} - - -
- -
- -
-
- ))} -
- ) - ) : ( -
- No data available -
- )} -
-
+ } + controlled={{ + isOpen, + set: (open) => { + if (!open) onClose(); + }, + }} + onClose={onClose} + styling={{ + maxWidth: "56rem", + width: "90vw", + height: "90vh", + }} + > + +
+
+

+ Execution ID: {execId} +
+ Pin:{" "} + {beautifyString(pinName)} +

+
- -
- {data.length} item{data.length !== 1 ? "s" : ""} total -
-
- {!useEnhancedRenderer && ( - +
+ {useEnhancedRenderer && outputItems.length > 0 && ( +
+ ({ + value: item.value, + metadata: item.metadata, + renderer: item.renderer, + }))} + /> +
)} - + +
+ {data.length > 0 ? ( + useEnhancedRenderer ? ( +
+ {outputItems.map((item) => ( + + ))} +
+ ) : ( +
+ {data.map((item, index) => ( +
+
+ + Item {index + 1} of {data.length} + + +
+ +
+ +
+
+ ))} +
+ ) + ) : ( +
+ No data available +
+ )} +
+
- - + + +
+ {data.length} item{data.length !== 1 ? "s" : ""} total +
+
+ {!useEnhancedRenderer && ( + + )} + +
+
+
+
); }; diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeInputs.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeInputs.tsx index 683a854c21..e9d077bde1 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeInputs.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeInputs.tsx @@ -2,6 +2,7 @@ import { ConnectionData, CustomNodeData, } from "@/app/(platform)/build/components/legacy-builder/CustomNode/CustomNode"; +import { NodeTableInput } from "@/app/(platform)/build/components/legacy-builder/NodeTableInput"; import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs"; import { Button } from "@/components/__legacy__/ui/button"; import { Calendar } from "@/components/__legacy__/ui/calendar"; @@ -28,7 +29,6 @@ import { } from "@/components/__legacy__/ui/select"; import { Switch } from "@/components/atoms/Switch/Switch"; import { GoogleDrivePickerInput } from "@/components/contextual/GoogleDrivePicker/GoogleDrivePickerInput"; -import { NodeTableInput } from "@/components/node-table-input"; import { BlockIOArraySubSchema, BlockIOBooleanSubSchema, diff --git a/autogpt_platform/frontend/src/components/node-table-input.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeTableInput.tsx similarity index 96% rename from autogpt_platform/frontend/src/components/node-table-input.tsx rename to autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeTableInput.tsx index 9c1a8003ee..0e27d98ba2 100644 --- a/autogpt_platform/frontend/src/components/node-table-input.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/NodeTableInput.tsx @@ -1,15 +1,15 @@ -import React, { FC, useCallback, useEffect, useState } from "react"; +import { FC, useCallback, useEffect, useState } from "react"; -import { PlusIcon, XIcon } from "@phosphor-icons/react"; -import { cn } from "@/lib/utils"; import NodeHandle from "@/app/(platform)/build/components/legacy-builder/NodeHandle"; import { BlockIOTableSubSchema, - TableRow, TableCellValue, + TableRow, } from "@/lib/autogpt-server-api/types"; -import { Input } from "./atoms/Input/Input"; -import { Button } from "./atoms/Button/Button"; +import { cn } from "@/lib/utils"; +import { PlusIcon, XIcon } from "@phosphor-icons/react"; +import { Button } from "../../../../../components/atoms/Button/Button"; +import { Input } from "../../../../../components/atoms/Input/Input"; interface NodeTableInputProps { /** Unique identifier for the node in the builder graph */ diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/RunnerInputUI.tsx b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/RunnerInputUI.tsx index bff21c46f2..15983be9f5 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/RunnerInputUI.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/components/legacy-builder/RunnerInputUI.tsx @@ -1,17 +1,11 @@ -import React, { useCallback } from "react"; +import { useCallback } from "react"; +import { AgentRunDraftView } from "@/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-draft-view"; +import { Dialog } from "@/components/molecules/Dialog/Dialog"; import type { CredentialsMetaInput, GraphMeta, } from "@/lib/autogpt-server-api/types"; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogDescription, -} from "@/components/__legacy__/ui/dialog"; -import { AgentRunDraftView } from "@/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-draft-view"; interface RunInputDialogProps { isOpen: boolean; @@ -70,21 +64,33 @@ export function RunnerInputDialog({ ); return ( - - - - Run your agent - {graph.name} - - - + { + if (!open) doClose(); + }, + }} + onClose={doClose} + styling={{ + maxWidth: "56rem", + width: "90vw", + }} + > + +
+

{graph.name}

+ +
+
); } diff --git a/autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts b/autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts index d0b488f26c..505303cc1e 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/hooks/useSaveGraph.ts @@ -15,6 +15,11 @@ import { useEdgeStore } from "../stores/edgeStore"; import { graphsEquivalent } from "../components/NewControlPanel/NewSaveControl/helpers"; import { useGraphStore } from "../stores/graphStore"; import { useShallow } from "zustand/react/shallow"; +import { + draftService, + clearTempFlowId, + getTempFlowId, +} from "@/services/builder-draft/draft-service"; export type SaveGraphOptions = { showToast?: boolean; @@ -52,12 +57,19 @@ export const useSaveGraph = ({ const { mutateAsync: createNewGraph, isPending: isCreating } = usePostV1CreateNewGraph({ mutation: { - onSuccess: (response) => { + onSuccess: async (response) => { const data = response.data as GraphModel; setQueryStates({ flowID: data.id, flowVersion: data.version, }); + + const tempFlowId = getTempFlowId(); + if (tempFlowId) { + await draftService.deleteDraft(tempFlowId); + clearTempFlowId(); + } + onSuccess?.(data); if (showToast) { toast({ @@ -82,12 +94,18 @@ export const useSaveGraph = ({ const { mutateAsync: updateGraph, isPending: isUpdating } = usePutV1UpdateGraphVersion({ mutation: { - onSuccess: (response) => { + onSuccess: async (response) => { const data = response.data as GraphModel; setQueryStates({ flowID: data.id, flowVersion: data.version, }); + + // Clear the draft for this flow after successful save + if (data.id) { + await draftService.deleteDraft(data.id); + } + onSuccess?.(data); if (showToast) { toast({ diff --git a/autogpt_platform/frontend/src/app/(platform)/build/page.tsx b/autogpt_platform/frontend/src/app/(platform)/build/page.tsx index f60c863657..f1d62ee5fb 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/page.tsx @@ -8,8 +8,8 @@ import { ReactFlowProvider } from "@xyflow/react"; import { useSearchParams } from "next/navigation"; import { useEffect } from "react"; import { BuilderViewTabs } from "./components/BuilderViewTabs/BuilderViewTabs"; -import { useBuilderView } from "./components/BuilderViewTabs/useBuilderViewTabs"; import { Flow } from "./components/FlowEditor/Flow/Flow"; +import { useBuilderView } from "./useBuilderView"; function BuilderContent() { const query = useSearchParams(); diff --git a/autogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts b/autogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts index 0d0e4202fb..7b17eecfb3 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/stores/edgeStore.ts @@ -4,6 +4,7 @@ import { CustomEdge } from "../components/FlowEditor/edges/CustomEdge"; import { customEdgeToLink, linkToCustomEdge } from "../components/helper"; import { MarkerType } from "@xyflow/react"; import { NodeExecutionResult } from "@/app/api/__generated__/models/nodeExecutionResult"; +import { cleanUpHandleId } from "@/components/renderers/InputRenderer/helpers"; type EdgeStore = { edges: CustomEdge[]; @@ -13,6 +14,8 @@ type EdgeStore = { removeEdge: (edgeId: string) => void; upsertMany: (edges: CustomEdge[]) => void; + removeEdgesByHandlePrefix: (nodeId: string, handlePrefix: string) => void; + getNodeEdges: (nodeId: string) => CustomEdge[]; isInputConnected: (nodeId: string, handle: string) => boolean; isOutputConnected: (nodeId: string, handle: string) => boolean; @@ -79,11 +82,27 @@ export const useEdgeStore = create((set, get) => ({ return { edges: Array.from(byKey.values()) }; }), + removeEdgesByHandlePrefix: (nodeId, handlePrefix) => + set((state) => ({ + edges: state.edges.filter( + (e) => + !( + e.target === nodeId && + e.targetHandle && + e.targetHandle.startsWith(handlePrefix) + ), + ), + })), + getNodeEdges: (nodeId) => get().edges.filter((e) => e.source === nodeId || e.target === nodeId), - isInputConnected: (nodeId, handle) => - get().edges.some((e) => e.target === nodeId && e.targetHandle === handle), + isInputConnected: (nodeId, handle) => { + const cleanedHandle = cleanUpHandleId(handle); + return get().edges.some( + (e) => e.target === nodeId && e.targetHandle === cleanedHandle, + ); + }, isOutputConnected: (nodeId, handle) => get().edges.some((e) => e.source === nodeId && e.sourceHandle === handle), @@ -105,20 +124,21 @@ export const useEdgeStore = create((set, get) => ({ targetNodeId: string, executionResult: NodeExecutionResult, ) => { - set((state) => ({ - edges: state.edges.map((edge) => { + set((state) => { + let hasChanges = false; + + const newEdges = state.edges.map((edge) => { if (edge.target !== targetNodeId) { return edge; } - const beadData = - edge.data?.beadData ?? - new Map(); + const beadData = new Map(edge.data?.beadData ?? new Map()); - if ( - edge.targetHandle && - edge.targetHandle in executionResult.input_data - ) { + const inputValue = edge.targetHandle + ? executionResult.input_data[edge.targetHandle] + : undefined; + + if (inputValue !== undefined && inputValue !== null) { beadData.set(executionResult.node_exec_id, executionResult.status); } @@ -136,6 +156,11 @@ export const useEdgeStore = create((set, get) => ({ beadUp = beadDown + 1; } + if (edge.data?.beadUp === beadUp && edge.data?.beadDown === beadDown) { + return edge; + } + + hasChanges = true; return { ...edge, data: { @@ -145,8 +170,10 @@ export const useEdgeStore = create((set, get) => ({ beadData, }, }; - }), - })); + }); + + return hasChanges ? { edges: newEdges } : state; + }); }, resetEdgeBeads: () => { diff --git a/autogpt_platform/frontend/src/app/(platform)/build/stores/nodeStore.ts b/autogpt_platform/frontend/src/app/(platform)/build/stores/nodeStore.ts index 2f41c3bb46..96478c5b6f 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/stores/nodeStore.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/stores/nodeStore.ts @@ -13,6 +13,10 @@ import { useHistoryStore } from "./historyStore"; import { useEdgeStore } from "./edgeStore"; import { BlockUIType } from "../components/types"; import { pruneEmptyValues } from "@/lib/utils"; +import { + ensurePathExists, + parseHandleIdToPath, +} from "@/components/renderers/InputRenderer/helpers"; // Minimum movement (in pixels) required before logging position change to history // Prevents spamming history with small movements when clicking on inputs inside blocks @@ -62,6 +66,8 @@ type NodeStore = { errors: { [key: string]: string }, ) => void; clearAllNodeErrors: () => void; // Add this + + syncHardcodedValuesWithHandleIds: (nodeId: string) => void; }; export const useNodeStore = create((set, get) => ({ @@ -305,4 +311,35 @@ export const useNodeStore = create((set, get) => ({ })), })); }, + + syncHardcodedValuesWithHandleIds: (nodeId: string) => { + const node = get().nodes.find((n) => n.id === nodeId); + if (!node) return; + + const handleIds = useEdgeStore.getState().getAllHandleIdsOfANode(nodeId); + const additionalHandles = handleIds.filter((h) => h.includes("_#_")); + + if (additionalHandles.length === 0) return; + + const hardcodedValues = JSON.parse( + JSON.stringify(node.data.hardcodedValues || {}), + ); + + let modified = false; + + additionalHandles.forEach((handleId) => { + const segments = parseHandleIdToPath(handleId); + if (ensurePathExists(hardcodedValues, segments)) { + modified = true; + } + }); + + if (modified) { + set((state) => ({ + nodes: state.nodes.map((n) => + n.id === nodeId ? { ...n, data: { ...n.data, hardcodedValues } } : n, + ), + })); + } + }, })); diff --git a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderViewTabs/useBuilderViewTabs.ts b/autogpt_platform/frontend/src/app/(platform)/build/useBuilderView.ts similarity index 95% rename from autogpt_platform/frontend/src/app/(platform)/build/components/BuilderViewTabs/useBuilderViewTabs.ts rename to autogpt_platform/frontend/src/app/(platform)/build/useBuilderView.ts index ac02becca5..e0e524ddf8 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/components/BuilderViewTabs/useBuilderViewTabs.ts +++ b/autogpt_platform/frontend/src/app/(platform)/build/useBuilderView.ts @@ -1,7 +1,7 @@ import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag"; import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { useEffect, useMemo } from "react"; -import { BuilderView } from "./BuilderViewTabs"; +import { BuilderView } from "./components/BuilderViewTabs/BuilderViewTabs"; export function useBuilderView() { const isNewFlowEditorEnabled = useGetFlag(Flag.NEW_FLOW_EDITOR); diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs.tsx index 07350fb610..60d61fab57 100644 --- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs.tsx @@ -143,6 +143,7 @@ export function CredentialsInput({ size="small" onClick={handleActionButtonClick} className="w-fit" + type="button" > {actionButtonText} @@ -155,6 +156,7 @@ export function CredentialsInput({ size="small" onClick={handleActionButtonClick} className="w-fit" + type="button" > {actionButtonText} diff --git a/autogpt_platform/frontend/src/app/(platform)/marketplace/agent/[creator]/[slug]/page.tsx b/autogpt_platform/frontend/src/app/(platform)/marketplace/agent/[creator]/[slug]/page.tsx index 124db89385..8106694fe7 100644 --- a/autogpt_platform/frontend/src/app/(platform)/marketplace/agent/[creator]/[slug]/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/marketplace/agent/[creator]/[slug]/page.tsx @@ -1,15 +1,15 @@ -import { Metadata } from "next"; -import { getServerUser } from "@/lib/supabase/server/getServerUser"; +import { prefetchGetV2GetAgentByStoreIdQuery } from "@/app/api/__generated__/endpoints/library/library"; import { getV2GetSpecificAgent, prefetchGetV2GetSpecificAgentQuery, prefetchGetV2ListStoreAgentsQuery, } from "@/app/api/__generated__/endpoints/store/store"; import { StoreAgentDetails } from "@/app/api/__generated__/models/storeAgentDetails"; -import { MainAgentPage } from "../../../components/MainAgentPage/MainAgentPage"; import { getQueryClient } from "@/lib/react-query/queryClient"; -import { prefetchGetV2GetAgentByStoreIdQuery } from "@/app/api/__generated__/endpoints/library/library"; +import { getServerUser } from "@/lib/supabase/server/getServerUser"; import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; +import { Metadata } from "next"; +import { MainAgentPage } from "../../../components/MainAgentPage/MainAgentPage"; export const dynamic = "force-dynamic"; diff --git a/autogpt_platform/frontend/src/app/(platform)/marketplace/creator/[creator]/page.tsx b/autogpt_platform/frontend/src/app/(platform)/marketplace/creator/[creator]/page.tsx index 7d8158b123..e2c9f2ee01 100644 --- a/autogpt_platform/frontend/src/app/(platform)/marketplace/creator/[creator]/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/marketplace/creator/[creator]/page.tsx @@ -1,13 +1,13 @@ -import { getQueryClient } from "@/lib/react-query/queryClient"; import { getV2GetCreatorDetails, prefetchGetV2GetCreatorDetailsQuery, prefetchGetV2ListStoreAgentsQuery, } from "@/app/api/__generated__/endpoints/store/store"; -import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; -import { MainCreatorPage } from "../../components/MainCreatorPage/MainCreatorPage"; -import { Metadata } from "next"; import { CreatorDetails } from "@/app/api/__generated__/models/creatorDetails"; +import { getQueryClient } from "@/lib/react-query/queryClient"; +import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; +import { Metadata } from "next"; +import { MainCreatorPage } from "../../components/MainCreatorPage/MainCreatorPage"; export const dynamic = "force-dynamic"; diff --git a/autogpt_platform/frontend/src/app/(platform)/marketplace/page.tsx b/autogpt_platform/frontend/src/app/(platform)/marketplace/page.tsx index e95e230377..9b7e6ae93b 100644 --- a/autogpt_platform/frontend/src/app/(platform)/marketplace/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/marketplace/page.tsx @@ -1,11 +1,11 @@ -import { Metadata } from "next"; -import { Suspense } from "react"; import { prefetchGetV2ListStoreAgentsQuery, prefetchGetV2ListStoreCreatorsQuery, } from "@/app/api/__generated__/endpoints/store/store"; import { getQueryClient } from "@/lib/react-query/queryClient"; import { dehydrate, HydrationBoundary } from "@tanstack/react-query"; +import { Metadata } from "next"; +import { Suspense } from "react"; import { MainMarkeplacePage } from "./components/MainMarketplacePage/MainMarketplacePage"; import { MainMarketplacePageLoading } from "./components/MainMarketplacePageLoading"; @@ -48,11 +48,6 @@ export const metadata: Metadata = { description: "Find and use AI Agents created by our community", images: ["/images/store-twitter.png"], }, - icons: { - icon: "/favicon.ico", - shortcut: "/favicon-16x16.png", - apple: "/apple-touch-icon.png", - }, }; export default async function MarketplacePage(): Promise { diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/integrations/page.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/integrations/page.tsx index 7d8ead9df7..17800a03a4 100644 --- a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/integrations/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/integrations/page.tsx @@ -13,7 +13,7 @@ import { import { Button } from "@/components/atoms/Button/Button"; import { Dialog } from "@/components/molecules/Dialog/Dialog"; import { useToast } from "@/components/molecules/Toast/use-toast"; -import { providerIcons } from "@/components/renderers/input-renderer/fields/CredentialField/helpers"; +import { providerIcons } from "@/components/renderers/InputRenderer/custom/CredentialField/helpers"; import { CredentialsProviderName } from "@/lib/autogpt-server-api"; import { useSupabase } from "@/lib/supabase/hooks/useSupabase"; import { CredentialsProvidersContext } from "@/providers/agent-credentials/credentials-provider"; diff --git a/autogpt_platform/frontend/src/app/global-error.tsx b/autogpt_platform/frontend/src/app/global-error.tsx index 07b22f1c15..5391c2e433 100644 --- a/autogpt_platform/frontend/src/app/global-error.tsx +++ b/autogpt_platform/frontend/src/app/global-error.tsx @@ -1,16 +1,15 @@ "use client"; -import * as Sentry from "@sentry/nextjs"; import { ErrorCard } from "@/components/molecules/ErrorCard/ErrorCard"; +import * as Sentry from "@sentry/nextjs"; import { useEffect } from "react"; -export default function GlobalError({ - error, - reset, -}: { +interface Props { error: Error & { digest?: string }; reset: () => void; -}) { +} + +export default function GlobalError({ error, reset }: Props) { useEffect(() => { Sentry.captureException(error); }, [error]); diff --git a/autogpt_platform/frontend/src/app/layout.tsx b/autogpt_platform/frontend/src/app/layout.tsx index ff2590dc61..453f816dee 100644 --- a/autogpt_platform/frontend/src/app/layout.tsx +++ b/autogpt_platform/frontend/src/app/layout.tsx @@ -15,9 +15,21 @@ import { environment } from "@/services/environment"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { headers } from "next/headers"; +const isDev = environment.isDev(); +const isLocal = environment.isLocal(); + +const faviconPath = isDev + ? "/favicon-dev.ico" + : isLocal + ? "/favicon-local.ico" + : "/favicon.ico"; + export const metadata: Metadata = { title: "AutoGPT Platform", description: "Your one stop shop to creating AI Agents", + icons: { + icon: faviconPath, + }, }; export default async function RootLayout({ @@ -27,8 +39,6 @@ export default async function RootLayout({ }>) { const headersList = await headers(); const host = headersList.get("host") || ""; - const isDev = environment.isDev(); - const isLocal = environment.isLocal(); return ( - diff --git a/autogpt_platform/frontend/src/components/contextual/GoogleDrivePicker/GoogleDrivePickerInput.tsx b/autogpt_platform/frontend/src/components/contextual/GoogleDrivePicker/GoogleDrivePickerInput.tsx index 2a1ada5012..66700cbdf6 100644 --- a/autogpt_platform/frontend/src/components/contextual/GoogleDrivePicker/GoogleDrivePickerInput.tsx +++ b/autogpt_platform/frontend/src/components/contextual/GoogleDrivePicker/GoogleDrivePickerInput.tsx @@ -4,6 +4,7 @@ import { cn } from "@/lib/utils"; import { Cross2Icon } from "@radix-ui/react-icons"; import React, { useCallback } from "react"; import { GoogleDrivePicker } from "./GoogleDrivePicker"; +import { isValidFile } from "./helpers"; export interface Props { config: GoogleDrivePickerConfig; @@ -27,13 +28,15 @@ export function GoogleDrivePickerInput({ const hasAutoCredentials = !!config.auto_credentials; // Strip _credentials_id from value for display purposes - const currentFiles = isMultiSelect - ? Array.isArray(value) - ? value - : [] - : value - ? [value] - : []; + // Only show files section when there are valid file objects + const currentFiles = React.useMemo(() => { + if (isMultiSelect) { + if (!Array.isArray(value)) return []; + return value.filter(isValidFile); + } + if (!value || !isValidFile(value)) return []; + return [value]; + }, [value, isMultiSelect]); const handlePicked = useCallback( (files: any[], credentialId?: string) => { @@ -85,23 +88,27 @@ export function GoogleDrivePickerInput({ return (
- {/* Picker Button */} - { - // User canceled - no action needed - }} - onError={handleError} - /> +
+ {/* Picker Button */} + { + // User canceled - no action needed + }} + onError={handleError} + /> +
{/* Display Selected Files */} {currentFiles.length > 0 && ( -
+
{currentFiles.map((file: any, idx: number) => (
- - {trigger || } - + {showTrigger && ( + + {trigger || } + + )}
{renderContent()}
diff --git a/autogpt_platform/frontend/src/components/contextual/PublishAgentModal/usePublishAgentModal.ts b/autogpt_platform/frontend/src/components/contextual/PublishAgentModal/usePublishAgentModal.ts index f83698d8e7..0f8a819c6e 100644 --- a/autogpt_platform/frontend/src/components/contextual/PublishAgentModal/usePublishAgentModal.ts +++ b/autogpt_platform/frontend/src/components/contextual/PublishAgentModal/usePublishAgentModal.ts @@ -30,6 +30,7 @@ export interface Props { onStateChange?: (state: PublishState) => void; preSelectedAgentId?: string; preSelectedAgentVersion?: number; + showTrigger?: boolean; } export function usePublishAgentModal({ diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/Navbar.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/Navbar.tsx index 1441cbfb65..c5e9cabd63 100644 --- a/autogpt_platform/frontend/src/components/layout/Navbar/Navbar.tsx +++ b/autogpt_platform/frontend/src/components/layout/Navbar/Navbar.tsx @@ -1,13 +1,157 @@ +"use client"; + +import { useGetV2GetUserProfile } from "@/app/api/__generated__/endpoints/store/store"; +import { okData } from "@/app/api/helpers"; +import { IconAutoGPTLogo, IconType } from "@/components/__legacy__/ui/icons"; +import { PreviewBanner } from "@/components/layout/Navbar/components/PreviewBanner/PreviewBanner"; +import { useBreakpoint } from "@/lib/hooks/useBreakpoint"; +import { useSupabase } from "@/lib/supabase/hooks/useSupabase"; import { environment } from "@/services/environment"; +import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag"; +import { AccountMenu } from "./components/AccountMenu/AccountMenu"; +import { AgentActivityDropdown } from "./components/AgentActivityDropdown/AgentActivityDropdown"; +import { LoginButton } from "./components/LoginButton"; +import { MobileNavBar } from "./components/MobileNavbar/MobileNavBar"; +import { NavbarLink } from "./components/NavbarLink"; +import { NavbarLoading } from "./components/NavbarLoading"; +import { Wallet } from "./components/Wallet/Wallet"; +import { getAccountMenuItems, loggedInLinks, loggedOutLinks } from "./helpers"; -import { NavbarView } from "./components/NavbarView"; -import { getNavbarAccountData } from "./data"; - -export async function Navbar() { - const { isLoggedIn } = await getNavbarAccountData(); +export function Navbar() { + const { user, isLoggedIn, isUserLoading } = useSupabase(); + const breakpoint = useBreakpoint(); + const isSmallScreen = breakpoint === "sm" || breakpoint === "base"; + const dynamicMenuItems = getAccountMenuItems(user?.role); + const isChatEnabled = useGetFlag(Flag.CHAT); const previewBranchName = environment.getPreviewStealingDev(); + const { data: profile, isLoading: isProfileLoading } = useGetV2GetUserProfile( + { + query: { + select: okData, + enabled: isLoggedIn && !!user, + // Include user ID in query key to ensure cache invalidation when user changes + queryKey: ["/api/store/profile", user?.id], + }, + }, + ); + + const isLoadingProfile = isProfileLoading || isUserLoading; + + const shouldShowPreviewBanner = Boolean(isLoggedIn && previewBranchName); + + const actualLoggedInLinks = + isChatEnabled === true + ? loggedInLinks.concat([{ name: "Chat", href: "/chat" }]) + : loggedInLinks; + + if (isUserLoading) { + return ; + } + return ( - + <> +
+ {shouldShowPreviewBanner && previewBranchName ? ( + + ) : null} + +
+ {/* Mobile Navbar - Adjust positioning */} + <> + {isLoggedIn && isSmallScreen ? ( +
+ + { + if (link.name === "Chat" && !isChatEnabled) { + return null; + } + + return { + icon: + link.name === "Marketplace" + ? IconType.Marketplace + : link.name === "Library" + ? IconType.Library + : link.name === "Build" + ? IconType.Builder + : link.name === "Chat" + ? IconType.Chat + : link.name === "Monitor" + ? IconType.Library + : IconType.LayoutDashboard, + text: link.name, + href: link.href, + }; + }) + .filter((item) => item !== null) as Array<{ + icon: IconType; + text: string; + href: string; + }>, + }, + ...dynamicMenuItems, + ]} + userEmail={profile?.name} + avatarSrc={profile?.avatar_url ?? ""} + /> +
+ ) : null} + + ); } diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/components/AccountMenu/components/AccountLogoutOption.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/components/AccountMenu/components/AccountLogoutOption.tsx index b0061ec2c9..570f05ca89 100644 --- a/autogpt_platform/frontend/src/components/layout/Navbar/components/AccountMenu/components/AccountLogoutOption.tsx +++ b/autogpt_platform/frontend/src/components/layout/Navbar/components/AccountMenu/components/AccountLogoutOption.tsx @@ -6,45 +6,42 @@ import { useSupabase } from "@/lib/supabase/hooks/useSupabase"; import { cn } from "@/lib/utils"; import * as Sentry from "@sentry/nextjs"; import { useRouter } from "next/navigation"; -import { useState } from "react"; +import { useTransition } from "react"; export function AccountLogoutOption() { - const [isLoggingOut, setIsLoggingOut] = useState(false); + const [isPending, startTransition] = useTransition(); const supabase = useSupabase(); const router = useRouter(); const { toast } = useToast(); - async function handleLogout() { - setIsLoggingOut(true); - try { - await supabase.logOut(); - router.push("/login"); - } catch (e) { - Sentry.captureException(e); - toast({ - title: "Error logging out", - description: - "Something went wrong when logging out. Please try again. If the problem persists, please contact support.", - variant: "destructive", - }); - } finally { - setTimeout(() => { - setIsLoggingOut(false); - }, 3000); - } + function handleLogout() { + startTransition(async () => { + try { + await supabase.logOut(); + router.replace("/login"); + } catch (e) { + Sentry.captureException(e); + toast({ + title: "Error logging out", + description: + "Something went wrong when logging out. Please try again. If the problem persists, please contact support.", + variant: "destructive", + }); + } + }); } return (
- {isLoggingOut ? ( + {isPending ? ( ) : ( <> diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarLoading.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarLoading.tsx index 42362d24d4..322574fdb0 100644 --- a/autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarLoading.tsx +++ b/autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarLoading.tsx @@ -5,16 +5,15 @@ export function NavbarLoading() { return ( ); diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarView.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarView.tsx deleted file mode 100644 index 863b9f601f..0000000000 --- a/autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarView.tsx +++ /dev/null @@ -1,144 +0,0 @@ -"use client"; - -import { useGetV2GetUserProfile } from "@/app/api/__generated__/endpoints/store/store"; -import { IconAutoGPTLogo, IconType } from "@/components/__legacy__/ui/icons"; -import { PreviewBanner } from "@/components/layout/Navbar/components/PreviewBanner/PreviewBanner"; -import { useBreakpoint } from "@/lib/hooks/useBreakpoint"; -import { useSupabase } from "@/lib/supabase/hooks/useSupabase"; -import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag"; -import { useMemo } from "react"; -import { okData } from "@/app/api/helpers"; -import { getAccountMenuItems, loggedInLinks, loggedOutLinks } from "../helpers"; -import { AccountMenu } from "./AccountMenu/AccountMenu"; -import { AgentActivityDropdown } from "./AgentActivityDropdown/AgentActivityDropdown"; -import { LoginButton } from "./LoginButton"; -import { MobileNavBar } from "./MobileNavbar/MobileNavBar"; -import { NavbarLink } from "./NavbarLink"; -import { Wallet } from "./Wallet/Wallet"; -interface NavbarViewProps { - isLoggedIn: boolean; - previewBranchName?: string | null; -} - -export function NavbarView({ isLoggedIn, previewBranchName }: NavbarViewProps) { - const { user } = useSupabase(); - const breakpoint = useBreakpoint(); - const isSmallScreen = breakpoint === "sm" || breakpoint === "base"; - const dynamicMenuItems = getAccountMenuItems(user?.role); - const isChatEnabled = useGetFlag(Flag.CHAT); - - const { data: profile, isLoading: isProfileLoading } = useGetV2GetUserProfile( - { - query: { - select: okData, - enabled: isLoggedIn && !!user, - // Include user ID in query key to ensure cache invalidation when user changes - queryKey: ["/api/store/profile", user?.id], - }, - }, - ); - - const { isUserLoading } = useSupabase(); - const isLoadingProfile = isProfileLoading || isUserLoading; - - const linksWithChat = useMemo(() => { - const chatLink = { name: "Chat", href: "/chat" }; - return isChatEnabled ? [...loggedInLinks, chatLink] : loggedInLinks; - }, [isChatEnabled]); - - const shouldShowPreviewBanner = Boolean(isLoggedIn && previewBranchName); - - return ( - <> -
- {shouldShowPreviewBanner && previewBranchName ? ( - - ) : null} - -
- {/* Mobile Navbar - Adjust positioning */} - <> - {isLoggedIn && isSmallScreen ? ( -
- - ({ - icon: - link.name === "Marketplace" - ? IconType.Marketplace - : link.name === "Library" - ? IconType.Library - : link.name === "Build" - ? IconType.Builder - : link.name === "Chat" - ? IconType.Chat - : link.name === "Monitor" - ? IconType.Library - : IconType.LayoutDashboard, - text: link.name, - href: link.href, - })), - }, - ...dynamicMenuItems, - ]} - userEmail={profile?.name} - avatarSrc={profile?.avatar_url ?? ""} - /> -
- ) : null} - - - ); -} diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/data.ts b/autogpt_platform/frontend/src/components/layout/Navbar/data.ts deleted file mode 100644 index 0d07cef78b..0000000000 --- a/autogpt_platform/frontend/src/components/layout/Navbar/data.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { prefetchGetV2GetUserProfileQuery } from "@/app/api/__generated__/endpoints/store/store"; -import { getQueryClient } from "@/lib/react-query/queryClient"; -import { getServerUser } from "@/lib/supabase/server/getServerUser"; - -export async function getNavbarAccountData() { - const { user } = await getServerUser(); - const isLoggedIn = Boolean(user); - const queryClient = getQueryClient(); - - if (!isLoggedIn) { - return { - profile: null, - isLoggedIn, - }; - } - try { - await prefetchGetV2GetUserProfileQuery(queryClient); - } catch (error) { - console.error("Error fetching profile:", error); - } - - return { - isLoggedIn, - }; -} diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/FormRenderer.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/FormRenderer.tsx similarity index 83% rename from autogpt_platform/frontend/src/components/renderers/input-renderer/FormRenderer.tsx rename to autogpt_platform/frontend/src/components/renderers/InputRenderer/FormRenderer.tsx index 53df5f9a37..50137aa04f 100644 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/FormRenderer.tsx +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/FormRenderer.tsx @@ -1,27 +1,17 @@ -import { BlockUIType } from "@/app/(platform)/build/components/types"; -import Form from "@rjsf/core"; import { RJSFSchema } from "@rjsf/utils"; -import { fields } from "./fields"; -import { templates } from "./templates"; -import { widgets } from "./widgets"; import { preprocessInputSchema } from "./utils/input-schema-pre-processor"; import { useMemo } from "react"; import { customValidator } from "./utils/custom-validator"; import { isLlmModelFieldSchema } from "./fields/LlmModelField/LlmModelField"; - -type FormContextType = { - nodeId?: string; - uiType?: BlockUIType; - showHandles?: boolean; - size?: "small" | "medium" | "large"; -}; +import Form from "./registry"; +import { ExtendedFormContextType } from "./types"; type FormRendererProps = { jsonSchema: RJSFSchema; handleChange: (formData: any) => void; uiSchema: any; initialValues: any; - formContext: FormContextType; + formContext: ExtendedFormContextType; }; export const FormRenderer = ({ @@ -43,18 +33,16 @@ export const FormRenderer = ({ return mergeUiSchema(uiSchema, llmModelUiSchema); }, [uiSchema, llmModelUiSchema]); return ( -
+
diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/anyof/AnyOfField.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/anyof/AnyOfField.tsx new file mode 100644 index 0000000000..3eb5b45a5e --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/anyof/AnyOfField.tsx @@ -0,0 +1,86 @@ +import { FieldProps, getUiOptions, getWidget } from "@rjsf/utils"; +import { AnyOfFieldTitle } from "./components/AnyOfFieldTitle"; +import { isEmpty } from "lodash"; +import { useAnyOfField } from "./useAnyOfField"; +import { getHandleId, updateUiOption } from "../../helpers"; +import { useEdgeStore } from "@/app/(platform)/build/stores/edgeStore"; +import { ANY_OF_FLAG } from "../../constants"; + +export const AnyOfField = (props: FieldProps) => { + const { registry, schema } = props; + const { fields } = registry; + const { SchemaField: _SchemaField } = fields; + const { nodeId } = registry.formContext; + + const { isInputConnected } = useEdgeStore(); + + const uiOptions = getUiOptions(props.uiSchema, props.globalUiOptions); + + const Widget = getWidget({ type: "string" }, "select", registry.widgets); + + const { + handleOptionChange, + enumOptions, + selectedOption, + optionSchema, + field_id, + } = useAnyOfField(props); + + const handleId = getHandleId({ + uiOptions, + id: field_id + ANY_OF_FLAG, + schema: schema, + }); + + const updatedUiSchema = updateUiOption(props.uiSchema, { + handleId: handleId, + label: false, + fromAnyOf: true, + }); + + const isHandleConnected = isInputConnected(nodeId, handleId); + + const optionsSchemaField = + (optionSchema && optionSchema.type !== "null" && ( + <_SchemaField + {...props} + schema={optionSchema} + uiSchema={updatedUiSchema} + /> + )) || + null; + + const selector = ( + = 0 ? selectedOption : undefined} + options={{ enumOptions }} + registry={registry} + placeholder={props.placeholder} + autocomplete={props.autocomplete} + className="-ml-1 h-[22px] w-fit gap-1 px-1 pl-2 text-xs font-medium" + autofocus={props.autofocus} + label="" + hideLabel={true} + readonly={props.readonly} + /> + ); + + return ( +
+ + {!isHandleConnected && optionsSchemaField} +
+ ); +}; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/anyof/components/AnyOfFieldTitle.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/anyof/components/AnyOfFieldTitle.tsx new file mode 100644 index 0000000000..bd8aadc6a5 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/anyof/components/AnyOfFieldTitle.tsx @@ -0,0 +1,78 @@ +import { + descriptionId, + FieldProps, + getTemplate, + getUiOptions, + titleId, +} from "@rjsf/utils"; +import { shouldShowTypeSelector } from "../helpers"; +import { useIsArrayItem } from "../../array/context/array-item-context"; +import { cleanUpHandleId } from "../../../helpers"; +import { useEdgeStore } from "@/app/(platform)/build/stores/edgeStore"; +import { Text } from "@/components/atoms/Text/Text"; +import { isOptionalType } from "../../../utils/schema-utils"; +import { getTypeDisplayInfo } from "@/app/(platform)/build/components/FlowEditor/nodes/helpers"; +import { cn } from "@/lib/utils"; + +interface customFieldProps extends FieldProps { + selector: JSX.Element; +} + +export const AnyOfFieldTitle = (props: customFieldProps) => { + const { uiSchema, schema, required, name, registry, fieldPathId, selector } = + props; + const { isInputConnected } = useEdgeStore(); + const { nodeId } = registry.formContext; + + const uiOptions = getUiOptions(uiSchema); + const TitleFieldTemplate = getTemplate( + "TitleFieldTemplate", + registry, + uiOptions, + ); + const DescriptionFieldTemplate = getTemplate( + "DescriptionFieldTemplate", + registry, + uiOptions, + ); + + const title_id = titleId(fieldPathId ?? ""); + const description_id = descriptionId(fieldPathId ?? ""); + + const isArrayItem = useIsArrayItem(); + + const handleId = cleanUpHandleId(uiOptions.handleId); + const isHandleConnected = isInputConnected(nodeId, handleId); + + const { isOptional, type } = isOptionalType(schema); // If we have something like int | null = we will treat it as optional int + const { displayType, colorClass } = getTypeDisplayInfo(type); + + const shouldShowSelector = + shouldShowTypeSelector(schema) && !isArrayItem && !isHandleConnected; + const shoudlShowType = isHandleConnected || (isOptional && type); + + return ( +
+ + {shoudlShowType && ( + + {isOptional ? `(${displayType})` : "(any)"} + + )} + {shouldShowSelector && selector} + +
+ ); +}; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/anyof/helpers.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/anyof/helpers.ts new file mode 100644 index 0000000000..0a18bbf4d7 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/anyof/helpers.ts @@ -0,0 +1,61 @@ +import { RJSFSchema, StrictRJSFSchema } from "@rjsf/utils"; + +const TYPE_PRIORITY = [ + "string", + "number", + "integer", + "boolean", + "array", + "object", +] as const; + +export function getDefaultTypeIndex(options: StrictRJSFSchema[]): number { + for (const preferredType of TYPE_PRIORITY) { + const index = options.findIndex((opt) => opt.type === preferredType); + if (index >= 0) return index; + } + + const nonNullIndex = options.findIndex((opt) => opt.type !== "null"); + return nonNullIndex >= 0 ? nonNullIndex : 0; +} + +/** + * Determines if a type selector should be shown for an anyOf schema + * Returns false for simple optional types (type | null) + * Returns true for complex anyOf (3+ types or multiple non-null types) + */ +export function shouldShowTypeSelector( + schema: RJSFSchema | undefined, +): boolean { + const anyOf = schema?.anyOf; + if (!anyOf || !Array.isArray(anyOf) || anyOf.length === 0) { + return false; + } + + if (anyOf.length === 2 && anyOf.some((opt: any) => opt.type === "null")) { + return false; + } + + return anyOf.length >= 3; +} + +export function isSimpleOptional(schema: RJSFSchema | undefined): boolean { + const anyOf = schema?.anyOf; + return ( + Array.isArray(anyOf) && + anyOf.length === 2 && + anyOf.some((opt: any) => opt.type === "null") + ); +} + +export function getOptionalType( + schema: RJSFSchema | undefined, +): string | undefined { + if (!isSimpleOptional(schema)) { + return undefined; + } + + const anyOf = schema?.anyOf; + const nonNullOption = anyOf?.find((opt: any) => opt.type !== "null"); + return nonNullOption ? (nonNullOption as any).type : undefined; +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/anyof/useAnyOfField.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/anyof/useAnyOfField.ts new file mode 100644 index 0000000000..15825902b6 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/anyof/useAnyOfField.ts @@ -0,0 +1,96 @@ +import { FieldProps, getFirstMatchingOption, mergeSchemas } from "@rjsf/utils"; +import { useRef, useState } from "react"; +import validator from "@rjsf/validator-ajv8"; +import { getDefaultTypeIndex } from "./helpers"; +import { cleanUpHandleId } from "../../helpers"; +import { useEdgeStore } from "@/app/(platform)/build/stores/edgeStore"; + +export const useAnyOfField = (props: FieldProps) => { + const { registry, schema, options, onChange, formData } = props; + const { schemaUtils } = registry; + + const getInitialOption = () => { + if (formData !== undefined && formData !== null) { + const option = getFirstMatchingOption( + validator, + formData, + options, + schema, + ); + return option !== undefined ? option : getDefaultTypeIndex(options); + } + return getDefaultTypeIndex(options); + }; + + const [selectedOption, setSelectedOption] = + useState(getInitialOption()); + const retrievedOptions = useRef( + options.map((opt: any) => schemaUtils.retrieveSchema(opt, formData)), + ); + + const option = + selectedOption >= 0 + ? retrievedOptions.current[selectedOption] || null + : null; + let optionSchema: any | undefined | null; + + // adding top level required to each option schema + if (option) { + const { required } = schema; + optionSchema = required + ? (mergeSchemas({ required }, option) as any) + : option; + } + + const field_id = props.fieldPathId.$id; + + const handleOptionChange = (option?: string) => { + const intOption = option !== undefined ? parseInt(option, 10) : -1; + if (intOption === selectedOption) return; + + const newOption = + intOption >= 0 ? retrievedOptions.current[intOption] : undefined; + const oldOption = + selectedOption >= 0 + ? retrievedOptions.current[selectedOption] + : undefined; + + // When we change the option, we need to clean the form data + let newFormData = schemaUtils.sanitizeDataForNewSchema( + newOption, + oldOption, + formData, + ); + + const handlePrefix = cleanUpHandleId(field_id); + console.log("handlePrefix", handlePrefix); + useEdgeStore + .getState() + .removeEdgesByHandlePrefix(registry.formContext.nodeId, handlePrefix); + + // We have cleaned the form data, now we need to get the default form state of new selected option + if (newOption) { + newFormData = schemaUtils.getDefaultFormState( + newOption, + newFormData, + "excludeObjectChildren", + ) as any; + } + + setSelectedOption(intOption); + onChange(newFormData, props.fieldPathId.path, undefined, field_id); + }; + + const enumOptions = retrievedOptions.current.map((option, index) => ({ + value: index, + label: option.type, + })); + + return { + handleOptionChange, + enumOptions, + selectedOption, + optionSchema, + field_id, + }; +}; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/ArrayFieldItemTemplate.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/ArrayFieldItemTemplate.tsx new file mode 100644 index 0000000000..f6b1583b14 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/ArrayFieldItemTemplate.tsx @@ -0,0 +1,34 @@ +import { + ArrayFieldItemTemplateProps, + getTemplate, + getUiOptions, +} from "@rjsf/utils"; + +export default function ArrayFieldItemTemplate( + props: ArrayFieldItemTemplateProps, +) { + const { children, buttonsProps, hasToolbar, uiSchema, registry } = props; + const uiOptions = getUiOptions(uiSchema); + const ArrayFieldItemButtonsTemplate = getTemplate( + "ArrayFieldItemButtonsTemplate", + registry, + uiOptions, + ); + + return ( +
+
+
+
{children}
+
+
+ {hasToolbar && ( +
+ +
+ )} +
+
+
+ ); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/ArrayFieldTemplate.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/ArrayFieldTemplate.tsx new file mode 100644 index 0000000000..7b3f60b5c8 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/ArrayFieldTemplate.tsx @@ -0,0 +1,105 @@ +import { + ArrayFieldTemplateProps, + buttonId, + getTemplate, + getUiOptions, +} from "@rjsf/utils"; +import { getHandleId, updateUiOption } from "../../helpers"; + +export default function ArrayFieldTemplate(props: ArrayFieldTemplateProps) { + const { + canAdd, + disabled, + fieldPathId, + uiSchema, + items, + optionalDataControl, + onAddClick, + readonly, + registry, + required, + schema, + title, + } = props; + + const uiOptions = getUiOptions(uiSchema); + + const ArrayFieldDescriptionTemplate = getTemplate( + "ArrayFieldDescriptionTemplate", + registry, + uiOptions, + ); + const ArrayFieldTitleTemplate = getTemplate( + "ArrayFieldTitleTemplate", + registry, + uiOptions, + ); + const showOptionalDataControlInTitle = !readonly && !disabled; + + const { + ButtonTemplates: { AddButton }, + } = registry.templates; + + const { fromAnyOf } = uiOptions; + + const handleId = getHandleId({ + uiOptions, + id: fieldPathId.$id, + schema: schema, + }); + const updatedUiSchema = updateUiOption(uiSchema, { + handleId: handleId, + }); + + return ( +
+
+
+ {!fromAnyOf && ( +
+ + +
+ )} +
+ {!showOptionalDataControlInTitle ? optionalDataControl : undefined} + {items} + {canAdd && ( +
+ +
+ )} +
+
+
+
+ ); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/ArraySchemaField.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/ArraySchemaField.tsx new file mode 100644 index 0000000000..b926b71f9a --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/ArraySchemaField.tsx @@ -0,0 +1,29 @@ +import { FieldProps, getUiOptions } from "@rjsf/utils"; +import { getHandleId, updateUiOption } from "../../helpers"; +import { ARRAY_ITEM_FLAG } from "../../constants"; + +const ArraySchemaField = (props: FieldProps) => { + const { index, registry, fieldPathId } = props; + const { SchemaField } = registry.fields; + + const uiOptions = getUiOptions(props.uiSchema); + + const handleId = getHandleId({ + uiOptions, + id: fieldPathId.$id, + schema: props.schema, + }); + const updatedUiSchema = updateUiOption(props.uiSchema, { + handleId: handleId + ARRAY_ITEM_FLAG, + }); + + return ( + + ); +}; + +export default ArraySchemaField; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/context/array-item-context.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/context/array-item-context.tsx new file mode 100644 index 0000000000..d4da5b9ea4 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/context/array-item-context.tsx @@ -0,0 +1,33 @@ +import React, { createContext, useContext } from "react"; + +interface ArrayItemContextValue { + isArrayItem: boolean; + arrayItemHandleId: string; +} + +const ArrayItemContext = createContext({ + isArrayItem: false, + arrayItemHandleId: "", +}); + +export const ArrayItemProvider: React.FC<{ + children: React.ReactNode; + arrayItemHandleId: string; +}> = ({ children, arrayItemHandleId }) => { + return ( + + {children} + + ); +}; + +export const useIsArrayItem = (): boolean => { + // here this will be true if field is inside an array + const context = useContext(ArrayItemContext); + return context.isArrayItem; +}; + +export const useArrayItemHandleId = (): string => { + const context = useContext(ArrayItemContext); + return context.arrayItemHandleId; +}; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/helpers.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/helpers.ts new file mode 100644 index 0000000000..96e74e1381 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/helpers.ts @@ -0,0 +1,3 @@ +export const generateArrayItemHandleId = (id: string) => { + return `array-item-${id}`; +}; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/index.ts new file mode 100644 index 0000000000..eb496d3d6d --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/array/index.ts @@ -0,0 +1,7 @@ +export { default as ArrayFieldTemplate } from "./ArrayFieldTemplate"; +export { default as ArrayFieldItemTemplate } from "./ArrayFieldItemTemplate"; +export { default as ArraySchemaField } from "./ArraySchemaField"; +export { + ArrayItemProvider, + useIsArrayItem, +} from "./context/array-item-context"; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/base-registry.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/base-registry.ts new file mode 100644 index 0000000000..253e13cbea --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/base-registry.ts @@ -0,0 +1,71 @@ +import { + RegistryFieldsType, + RegistryWidgetsType, + TemplatesType, +} from "@rjsf/utils"; +import { AnyOfField } from "./anyof/AnyOfField"; +import { + ArrayFieldItemTemplate, + ArrayFieldTemplate, + ArraySchemaField, +} from "./array"; +import { + ObjectFieldTemplate, + OptionalDataControlsTemplate, + WrapIfAdditionalTemplate, +} from "./object"; +import { DescriptionField, FieldTemplate, TitleField } from "./standard"; +import { AddButton, CopyButton, RemoveButton } from "./standard/buttons"; +import { + CheckboxWidget, + DateTimeWidget, + DateWidget, + FileWidget, + GoogleDrivePickerWidget, + SelectWidget, + TextWidget, + TimeWidget, +} from "./standard/widgets"; + +const NoButton = () => null; + +export function generateBaseFields(): RegistryFieldsType { + return { + AnyOfField, + ArraySchemaField, + }; +} + +export function generateBaseTemplates(): Partial { + return { + ArrayFieldItemTemplate, + ArrayFieldTemplate, + ButtonTemplates: { + AddButton, + CopyButton, + MoveDownButton: NoButton, + MoveUpButton: NoButton, + RemoveButton, + SubmitButton: NoButton, + }, + DescriptionFieldTemplate: DescriptionField, + FieldTemplate, + ObjectFieldTemplate, + OptionalDataControlsTemplate, + TitleFieldTemplate: TitleField, + WrapIfAdditionalTemplate, + }; +} + +export function generateBaseWidgets(): RegistryWidgetsType { + return { + TextWidget, + SelectWidget, + CheckboxWidget, + FileWidget, + DateWidget, + TimeWidget, + DateTimeWidget, + GoogleDrivePickerWidget, + }; +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/index.ts new file mode 100644 index 0000000000..cfd0a86fa4 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/index.ts @@ -0,0 +1,5 @@ +export * from "./array"; +export * from "./object"; +export * from "./standard"; +export * from "./standard/widgets"; +export * from "./standard/buttons"; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/object/ObjectFieldTemplate.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/object/ObjectFieldTemplate.tsx new file mode 100644 index 0000000000..80d7682bbc --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/object/ObjectFieldTemplate.tsx @@ -0,0 +1,122 @@ +import { + ADDITIONAL_PROPERTY_FLAG, + buttonId, + canExpand, + descriptionId, + getTemplate, + getUiOptions, + ObjectFieldTemplateProps, + titleId, +} from "@rjsf/utils"; +import { getHandleId, updateUiOption } from "../../helpers"; +import React from "react"; + +export default function ObjectFieldTemplate(props: ObjectFieldTemplateProps) { + const { + description, + title, + properties, + required, + uiSchema, + fieldPathId, + schema, + formData, + optionalDataControl, + onAddProperty, + disabled, + readonly, + registry, + } = props; + const uiOptions = getUiOptions(uiSchema); + + const TitleFieldTemplate = getTemplate( + "TitleFieldTemplate", + registry, + uiOptions, + ); + + const DescriptionFieldTemplate = getTemplate( + "DescriptionFieldTemplate", + registry, + uiOptions, + ); + const showOptionalDataControlInTitle = !readonly && !disabled; + + const { + ButtonTemplates: { AddButton }, + } = registry.templates; + + const additional = ADDITIONAL_PROPERTY_FLAG in schema; + + const handleId = getHandleId({ + uiOptions, + id: fieldPathId.$id, + schema, + }); + + const updatedUiSchema = updateUiOption(uiSchema, { + handleId: handleId, + }); + + return ( + <> +
+ {title && !additional && ( + + )} + {description && ( + + )} +
+ +
+ {!showOptionalDataControlInTitle ? optionalDataControl : undefined} + + {/* I have cloned it - so i could pass updated uiSchema to the nested children */} + {properties.map((element: any, index: number) => { + const clonedContent = React.cloneElement(element.content, { + ...element.content.props, + uiSchema: updateUiOption(element.content.props.uiSchema, { + handleId: handleId, + }), + }); + + return ( +
+
{clonedContent}
+
+ ); + })} + {canExpand(schema, uiSchema, formData) ? ( +
+ +
+ ) : null} +
+ + ); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/object/OptionalDataControlsTemplate.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/object/OptionalDataControlsTemplate.tsx new file mode 100644 index 0000000000..dc53c6ce5b --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/object/OptionalDataControlsTemplate.tsx @@ -0,0 +1,35 @@ +import { OptionalDataControlsTemplateProps } from "@rjsf/utils"; +import { PlusCircle } from "lucide-react"; + +import { IconButton, RemoveButton } from "../standard/buttons"; + +export default function OptionalDataControlsTemplate( + props: OptionalDataControlsTemplateProps, +) { + const { id, registry, label, onAddClick, onRemoveClick } = props; + if (onAddClick) { + return ( + } + size="small" + /> + ); + } else if (onRemoveClick) { + return ( + + ); + } + return {label}; +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/object/WrapIfAdditionalTemplate.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/object/WrapIfAdditionalTemplate.tsx new file mode 100644 index 0000000000..97478e9eaf --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/object/WrapIfAdditionalTemplate.tsx @@ -0,0 +1,114 @@ +import { + ADDITIONAL_PROPERTY_FLAG, + buttonId, + getTemplate, + getUiOptions, + titleId, + WrapIfAdditionalTemplateProps, +} from "@rjsf/utils"; + +import { Input } from "@/components/atoms/Input/Input"; +import { useEdgeStore } from "@/app/(platform)/build/stores/edgeStore"; + +export default function WrapIfAdditionalTemplate( + props: WrapIfAdditionalTemplateProps, +) { + const { + classNames, + style, + children, + disabled, + id, + label, + onRemoveProperty, + onKeyRenameBlur, + readonly, + required, + schema, + uiSchema, + registry, + } = props; + const { templates, formContext } = registry; + const uiOptions = getUiOptions(uiSchema); + // Button templates are not overridden in the uiSchema + const { RemoveButton } = templates.ButtonTemplates; + const { isInputConnected } = useEdgeStore(); + + const additional = ADDITIONAL_PROPERTY_FLAG in schema; + const { nodeId } = formContext; + const handleId = uiOptions.handleId; + + const TitleFieldTemplate = getTemplate( + "TitleFieldTemplate", + registry, + uiOptions, + ); + + if (!additional) { + return ( +
+ {children} +
+ ); + } + + const keyId = `${id}-key`; + const generateObjectPropertyTitleId = (id: string, label: string) => { + return id.replace(`_${label}`, `_#_${label}`); + }; + const title_id = generateObjectPropertyTitleId(id, label); + + const handleBlur = (e: React.FocusEvent) => { + if (e.target.value == "") { + onRemoveProperty(); + } else { + onKeyRenameBlur(e); + } + }; + + const isHandleConnected = isInputConnected(nodeId, handleId); + + return ( + <> +
+ + {!isHandleConnected && ( +
+ +
{children}
+
+ )} + {!isHandleConnected && ( +
+ +
+ )} +
+ + ); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/object/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/object/index.ts new file mode 100644 index 0000000000..84bf8baa16 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/object/index.ts @@ -0,0 +1,3 @@ +export { default as ObjectFieldTemplate } from "./ObjectFieldTemplate"; +export { default as WrapIfAdditionalTemplate } from "./WrapIfAdditionalTemplate"; +export { default as OptionalDataControlsTemplate } from "./OptionalDataControlsTemplate"; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/DescriptionField.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/DescriptionField.tsx new file mode 100644 index 0000000000..65b5e4be43 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/DescriptionField.tsx @@ -0,0 +1,32 @@ +import { DescriptionFieldProps } from "@rjsf/utils"; +import { RichDescription } from "@rjsf/core"; +import { InfoIcon } from "@phosphor-icons/react"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/atoms/Tooltip/BaseTooltip"; + +export default function DescriptionField(props: DescriptionFieldProps) { + const { id, description, registry, uiSchema } = props; + if (!description) { + return null; + } + + return ( +
+ + + + + + + + +
+ ); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/FieldError.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/FieldError.tsx new file mode 100644 index 0000000000..e25a9bc80f --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/FieldError.tsx @@ -0,0 +1,27 @@ +import { useNodeStore } from "@/app/(platform)/build/stores/nodeStore"; +import { Text } from "@/components/atoms/Text/Text"; + +export const FieldError = ({ + nodeId, + fieldId, +}: { + nodeId: string; + fieldId: string; +}) => { + const nodeErrors = useNodeStore((state) => { + const node = state.nodes.find((n) => n.id === nodeId); + return node?.data?.errors; + }); + const fieldError = + nodeErrors?.[fieldId] || nodeErrors?.[fieldId.replace(/_%_/g, ".")] || null; + + return ( +
+ {fieldError && ( + + {fieldError} + + )} +
+ ); +}; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/FieldTemplate.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/FieldTemplate.tsx new file mode 100644 index 0000000000..d912d9d06e --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/FieldTemplate.tsx @@ -0,0 +1,131 @@ +import { + ADDITIONAL_PROPERTY_FLAG, + FieldTemplateProps, + getTemplate, + getUiOptions, + titleId, +} from "@rjsf/utils"; + +import { isAnyOfChild, isAnyOfSchema } from "../../utils/schema-utils"; +import { + cleanUpHandleId, + getHandleId, + isPartOfAnyOf, + updateUiOption, +} from "../../helpers"; + +import { useNodeStore } from "@/app/(platform)/build/stores/nodeStore"; +import { useEdgeStore } from "@/app/(platform)/build/stores/edgeStore"; +import { FieldError } from "./FieldError"; + +export default function FieldTemplate(props: FieldTemplateProps) { + const { + id, + children, + displayLabel, + description, + rawDescription, + label, + hidden, + required, + schema, + uiSchema, + registry, + classNames, + style, + disabled, + onKeyRename, + onKeyRenameBlur, + onRemoveProperty, + readonly, + } = props; + const { nodeId } = registry.formContext; + + const { isInputConnected } = useEdgeStore(); + const showAdvanced = useNodeStore( + (state) => state.nodeAdvancedStates[registry.formContext.nodeId ?? ""], + ); + + if (hidden) { + return
{children}
; + } + + const uiOptions = getUiOptions(uiSchema); + const TitleFieldTemplate = getTemplate( + "TitleFieldTemplate", + registry, + uiOptions, + ); + const WrapIfAdditionalTemplate = getTemplate( + "WrapIfAdditionalTemplate", + registry, + uiOptions, + ); + + const additional = ADDITIONAL_PROPERTY_FLAG in schema; + + const handleId = getHandleId({ + uiOptions, + id: id, + schema: schema, + }); + const updatedUiSchema = updateUiOption(uiSchema, { + handleId: handleId, + }); + const isHandleConnected = isInputConnected(nodeId, cleanUpHandleId(handleId)); + + const shouldDisplayLabel = + displayLabel || + (schema.type === "boolean" && !isAnyOfChild(uiSchema as any)); + const shouldShowTitleSection = !isAnyOfSchema(schema) && !additional; + const shouldShowChildren = isAnyOfSchema(schema) || !isHandleConnected; + + const isAdvancedField = (schema as any).advanced === true; + if (!showAdvanced && isAdvancedField && !isHandleConnected) { + return null; + } + + const marginBottom = + isPartOfAnyOf({ uiOptions }) || isAnyOfSchema(schema) ? 0 : 16; + + return ( + +
+ {shouldShowTitleSection && ( +
+ {shouldDisplayLabel && ( + + )} + {shouldDisplayLabel && rawDescription && {description}} +
+ )} + {shouldShowChildren && children} + + +
+
+ ); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/TitleField.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/TitleField.tsx new file mode 100644 index 0000000000..378ffa7f34 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/TitleField.tsx @@ -0,0 +1,55 @@ +import { + ADDITIONAL_PROPERTY_FLAG, + descriptionId, + getUiOptions, + TitleFieldProps, +} from "@rjsf/utils"; + +import { Text } from "@/components/atoms/Text/Text"; +import { getTypeDisplayInfo } from "@/app/(platform)/build/components/FlowEditor/nodes/helpers"; +import { isAnyOfSchema } from "../../utils/schema-utils"; +import { cn } from "@/lib/utils"; +import { isArrayItem } from "../../helpers"; +import { InputNodeHandle } from "@/app/(platform)/build/components/FlowEditor/handlers/NodeHandle"; + +export default function TitleField(props: TitleFieldProps) { + const { id, title, required, schema, registry, uiSchema } = props; + const { nodeId, showHandles } = registry.formContext; + const uiOptions = getUiOptions(uiSchema); + + const isAnyOf = isAnyOfSchema(schema); + const { displayType, colorClass } = getTypeDisplayInfo(schema); + const description_id = descriptionId(id); + + const additional = ADDITIONAL_PROPERTY_FLAG in schema; + const isArrayItemFlag = isArrayItem({ uiOptions }); + const smallText = isArrayItemFlag || additional; + + const showHandle = uiOptions.showHandles ?? showHandles; + return ( +
+ {showHandle !== false && ( + + )} + + {title} + + + {required ? "*" : null} + + {!isAnyOf && ( + + ({displayType}) + + )} +
+ ); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/buttons/AddButton.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/buttons/AddButton.tsx new file mode 100644 index 0000000000..901716f56a --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/buttons/AddButton.tsx @@ -0,0 +1,27 @@ +import { IconButtonProps, TranslatableString } from "@rjsf/utils"; +import { cn } from "@/lib/utils"; +import { Button } from "@/components/atoms/Button/Button"; +import { PlusIcon } from "@phosphor-icons/react"; + +export default function AddButton({ + registry, + className, + uiSchema: _uiSchema, + ...props +}: IconButtonProps) { + const { translateString } = registry; + return ( +
+ +
+ ); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/buttons/IconButton.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/buttons/IconButton.tsx new file mode 100644 index 0000000000..4a7f011b82 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/buttons/IconButton.tsx @@ -0,0 +1,101 @@ +import { + FormContextType, + IconButtonProps, + RJSFSchema, + StrictRJSFSchema, + TranslatableString, +} from "@rjsf/utils"; +import { ChevronDown, ChevronUp, Copy } from "lucide-react"; +import type { VariantProps } from "class-variance-authority"; + +import { Button } from "@/components/atoms/Button/Button"; +import { extendedButtonVariants } from "@/components/atoms/Button/helpers"; +import { TrashIcon } from "@phosphor-icons/react"; +import { cn } from "@/lib/utils"; +import { Text } from "@/components/atoms/Text/Text"; + +export type AutogptIconButtonProps< + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any, +> = IconButtonProps & VariantProps; + +export default function IconButton(props: AutogptIconButtonProps) { + const { + icon, + className, + uiSchema: _uiSchema, + registry: _registry, + iconType: _iconType, + ...otherProps + } = props; + + return ( + + ); +} + +export function CopyButton(props: AutogptIconButtonProps) { + const { + registry: { translateString }, + } = props; + return ( + } + /> + ); +} + +export function MoveDownButton(props: AutogptIconButtonProps) { + const { + registry: { translateString }, + } = props; + return ( + } + /> + ); +} + +export function MoveUpButton(props: AutogptIconButtonProps) { + const { + registry: { translateString }, + } = props; + return ( + } + /> + ); +} + +export function RemoveButton(props: AutogptIconButtonProps) { + const { + registry: { translateString }, + } = props; + return ( + } + /> + ); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/buttons/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/buttons/index.ts new file mode 100644 index 0000000000..f083306249 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/buttons/index.ts @@ -0,0 +1,8 @@ +export { default as AddButton } from "./AddButton"; +export { + default as IconButton, + CopyButton, + RemoveButton, + MoveUpButton, + MoveDownButton, +} from "./IconButton"; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/errors/ErrorList.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/errors/ErrorList.tsx new file mode 100644 index 0000000000..39175ef13e --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/errors/ErrorList.tsx @@ -0,0 +1,24 @@ +import { ErrorListProps, TranslatableString } from "@rjsf/utils"; +import { AlertCircle } from "lucide-react"; + +import { + Alert, + AlertDescription, + AlertTitle, +} from "@/components/molecules/Alert/Alert"; + +export default function ErrorList(props: ErrorListProps) { + const { errors, registry } = props; + const { translateString } = registry; + return ( + + + {translateString(TranslatableString.ErrorsLabel)} + + {errors.map((error, i: number) => { + return • {error.stack}; + })} + + + ); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/errors/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/errors/index.ts new file mode 100644 index 0000000000..ccecd3c8c6 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/errors/index.ts @@ -0,0 +1 @@ +export { default as ErrorList } from "./ErrorList"; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/helpers.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/helpers.ts new file mode 100644 index 0000000000..327a0e06b6 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/helpers.ts @@ -0,0 +1,76 @@ +import { RJSFSchema } from "@rjsf/utils"; + +export function parseFieldPath( + rootSchema: RJSFSchema, + id: string, + additional: boolean, + idSeparator: string = "_%_", +): { path: string[]; typeHints: string[] } { + const segments = id.split(idSeparator).filter(Boolean); + const typeHints: string[] = []; + + let currentSchema = rootSchema; + + for (let i = 0; i < segments.length; i++) { + const segment = segments[i]; + const isNumeric = /^\d+$/.test(segment); + + if (isNumeric) { + typeHints.push("array"); + } else { + if (additional) { + typeHints.push("object-key"); + } else { + typeHints.push("object-property"); + } + currentSchema = (currentSchema.properties?.[segment] as RJSFSchema) || {}; + } + } + + return { path: segments, typeHints }; +} + +// This helper work is simple - it just help us to convert rjsf id to our backend compatible id +// Example : List[dict] = agpt_%_List_0_dict__title -> List_$_0_#_dict +// We remove the prefix and suffix and then we split id by our custom delimiter (_%_) +// then add _$_ delimiter for array and _#_ delimiter for object-key +// and for normal property we add . delimiter + +export function getHandleId( + rootSchema: RJSFSchema, + id: string, + additional: boolean, + idSeparator: string = "_%_", +): string { + const idPrefix = "agpt_%_"; + const idSuffix = "__title"; + + if (id.startsWith(idPrefix)) { + id = id.slice(idPrefix.length); + } + if (id.endsWith(idSuffix)) { + id = id.slice(0, -idSuffix.length); + } + + const { path, typeHints } = parseFieldPath( + rootSchema, + id, + additional, + idSeparator, + ); + + return path + .map((seg, i) => { + const type = typeHints[i]; + if (type === "array") { + return `_$_${seg}`; + } + if (type === "object-key") { + return `_${seg}`; // we haven't added _#_ delimiter for object-key because it's already added in the id - check WrapIfAdditionalTemplate.tsx + } + + return `.${seg}`; + }) + .join("") + .slice(1); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/index.ts new file mode 100644 index 0000000000..870753151c --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/index.ts @@ -0,0 +1,3 @@ +export { default as FieldTemplate } from "./FieldTemplate"; +export { default as TitleField } from "./TitleField"; +export { default as DescriptionField } from "./DescriptionField"; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/SwitchWidget.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/CheckboxInput/CheckBoxWidget.tsx similarity index 87% rename from autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/SwitchWidget.tsx rename to autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/CheckboxInput/CheckBoxWidget.tsx index d15ec18a9a..ff93528492 100644 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/SwitchWidget.tsx +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/CheckboxInput/CheckBoxWidget.tsx @@ -1,8 +1,9 @@ import { WidgetProps } from "@rjsf/utils"; import { Switch } from "@/components/atoms/Switch/Switch"; -export function SwitchWidget(props: WidgetProps) { +export function CheckboxWidget(props: WidgetProps) { const { value = false, onChange, disabled, readonly, autofocus, id } = props; + return ( { +export const DateWidget = (props: WidgetProps) => { const { value, onChange, diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/DateInput/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/DateInput/index.ts new file mode 100644 index 0000000000..3ba465c4f4 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/DateInput/index.ts @@ -0,0 +1 @@ +export { DateWidget } from "./DateWidget"; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/DateTimeInputWidget.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/DateTimeInput/DateTimeWidget.tsx similarity index 91% rename from autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/DateTimeInputWidget.tsx rename to autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/DateTimeInput/DateTimeWidget.tsx index 2e85a610b5..50f6e378fb 100644 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/DateTimeInputWidget.tsx +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/DateTimeInput/DateTimeWidget.tsx @@ -1,7 +1,7 @@ import { WidgetProps } from "@rjsf/utils"; import { DateTimeInput } from "@/components/atoms/DateTimeInput/DateTimeInput"; -export const DateTimeInputWidget = (props: WidgetProps) => { +export const DateTimeWidget = (props: WidgetProps) => { const { value, onChange, diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/DateTimeInput/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/DateTimeInput/index.ts new file mode 100644 index 0000000000..bf7c084f5a --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/DateTimeInput/index.ts @@ -0,0 +1 @@ +export { DateTimeWidget } from "./DateTimeWidget"; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/FileWidget.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/FileInput/FileWidget.tsx similarity index 100% rename from autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/FileWidget.tsx rename to autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/FileInput/FileWidget.tsx diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/FileInput/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/FileInput/index.ts new file mode 100644 index 0000000000..e23b50bfd0 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/FileInput/index.ts @@ -0,0 +1 @@ +export { FileWidget } from "./FileWidget"; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/GoogleDrivePicker/GoogleDrivePicketWidget.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/GoogleDrivePicker/GoogleDrivePicketWidget.tsx new file mode 100644 index 0000000000..45ed9611cf --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/GoogleDrivePicker/GoogleDrivePicketWidget.tsx @@ -0,0 +1,55 @@ +import { useNodeStore } from "@/app/(platform)/build/stores/nodeStore"; +import { GoogleDrivePickerInput } from "@/components/contextual/GoogleDrivePicker/GoogleDrivePickerInput"; +import { getFieldErrorKey } from "@/components/renderers/InputRenderer/utils/helpers"; +import type { GoogleDrivePickerConfig } from "@/lib/autogpt-server-api/types"; +import { cn } from "@/lib/utils"; +import { WidgetProps } from "@rjsf/utils"; + +function hasGoogleDrivePickerConfig( + schema: unknown, +): schema is { google_drive_picker_config?: GoogleDrivePickerConfig } { + return ( + typeof schema === "object" && + schema !== null && + "google_drive_picker_config" in schema + ); +} + +export function GoogleDrivePickerWidget(props: WidgetProps) { + const { onChange, disabled, readonly, value, schema, id, formContext } = + props; + const { nodeId } = formContext || {}; + + const nodeErrors = useNodeStore((state) => { + const node = state.nodes.find((n) => n.id === nodeId); + return node?.data?.errors; + }); + + const fieldErrorKey = getFieldErrorKey(id ?? ""); + const fieldError = + nodeErrors?.[fieldErrorKey] || + nodeErrors?.[fieldErrorKey.replace(/_/g, ".")] || + nodeErrors?.[fieldErrorKey.replace(/\./g, "_")] || + undefined; + + const config: GoogleDrivePickerConfig = hasGoogleDrivePickerConfig(schema) + ? schema.google_drive_picker_config || {} + : {}; + + function handleChange(newValue: unknown) { + onChange(newValue); + } + + return ( + + ); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/GoogleDrivePicker/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/GoogleDrivePicker/index.ts new file mode 100644 index 0000000000..c0608ac9fe --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/GoogleDrivePicker/index.ts @@ -0,0 +1 @@ +export { GoogleDrivePickerWidget } from "./GoogleDrivePicketWidget"; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/SelectWidget.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/SelectInput/SelectWidget.tsx similarity index 87% rename from autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/SelectWidget.tsx rename to autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/SelectInput/SelectWidget.tsx index db68a1628c..894004db40 100644 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/SelectWidget.tsx +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/SelectInput/SelectWidget.tsx @@ -14,8 +14,16 @@ import { } from "@/components/__legacy__/ui/multiselect"; export const SelectWidget = (props: WidgetProps) => { - const { options, value, onChange, disabled, readonly, id, formContext } = - props; + const { + options, + value, + onChange, + disabled, + readonly, + className, + id, + formContext, + } = props; const enumOptions = options.enumOptions || []; const type = mapJsonSchemaTypeToInputType(props.schema); const { size = "small" } = formContext || {}; @@ -36,7 +44,7 @@ export const SelectWidget = (props: WidgetProps) => { - {enumOptions?.map((option) => ( + {enumOptions?.map((option: any) => ( {option.label} @@ -56,12 +64,13 @@ export const SelectWidget = (props: WidgetProps) => { value={value ?? ""} onValueChange={onChange} options={ - enumOptions?.map((option) => ({ + enumOptions?.map((option: any) => ({ value: option.value, label: option.label, })) || [] } wrapperClassName="!mb-0 " + className={className} /> ); }; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/SelectInput/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/SelectInput/index.ts new file mode 100644 index 0000000000..9a64291abc --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/SelectInput/index.ts @@ -0,0 +1 @@ +export { SelectWidget } from "./SelectWidget"; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/TextInputWidget/InputExpanderModal.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TextInput/TextInputExpanderModal.tsx similarity index 100% rename from autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/TextInputWidget/InputExpanderModal.tsx rename to autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TextInput/TextInputExpanderModal.tsx diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/TextInputWidget/TextInputWidget.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TextInput/TextWidget.tsx similarity index 91% rename from autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/TextInputWidget/TextInputWidget.tsx rename to autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TextInput/TextWidget.tsx index d9fea28a8d..33a55581c7 100644 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/TextInputWidget/TextInputWidget.tsx +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TextInput/TextWidget.tsx @@ -14,15 +14,12 @@ import { TooltipTrigger, } from "@/components/atoms/Tooltip/BaseTooltip"; import { BlockUIType } from "@/lib/autogpt-server-api/types"; -import { InputExpanderModal } from "./InputExpanderModal"; import { ArrowsOutIcon } from "@phosphor-icons/react"; +import { InputExpanderModal } from "./TextInputExpanderModal"; -export const TextInputWidget = (props: WidgetProps) => { - const { schema, formContext } = props; - const { uiType, size = "small" } = formContext as { - uiType: BlockUIType; - size?: string; - }; +export default function TextWidget(props: WidgetProps) { + const { schema, placeholder, registry } = props; + const { size, uiType } = registry.formContext; const [isModalOpen, setIsModalOpen] = useState(false); @@ -122,7 +119,7 @@ export const TextInputWidget = (props: WidgetProps) => { wrapperClassName="mb-0 flex-1" value={props.value ?? ""} onChange={handleChange} - placeholder={schema.placeholder || config.placeholder} + placeholder={placeholder || config.placeholder} required={props.required} disabled={props.disabled} className={showExpandButton ? "pr-8" : ""} @@ -152,8 +149,8 @@ export const TextInputWidget = (props: WidgetProps) => { title={schema.title || "Edit value"} description={schema.description || ""} defaultValue={props.value ?? ""} - placeholder={schema.placeholder || config.placeholder} + placeholder={placeholder || config.placeholder} /> ); -}; +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TextInput/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TextInput/index.ts new file mode 100644 index 0000000000..102db07ade --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TextInput/index.ts @@ -0,0 +1,2 @@ +export { default } from "./TextWidget"; +export { InputExpanderModal } from "./TextInputExpanderModal"; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/TimeInputWidget.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TimeInput/TimeWidget.tsx similarity index 91% rename from autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/TimeInputWidget.tsx rename to autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TimeInput/TimeWidget.tsx index 032c33e62c..152aae7298 100644 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/TimeInputWidget.tsx +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TimeInput/TimeWidget.tsx @@ -1,7 +1,7 @@ import { WidgetProps } from "@rjsf/utils"; import { TimeInput } from "@/components/atoms/TimeInput/TimeInput"; -export const TimeInputWidget = (props: WidgetProps) => { +export const TimeWidget = (props: WidgetProps) => { const { value, onChange, disabled, readonly, placeholder, id, formContext } = props; const { size = "small" } = formContext || {}; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TimeInput/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TimeInput/index.ts new file mode 100644 index 0000000000..488e184c08 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/TimeInput/index.ts @@ -0,0 +1 @@ +export { TimeWidget } from "./TimeWidget"; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/index.ts new file mode 100644 index 0000000000..f68117fa83 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/base/standard/widgets/index.ts @@ -0,0 +1,8 @@ +export { CheckboxWidget } from "./CheckboxInput"; +export { DateWidget } from "./DateInput"; +export { DateTimeWidget } from "./DateTimeInput"; +export { FileWidget } from "./FileInput"; +export { GoogleDrivePickerWidget } from "./GoogleDrivePicker"; +export { SelectWidget } from "./SelectInput"; +export { default as TextWidget } from "./TextInput"; +export { TimeWidget } from "./TimeInput"; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/constants.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/constants.ts new file mode 100644 index 0000000000..144d850a00 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/constants.ts @@ -0,0 +1,8 @@ +export const ANY_OF_FLAG = "__anyOf"; +export const ARRAY_FLAG = "__array"; +export const OBJECT_FLAG = "__object"; +export const KEY_PAIR_FLAG = "__keyPair"; +export const TITLE_FLAG = "__title"; +export const ARRAY_ITEM_FLAG = "__arrayItem"; +export const ID_PREFIX = "agpt_@_"; +export const ID_PREFIX_ARRAY = "agpt_%_"; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/CredentialField/CredentialField.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/CredentialField/CredentialField.tsx new file mode 100644 index 0000000000..f814fba93f --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/CredentialField/CredentialField.tsx @@ -0,0 +1,73 @@ +import React, { useMemo } from "react"; +import { FieldProps, getUiOptions } from "@rjsf/utils"; +import { + BlockIOCredentialsSubSchema, + CredentialsMetaInput, +} from "@/lib/autogpt-server-api"; +import { CredentialsInput } from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/modals/CredentialsInputs/CredentialsInputs"; +import { useNodeStore } from "@/app/(platform)/build/stores/nodeStore"; +import { useShallow } from "zustand/react/shallow"; +import { CredentialFieldTitle } from "./components/CredentialFieldTitle"; + +export const CredentialsField = (props: FieldProps) => { + const { formData, onChange, schema, registry, fieldPathId } = props; + + const formContext = registry.formContext; + const uiOptions = getUiOptions(props.uiSchema); + const nodeId = formContext?.nodeId; + + // Get sibling inputs (hardcoded values) from the node store + const hardcodedValues = useNodeStore( + useShallow((state) => (nodeId ? state.getHardCodedValues(nodeId) : {})), + ); + + const handleChange = (newValue: any) => { + onChange(newValue, fieldPathId?.path); + }; + + const handleSelectCredentials = (credentialsMeta?: CredentialsMetaInput) => { + if (credentialsMeta) { + handleChange({ + id: credentialsMeta.id, + provider: credentialsMeta.provider, + title: credentialsMeta.title, + type: credentialsMeta.type, + }); + } else { + handleChange(undefined); + } + }; + + // Convert formData to CredentialsMetaInput format + const selectedCredentials: CredentialsMetaInput | undefined = useMemo( + () => + formData?.id + ? { + id: formData.id, + provider: formData.provider, + title: formData.title, + type: formData.type, + } + : undefined, + [formData?.id, formData?.provider, formData?.title, formData?.type], + ); + + return ( +
+ + +
+ ); +}; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/CredentialField/components/CredentialFieldTitle.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/CredentialField/components/CredentialFieldTitle.tsx new file mode 100644 index 0000000000..ca14c8a4ce --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/CredentialField/components/CredentialFieldTitle.tsx @@ -0,0 +1,66 @@ +import { + getTemplate, + UiSchema, + Registry, + RJSFSchema, + FieldPathId, + titleId, + descriptionId, +} from "@rjsf/utils"; +import { getCredentialProviderFromSchema, toDisplayName } from "../helpers"; +import { useNodeStore } from "@/app/(platform)/build/stores/nodeStore"; +import { BlockIOCredentialsSubSchema } from "@/lib/autogpt-server-api"; +import { updateUiOption } from "../../../helpers"; +import { uiSchema } from "@/app/(platform)/build/components/FlowEditor/nodes/uiSchema"; + +export const CredentialFieldTitle = (props: { + registry: Registry; + uiOptions: UiSchema; + schema: RJSFSchema; + fieldPathId: FieldPathId; +}) => { + const { registry, uiOptions, schema, fieldPathId } = props; + const { nodeId } = registry.formContext; + + const TitleFieldTemplate = getTemplate( + "TitleFieldTemplate", + registry, + uiOptions, + ); + + const DescriptionFieldTemplate = getTemplate( + "DescriptionFieldTemplate", + registry, + uiOptions, + ); + + const credentialProvider = toDisplayName( + getCredentialProviderFromSchema( + useNodeStore.getState().getHardCodedValues(nodeId), + schema as BlockIOCredentialsSubSchema, + ) ?? "", + ); + + const updatedUiSchema = updateUiOption(uiSchema, { + showHandles: false, + }); + + return ( +
+ + +
+ ); +}; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/helpers.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/CredentialField/helpers.ts similarity index 100% rename from autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/helpers.ts rename to autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/CredentialField/helpers.ts diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/GoogleDrivePickerField/GoogleDrivePickerField.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/GoogleDrivePickerField/GoogleDrivePickerField.tsx new file mode 100644 index 0000000000..51c5806ae5 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/GoogleDrivePickerField/GoogleDrivePickerField.tsx @@ -0,0 +1,21 @@ +import { GoogleDrivePickerInput } from "@/components/contextual/GoogleDrivePicker/GoogleDrivePickerInput"; +import { GoogleDrivePickerConfig } from "@/lib/autogpt-server-api"; +import { FieldProps, getUiOptions } from "@rjsf/utils"; + +export const GoogleDrivePickerField = (props: FieldProps) => { + const { schema, uiSchema, onChange, fieldPathId, formData } = props; + const uiOptions = getUiOptions(uiSchema); + const config: GoogleDrivePickerConfig = schema.google_drive_picker_config; + + return ( +
+ onChange(value, fieldPathId.path)} + className={uiOptions.className} + showRemoveButton={true} + /> +
+ ); +}; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/custom-registry.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/custom-registry.ts new file mode 100644 index 0000000000..91850e3f10 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/custom/custom-registry.ts @@ -0,0 +1,52 @@ +import { FieldProps, RJSFSchema, RegistryFieldsType } from "@rjsf/utils"; +import { CredentialsField } from "./CredentialField/CredentialField"; +import { GoogleDrivePickerField } from "./GoogleDrivePickerField/GoogleDrivePickerField"; + +export interface CustomFieldDefinition { + id: string; + matcher: (schema: any) => boolean; + component: (props: FieldProps) => JSX.Element | null; +} + +export const CUSTOM_FIELDS: CustomFieldDefinition[] = [ + { + id: "custom/credential_field", + matcher: (schema: any) => { + return ( + typeof schema === "object" && + schema !== null && + "credentials_provider" in schema + ); + }, + component: CredentialsField, + }, + { + id: "custom/google_drive_picker_field", + matcher: (schema: any) => { + return ( + "google_drive_picker_config" in schema || + ("format" in schema && schema.format === "google-drive-picker") + ); + }, + component: GoogleDrivePickerField, + }, +]; + +export function findCustomFieldId(schema: any): string | null { + for (const field of CUSTOM_FIELDS) { + if (field.matcher(schema)) { + return field.id; + } + } + return null; +} + +export function generateCustomFields(): RegistryFieldsType { + return CUSTOM_FIELDS.reduce( + (acc, field) => { + acc[field.id] = field.component; + return acc; + }, + {} as Record, + ); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/docs/HEIRARCHY.md b/autogpt_platform/frontend/src/components/renderers/InputRenderer/docs/HEIRARCHY.md new file mode 100644 index 0000000000..45b4276946 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/docs/HEIRARCHY.md @@ -0,0 +1,291 @@ +# Input Renderer 2 - Hierarchy + +## Flow Overview + +``` +FormRenderer2 → Form (RJSF) → ObjectFieldTemplate → FieldTemplate → Widget/Field +``` + +--- + +## Component Layers + +### 1. Root (FormRenderer2) + +- Entry point +- Preprocesses schema +- Passes to RJSF Form + +### 2. Form (registry/Form.tsx) + +- RJSF themed form +- Combines: templates + widgets + fields + +### 3. Templates (decide layout/structure) + +| Template | When Used | +| -------------------------- | ------------------------------------------- | +| `ObjectFieldTemplate` | `type: "object"` | +| `ArrayFieldTemplate` | `type: "array"` | +| `FieldTemplate` | Wraps every field (title, errors, children) | +| `ArrayFieldItemTemplate` | Each array item | +| `WrapIfAdditionalTemplate` | Additional properties in objects | + +### 4. Fields (custom rendering logic) + +| Field | When Used | +| ------------------ | ---------------------------- | +| `AnyOfField` | `anyOf` or `oneOf` in schema | +| `ArraySchemaField` | Array type handling | + +### 5. Widgets (actual input elements) + +| Widget | Input Type | +| ---------------- | ----------------------- | +| `TextWidget` | string, number, integer | +| `SelectWidget` | enum, anyOf selector | +| `CheckboxWidget` | boolean | +| `FileWidget` | file upload | +| `DateWidget` | date | +| `TimeWidget` | time | +| `DateTimeWidget` | datetime | + +--- + +## Your Schema Hierarchy + +``` +Root (type: object) +└── ObjectFieldTemplate + │ + ├── name (string, required) + │ └── FieldTemplate → TextWidget + │ + ├── value (anyOf) + │ └── FieldTemplate → AnyOfField + │ └── Selector dropdown + selected type: + │ ├── String → TextWidget + │ ├── Number → TextWidget + │ ├── Integer → TextWidget + │ ├── Boolean → CheckboxWidget + │ ├── Array → ArrayFieldTemplate → items + │ ├── Object → ObjectFieldTemplate + │ └── Null → nothing + │ + ├── title (anyOf: string | null) + │ └── FieldTemplate → AnyOfField + │ └── String → TextWidget OR Null → nothing + │ + ├── description (anyOf: string | null) + │ └── FieldTemplate → AnyOfField + │ └── String → TextWidget OR Null → nothing + │ + ├── placeholder_values (array of strings) + │ └── FieldTemplate → ArrayFieldTemplate + │ └── ArrayFieldItemTemplate (per item) + │ └── TextWidget + │ + ├── advanced (boolean) + │ └── FieldTemplate → CheckboxWidget + │ + └── secret (boolean) + └── FieldTemplate → CheckboxWidget +``` + +--- + +## Nested Examples (up to 3 levels) + +### Simple Array (strings) + +```json +{ "tags": { "type": "array", "items": { "type": "string" } } } +``` + +``` +Level 1: ObjectFieldTemplate (root) +└── Level 2: FieldTemplate → ArrayFieldTemplate + └── Level 3: ArrayFieldItemTemplate → TextWidget +``` + +### Array of Objects + +```json +{ + "users": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "age": { "type": "integer" } + } + } + } +} +``` + +``` +Level 1: ObjectFieldTemplate (root) +└── Level 2: FieldTemplate → ArrayFieldTemplate + └── Level 3: ArrayFieldItemTemplate → ObjectFieldTemplate + ├── FieldTemplate → TextWidget (name) + └── FieldTemplate → TextWidget (age) +``` + +### Nested Object (3 levels) + +```json +{ + "config": { + "type": "object", + "properties": { + "database": { + "type": "object", + "properties": { + "host": { "type": "string" }, + "port": { "type": "integer" } + } + } + } + } +} +``` + +``` +Level 1: ObjectFieldTemplate (root) +└── config + └── Level 2: FieldTemplate → ObjectFieldTemplate + └── database + └── Level 3: FieldTemplate → ObjectFieldTemplate + ├── FieldTemplate → TextWidget (host) + └── FieldTemplate → TextWidget (port) +``` + +### Array of Arrays (nested array) + +```json +{ + "matrix": { + "type": "array", + "items": { + "type": "array", + "items": { "type": "number" } + } + } +} +``` + +``` +Level 1: ObjectFieldTemplate (root) +└── Level 2: FieldTemplate → ArrayFieldTemplate + └── Level 3: ArrayFieldItemTemplate → ArrayFieldTemplate + └── ArrayFieldItemTemplate → TextWidget +``` + +### Complex: Object → Array → Object + +```json +{ + "company": { + "type": "object", + "properties": { + "departments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "budget": { "type": "number" } + } + } + } + } + } +} +``` + +``` +Level 1: ObjectFieldTemplate (root) +└── company + └── Level 2: FieldTemplate → ObjectFieldTemplate + └── departments + └── Level 3: FieldTemplate → ArrayFieldTemplate + └── ArrayFieldItemTemplate → ObjectFieldTemplate + ├── FieldTemplate → TextWidget (name) + └── FieldTemplate → TextWidget (budget) +``` + +### anyOf inside Array + +```json +{ + "items": { + "type": "array", + "items": { + "anyOf": [ + { "type": "string" }, + { "type": "object", "properties": { "id": { "type": "string" } } } + ] + } + } +} +``` + +``` +Level 1: ObjectFieldTemplate (root) +└── Level 2: FieldTemplate → ArrayFieldTemplate + └── Level 3: ArrayFieldItemTemplate → AnyOfField + └── Selector + selected: + ├── String → TextWidget + └── Object → ObjectFieldTemplate + └── FieldTemplate → TextWidget (id) +``` + +--- + +## Nesting Pattern Summary + +| Parent Type | Child Wrapper | +| ----------- | ----------------------------------------------- | +| object | `ObjectFieldTemplate` → `FieldTemplate` | +| array | `ArrayFieldTemplate` → `ArrayFieldItemTemplate` | +| anyOf | `AnyOfField` → selected schema's template | +| primitive | `Widget` (leaf - no children) | + +**Pattern:** Each level adds FieldTemplate wrapper except array items (use ArrayFieldItemTemplate) + +--- + +## Key Points + +1. **FieldTemplate wraps everything** - handles title, description, errors +2. **anyOf = AnyOfField** - shows dropdown to pick type, then renders selected schema +3. **ObjectFieldTemplate loops properties** - each property gets FieldTemplate +4. **ArrayFieldTemplate loops items** - each item gets ArrayFieldItemTemplate +5. **Widgets are leaf nodes** - actual input controls user interacts with +6. **Nesting repeats the pattern** - object/array/anyOf can contain object/array/anyOf recursively + +--- + +## Decision Flow + +``` +Schema Type? +├── object → ObjectFieldTemplate → loop properties +├── array → ArrayFieldTemplate → loop items +├── anyOf/oneOf → AnyOfField → selector + selected schema +└── primitive (string/number/boolean) → Widget +``` + +--- + +## Template Wrapping Order + +``` +ObjectFieldTemplate (root) +└── FieldTemplate (per property) + └── WrapIfAdditionalTemplate (if additionalProperties) + └── TitleField + DescriptionField + children + └── Widget OR nested Template/Field +``` diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/helpers.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/helpers.ts new file mode 100644 index 0000000000..f3302fcb85 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/helpers.ts @@ -0,0 +1,276 @@ +import { + RJSFSchema, + UIOptionsType, + StrictRJSFSchema, + FormContextType, + ADDITIONAL_PROPERTY_FLAG, +} from "@rjsf/utils"; + +import { + ANY_OF_FLAG, + ARRAY_ITEM_FLAG, + ID_PREFIX, + ID_PREFIX_ARRAY, + KEY_PAIR_FLAG, + OBJECT_FLAG, +} from "./constants"; +import { PathSegment } from "./types"; + +export function updateUiOption>( + uiSchema: T | undefined, + options: Record, +): T & { "ui:options": Record } { + return { + ...(uiSchema || {}), + "ui:options": { + ...uiSchema?.["ui:options"], + ...options, + }, + } as T & { "ui:options": Record }; +} + +export const cleanUpHandleId = (handleId: string) => { + let newHandleId = handleId; + if (handleId.includes(ANY_OF_FLAG)) { + newHandleId = newHandleId.replace(ANY_OF_FLAG, ""); + } + if (handleId.includes(ARRAY_ITEM_FLAG)) { + newHandleId = newHandleId.replace(ARRAY_ITEM_FLAG, ""); + } + if (handleId.includes(KEY_PAIR_FLAG)) { + newHandleId = newHandleId.replace(KEY_PAIR_FLAG, ""); + } + if (handleId.includes(OBJECT_FLAG)) { + newHandleId = newHandleId.replace(OBJECT_FLAG, ""); + } + if (handleId.includes(ID_PREFIX_ARRAY)) { + newHandleId = newHandleId.replace(ID_PREFIX_ARRAY, ""); + } + if (handleId.includes(ID_PREFIX)) { + newHandleId = newHandleId.replace(ID_PREFIX, ""); + } + return newHandleId; +}; + +export const isArrayItem = < + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any, +>({ + uiOptions, +}: { + uiOptions: UIOptionsType; +}) => { + return uiOptions.handleId?.endsWith(ARRAY_ITEM_FLAG); +}; + +export const isKeyValuePair = ({ schema }: { schema: RJSFSchema }) => { + return ADDITIONAL_PROPERTY_FLAG in schema; +}; + +export const isNormal = < + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any, +>({ + uiOptions, +}: { + uiOptions: UIOptionsType; +}) => { + return uiOptions.handleId === undefined; +}; + +export const isPartOfAnyOf = < + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any, +>({ + uiOptions, +}: { + uiOptions: UIOptionsType; +}) => { + return uiOptions.handleId?.endsWith(ANY_OF_FLAG); +}; +export const isObjectProperty = < + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any, +>({ + uiOptions, + schema, +}: { + uiOptions: UIOptionsType; + schema: RJSFSchema; +}) => { + return ( + !isArrayItem({ uiOptions }) && + !isKeyValuePair({ schema }) && + !isNormal({ uiOptions }) && + !isPartOfAnyOf({ uiOptions }) + ); +}; + +export const getHandleId = < + T = any, + S extends StrictRJSFSchema = RJSFSchema, + F extends FormContextType = any, +>({ + id, + schema, + uiOptions, +}: { + id: string; + schema: RJSFSchema; + uiOptions: UIOptionsType; +}) => { + const parentHandleId = uiOptions.handleId; + + if (isNormal({ uiOptions })) { + return id; + } + + if (isPartOfAnyOf({ uiOptions })) { + return parentHandleId + ANY_OF_FLAG; + } + + if (isKeyValuePair({ schema })) { + const key = id.split("_%_").at(-1); + let prefix = ""; + if (parentHandleId) { + prefix = parentHandleId; + } else { + prefix = id.split("_%_").slice(0, -1).join("_%_"); + } + + const handleId = `${prefix}_#_${key}`; + return handleId + KEY_PAIR_FLAG; + } + + if (isArrayItem({ uiOptions })) { + const index = id.split("_%_").at(-1); + const prefix = id.split("_%_").slice(0, -1).join("_%_"); + const handleId = `${prefix}_$_${index}`; + return handleId + ARRAY_ITEM_FLAG; + } + + if (isObjectProperty({ uiOptions, schema })) { + const key = id.split("_%_").at(-1); + const prefix = id.split("_%_").slice(0, -1).join("_%_"); + const handleId = `${prefix}_@_${key}`; + return handleId + OBJECT_FLAG; + } + return parentHandleId; +}; + +export function isCredentialFieldSchema(schema: any): boolean { + return ( + typeof schema === "object" && + schema !== null && + "credentials_provider" in schema + ); +} + +export function parseHandleIdToPath(handleId: string): PathSegment[] { + const cleanedId = cleanUpHandleId(handleId); + const segments: PathSegment[] = []; + const parts = cleanedId.split(/(_#_|_@_|_\$_|\.)/); + + let currentType: "property" | "item" | "additional" | "normal" = "normal"; + + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + + if (part === "_#_") { + currentType = "additional"; + } else if (part === "_@_") { + currentType = "property"; + } else if (part === "_$_") { + currentType = "item"; + } else if (part === ".") { + currentType = "normal"; + } else if (part) { + const isNumeric = /^\d+$/.test(part); + if (currentType === "item" && isNumeric) { + segments.push({ + key: part, + type: "item", + index: parseInt(part, 10), + }); + } else { + segments.push({ + key: part, + type: currentType, + }); + } + currentType = "normal"; + } + } + + return segments; +} + +/** + * Ensure a path exists in an object, creating intermediate objects/arrays as needed + * Returns true if any modifications were made + */ +export function ensurePathExists( + obj: Record, + segments: PathSegment[], +): boolean { + if (segments.length === 0) return false; + + let current = obj; + let modified = false; + + for (let i = 0; i < segments.length; i++) { + const segment = segments[i]; + const isLast = i === segments.length - 1; + const nextSegment = segments[i + 1]; + + const getDefaultValue = () => { + if (isLast) { + return ""; + } + if (nextSegment?.type === "item") { + return []; + } + return {}; + }; + + if (segment.type === "item" && segment.index !== undefined) { + if (!Array.isArray(current)) { + return modified; + } + + while (current.length <= segment.index) { + current.push(isLast ? "" : {}); + modified = true; + } + + if (!isLast) { + if ( + current[segment.index] === undefined || + current[segment.index] === null + ) { + current[segment.index] = getDefaultValue(); + modified = true; + } + current = current[segment.index]; + } + } else { + if (!(segment.key in current)) { + current[segment.key] = getDefaultValue(); + modified = true; + } else if (!isLast && current[segment.key] === undefined) { + current[segment.key] = getDefaultValue(); + modified = true; + } + + if (!isLast) { + current = current[segment.key]; + } + } + } + + return modified; +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/index.ts new file mode 100644 index 0000000000..c25af0b231 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/index.ts @@ -0,0 +1,3 @@ +export { FormRenderer } from "./FormRenderer"; +export { default as Form } from "./registry"; +export type { ExtendedFormContextType } from "./types"; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/registry/Form.tsx b/autogpt_platform/frontend/src/components/renderers/InputRenderer/registry/Form.tsx new file mode 100644 index 0000000000..5bf720a994 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/registry/Form.tsx @@ -0,0 +1,23 @@ +import { ComponentType } from "react"; +import { FormProps, withTheme, ThemeProps } from "@rjsf/core"; +import { + generateBaseFields, + generateBaseTemplates, + generateBaseWidgets, +} from "../base/base-registry"; +import { generateCustomFields } from "../custom/custom-registry"; + +export function generateForm(): ComponentType { + const theme: ThemeProps = { + templates: generateBaseTemplates(), + widgets: generateBaseWidgets(), + fields: { + ...generateBaseFields(), + ...generateCustomFields(), + }, + }; + + return withTheme(theme); +} + +export default generateForm(); diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/registry/index.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/registry/index.ts new file mode 100644 index 0000000000..641c2a97d6 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/registry/index.ts @@ -0,0 +1,10 @@ +export { default, generateForm } from "./Form"; +export { + generateBaseFields, + generateBaseTemplates, + generateBaseWidgets, +} from "../base/base-registry"; +export { + generateCustomFields, + findCustomFieldId, +} from "../custom/custom-registry"; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/registry/types.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/registry/types.ts new file mode 100644 index 0000000000..cab84afcc3 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/registry/types.ts @@ -0,0 +1,7 @@ +import { BlockUIType } from "@/app/(platform)/build/components/types"; + +export type ExtraContext = { + nodeId?: string; + uiType?: BlockUIType; + size?: "small" | "medium" | "large"; +}; diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/types.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/types.ts new file mode 100644 index 0000000000..af2e8b7866 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/types.ts @@ -0,0 +1,15 @@ +import { BlockUIType } from "@/lib/autogpt-server-api/types"; +import { FormContextType } from "@rjsf/utils"; + +export interface ExtendedFormContextType extends FormContextType { + nodeId?: string; + uiType?: BlockUIType; + showHandles?: boolean; + size?: "small" | "medium" | "large"; +} + +export type PathSegment = { + key: string; + type: "property" | "item" | "additional" | "normal"; + index?: number; +}; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/utils/custom-validator.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/utils/custom-validator.ts similarity index 100% rename from autogpt_platform/frontend/src/components/renderers/input-renderer/utils/custom-validator.ts rename to autogpt_platform/frontend/src/components/renderers/InputRenderer/utils/custom-validator.ts diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/utils/helpers.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/utils/helpers.ts similarity index 100% rename from autogpt_platform/frontend/src/components/renderers/input-renderer/utils/helpers.ts rename to autogpt_platform/frontend/src/components/renderers/InputRenderer/utils/helpers.ts diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/utils/input-schema-pre-processor.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/utils/input-schema-pre-processor.ts similarity index 91% rename from autogpt_platform/frontend/src/components/renderers/input-renderer/utils/input-schema-pre-processor.ts rename to autogpt_platform/frontend/src/components/renderers/InputRenderer/utils/input-schema-pre-processor.ts index ffbcbf52b2..dad95251ed 100644 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/utils/input-schema-pre-processor.ts +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/utils/input-schema-pre-processor.ts @@ -1,9 +1,11 @@ import { RJSFSchema } from "@rjsf/utils"; +import { findCustomFieldId } from "../custom/custom-registry"; /** * Pre-processes the input schema to ensure all properties have a type defined. * If a property doesn't have a type, it assigns a union of all supported JSON Schema types. */ + export function preprocessInputSchema(schema: RJSFSchema): RJSFSchema { if (!schema || typeof schema !== "object") { return schema; @@ -19,6 +21,12 @@ export function preprocessInputSchema(schema: RJSFSchema): RJSFSchema { if (property && typeof property === "object") { const processedProperty = { ...property }; + // adding $id for custom field + const customFieldId = findCustomFieldId(processedProperty); + if (customFieldId) { + processedProperty.$id = customFieldId; + } + // Only add type if no type is defined AND no anyOf/oneOf/allOf is present if ( !processedProperty.type && @@ -32,7 +40,7 @@ export function preprocessInputSchema(schema: RJSFSchema): RJSFSchema { { type: "integer" }, { type: "boolean" }, { type: "array", items: { type: "string" } }, - { type: "object" }, + { type: "object", title: "Object", additionalProperties: true }, { type: "null" }, ]; } diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/utils/rjsf-utils.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/utils/rjsf-utils.ts new file mode 100644 index 0000000000..365058cebd --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/utils/rjsf-utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/autogpt_platform/frontend/src/components/renderers/InputRenderer/utils/schema-utils.ts b/autogpt_platform/frontend/src/components/renderers/InputRenderer/utils/schema-utils.ts new file mode 100644 index 0000000000..b1cfd37967 --- /dev/null +++ b/autogpt_platform/frontend/src/components/renderers/InputRenderer/utils/schema-utils.ts @@ -0,0 +1,35 @@ +import { getUiOptions, RJSFSchema, UiSchema } from "@rjsf/utils"; + +export function isAnyOfSchema(schema: RJSFSchema | undefined): boolean { + return Array.isArray(schema?.anyOf) && schema!.anyOf.length > 0; +} + +export const isAnyOfChild = ( + uiSchema: UiSchema | undefined, +): boolean => { + const uiOptions = getUiOptions(uiSchema); + return uiOptions.label === false; +}; + +export function isOptionalType(schema: RJSFSchema | undefined): { + isOptional: boolean; + type?: any; +} { + if ( + !Array.isArray(schema?.anyOf) || + schema!.anyOf.length !== 2 || + !schema!.anyOf.some((opt: any) => opt.type === "null") + ) { + return { isOptional: false }; + } + + const nonNullType = schema!.anyOf?.find((opt: any) => opt.type !== "null"); + + return { + isOptional: true, + type: nonNullType, + }; +} +export function isAnyOfSelector(name: string) { + return name.includes("anyof_select"); +} diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/ARCHITECTURE_INPUT_RENDERER.md b/autogpt_platform/frontend/src/components/renderers/input-renderer/ARCHITECTURE_INPUT_RENDERER.md deleted file mode 100644 index 7ae3d2b546..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/ARCHITECTURE_INPUT_RENDERER.md +++ /dev/null @@ -1,938 +0,0 @@ -# Input-Renderer Architecture Documentation - -## Overview - -The Input-Renderer is a **JSON Schema-based form generation system** built on top of **React JSON Schema Form (RJSF)**. It dynamically creates form inputs for block nodes in the FlowEditor based on JSON schemas defined in the backend. - -This system allows blocks to define their input requirements declaratively, and the frontend automatically generates appropriate UI components. - ---- - -## High-Level Architecture - -``` -┌─────────────────────────────────────────────────────────┐ -│ FormRenderer │ -│ (Entry point, wraps RJSF Form) │ -└─────────────────────┬───────────────────────────────────┘ - │ - ┌─────────▼─────────┐ - │ RJSF Core │ - │ │ - └───────┬───────────┘ - │ - ┌───────────┼───────────┬──────────────┐ - │ │ │ │ - ┌────▼────┐ ┌───▼────┐ ┌────▼─────┐ ┌────▼────┐ - │ Fields │ │Templates│ │ Widgets │ │ Schemas │ - └─────────┘ └─────────┘ └──────────┘ └─────────┘ - │ │ │ │ - │ │ │ │ - Handles Wrapper Actual JSON Schema - complex layouts input (from backend) - types & labels components -``` - ---- - -## What is RJSF (React JSON Schema Form)? - -**RJSF** is a library that generates React forms from JSON Schema definitions. It follows a specific hierarchy to render forms: - -### **RJSF Rendering Flow:** - -``` -1. JSON Schema (defines data structure) - ↓ -2. Schema Field (decides which Field component to use) - ↓ -3. Field Component (handles specific type logic) - ↓ -4. Field Template (wraps field with label, description) - ↓ -5. Widget (actual input element - TextInput, Select, etc.) -``` - -### **Example Flow:** - -```json -// JSON Schema -{ - "type": "object", - "properties": { - "name": { - "type": "string", - "title": "Name" - } - } -} -``` - -**Becomes:** - -``` -SchemaField (detects "string" type) - ↓ -StringField (default RJSF field) - ↓ -FieldTemplate (adds label "Name") - ↓ -TextWidget (renders ) -``` - ---- - -## Core Components of Input-Renderer - -### 1. **FormRenderer** (`FormRenderer.tsx`) - -The main entry point that wraps RJSF `` component. - -```typescript -export const FormRenderer = ({ - jsonSchema, // JSON Schema from backend - handleChange, // Callback when form changes - uiSchema, // UI customization - initialValues, // Pre-filled values - formContext, // Extra context (nodeId, uiType, etc.) -}: FormRendererProps) => { - const preprocessedSchema = preprocessInputSchema(jsonSchema); - - return ( - - ); -}; -``` - -**Key Props:** - -- **`fields`** - Custom components for complex types (anyOf, credentials, objects) -- **`templates`** - Layout wrappers (FieldTemplate, ArrayFieldTemplate) -- **`widgets`** - Actual input components (TextInput, Select, FileWidget) -- **`formContext`** - Shared data (nodeId, showHandles, size) - ---- - -### 2. **Schema Pre-Processing** (`utils/input-schema-pre-processor.ts`) - -Before rendering, schemas are transformed to ensure RJSF compatibility. - -**Purpose:** - -- Add missing `type` fields (prevents RJSF errors) -- Recursively process nested objects and arrays -- Normalize inconsistent schemas from backend - -**Example:** - -```typescript -// Backend schema (missing type) -{ - "properties": { - "value": {} // No type defined! - } -} - -// After pre-processing -{ - "properties": { - "value": { - "anyOf": [ - { "type": "string" }, - { "type": "number" }, - { "type": "boolean" }, - // ... all possible types - ] - } - } -} -``` - -**Why?** RJSF requires explicit types. Without this, it would crash or render incorrectly. - ---- - -## The Three Pillars: Fields, Templates, Widgets - -### **A. Fields** (`fields/`) - -Fields handle **complex type logic** that goes beyond simple inputs. - -**Registered Fields:** - -```typescript -export const fields: RegistryFieldsType = { - AnyOfField: AnyOfField, // Handles anyOf/oneOf - credentials: CredentialsField, // OAuth/API key handling - ObjectField: ObjectField, // Free-form objects -}; -``` - -#### **1. AnyOfField** (`fields/AnyOfField/AnyOfField.tsx`) - -Handles schemas with multiple possible types (union types). - -**When Used:** - -```json -{ - "anyOf": [{ "type": "string" }, { "type": "number" }, { "type": "boolean" }] -} -``` - -**Rendering:** - -``` -┌─────────────────────────────────────┐ -│ Parameter Name (string) ▼ │ ← Type selector dropdown -├─────────────────────────────────────┤ -│ [Text Input] │ ← Widget for selected type -└─────────────────────────────────────┘ -``` - -**Features:** - -- Type selector dropdown -- Nullable types (with toggle switch) -- Recursive rendering (can contain arrays, objects) -- Connection-aware (hides input when connected) - -**Special Case: Nullable Types** - -```json -{ - "anyOf": [{ "type": "string" }, { "type": "null" }] -} -``` - -**Renders as:** - -``` -┌─────────────────────────────────────┐ -│ Parameter Name (string | null) [✓] │ ← Toggle switch -├─────────────────────────────────────┤ -│ [Text Input] (only if enabled) │ -└─────────────────────────────────────┘ -``` - ---- - -#### **2. CredentialsField** (`fields/CredentialField/CredentialField.tsx`) - -Handles authentication credentials (OAuth, API Keys, Passwords). - -**When Used:** - -```json -{ - "type": "object", - "credentials": { - "provider": "google", - "scopes": ["email", "profile"] - } -} -``` - -**Flow:** - -``` -1. Renders SelectCredential dropdown - ↓ -2. User selects existing credential OR clicks "Add New" - ↓ -3. Modal opens (OAuthModal/APIKeyModal/PasswordModal) - ↓ -4. User authorizes/enters credentials - ↓ -5. Credential saved to backend - ↓ -6. Dropdown shows selected credential -``` - -**Credential Types:** - -- **OAuth** - 3rd party authorization (Google, GitHub, etc.) -- **API Key** - Simple key-based auth -- **Password** - Username/password pairs - ---- - -#### **3. ObjectField** (`fields/ObjectField.tsx`) - -Handles free-form objects (key-value pairs). - -**When Used:** - -```json -{ - "type": "object", - "additionalProperties": true // Free-form -} -``` - -vs - -```json -{ - "type": "object", - "properties": { - "name": { "type": "string" } // Fixed schema - } -} -``` - -**Behavior:** - -- **Fixed schema** → Uses default RJSF rendering -- **Free-form** → Uses ObjectEditorWidget (JSON editor) - ---- - -### **B. Templates** (`templates/`) - -Templates control **layout and wrapping** of fields. - -#### **1. FieldTemplate** (`templates/FieldTemplate.tsx`) - -Wraps every field with label, type indicator, and connection handle. - -**Rendering Structure:** - -``` -┌────────────────────────────────────────┐ -│ ○ Label (type) ⓘ │ ← Handle + Label + Type + Info icon -├────────────────────────────────────────┤ -│ [Actual Input Widget] │ ← The input itself -└────────────────────────────────────────┘ -``` - -**Responsibilities:** - -- Shows/hides input based on connection status -- Renders connection handle (NodeHandle) -- Displays type information -- Shows tooltip with description -- Handles "advanced" field visibility -- Formats credential field labels - -**Key Logic:** - -```typescript -// Hide input if connected -{(isAnyOf || !isConnected) && ( -
{children}
-)} - -// Show handle for most fields -{shouldShowHandle && ( - -)} -``` - -**Context-Aware Behavior:** - -- Inside `AnyOfField` → No handle (parent handles it) -- Credential field → Special label formatting -- Array item → Uses parent handle -- INPUT/OUTPUT/WEBHOOK blocks → Different handle positioning - ---- - -#### **2. ArrayFieldTemplate** (`templates/ArrayFieldTemplate.tsx`) - -Wraps array fields to use custom ArrayEditorWidget. - -**Simple Wrapper:** - -```typescript -function ArrayFieldTemplate(props: ArrayFieldTemplateProps) { - const { items, canAdd, onAddClick, nodeId } = props; - - return ( - - ); -} -``` - ---- - -### **C. Widgets** (`widgets/`) - -Widgets are **actual input components** - the final rendered HTML elements. - -**Registered Widgets:** - -```typescript -export const widgets: RegistryWidgetsType = { - TextWidget: TextInputWidget, // - SelectWidget: SelectWidget, // - ↓ -8. User types "Hello" - ↓ -9. onChange callback fires - ↓ -10. FormCreator updates nodeStore.updateNodeData() -``` - ---- - -### **Example: Rendering AnyOf Field** - -```json -// Backend Schema -{ - "anyOf": [{ "type": "string" }, { "type": "number" }], - "title": "Value" -} -``` - -**Rendering Flow:** - -``` -1. RJSF detects "anyOf" - ↓ -2. Uses AnyOfField (custom field) - ↓ -3. AnyOfField renders: - ┌─────────────────────────────────┐ - │ ○ Value (string) ▼ │ ← Self-managed handle & selector - ├─────────────────────────────────┤ - │ [Text Input] │ ← Recursively renders SchemaField - └─────────────────────────────────┘ - ↓ -4. User changes type to "number" - ↓ -5. AnyOfField re-renders with NumberWidget - ↓ -6. User enters "42" - ↓ -7. onChange({ type: "number", value: 42 }) -``` - -**Key Point:** AnyOfField **does NOT use FieldTemplate** for itself. It manages its own handle and label to avoid duplication. But it **recursively calls SchemaField** for the selected type, which may use FieldTemplate. - ---- - -### **Example: Rendering Array Field** - -```json -// Backend Schema -{ - "type": "array", - "items": { - "type": "string" - }, - "title": "Tags" -} -``` - -**Rendering Flow:** - -``` -1. RJSF detects "array" type - ↓ -2. Uses ArrayFieldTemplate - ↓ -3. ArrayFieldTemplate passes to ArrayEditorWidget - ↓ -4. ArrayEditorWidget renders: - ┌─────────────────────────────────┐ - │ ○ Tag 1 [Text Input] [X] │ ← Each item wrapped in context - │ ○ Tag 2 [Text Input] [X] │ - │ [+ Add Item] │ - └─────────────────────────────────┘ - ↓ -5. Each item wrapped in ArrayEditorContext - ↓ -6. FieldTemplate reads context: - - isArrayItem = true - - Uses arrayFieldHandleId instead of own handle - ↓ -7. TextWidget renders for each item -``` - ---- - -## Hierarchy: What Comes First? - -This is the **order of execution** from schema to rendered input: - -``` -1. JSON Schema (from backend) - ↓ -2. preprocessInputSchema() (normalization) - ↓ -3. RJSF (library entry point) - ↓ -4. SchemaField (RJSF internal - decides which field) - ↓ -5. Field Component (AnyOfField, CredentialsField, or default) - ↓ -6. Template (FieldTemplate or ArrayFieldTemplate) - ↓ -7. Widget (TextWidget, SelectWidget, etc.) - ↓ -8. Actual HTML (, onChange(e.target.value)} />; -}; -``` - -2. Register in `widgets/index.ts`: - -```typescript -export const widgets: RegistryWidgetsType = { - // ... - MyCustomWidget: MyWidget, -}; -``` - -3. Use in uiSchema or schema format: - -```json -{ - "type": "string", - "format": "my-custom-format" // RJSF maps format → widget -} -``` - ---- - -### **Adding a New Field** - -1. Create field component in `fields/`: - -```typescript -export const MyField = ({ schema, formData, onChange, ...props }: FieldProps) => { - // Custom logic here - return
...
; -}; -``` - -2. Register in `fields/index.ts`: - -```typescript -export const fields: RegistryFieldsType = { - // ... - MyField: MyField, -}; -``` - -3. RJSF uses it based on schema structure (e.g., custom keyword). - ---- - -## Integration with FlowEditor - -``` -CustomNode - ↓ -FormCreator - ↓ -FormRenderer ← YOU ARE HERE - ↓ -RJSF - ↓ -(Fields, Templates, Widgets) - ↓ -User Input - ↓ -onChange callback - ↓ -FormCreator.handleChange() - ↓ -nodeStore.updateNodeData(nodeId, { hardcodedValues }) - ↓ -historyStore.pushState() (undo/redo) -``` - ---- - -## Debugging Tips - -### **Field Not Rendering** - -- Check if `preprocessInputSchema()` is handling it correctly -- Verify schema has `type` field -- Check RJSF console for validation errors - -### **Widget Wrong Type** - -- Check schema `type` and `format` fields -- Verify widget is registered in `widgets/index.ts` -- Check if custom field is overriding default behavior - -### **Handle Not Appearing** - -- Check `showHandles` in formContext -- Verify not inside `fromAnyOf` context -- Check if field is credential or array item - -### **Value Not Saving** - -- Verify `onChange` callback is firing -- Check `handleChange` in FormCreator -- Look for console errors in `updateNodeData` - ---- - -## Summary - -The Input-Renderer is a sophisticated form system that: - -1. **Uses RJSF** as the foundation for JSON Schema → React forms -2. **Extends RJSF** with custom Fields, Templates, and Widgets -3. **Integrates** with FlowEditor's connection system -4. **Handles** complex types (anyOf, credentials, free-form objects) -5. **Provides** connection-aware, type-safe input rendering - -**Key Hierarchy (What Comes First):** - -``` -JSON Schema - → Pre-processing - → RJSF Form - → SchemaField (RJSF internal) - → Field (AnyOfField, CredentialsField, etc.) - → Template (FieldTemplate, ArrayFieldTemplate) - → Widget (TextWidget, SelectWidget, etc.) - → HTML Element -``` - -**Mental Model:** - -- **Fields** = Smart logic layers (type selection, OAuth flows) -- **Templates** = Layout wrappers (handles, labels, tooltips) -- **Widgets** = Actual inputs (text boxes, dropdowns) - -**Integration Point:** - -- FormRenderer receives schema from `node.data.inputSchema` -- User edits form → `onChange` → `nodeStore.updateNodeData()` -- Values saved as `node.data.hardcodedValues` diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/AnyOfField/AnyOfField.tsx b/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/AnyOfField/AnyOfField.tsx deleted file mode 100644 index 79fa15304d..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/AnyOfField/AnyOfField.tsx +++ /dev/null @@ -1,232 +0,0 @@ -import React from "react"; -import { FieldProps, RJSFSchema } from "@rjsf/utils"; - -import { Text } from "@/components/atoms/Text/Text"; -import { Switch } from "@/components/atoms/Switch/Switch"; -import { Select } from "@/components/atoms/Select/Select"; -import { - InputType, - mapJsonSchemaTypeToInputType, -} from "@/app/(platform)/build/components/FlowEditor/nodes/helpers"; - -import { InfoIcon } from "@phosphor-icons/react"; -import { useAnyOfField } from "./useAnyOfField"; -import NodeHandle from "@/app/(platform)/build/components/FlowEditor/handlers/NodeHandle"; -import { useEdgeStore } from "@/app/(platform)/build/stores/edgeStore"; -import { generateHandleId } from "@/app/(platform)/build/components/FlowEditor/handlers/helpers"; -import { getTypeDisplayInfo } from "@/app/(platform)/build/components/FlowEditor/nodes/helpers"; -import merge from "lodash/merge"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/components/atoms/Tooltip/BaseTooltip"; -import { cn } from "@/lib/utils"; -import { BlockUIType } from "@/app/(platform)/build/components/types"; - -type TypeOption = { - type: string; - title: string; - index: number; - format?: string; - enum?: any[]; - secret?: boolean; - schema: RJSFSchema; -}; - -export const AnyOfField = ({ - schema, - formData, - onChange, - name, - idSchema, - formContext, - registry, - uiSchema, - disabled, - onBlur, - onFocus, -}: FieldProps) => { - const handleId = - formContext.uiType === BlockUIType.AGENT - ? (idSchema.$id ?? "") - .split("_") - .filter((p) => p !== "root" && p !== "properties" && p.length > 0) - .join("_") || "" - : generateHandleId(idSchema.$id ?? ""); - - const updatedFormContexrt = { ...formContext, fromAnyOf: true }; - - const { nodeId, showHandles = true } = updatedFormContexrt; - const { isInputConnected } = useEdgeStore(); - const isConnected = showHandles ? isInputConnected(nodeId, handleId) : false; - const { - isNullableType, - nonNull, - selectedType, - handleTypeChange, - handleNullableToggle, - handleValueChange, - currentTypeOption, - isEnabled, - typeOptions, - } = useAnyOfField(schema, formData, onChange); - - const renderInput = (typeOption: TypeOption) => { - const optionSchema = (typeOption.schema || { - type: typeOption.type, - format: typeOption.format, - secret: typeOption.secret, - enum: typeOption.enum, - }) as RJSFSchema; - const inputType = mapJsonSchemaTypeToInputType(optionSchema); - - // Help us to tell the field under the anyOf field that you are a part of anyOf field. - // We can't use formContext in this case that's why we are using this. - // We could use context api here, but i think it's better to keep it simple. - const uiSchemaFromAnyOf = merge({}, uiSchema, { - "ui:options": { fromAnyOf: true }, - }); - - // We are using SchemaField to render the field recursively. - if (inputType === InputType.ARRAY_EDITOR) { - const SchemaField = registry.fields.SchemaField; - return ( -
- -
- ); - } - - const SchemaField = registry.fields.SchemaField; - return ( -
- -
- ); - }; - - // I am doing this, because we need different UI for optional types. - if (isNullableType && nonNull) { - const { displayType, colorClass } = getTypeDisplayInfo(nonNull); - - return ( -
-
-
- {showHandles && ( - - )} - - {schema.title || name.charAt(0).toUpperCase() + name.slice(1)} - - - ({displayType} | null) - -
- {!isConnected && ( - - )} -
-
- {!isConnected && isEnabled && renderInput(nonNull)} -
-
- ); - } - - return ( -
-
- {showHandles && ( - - )} - - {schema.title || name.charAt(0).toUpperCase() + name.slice(1)} - - {!isConnected && ( - - - -
- ); -}; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/APIKeyCredentialModal/APIKeyCredentialModal.tsx b/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/APIKeyCredentialModal/APIKeyCredentialModal.tsx deleted file mode 100644 index 5a51e1e36c..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/APIKeyCredentialModal/APIKeyCredentialModal.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { Input } from "@/components/atoms/Input/Input"; -import { Button } from "@/components/atoms/Button/Button"; -import { Dialog } from "@/components/molecules/Dialog/Dialog"; -import { - Form, - FormDescription, - FormField, -} from "@/components/__legacy__/ui/form"; -import { BlockIOCredentialsSubSchema } from "@/lib/autogpt-server-api/types"; // we need to find a way to replace it with autogenerated types -import { useAPIKeyCredentialsModal } from "./useAPIKeyCredentialsModal"; -import { toDisplayName } from "../../helpers"; -import { KeyIcon } from "@phosphor-icons/react"; -import { Text } from "@/components/atoms/Text/Text"; - -type Props = { - schema: BlockIOCredentialsSubSchema; - provider: string; -}; - -export function APIKeyCredentialsModal({ schema, provider }: Props) { - const { form, schemaDescription, onSubmit, isOpen, setIsOpen } = - useAPIKeyCredentialsModal({ schema, provider }); - - return ( - <> - { - if (!isOpen) setIsOpen(false); - }, - }} - onClose={() => setIsOpen(false)} - styling={{ - maxWidth: "25rem", - }} - > - - {schemaDescription && ( -

{schemaDescription}

- )} - - - - ( - <> - - Required scope(s) for this block:{" "} - {schema.credentials_scopes?.map((s, i, a) => ( - - {s} - {i < a.length - 1 && ", "} - - ))} - - ) : null - } - {...field} - /> - - )} - /> - ( - - )} - /> - ( - - )} - /> - - - -
-
- - - ); -} diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/APIKeyCredentialModal/useAPIKeyCredentialsModal.ts b/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/APIKeyCredentialModal/useAPIKeyCredentialsModal.ts deleted file mode 100644 index 499cacf1ce..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/APIKeyCredentialModal/useAPIKeyCredentialsModal.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { z } from "zod"; -import { useForm, type UseFormReturn } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { BlockIOCredentialsSubSchema } from "@/lib/autogpt-server-api/types"; -import { - getGetV1ListCredentialsQueryKey, - usePostV1CreateCredentials, -} from "@/app/api/__generated__/endpoints/integrations/integrations"; -import { useToast } from "@/components/molecules/Toast/use-toast"; -import { APIKeyCredentials } from "@/app/api/__generated__/models/aPIKeyCredentials"; -import { useQueryClient } from "@tanstack/react-query"; -import { useState } from "react"; - -export type APIKeyFormValues = { - apiKey: string; - title: string; - expiresAt?: string; -}; - -type useAPIKeyCredentialsModalType = { - schema: BlockIOCredentialsSubSchema; - provider: string; -}; - -export function useAPIKeyCredentialsModal({ - schema, - provider, -}: useAPIKeyCredentialsModalType): { - form: UseFormReturn; - schemaDescription?: string; - onSubmit: (values: APIKeyFormValues) => Promise; - isOpen: boolean; - setIsOpen: (isOpen: boolean) => void; -} { - const { toast } = useToast(); - const [isOpen, setIsOpen] = useState(false); - const queryClient = useQueryClient(); - - const { mutateAsync: createCredentials } = usePostV1CreateCredentials({ - mutation: { - onSuccess: async () => { - form.reset(); - setIsOpen(false); - toast({ - title: "Success", - description: "Credentials created successfully", - variant: "default", - }); - - await queryClient.refetchQueries({ - queryKey: getGetV1ListCredentialsQueryKey(), - }); - }, - onError: () => { - toast({ - title: "Error", - description: "Failed to create credentials.", - variant: "destructive", - }); - }, - }, - }); - - const formSchema = z.object({ - apiKey: z.string().min(1, "API Key is required"), - title: z.string().min(1, "Name is required"), - expiresAt: z.string().optional(), - }); - - const form = useForm({ - resolver: zodResolver(formSchema), - defaultValues: { - apiKey: "", - title: "", - expiresAt: "", - }, - }); - - async function onSubmit(values: APIKeyFormValues) { - const expiresAt = values.expiresAt - ? new Date(values.expiresAt).getTime() / 1000 - : undefined; - - createCredentials({ - provider: provider, - data: { - provider: provider, - type: "api_key", - api_key: values.apiKey, - title: values.title, - expires_at: expiresAt, - } as APIKeyCredentials, - }); - } - - return { - form, - schemaDescription: schema.description, - onSubmit, - isOpen, - setIsOpen, - }; -} diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/HostScopedCredentialsModal/HostScopedCredentialsModal.tsx b/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/HostScopedCredentialsModal/HostScopedCredentialsModal.tsx deleted file mode 100644 index 3264fca76f..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/HostScopedCredentialsModal/HostScopedCredentialsModal.tsx +++ /dev/null @@ -1,185 +0,0 @@ -import { Input } from "@/components/atoms/Input/Input"; -import { Button } from "@/components/atoms/Button/Button"; -import { Dialog } from "@/components/molecules/Dialog/Dialog"; -import { - Form, - FormDescription, - FormField, - FormLabel, -} from "@/components/__legacy__/ui/form"; -import { BlockIOCredentialsSubSchema } from "@/lib/autogpt-server-api/types"; -import { useHostScopedCredentialsModal } from "./useHostScopedCredentialsModal"; -import { toDisplayName } from "../../helpers"; -import { GlobeIcon, PlusIcon, TrashIcon } from "@phosphor-icons/react"; -import { Text } from "@/components/atoms/Text/Text"; - -type Props = { - schema: BlockIOCredentialsSubSchema; - provider: string; - discriminatorValue?: string; -}; - -export function HostScopedCredentialsModal({ - schema, - provider, - discriminatorValue, -}: Props) { - const { - form, - schemaDescription, - onSubmit, - isOpen, - setIsOpen, - headerPairs, - addHeaderPair, - removeHeaderPair, - updateHeaderPair, - currentHost, - } = useHostScopedCredentialsModal({ schema, provider, discriminatorValue }); - - return ( - <> - { - if (!isOpen) setIsOpen(false); - }, - }} - onClose={() => setIsOpen(false)} - styling={{ - maxWidth: "38rem", - }} - > - -
- {schemaDescription && ( -

{schemaDescription}

- )} - -
- - ( - - )} - /> - - ( - - )} - /> - -
- Headers - - Add sensitive headers (like Authorization, X-API-Key) that - should be automatically included in requests to the - specified host. - - - {headerPairs.map((pair, index) => ( -
- - updateHeaderPair(index, "key", e.target.value) - } - /> - - - updateHeaderPair(index, "value", e.target.value) - } - /> - -
- ))} - - -
- - - - -
-
-
- - - ); -} diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/HostScopedCredentialsModal/useHostScopedCredentialsModal.ts b/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/HostScopedCredentialsModal/useHostScopedCredentialsModal.ts deleted file mode 100644 index 066bc05b51..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/HostScopedCredentialsModal/useHostScopedCredentialsModal.ts +++ /dev/null @@ -1,167 +0,0 @@ -import { z } from "zod"; -import { useForm, type UseFormReturn } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { BlockIOCredentialsSubSchema } from "@/lib/autogpt-server-api/types"; -import { - getGetV1ListCredentialsQueryKey, - usePostV1CreateCredentials, -} from "@/app/api/__generated__/endpoints/integrations/integrations"; -import { useToast } from "@/components/molecules/Toast/use-toast"; -import { HostScopedCredentialsInput } from "@/app/api/__generated__/models/hostScopedCredentialsInput"; -import { useQueryClient } from "@tanstack/react-query"; -import { useState } from "react"; -import { getHostFromUrl } from "@/lib/utils/url"; - -export type HeaderPair = { - key: string; - value: string; -}; - -export type HostScopedFormValues = { - host: string; - title?: string; -}; - -type UseHostScopedCredentialsModalType = { - schema: BlockIOCredentialsSubSchema; - provider: string; - discriminatorValue?: string; -}; - -export function useHostScopedCredentialsModal({ - schema, - provider, - discriminatorValue, -}: UseHostScopedCredentialsModalType): { - form: UseFormReturn; - schemaDescription?: string; - onSubmit: (values: HostScopedFormValues) => Promise; - isOpen: boolean; - setIsOpen: (isOpen: boolean) => void; - headerPairs: HeaderPair[]; - addHeaderPair: () => void; - removeHeaderPair: (index: number) => void; - updateHeaderPair: ( - index: number, - field: "key" | "value", - value: string, - ) => void; - currentHost: string | null; -} { - const { toast } = useToast(); - const [isOpen, setIsOpen] = useState(false); - const [headerPairs, setHeaderPairs] = useState([ - { key: "", value: "" }, - ]); - const queryClient = useQueryClient(); - - // Get current host from discriminatorValue (URL field) - const currentHost = discriminatorValue - ? getHostFromUrl(discriminatorValue) - : null; - - const { mutateAsync: createCredentials } = usePostV1CreateCredentials({ - mutation: { - onSuccess: async () => { - form.reset(); - setHeaderPairs([{ key: "", value: "" }]); - setIsOpen(false); - toast({ - title: "Success", - description: "Host-scoped credentials created successfully", - variant: "default", - }); - - await queryClient.refetchQueries({ - queryKey: getGetV1ListCredentialsQueryKey(), - }); - }, - onError: () => { - toast({ - title: "Error", - description: "Failed to create host-scoped credentials.", - variant: "destructive", - }); - }, - }, - }); - - const formSchema = z.object({ - host: z.string().min(1, "Host is required"), - title: z.string().optional().default(""), - }); - - const form = useForm({ - resolver: zodResolver(formSchema), - defaultValues: { - host: currentHost || "", - title: currentHost || "Manual Entry", - }, - }); - - // Update form values when modal opens and discriminatorValue changes - const handleSetIsOpen = (open: boolean) => { - if (open && currentHost) { - form.setValue("host", currentHost); - form.setValue("title", currentHost); - } - setIsOpen(open); - }; - - const addHeaderPair = () => { - setHeaderPairs([...headerPairs, { key: "", value: "" }]); - }; - - const removeHeaderPair = (index: number) => { - if (headerPairs.length > 1) { - setHeaderPairs(headerPairs.filter((_, i) => i !== index)); - } - }; - - const updateHeaderPair = ( - index: number, - field: "key" | "value", - value: string, - ) => { - const newPairs = [...headerPairs]; - newPairs[index][field] = value; - setHeaderPairs(newPairs); - }; - - async function onSubmit(values: HostScopedFormValues) { - // Convert header pairs to object, filtering out empty pairs - const headers = headerPairs.reduce( - (acc, pair) => { - if (pair.key.trim() && pair.value.trim()) { - acc[pair.key.trim()] = pair.value.trim(); - } - return acc; - }, - {} as Record, - ); - - createCredentials({ - provider: provider, - data: { - provider: provider, - type: "host_scoped", - host: values.host, - title: values.title || values.host, - headers: headers, - } as HostScopedCredentialsInput, - }); - } - - return { - form, - schemaDescription: schema.description, - onSubmit, - isOpen, - setIsOpen: handleSetIsOpen, - headerPairs, - addHeaderPair, - removeHeaderPair, - updateHeaderPair, - currentHost, - }; -} diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/OAuthCredentialModal/OAuthCredentialModal.tsx b/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/OAuthCredentialModal/OAuthCredentialModal.tsx deleted file mode 100644 index 9824ace169..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/OAuthCredentialModal/OAuthCredentialModal.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { Button } from "@/components/atoms/Button/Button"; -import { Dialog } from "@/components/molecules/Dialog/Dialog"; -import { providerIcons, toDisplayName } from "../../helpers"; -import { useOAuthCredentialModal } from "./useOAuthCredentialModal"; -import { Text } from "@/components/atoms/Text/Text"; - -type OAuthCredentialModalProps = { - provider: string; -}; - -export const OAuthCredentialModal = ({ - provider, -}: OAuthCredentialModalProps) => { - const Icon = providerIcons[provider]; - const { handleOAuthLogin, loading, error, onClose, open, setOpen } = - useOAuthCredentialModal({ - provider, - }); - return ( - <> - { - if (!isOpen) setOpen(false); - }, - }} - onClose={onClose} - > - -

- Complete the sign-in process in the pop-up window. -
- Closing this dialog will cancel the sign-in process. -

-
-
- - - {error && ( -
- - {error as string} - -
- )} - - ); -}; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/OAuthCredentialModal/useOAuthCredentialModal.ts b/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/OAuthCredentialModal/useOAuthCredentialModal.ts deleted file mode 100644 index cf82ebca70..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/OAuthCredentialModal/useOAuthCredentialModal.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { - getGetV1ListCredentialsQueryKey, - useGetV1InitiateOauthFlow, - usePostV1ExchangeOauthCodeForTokens, -} from "@/app/api/__generated__/endpoints/integrations/integrations"; -import { LoginResponse } from "@/app/api/__generated__/models/loginResponse"; -import { useToast } from "@/components/molecules/Toast/use-toast"; -import { useQueryClient } from "@tanstack/react-query"; -import { useState } from "react"; - -type useOAuthCredentialModalProps = { - provider: string; - scopes?: string[]; -}; - -export type OAuthPopupResultMessage = { message_type: "oauth_popup_result" } & ( - | { - success: true; - code: string; - state: string; - } - | { - success: false; - message: string; - } -); - -export const useOAuthCredentialModal = ({ - provider, - scopes, -}: useOAuthCredentialModalProps) => { - const { toast } = useToast(); - - const [open, setOpen] = useState(false); - const [oAuthPopupController, setOAuthPopupController] = - useState(null); - const [oAuthError, setOAuthError] = useState(null); - const [isOAuth2FlowInProgress, setOAuth2FlowInProgress] = useState(false); - - const queryClient = useQueryClient(); - - const { - refetch: initiateOauthFlow, - isRefetching: isInitiatingOauthFlow, - isRefetchError: initiatingOauthFlowError, - } = useGetV1InitiateOauthFlow( - provider, - { - scopes: scopes?.join(","), - }, - { - query: { - enabled: false, - select: (res) => { - return res.data as LoginResponse; - }, - }, - }, - ); - - const { - mutateAsync: oAuthCallback, - isPending: isOAuthCallbackPending, - error: oAuthCallbackError, - } = usePostV1ExchangeOauthCodeForTokens({ - mutation: { - onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: getGetV1ListCredentialsQueryKey(), - }); - setOpen(false); - toast({ - title: "Success", - description: "Credential added successfully", - variant: "default", - }); - }, - }, - }); - - const handleOAuthLogin = async () => { - const { data } = await initiateOauthFlow(); - if (!data || !data.login_url || !data.state_token) { - toast({ - title: "Failed to initiate OAuth flow", - variant: "destructive", - }); - setOAuthError( - data && typeof data === "object" && "detail" in data - ? (data.detail as string) - : "Failed to initiate OAuth flow", - ); - return; - } - - setOpen(true); - setOAuth2FlowInProgress(true); - - const { login_url, state_token } = data; - - const popup = window.open(login_url, "_blank", "popup=true"); - - if (!popup) { - throw new Error( - "Failed to open popup window. Please allow popups for this site.", - ); - } - - const controller = new AbortController(); - setOAuthPopupController(controller); - - controller.signal.onabort = () => { - console.debug("OAuth flow aborted"); - popup.close(); - }; - - const handleMessage = async (e: MessageEvent) => { - console.debug("Message received:", e.data); - if ( - typeof e.data != "object" || - !("message_type" in e.data) || - e.data.message_type !== "oauth_popup_result" - ) { - console.debug("Ignoring irrelevant message"); - return; - } - - if (!e.data.success) { - console.error("OAuth flow failed:", e.data.message); - setOAuthError(`OAuth flow failed: ${e.data.message}`); - setOAuth2FlowInProgress(false); - return; - } - - if (e.data.state !== state_token) { - console.error("Invalid state token received"); - setOAuthError("Invalid state token received"); - setOAuth2FlowInProgress(false); - return; - } - - try { - console.debug("Processing OAuth callback"); - await oAuthCallback({ - provider, - data: { - code: e.data.code, - state_token: e.data.state, - }, - }); - - console.debug("OAuth callback processed successfully"); - } catch (error) { - console.error("Error in OAuth callback:", error); - setOAuthError( - `Error in OAuth callback: ${ - error instanceof Error ? error.message : String(error) - }`, - ); - } finally { - console.debug("Finalizing OAuth flow"); - setOAuth2FlowInProgress(false); - controller.abort("success"); - } - }; - - window.addEventListener("message", handleMessage, { - signal: controller.signal, - }); - - setTimeout( - () => { - console.debug("OAuth flow timed out"); - controller.abort("timeout"); - setOAuth2FlowInProgress(false); - setOAuthError("OAuth flow timed out"); - }, - 5 * 60 * 1000, - ); - }; - - const onClose = () => { - oAuthPopupController?.abort("canceled"); - setOpen(false); - }; - - return { - handleOAuthLogin, - loading: - isOAuth2FlowInProgress || isOAuthCallbackPending || isInitiatingOauthFlow, - error: oAuthError || initiatingOauthFlowError || oAuthCallbackError, - onClose, - open, - setOpen, - }; -}; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/PasswordCredentialModal/PasswordCredentialModal.tsx b/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/PasswordCredentialModal/PasswordCredentialModal.tsx deleted file mode 100644 index 9abaa8b328..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/PasswordCredentialModal/PasswordCredentialModal.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { Input } from "@/components/atoms/Input/Input"; -import { Button } from "@/components/atoms/Button/Button"; -import { Dialog } from "@/components/molecules/Dialog/Dialog"; -import { Form, FormField } from "@/components/__legacy__/ui/form"; -import { usePasswordCredentialModal } from "./usePasswordCredentialModal"; -import { toDisplayName } from "../../helpers"; -import { UserIcon } from "@phosphor-icons/react"; -import { Text } from "@/components/atoms/Text/Text"; - -type Props = { - provider: string; -}; - -export function PasswordCredentialsModal({ provider }: Props) { - const { form, onSubmit, open, setOpen } = usePasswordCredentialModal({ - provider, - }); - - return ( - <> - { - if (!isOpen) setOpen(false); - }, - }} - onClose={() => setOpen(false)} - styling={{ - maxWidth: "25rem", - }} - > - -
- - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - - - -
-
- - - ); -} diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/PasswordCredentialModal/usePasswordCredentialModal.ts b/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/PasswordCredentialModal/usePasswordCredentialModal.ts deleted file mode 100644 index e0a4e4a805..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/models/PasswordCredentialModal/usePasswordCredentialModal.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { useState } from "react"; -import z from "zod"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { - getGetV1ListCredentialsQueryKey, - usePostV1CreateCredentials, -} from "@/app/api/__generated__/endpoints/integrations/integrations"; -import { useToast } from "@/components/molecules/Toast/use-toast"; -import { useQueryClient } from "@tanstack/react-query"; - -type usePasswordCredentialModalType = { - provider: string; -}; - -export const usePasswordCredentialModal = ({ - provider, -}: usePasswordCredentialModalType) => { - const [open, setOpen] = useState(false); - const { toast } = useToast(); - const queryClient = useQueryClient(); - - const formSchema = z.object({ - username: z.string().min(1, "Username is required"), - password: z.string().min(1, "Password is required"), - title: z.string().min(1, "Name is required"), - }); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - username: "", - password: "", - title: "", - }, - }); - - const { mutateAsync: createCredentials } = usePostV1CreateCredentials({ - mutation: { - onSuccess: async () => { - form.reset(); - setOpen(false); - toast({ - title: "Success", - description: "Credentials created successfully", - variant: "default", - }); - - await queryClient.refetchQueries({ - queryKey: getGetV1ListCredentialsQueryKey(), - }); - }, - }, - }); - - async function onSubmit(values: z.infer) { - createCredentials({ - provider: provider, - data: { - provider: provider, - type: "user_password", - username: values.username, - password: values.password, - title: values.title, - }, - }); - } - - return { - form, - onSubmit, - open, - setOpen, - }; -}; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/useCredentialField.ts b/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/useCredentialField.ts deleted file mode 100644 index a90add8aeb..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/CredentialField/useCredentialField.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { useGetV1ListCredentials } from "@/app/api/__generated__/endpoints/integrations/integrations"; -import { CredentialsMetaResponse } from "@/app/api/__generated__/models/credentialsMetaResponse"; -import { BlockIOCredentialsSubSchema } from "@/lib/autogpt-server-api"; -import { - filterCredentialsByProvider, - getCredentialProviderFromSchema, - getDiscriminatorValue, -} from "./helpers"; -import { useNodeStore } from "@/app/(platform)/build/stores/nodeStore"; -import { useEffect, useRef } from "react"; -import { useShallow } from "zustand/react/shallow"; - -export const useCredentialField = ({ - credentialSchema, - formData, - nodeId, - onChange, -}: { - credentialSchema: BlockIOCredentialsSubSchema; // Here we are using manual typing, we need to fix it with automatic one - formData: Record; - nodeId: string; - onChange: (value: Record) => void; -}) => { - const previousProviderRef = useRef(null); - - // Fetch all the credentials from the backend - // We will save it in cache for 10 min, if user edits the credential, we will invalidate the cache - // Whenever user adds a block, we filter the credentials list and check if this block's provider is in the list - const { data: credentials, isLoading: isCredentialListLoading } = - useGetV1ListCredentials({ - query: { - refetchInterval: 10 * 60 * 1000, - select: (x) => { - return x.data as CredentialsMetaResponse[]; - }, - }, - }); - - const hardcodedValues = useNodeStore( - useShallow((state) => state.getHardCodedValues(nodeId)), - ); - - const credentialProvider = getCredentialProviderFromSchema( - hardcodedValues, - credentialSchema, - ); - - const discriminatorValue = getDiscriminatorValue( - hardcodedValues, - credentialSchema, - ); - - const supportsApiKey = credentialSchema.credentials_types.includes("api_key"); - const supportsOAuth2 = credentialSchema.credentials_types.includes("oauth2"); - const supportsUserPassword = - credentialSchema.credentials_types.includes("user_password"); - const supportsHostScoped = - credentialSchema.credentials_types.includes("host_scoped"); - - const { credentials: filteredCredentials, exists: credentialsExists } = - filterCredentialsByProvider( - credentials, - credentialProvider ?? "", - credentialSchema, - discriminatorValue, - ); - - const setCredential = (credentialId: string) => { - const selectedCredential = filteredCredentials.find( - (c) => c.id === credentialId, - ); - if (selectedCredential) { - onChange({ - ...formData, - id: selectedCredential.id, - provider: selectedCredential.provider, - title: selectedCredential.title, - type: selectedCredential.type, - }); - } - }; - - // This side effect is used to clear the hardcoded value in credential formData when the provider changes - useEffect(() => { - if (!credentialProvider) return; - // If provider has changed and we have a credential selected - if ( - previousProviderRef.current !== null && - previousProviderRef.current !== credentialProvider && - formData.id - ) { - // Check if the current credential belongs to the new provider - const currentCredentialBelongsToProvider = filteredCredentials.some( - (c) => c.id === formData.id, - ); - - // If not, clear the credential - if (!currentCredentialBelongsToProvider) { - onChange({ - id: "", - provider: "", - title: "", - type: "", - }); - } - } - previousProviderRef.current = credentialProvider; - }, [credentialProvider, formData.id, credentials, onChange]); - - // This side effect is used to auto-select the latest credential when none is selected [latest means last one in the list of credentials] - useEffect(() => { - if ( - !isCredentialListLoading && - filteredCredentials.length > 0 && - !formData.id && // No credential currently selected - credentialProvider // Provider is set - ) { - const latestCredential = - filteredCredentials[filteredCredentials.length - 1]; - setCredential(latestCredential.id); - } - }, [ - isCredentialListLoading, - filteredCredentials.length, - formData.id, - credentialProvider, - ]); - - return { - credentials: filteredCredentials, - isCredentialListLoading, - setCredential, - supportsApiKey, - supportsOAuth2, - supportsUserPassword, - supportsHostScoped, - credentialsExists, - credentialProvider, - discriminatorValue, - }; -}; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/ObjectField.tsx b/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/ObjectField.tsx deleted file mode 100644 index 8189116a4e..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/ObjectField.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from "react"; -import { FieldProps } from "@rjsf/utils"; -import { getDefaultRegistry } from "@rjsf/core"; -import { generateHandleId } from "@/app/(platform)/build/components/FlowEditor/handlers/helpers"; -import { ObjectEditor } from "../widgets/ObjectEditorWidget/ObjectEditorWidget"; - -export const ObjectField = (props: FieldProps) => { - const { - schema, - formData = {}, - onChange, - name, - idSchema, - formContext, - } = props; - const DefaultObjectField = getDefaultRegistry().fields.ObjectField; - - // Let the default field render for root or fixed-schema objects - let isFreeForm = false; - if ("additionalProperties" in schema || !("properties" in schema)) { - isFreeForm = true; - } - - if (idSchema?.$id === "root" || !isFreeForm) { - // TODO : We need to create better one - return ; - } - - const fieldKey = generateHandleId(idSchema.$id ?? ""); - const { nodeId } = formContext; - - return ( - - ); -}; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/index.ts b/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/index.ts deleted file mode 100644 index e2dfb2e5de..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/fields/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { RegistryFieldsType } from "@rjsf/utils"; -import { CredentialsField } from "./CredentialField/CredentialField"; -import { AnyOfField } from "./AnyOfField/AnyOfField"; -import { ObjectField } from "./ObjectField"; -import { LlmModelField } from "./LlmModelField/LlmModelField"; - -export const fields: RegistryFieldsType = { - AnyOfField: AnyOfField, - credentials: CredentialsField, - ObjectField: ObjectField, - llmModel: LlmModelField, -}; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/templates/ArrayFieldTemplate.tsx b/autogpt_platform/frontend/src/components/renderers/input-renderer/templates/ArrayFieldTemplate.tsx deleted file mode 100644 index e9f708de5a..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/templates/ArrayFieldTemplate.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from "react"; -import { ArrayFieldTemplateProps } from "@rjsf/utils"; -import { ArrayEditorWidget } from "../widgets/ArrayEditorWidget/ArrayEditorWidget"; - -function ArrayFieldTemplate(props: ArrayFieldTemplateProps) { - const { - items, - canAdd, - onAddClick, - disabled, - readonly, - formContext, - idSchema, - } = props; - const { nodeId } = formContext; - - return ( - - ); -} - -export default ArrayFieldTemplate; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/templates/FieldTemplate.tsx b/autogpt_platform/frontend/src/components/renderers/input-renderer/templates/FieldTemplate.tsx deleted file mode 100644 index ebc8a1f038..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/templates/FieldTemplate.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import React, { useContext } from "react"; -import { FieldTemplateProps } from "@rjsf/utils"; -import { InfoIcon } from "@phosphor-icons/react"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/components/atoms/Tooltip/BaseTooltip"; -import { Text } from "@/components/atoms/Text/Text"; - -import { useEdgeStore } from "@/app/(platform)/build/stores/edgeStore"; -import { useNodeStore } from "@/app/(platform)/build/stores/nodeStore"; -import { generateHandleId } from "@/app/(platform)/build/components/FlowEditor/handlers/helpers"; -import { getTypeDisplayInfo } from "@/app/(platform)/build/components/FlowEditor/nodes/helpers"; -import { ArrayEditorContext } from "../widgets/ArrayEditorWidget/ArrayEditorContext"; -import { - isCredentialFieldSchema, - toDisplayName, - getCredentialProviderFromSchema, -} from "../fields/CredentialField/helpers"; -import { cn } from "@/lib/utils"; -import { BlockIOCredentialsSubSchema } from "@/lib/autogpt-server-api"; -import { BlockUIType } from "@/lib/autogpt-server-api"; -import NodeHandle from "@/app/(platform)/build/components/FlowEditor/handlers/NodeHandle"; -import { getFieldErrorKey } from "../utils/helpers"; - -const FieldTemplate: React.FC = ({ - id: fieldId, - label, - required, - description, - children, - schema, - formContext, - uiSchema, -}) => { - const { isInputConnected } = useEdgeStore(); - const { nodeId, showHandles = true, size = "small" } = formContext; - const uiType = formContext.uiType; - - const showAdvanced = useNodeStore( - (state) => state.nodeAdvancedStates[nodeId] ?? false, - ); - - const nodeErrors = useNodeStore((state) => { - const node = state.nodes.find((n) => n.id === nodeId); - return node?.data?.errors; - }); - - const { isArrayItem, arrayFieldHandleId } = useContext(ArrayEditorContext); - - const isAnyOf = - Array.isArray((schema as any)?.anyOf) && !(schema as any)?.enum; - const isOneOf = Array.isArray((schema as any)?.oneOf); - const isCredential = isCredentialFieldSchema(schema); - const suppressHandle = isAnyOf || isOneOf; - - let handleId = null; - if (!isArrayItem) { - if (uiType === BlockUIType.AGENT) { - const parts = fieldId.split("_"); - const filtered = parts.filter( - (p) => p !== "root" && p !== "properties" && p.length > 0, - ); - handleId = filtered.join("_") || ""; - } else { - handleId = generateHandleId(fieldId); - } - } else { - handleId = arrayFieldHandleId; - } - - const isConnected = showHandles ? isInputConnected(nodeId, handleId) : false; - - if (!showAdvanced && schema.advanced === true && !isConnected) { - return null; - } - - const fromAnyOf = - Boolean((uiSchema as any)?.["ui:options"]?.fromAnyOf) || - Boolean((formContext as any)?.fromAnyOf); - - const { displayType, colorClass } = getTypeDisplayInfo(schema); - - let credentialProvider = null; - if (isCredential) { - credentialProvider = getCredentialProviderFromSchema( - useNodeStore.getState().getHardCodedValues(nodeId), - schema as BlockIOCredentialsSubSchema, - ); - } - if (formContext.uiType === BlockUIType.NOTE) { - return
{children}
; - } - - // Size-based styling - let shouldShowHandle = - showHandles && !suppressHandle && !fromAnyOf && !isCredential; - - // We do not want handle for output block's name field - if (uiType === BlockUIType.OUTPUT && fieldId === "root_name") { - shouldShowHandle = false; - } - - const fieldErrorKey = getFieldErrorKey(fieldId); - const fieldError = - nodeErrors?.[fieldErrorKey] || - nodeErrors?.[fieldErrorKey.replace(/_/g, ".")] || - nodeErrors?.[fieldErrorKey.replace(/\./g, "_")] || - null; - - return ( -
- {!isAnyOf && !fromAnyOf && label && ( - - )} - {(isAnyOf || !isConnected) && ( -
- {children} -
- )} - {fieldError && ( - - {fieldError} - - )} -
- ); -}; - -export default FieldTemplate; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/templates/index.ts b/autogpt_platform/frontend/src/components/renderers/input-renderer/templates/index.ts deleted file mode 100644 index 203526cd75..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/templates/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import ArrayFieldTemplate from "./ArrayFieldTemplate"; -import FieldTemplate from "./FieldTemplate"; - -const NoSubmitButton = () => null; - -export const templates = { - FieldTemplate, - ButtonTemplates: { SubmitButton: NoSubmitButton }, - ArrayFieldTemplate, -}; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/ArrayEditorWidget/ArrayEditorContext.tsx b/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/ArrayEditorWidget/ArrayEditorContext.tsx deleted file mode 100644 index 09fe11bc94..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/ArrayEditorWidget/ArrayEditorContext.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { createContext } from "react"; - -export const ArrayEditorContext = createContext<{ - isArrayItem: boolean; - arrayFieldHandleId: string; - isConnected: boolean; -}>({ - isArrayItem: false, - arrayFieldHandleId: "", - isConnected: false, -}); diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/ArrayEditorWidget/ArrayEditorWidget.tsx b/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/ArrayEditorWidget/ArrayEditorWidget.tsx deleted file mode 100644 index 01a2af2e40..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/ArrayEditorWidget/ArrayEditorWidget.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { ArrayFieldTemplateItemType, RJSFSchema } from "@rjsf/utils"; -import { ArrayEditorContext } from "./ArrayEditorContext"; -import { Button } from "@/components/atoms/Button/Button"; -import { PlusIcon, XIcon } from "@phosphor-icons/react"; -import { useEdgeStore } from "@/app/(platform)/build/stores/edgeStore"; -import { - generateHandleId, - HandleIdType, -} from "@/app/(platform)/build/components/FlowEditor/handlers/helpers"; - -export interface ArrayEditorProps { - items?: ArrayFieldTemplateItemType[]; - nodeId: string; - canAdd: boolean | undefined; - onAddClick?: () => void; - disabled: boolean | undefined; - readonly: boolean | undefined; - id: string; -} - -export const ArrayEditorWidget = ({ - items, - nodeId, - canAdd, - onAddClick, - disabled, - readonly, - id: fieldId, -}: ArrayEditorProps) => { - const { isInputConnected } = useEdgeStore(); - - return ( -
-
-
- {items?.map((element) => { - const arrayFieldHandleId = generateHandleId( - fieldId, - [element.index.toString()], - HandleIdType.ARRAY, - ); - const isConnected = isInputConnected(nodeId, arrayFieldHandleId); - return ( -
- - {element.children} - - - {element.hasRemove && - !readonly && - !disabled && - !isConnected && ( - - )} -
- ); - })} -
-
- - {canAdd && !readonly && !disabled && ( - - )} -
- ); -}; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/ObjectEditorWidget/ObjectEditorWidget.tsx b/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/ObjectEditorWidget/ObjectEditorWidget.tsx deleted file mode 100644 index 80d504421d..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/ObjectEditorWidget/ObjectEditorWidget.tsx +++ /dev/null @@ -1,183 +0,0 @@ -"use client"; - -import React from "react"; -import { Plus, X } from "lucide-react"; -import { Text } from "@/components/atoms/Text/Text"; -import { Button } from "@/components/atoms/Button/Button"; -import { Input } from "@/components/atoms/Input/Input"; -import NodeHandle from "@/app/(platform)/build/components/FlowEditor/handlers/NodeHandle"; -import { useEdgeStore } from "@/app/(platform)/build/stores/edgeStore"; -import { - generateHandleId, - HandleIdType, - parseKeyValueHandleId, -} from "@/app/(platform)/build/components/FlowEditor/handlers/helpers"; - -export interface ObjectEditorProps { - id: string; - value?: Record; - onChange?: (value: Record) => void; - placeholder?: string; - disabled?: boolean; - className?: string; - nodeId: string; - fieldKey: string; -} - -export const ObjectEditor = React.forwardRef( - ( - { - id: parentFieldId, - value = {}, - onChange, - placeholder = "Enter value", - disabled = false, - className, - nodeId, - }, - ref, - ) => { - const getAllHandleIdsOfANode = useEdgeStore( - (state) => state.getAllHandleIdsOfANode, - ); - const setProperty = (key: string, propertyValue: any) => { - if (!onChange) return; - - const newData: Record = { ...value }; - if (propertyValue === undefined || propertyValue === "") { - delete newData[key]; - } else { - newData[key] = propertyValue; - } - onChange(newData); - }; - - const addProperty = () => { - if (!onChange) return; - onChange({ ...value, [""]: "" }); - }; - - const removeProperty = (key: string) => { - if (!onChange) return; - const newData = { ...value }; - delete newData[key]; - onChange(newData); - }; - - const updateKey = (oldKey: string, newKey: string) => { - if (!onChange || oldKey === newKey) return; - - const propertyValue = value[oldKey]; - const newData: Record = { ...value }; - delete newData[oldKey]; - newData[newKey] = propertyValue; - onChange(newData); - }; - - const hasEmptyKeys = Object.keys(value).some((key) => key.trim() === ""); - - const { isInputConnected } = useEdgeStore(); - - const allHandleIdsOfANode = getAllHandleIdsOfANode(nodeId); - const allKeyValueHandleIdsOfANode = allHandleIdsOfANode.filter((handleId) => - handleId.includes("_#_"), - ); - allKeyValueHandleIdsOfANode.forEach((handleId) => { - const key = parseKeyValueHandleId(handleId, HandleIdType.KEY_VALUE); - if (!value[key]) { - value[key] = null; - } - }); - - // Note: ObjectEditor is always used in node context, so showHandles is always true - // If you need to use it in dialog context, you'll need to pass showHandles via props - const showHandles = true; - - return ( -
- {Object.entries(value).map(([key, propertyValue], idx) => { - const handleId = generateHandleId( - parentFieldId, - [key], - HandleIdType.KEY_VALUE, - ); - const isDynamicPropertyConnected = isInputConnected(nodeId, handleId); - - return ( -
-
- {showHandles && ( - - )} - - #{key.trim() === "" ? "" : key} - - - (string) - -
- {!isDynamicPropertyConnected && ( -
- updateKey(key, e.target.value)} - placeholder="Key" - wrapperClassName="mb-0" - disabled={disabled} - /> - setProperty(key, e.target.value)} - placeholder={placeholder} - wrapperClassName="mb-0" - disabled={disabled} - /> - -
- )} -
- ); - })} - - -
- ); - }, -); - -ObjectEditor.displayName = "ObjectEditor"; diff --git a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/index.ts b/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/index.ts deleted file mode 100644 index 3788e74fbf..0000000000 --- a/autogpt_platform/frontend/src/components/renderers/input-renderer/widgets/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { RegistryWidgetsType } from "@rjsf/utils"; -import { SelectWidget } from "./SelectWidget"; -import { TextInputWidget } from "./TextInputWidget/TextInputWidget"; -import { SwitchWidget } from "./SwitchWidget"; -import { FileWidget } from "./FileWidget"; -import { DateInputWidget } from "./DateInputWidget"; -import { TimeInputWidget } from "./TimeInputWidget"; -import { DateTimeInputWidget } from "./DateTimeInputWidget"; - -export const widgets: RegistryWidgetsType = { - TextWidget: TextInputWidget, - SelectWidget: SelectWidget, - CheckboxWidget: SwitchWidget, - FileWidget: FileWidget, - DateWidget: DateInputWidget, - TimeWidget: TimeInputWidget, - DateTimeWidget: DateTimeInputWidget, -}; diff --git a/autogpt_platform/frontend/src/lib/dexie/db.ts b/autogpt_platform/frontend/src/lib/dexie/db.ts new file mode 100644 index 0000000000..05e749ca4b --- /dev/null +++ b/autogpt_platform/frontend/src/lib/dexie/db.ts @@ -0,0 +1,46 @@ +import Dexie, { type EntityTable } from "dexie"; +import type { CustomNode } from "@/app/(platform)/build/components/FlowEditor/nodes/CustomNode/CustomNode"; +import type { CustomEdge } from "@/app/(platform)/build/components/FlowEditor/edges/CustomEdge"; + +// 24 hrs expiry +export const DRAFT_EXPIRY_MS = 24 * 60 * 60 * 1000; + +export interface BuilderDraft { + id: string; + nodes: CustomNode[]; + edges: CustomEdge[]; + graphSchemas: { + input: Record | null; + credentials: Record | null; + output: Record | null; + }; + nodeCounter: number; + savedAt: number; + flowVersion?: number; +} + +class BuilderDatabase extends Dexie { + drafts!: EntityTable; + + constructor() { + super("AutoGPTBuilderDB"); + + this.version(1).stores({ + drafts: "id, savedAt", + }); + } +} + +// Singleton database instance +export const db = new BuilderDatabase(); + +export async function cleanupExpiredDrafts(): Promise { + const expiryThreshold = Date.now() - DRAFT_EXPIRY_MS; + + const deletedCount = await db.drafts + .where("savedAt") + .below(expiryThreshold) + .delete(); + + return deletedCount; +} diff --git a/autogpt_platform/frontend/src/lib/dexie/draft-utils.ts b/autogpt_platform/frontend/src/lib/dexie/draft-utils.ts new file mode 100644 index 0000000000..03232ede30 --- /dev/null +++ b/autogpt_platform/frontend/src/lib/dexie/draft-utils.ts @@ -0,0 +1,112 @@ +import type { CustomNode } from "@/app/(platform)/build/components/FlowEditor/nodes/CustomNode/CustomNode"; +import type { CustomEdge } from "@/app/(platform)/build/components/FlowEditor/edges/CustomEdge"; +import isEqual from "lodash/isEqual"; + +export function cleanNode(node: CustomNode) { + return { + id: node.id, + position: node.position, + data: { + hardcodedValues: node.data.hardcodedValues, + title: node.data.title, + block_id: node.data.block_id, + metadata: node.data.metadata, + }, + }; +} + +export function cleanEdge(edge: CustomEdge) { + return { + id: edge.id, + source: edge.source, + target: edge.target, + sourceHandle: edge.sourceHandle, + targetHandle: edge.targetHandle, + }; +} + +export function cleanNodes(nodes: CustomNode[]) { + return nodes.map(cleanNode); +} + +export function cleanEdges(edges: CustomEdge[]) { + return edges.map(cleanEdge); +} + +export interface DraftDiff { + nodes: { + added: number; + removed: number; + modified: number; + }; + edges: { + added: number; + removed: number; + modified: number; + }; +} + +/** + * Calculate the diff between draft and current nodes/edges. + * - Added: items in draft but not in current (will be restored) + * - Removed: items in current but not in draft (will be removed if draft is loaded) + * - Modified: items with same ID but different content + */ +export function calculateDraftDiff( + draftNodes: CustomNode[], + draftEdges: CustomEdge[], + currentNodes: CustomNode[], + currentEdges: CustomEdge[], +): DraftDiff { + const draftNodeIds = new Set(draftNodes.map((n) => n.id)); + const currentNodeIds = new Set(currentNodes.map((n) => n.id)); + const draftEdgeIds = new Set(draftEdges.map((e) => e.id)); + const currentEdgeIds = new Set(currentEdges.map((e) => e.id)); + + // Nodes diff + const nodesAdded = draftNodes.filter((n) => !currentNodeIds.has(n.id)).length; + const nodesRemoved = currentNodes.filter( + (n) => !draftNodeIds.has(n.id), + ).length; + + // Modified nodes: same ID but different content + const draftNodeMap = new Map(draftNodes.map((n) => [n.id, cleanNode(n)])); + const currentNodeMap = new Map(currentNodes.map((n) => [n.id, cleanNode(n)])); + let nodesModified = 0; + for (const [id, draftClean] of draftNodeMap) { + const currentClean = currentNodeMap.get(id); + if (currentClean && !isEqual(draftClean, currentClean)) { + nodesModified++; + } + } + + // Edges diff + const edgesAdded = draftEdges.filter((e) => !currentEdgeIds.has(e.id)).length; + const edgesRemoved = currentEdges.filter( + (e) => !draftEdgeIds.has(e.id), + ).length; + + // Modified edges: same ID but different content + const draftEdgeMap = new Map(draftEdges.map((e) => [e.id, cleanEdge(e)])); + const currentEdgeMap = new Map(currentEdges.map((e) => [e.id, cleanEdge(e)])); + let edgesModified = 0; + for (const [id, draftClean] of draftEdgeMap) { + const currentClean = currentEdgeMap.get(id); + if (currentClean && !isEqual(draftClean, currentClean)) { + edgesModified++; + } + } + + return { + nodes: { + added: nodesAdded, + removed: nodesRemoved, + modified: nodesModified, + }, + edges: { + added: edgesAdded, + removed: edgesRemoved, + modified: edgesModified, + }, + }; +} diff --git a/autogpt_platform/frontend/src/services/builder-draft/draft-service.ts b/autogpt_platform/frontend/src/services/builder-draft/draft-service.ts new file mode 100644 index 0000000000..6d35d23bf4 --- /dev/null +++ b/autogpt_platform/frontend/src/services/builder-draft/draft-service.ts @@ -0,0 +1,118 @@ +import { + db, + BuilderDraft, + DRAFT_EXPIRY_MS, + cleanupExpiredDrafts, +} from "../../lib/dexie/db"; +import type { CustomNode } from "@/app/(platform)/build/components/FlowEditor/nodes/CustomNode/CustomNode"; +import type { CustomEdge } from "@/app/(platform)/build/components/FlowEditor/edges/CustomEdge"; +import { cleanNodes, cleanEdges } from "../../lib/dexie/draft-utils"; +import isEqual from "lodash/isEqual"; +import { environment } from "@/services/environment"; + +const SESSION_TEMP_ID_KEY = "builder_temp_flow_id"; + +export function getOrCreateTempFlowId(): string { + if (environment.isServerSide()) { + return `temp_${crypto.randomUUID()}`; + } + + let tempId = sessionStorage.getItem(SESSION_TEMP_ID_KEY); + if (!tempId) { + tempId = `temp_${crypto.randomUUID()}`; + sessionStorage.setItem(SESSION_TEMP_ID_KEY, tempId); + } + return tempId; +} + +export function clearTempFlowId(): void { + if (environment.isClientSide()) { + sessionStorage.removeItem(SESSION_TEMP_ID_KEY); + } +} + +export function getTempFlowId(): string | null { + if (environment.isServerSide()) { + return null; + } + return sessionStorage.getItem(SESSION_TEMP_ID_KEY); +} + +export interface DraftData { + nodes: CustomNode[]; + edges: CustomEdge[]; + graphSchemas: { + input: Record | null; + credentials: Record | null; + output: Record | null; + }; + nodeCounter: number; + flowVersion?: number; +} + +export const draftService = { + async saveDraft(flowId: string, data: DraftData): Promise { + const draft: BuilderDraft = { + id: flowId, + nodes: data.nodes, + edges: data.edges, + graphSchemas: data.graphSchemas, + nodeCounter: data.nodeCounter, + savedAt: Date.now(), + flowVersion: data.flowVersion, + }; + + await db.drafts.put(draft); + }, + + async loadDraft(flowId: string): Promise { + const draft = await db.drafts.get(flowId); + + if (!draft) { + return null; + } + const age = Date.now() - draft.savedAt; + if (age > DRAFT_EXPIRY_MS) { + await this.deleteDraft(flowId); + return null; + } + + return draft; + }, + + async deleteDraft(flowId: string): Promise { + await db.drafts.delete(flowId); + }, + + async hasDraft(flowId: string): Promise { + const draft = await db.drafts.get(flowId); + if (!draft) return false; + + // Check expiry + const age = Date.now() - draft.savedAt; + if (age > DRAFT_EXPIRY_MS) { + await this.deleteDraft(flowId); + return false; + } + + return true; + }, + + isDraftDifferent( + draft: BuilderDraft, + currentNodes: CustomNode[], + currentEdges: CustomEdge[], + ): boolean { + const draftNodesClean = cleanNodes(draft.nodes); + const currentNodesClean = cleanNodes(currentNodes); + const draftEdgesClean = cleanEdges(draft.edges); + const currentEdgesClean = cleanEdges(currentEdges); + + const nodesDifferent = !isEqual(draftNodesClean, currentNodesClean); + const edgesDifferent = !isEqual(draftEdgesClean, currentEdgesClean); + + return nodesDifferent || edgesDifferent; + }, + + cleanupExpired: cleanupExpiredDrafts, +}; diff --git a/autogpt_platform/frontend/src/services/feature-flags/use-get-flag.ts b/autogpt_platform/frontend/src/services/feature-flags/use-get-flag.ts index e80adeb7b5..64b69895f3 100644 --- a/autogpt_platform/frontend/src/services/feature-flags/use-get-flag.ts +++ b/autogpt_platform/frontend/src/services/feature-flags/use-get-flag.ts @@ -2,7 +2,6 @@ import { DEFAULT_SEARCH_TERMS } from "@/app/(platform)/marketplace/components/HeroSection/helpers"; import { useFlags } from "launchdarkly-react-client-sdk"; -import { environment } from "../environment"; export enum Flag { BETA_BLOCKS = "beta-blocks", @@ -40,7 +39,7 @@ const mockFlags = { [Flag.BETA_BLOCKS]: [], [Flag.NEW_BLOCK_MENU]: false, [Flag.NEW_AGENT_RUNS]: false, - [Flag.GRAPH_SEARCH]: true, + [Flag.GRAPH_SEARCH]: false, [Flag.ENABLE_ENHANCED_OUTPUT_HANDLING]: false, [Flag.NEW_FLOW_EDITOR]: false, [Flag.BUILDER_VIEW_SWITCH]: false, @@ -48,17 +47,20 @@ const mockFlags = { [Flag.AGENT_FAVORITING]: false, [Flag.MARKETPLACE_SEARCH_TERMS]: DEFAULT_SEARCH_TERMS, [Flag.ENABLE_PLATFORM_PAYMENT]: false, - [Flag.CHAT]: true, + [Flag.CHAT]: false, }; export function useGetFlag(flag: T): FlagValues[T] | null { const currentFlags = useFlags(); const flagValue = currentFlags[flag]; - const isCloud = environment.isCloud(); - if ((isPwMockEnabled && !isCloud) || flagValue === undefined) { + const envEnabled = process.env.NEXT_PUBLIC_LAUNCHDARKLY_ENABLED === "true"; + const clientId = process.env.NEXT_PUBLIC_LAUNCHDARKLY_CLIENT_ID; + const isLaunchDarklyConfigured = envEnabled && Boolean(clientId); + + if (!isLaunchDarklyConfigured || isPwMockEnabled) { return mockFlags[flag]; } - return flagValue; + return flagValue ?? mockFlags[flag]; } diff --git a/docs/.gitbook/assets/AGPT_Platform.png b/docs/.gitbook/assets/AGPT_Platform.png new file mode 100644 index 0000000000..25df18a82a Binary files /dev/null and b/docs/.gitbook/assets/AGPT_Platform.png differ diff --git a/docs/.gitbook/assets/Banner_image.png b/docs/.gitbook/assets/Banner_image.png new file mode 100644 index 0000000000..6dbcd2b632 Binary files /dev/null and b/docs/.gitbook/assets/Banner_image.png differ diff --git a/docs/.gitbook/assets/Contribute.png b/docs/.gitbook/assets/Contribute.png new file mode 100644 index 0000000000..b9fb058898 Binary files /dev/null and b/docs/.gitbook/assets/Contribute.png differ diff --git a/docs/.gitbook/assets/Integrations.png b/docs/.gitbook/assets/Integrations.png new file mode 100644 index 0000000000..9941a5ff08 Binary files /dev/null and b/docs/.gitbook/assets/Integrations.png differ diff --git a/docs/.gitbook/assets/Screenshot 2025-08-11 at 12.21.17 PM.png b/docs/.gitbook/assets/Screenshot 2025-08-11 at 12.21.17 PM.png new file mode 100644 index 0000000000..87cb432f95 Binary files /dev/null and b/docs/.gitbook/assets/Screenshot 2025-08-11 at 12.21.17 PM.png differ diff --git a/docs/.gitbook/assets/api-reference.jpg b/docs/.gitbook/assets/api-reference.jpg new file mode 100644 index 0000000000..13440be7dc Binary files /dev/null and b/docs/.gitbook/assets/api-reference.jpg differ diff --git a/docs/.gitbook/assets/hosted.jpg b/docs/.gitbook/assets/hosted.jpg new file mode 100644 index 0000000000..d5f275ebf9 Binary files /dev/null and b/docs/.gitbook/assets/hosted.jpg differ diff --git a/docs/.gitbook/assets/no-code.jpg b/docs/.gitbook/assets/no-code.jpg new file mode 100644 index 0000000000..fdf54211a3 Binary files /dev/null and b/docs/.gitbook/assets/no-code.jpg differ diff --git a/docs/.gitbook/assets/replicate-prediction-yt6p2d3gjhrma0ctdsv8vp1t70.jpeg b/docs/.gitbook/assets/replicate-prediction-yt6p2d3gjhrma0ctdsv8vp1t70.jpeg new file mode 100644 index 0000000000..d52ba99c76 Binary files /dev/null and b/docs/.gitbook/assets/replicate-prediction-yt6p2d3gjhrma0ctdsv8vp1t70.jpeg differ diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..66d96c7e02 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,52 @@ +--- +description: Welcome to your team’s developer platform +cover: .gitbook/assets/Banner_image.png +coverY: 56.53835084561286 +layout: + width: wide + cover: + visible: true + size: full + title: + visible: false + description: + visible: false + tableOfContents: + visible: false + outline: + visible: false + pagination: + visible: false + metadata: + visible: true +--- + +# Developer Platform + +

AutoGPT Documentation

+ +

Create innovative agents that amplify human potential

+ + + +
Cover image

:leaf:

AutoGPT PlatformGet started with the developer platform in 5 minutes.AutoGPT PlatformAGPT_Platform.png

:server:

IntegrationsLearn more about hosting the developer platform.IntegrationsIntegrations.png

:terminal:

ContributeBrowse, test, and implement APIs.Contributing to the DocsContribute.png
+ +{% columns %} +{% column valign="middle" %} +

AutoGPT License on GitHub

+{% endcolumn %} + +{% column %} +

AutoGPT Classic (Local Installation)

+{% endcolumn %} +{% endcolumns %} + +*** + + + +

Join a community of 65,000+ developers

+ +

Join our Discord community or create your first PR in just a few steps.

+ +

:discord:

Discord communityJoin our Discord community to post questions, get help, and share resources with our growing community of over 55,000 members.Join Discordhttps://discord.com/invite/autogpt

:github:

GitHubOur product is 100% open source and built by developers just like you. Head to our GitHub repository to learn how to submit your first PR.Submit a PRhttps://github.com/Significant-Gravitas/AutoGPT
diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md new file mode 100644 index 0000000000..d338256bc0 --- /dev/null +++ b/docs/SUMMARY.md @@ -0,0 +1,3 @@ +# Table of contents + +* [Developer Platform](README.md)