From d9a636adefef8ec9ef43ccb4a78f3c4863b78ef6 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Fri, 7 Aug 2026 14:06:51 +0200 Subject: [PATCH 01/48] fix(core): cap and consolidate group mutation event related resources GroupMutatedEvent.get_related() built an unbounded related-resources list (2 entries per member, 3 per ancestor), so a single mutation changing a few hundred members exceeded the Prefect maximum and the whole event was silently dropped: no activity log, no membership automations. Members and ancestors are now a single entry each (dropping the duplicate related.node and the dead group.update roles) and the list is truncated at the Prefect maximum, so the event is always recorded. The event query filter and related-nodes output read all three related-node roles and dedupe by id, keeping output identical across the consolidated format and older events still in Prefect retention. Fixes #10127 --- backend/infrahub/events/group_action.py | 48 ++-- backend/infrahub/task_manager/event.py | 11 +- backend/infrahub/task_manager/models.py | 13 +- .../test_files/group_member_schema.yml | 11 + .../test_group_event_related_resources.py | 100 ++++++++ backend/tests/unit/event/test_group_action.py | 232 +++++++++++++++++- changelog/10127.fixed.md | 1 + dev/knowledge/backend/events.md | 13 + 8 files changed, 398 insertions(+), 31 deletions(-) create mode 100644 backend/tests/integration_docker/test_files/group_member_schema.yml create mode 100644 backend/tests/integration_docker/test_group_event_related_resources.py create mode 100644 changelog/10127.fixed.md diff --git a/backend/infrahub/events/group_action.py b/backend/infrahub/events/group_action.py index 7c0bd2ab6ec..32aedd6d76e 100644 --- a/backend/infrahub/events/group_action.py +++ b/backend/infrahub/events/group_action.py @@ -5,10 +5,14 @@ from infrahub.core.constants import InfrahubKind, MutationAction from infrahub.external_protocols import ExternalAuthProtocol +from infrahub.log import get_logger from .constants import EVENT_NAMESPACE +from .limits import get_prefect_max_related_resources from .models import EventNode, InfrahubEvent +log = get_logger() + class GroupMutatedEvent(InfrahubEvent): """Event generated when a node has been mutated.""" @@ -35,14 +39,13 @@ def get_related(self) -> list[dict[str, str]]: "infrahub.node.kind": self.kind, } ) - related.append( - { - "prefect.resource.id": self.node_id, - "prefect.resource.role": "infrahub.group.update", - "infrahub.node.kind": self.kind, - } - ) + # Members and ancestors grow with the size of the mutation, so they come + # last and the list is capped: the Prefect API rejects any event whose + # related resources exceed the configured maximum, and an oversized event + # would never be recorded at all. Each member and ancestor is a single + # entry (also matched as a related node through its own role), so a plain + # ordered truncation keeps the fixed and group-scoped entries intact. for member in self.members: related.append( { @@ -51,13 +54,6 @@ def get_related(self) -> list[dict[str, str]]: "infrahub.node.kind": member.kind, } ) - related.append( - { - "prefect.resource.id": member.id, - "prefect.resource.role": "infrahub.related.node", - "infrahub.node.kind": member.kind, - } - ) for ancestor in self.ancestors: related.append( @@ -67,20 +63,18 @@ def get_related(self) -> list[dict[str, str]]: "infrahub.node.kind": ancestor.kind, } ) - related.append( - { - "prefect.resource.id": ancestor.id, - "prefect.resource.role": "infrahub.related.node", - "infrahub.node.kind": ancestor.kind, - } - ) - related.append( - { - "prefect.resource.id": ancestor.id, - "prefect.resource.role": "infrahub.group.update", - "infrahub.node.kind": ancestor.kind, - } + + max_related = get_prefect_max_related_resources() + if len(related) > max_related: + log.warning( + "Truncating the related resources of a group mutation event to the Prefect maximum", + event_name=self.event_name, + kind=self.kind, + node_id=self.node_id, + related_resources=len(related), + maximum=max_related, ) + related = related[:max_related] return related diff --git a/backend/infrahub/task_manager/event.py b/backend/infrahub/task_manager/event.py index 8212aa010d1..473a69d4149 100644 --- a/backend/infrahub/task_manager/event.py +++ b/backend/infrahub/task_manager/event.py @@ -65,9 +65,15 @@ def get_primary_node(self) -> dict[str, str] | None: return None def get_related_nodes(self) -> list[dict[str, str]]: + # Group members and ancestors are related nodes carrying their own roles. + # Deduplicating by id keeps the output identical whether a member appears + # once (consolidated group event) or twice (older events that also listed + # it under the generic related-node role). + related_roles = {"infrahub.related.node", "infrahub.group.member", "infrahub.group.ancestor"} related_nodes = [] + seen: set[str] = set() for resource in self.related: - if resource.get("prefect.resource.role") != "infrahub.related.node": + if resource.get("prefect.resource.role") not in related_roles: continue node_id = resource.get("prefect.resource.id") @@ -75,7 +81,8 @@ def get_related_nodes(self) -> list[dict[str, str]]: if node_id == self.resource.get("infrahub.node.id"): # Don't include the primary node as a related node. continue - if node_id and node_kind: + if node_id and node_kind and node_id not in seen: + seen.add(node_id) related_nodes.append({"id": node_id, "kind": node_kind}) return related_nodes diff --git a/backend/infrahub/task_manager/models.py b/backend/infrahub/task_manager/models.py index 4397218bb67..c2ae6817f92 100644 --- a/backend/infrahub/task_manager/models.py +++ b/backend/infrahub/task_manager/models.py @@ -201,10 +201,21 @@ def add_parent_filter(self, parent__ids: list[str] | None) -> None: def add_related_node_filter(self, related_node__ids: list[str] | None) -> None: if related_node__ids: + # Group members and ancestors are related nodes of the event, but they + # carry their own roles rather than the generic one. Matching all three + # roles keeps this filter correct for both the consolidated group event + # format and any older events still listing members as related nodes. self.add_related_filter( EventRelatedFilter( labels=ResourceSpecification( - {"prefect.resource.role": "infrahub.related.node", "prefect.resource.id": related_node__ids} + { + "prefect.resource.role": [ + "infrahub.related.node", + "infrahub.group.member", + "infrahub.group.ancestor", + ], + "prefect.resource.id": related_node__ids, + } ) ) ) diff --git a/backend/tests/integration_docker/test_files/group_member_schema.yml b/backend/tests/integration_docker/test_files/group_member_schema.yml new file mode 100644 index 00000000000..41e50ebde0b --- /dev/null +++ b/backend/tests/integration_docker/test_files/group_member_schema.yml @@ -0,0 +1,11 @@ +--- +version: "1.0" +nodes: + - name: EventMember + namespace: Testing + label: "Event Member" + human_friendly_id: ['name__value'] + attributes: + - name: name + kind: Text + unique: true diff --git a/backend/tests/integration_docker/test_group_event_related_resources.py b/backend/tests/integration_docker/test_group_event_related_resources.py new file mode 100644 index 00000000000..7072fb91fb4 --- /dev/null +++ b/backend/tests/integration_docker/test_group_event_related_resources.py @@ -0,0 +1,100 @@ +"""Group member-added events must be recorded even when many members change at once. + +The Prefect API rejects any event whose related resources exceed the configured +maximum (``PREFECT_SERVER_EVENTS_MAXIMUM_RELATED_RESOURCES``, 500 in the Infrahub +image). A group mutation event lists every added member in its related +resources, so adding a few hundred members in a single mutation produced an +event too large to record: the mutation succeeded but the ``member_added`` +event silently never landed in the event store, so the group's activity log was +missing and membership-driven automations never fired. +""" + +from __future__ import annotations + +import time +from asyncio import sleep +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest +import yaml +from infrahub_sdk.testing.docker import TestInfrahubDockerClient + +if TYPE_CHECKING: + from infrahub_sdk import InfrahubClient + +CURRENT_DIRECTORY = Path(__file__).parent.resolve() + +# Before consolidation each member added two related resources to the event, so +# 300 members put it above the 500 maximum (~247 members was already enough) +# while the single-member control group stayed far below it. After consolidation +# the same 300 members produce ~300 related resources, comfortably under the +# maximum, so the event is recorded again. The truncation cap itself is exercised +# by the unit tests, which push past the maximum. +MEMBER_COUNT = 300 + +EVENT_WAIT_SECONDS = 120 + +MEMBER_ADDED_EVENT_COUNT_QUERY = """ +query MemberAddedEventCount($node_ids: [String!]) { + InfrahubEvent(event_type: ["infrahub.group.member_added"], primary_node__ids: $node_ids) { + count + } +} +""" + + +async def member_added_event_recorded(client: InfrahubClient, group_id: str) -> bool: + """Poll the event store until the member-added event of the group shows up.""" + deadline = time.monotonic() + EVENT_WAIT_SECONDS + while time.monotonic() < deadline: + result = await client.execute_graphql(query=MEMBER_ADDED_EVENT_COUNT_QUERY, variables={"node_ids": [group_id]}) + if result["InfrahubEvent"]["count"] > 0: + return True + await sleep(1) + return False + + +class TestGroupEventRelatedResources(TestInfrahubDockerClient): + @pytest.fixture(scope="class") + def infrahub_version(self) -> str: + return "local" + + @pytest.fixture(scope="class") + def schema_group_member(self) -> dict: + return yaml.safe_load( + Path(CURRENT_DIRECTORY / "test_files/group_member_schema.yml").read_text(encoding="utf-8") + ) + + async def test_load_schema(self, client: InfrahubClient, schema_group_member: dict) -> None: + response = await client.schema.load(schemas=[schema_group_member], wait_until_converged=True) + assert response.schema_updated + + async def test_member_added_event_recorded_for_group_with_many_members(self, client: InfrahubClient) -> None: + # The members the groups will contain. + member_batch = await client.create_batch() + members = [] + for idx in range(MEMBER_COUNT): + member = await client.create(kind="TestingEventMember", name=f"member-{idx}") + member_batch.add(task=member.save, node=member) + members.append(member) + async for _ in member_batch.execute(): + pass + + # Control: a group with a single member, proving the event pipeline + # (emission, ingestion, query) works for this mutation shape. + small_group = await client.create(kind="CoreStandardGroup", name="control-group", members=[members[0].id]) + await small_group.save() + + # The reproduction target: a group taking on the whole member set at once. + large_group = await client.create( + kind="CoreStandardGroup", name="large-group", members=[member.id for member in members] + ) + await large_group.save() + + assert await member_added_event_recorded(client=client, group_id=small_group.id), ( + "the member_added event of the single-member group was never recorded" + ) + assert await member_added_event_recorded(client=client, group_id=large_group.id), ( + f"the member_added event of the group taking on {MEMBER_COUNT} members was never recorded" + ) diff --git a/backend/tests/unit/event/test_group_action.py b/backend/tests/unit/event/test_group_action.py index 466ab8967f0..87407771193 100644 --- a/backend/tests/unit/event/test_group_action.py +++ b/backend/tests/unit/event/test_group_action.py @@ -3,19 +3,25 @@ from uuid import uuid4 import pytest +from prefect.events.schemas.events import RelatedResource, Resource from infrahub.auth.session import AccountSession from infrahub.auth.types import AuthType from infrahub.context import InfrahubContext from infrahub.core.branch import Branch from infrahub.core.constants import InfrahubKind +from infrahub.events.constants import EventSortOrder from infrahub.events.group_action import ( GroupAutoCreateCappedEvent, GroupAutoCreatedEvent, GroupAutoCreateRejectedEvent, + GroupMemberAddedEvent, ) -from infrahub.events.models import EventMeta +from infrahub.events.limits import get_prefect_max_related_resources +from infrahub.events.models import EventMeta, EventNode from infrahub.external_protocols import ExternalAuthProtocol +from infrahub.task_manager.event import PrefectEventData +from infrahub.task_manager.models import InfrahubEventFilter def _make_meta(account_id: str = "acct-123") -> EventMeta: @@ -30,6 +36,92 @@ def _make_meta(account_id: str = "acct-123") -> EventMeta: ) +def _make_member_added_event( + node_id: str, + members: list[EventNode], + ancestors: list[EventNode] | None = None, + kind: str = InfrahubKind.STANDARDGROUP, +) -> GroupMemberAddedEvent: + return GroupMemberAddedEvent( + meta=_make_meta(), + kind=kind, + node_id=node_id, + members=members, + ancestors=ancestors or [], + ) + + +def _old_format_related(event: GroupMemberAddedEvent) -> list[dict[str, str]]: + """Rebuild the pre-consolidation related list for the same event. + + Members carried a duplicate ``infrahub.related.node`` entry, ancestors carried + that duplicate plus an ``infrahub.group.update`` entry, and the group itself + carried an ``infrahub.group.update`` entry. This is the wire format of events + still sitting in Prefect retention when the consolidation ships. + """ + related = event.meta.get_related() + related.append( + { + "prefect.resource.id": event.node_id, + "prefect.resource.role": "infrahub.related.node", + "infrahub.node.kind": event.kind, + } + ) + related.append( + { + "prefect.resource.id": event.node_id, + "prefect.resource.role": "infrahub.group.update", + "infrahub.node.kind": event.kind, + } + ) + for member in event.members: + related.append( + { + "prefect.resource.id": member.id, + "prefect.resource.role": "infrahub.group.member", + "infrahub.node.kind": member.kind, + } + ) + related.append( + { + "prefect.resource.id": member.id, + "prefect.resource.role": "infrahub.related.node", + "infrahub.node.kind": member.kind, + } + ) + for ancestor in event.ancestors: + related.append( + { + "prefect.resource.id": ancestor.id, + "prefect.resource.role": "infrahub.group.ancestor", + "infrahub.node.kind": ancestor.kind, + } + ) + related.append( + { + "prefect.resource.id": ancestor.id, + "prefect.resource.role": "infrahub.related.node", + "infrahub.node.kind": ancestor.kind, + } + ) + related.append( + { + "prefect.resource.id": ancestor.id, + "prefect.resource.role": "infrahub.group.update", + "infrahub.node.kind": ancestor.kind, + } + ) + return related + + +def _event_data(event: GroupMemberAddedEvent, related: list[dict[str, str]]) -> PrefectEventData: + return PrefectEventData( + event=event.event_name, + resource=Resource(event.get_resource()), + related=[RelatedResource(item) for item in related], + ) + + def test_group_auto_created_get_resource_pins_wire_format() -> None: triggering_user_id = uuid4() group_id = uuid4() @@ -161,3 +253,141 @@ def test_group_auto_create_capped_get_related_pins_dropped_claim_shape( } for idx, claim in enumerate(dropped_claims) ] + + +def test_group_member_added_get_related_consolidates_member_and_ancestor_entries() -> None: + """Each member and ancestor is a single entry; the duplicate roles are gone.""" + group_id = str(uuid4()) + members = [EventNode(id=str(uuid4()), kind="TestPerson") for _ in range(3)] + ancestors = [EventNode(id=str(uuid4()), kind=InfrahubKind.STANDARDGROUP) for _ in range(2)] + event = _make_member_added_event(node_id=group_id, members=members, ancestors=ancestors) + + related = event.get_related() + + member_entries = [item for item in related if item["prefect.resource.role"] == "infrahub.group.member"] + assert member_entries == [ + { + "prefect.resource.id": member.id, + "prefect.resource.role": "infrahub.group.member", + "infrahub.node.kind": member.kind, + } + for member in members + ] + + ancestor_entries = [item for item in related if item["prefect.resource.role"] == "infrahub.group.ancestor"] + assert ancestor_entries == [ + { + "prefect.resource.id": ancestor.id, + "prefect.resource.role": "infrahub.group.ancestor", + "infrahub.node.kind": ancestor.kind, + } + for ancestor in ancestors + ] + + # The only related-node entry is the group itself; members/ancestors no longer + # carry a duplicate, and the dead group.update role is gone entirely. + related_node_ids = [ + item["prefect.resource.id"] for item in related if item["prefect.resource.role"] == "infrahub.related.node" + ] + assert related_node_ids == [group_id] + assert not [item for item in related if item["prefect.resource.role"] == "infrahub.group.update"] + + +def test_group_member_added_related_resources_stay_within_prefect_maximum() -> None: + """A member add of any size keeps its event: the related list is capped.""" + max_related = get_prefect_max_related_resources() + members = [EventNode(id=str(uuid4()), kind="TestPerson") for _ in range(max_related + 50)] + event = _make_member_added_event(node_id=str(uuid4()), members=members) + + related = event.get_related() + + assert len(related) == max_related + + +def test_group_member_added_cap_keeps_fixed_and_group_scoped_entries() -> None: + """Truncation drops overflow members, never the fixed or group-scoped entries.""" + group_id = str(uuid4()) + max_related = get_prefect_max_related_resources() + members = [EventNode(id=str(uuid4()), kind="TestPerson") for _ in range(max_related + 50)] + event = _make_member_added_event(node_id=group_id, members=members) + + related = event.get_related() + + related_node_ids = [ + item["prefect.resource.id"] for item in related if item["prefect.resource.role"] == "infrahub.related.node" + ] + assert related_node_ids == [group_id] + assert len(related) == max_related + + +def test_related_node_filter_matches_old_and_new_group_event_formats() -> None: + """One broadened filter matches a member whether it carries the old or new role.""" + member_id = str(uuid4()) + filters = InfrahubEventFilter.from_filters(order=EventSortOrder.DESC, related_node__ids=[member_id]) + assert isinstance(filters.related, list) + spec = filters.related[-1].labels + assert spec is not None + + old_format = [ + RelatedResource( + root={ + "prefect.resource.id": member_id, + "prefect.resource.role": "infrahub.related.node", + "infrahub.node.kind": "TestPerson", + } + ) + ] + new_format = [ + RelatedResource( + root={ + "prefect.resource.id": member_id, + "prefect.resource.role": "infrahub.group.member", + "infrahub.node.kind": "TestPerson", + } + ) + ] + ancestor_format = [ + RelatedResource( + root={ + "prefect.resource.id": member_id, + "prefect.resource.role": "infrahub.group.ancestor", + "infrahub.node.kind": InfrahubKind.STANDARDGROUP, + } + ) + ] + other_id = [ + RelatedResource( + root={ + "prefect.resource.id": str(uuid4()), + "prefect.resource.role": "infrahub.group.member", + "infrahub.node.kind": "TestPerson", + } + ) + ] + + assert spec.includes(old_format) is True + assert spec.includes(new_format) is True + assert spec.includes(ancestor_format) is True + assert spec.includes(other_id) is False + + +def test_group_event_output_identical_across_old_and_new_formats() -> None: + """The event-query output is byte-identical whether the event is old or new format.""" + group_id = str(uuid4()) + members = [EventNode(id=str(uuid4()), kind="TestPerson") for _ in range(3)] + ancestors = [EventNode(id=str(uuid4()), kind=InfrahubKind.STANDARDGROUP) for _ in range(2)] + event = _make_member_added_event(node_id=group_id, members=members, ancestors=ancestors) + + new_event = _event_data(event, event.get_related()) + old_event = _event_data(event, _old_format_related(event)) + + expected_related_nodes = [{"id": node.id, "kind": node.kind} for node in members + ancestors] + assert new_event.get_related_nodes() == expected_related_nodes + assert old_event.get_related_nodes() == expected_related_nodes + + expected_group = { + "members": [{"id": member.id, "kind": member.kind} for member in members], + "ancestors": [{"id": ancestor.id, "kind": ancestor.kind} for ancestor in ancestors], + } + assert new_event._return_group_event() == expected_group + assert old_event._return_group_event() == expected_group diff --git a/changelog/10127.fixed.md b/changelog/10127.fixed.md new file mode 100644 index 00000000000..421376eedb5 --- /dev/null +++ b/changelog/10127.fixed.md @@ -0,0 +1 @@ +Fixed group mutation events (`member_added` / `member_removed`) being silently dropped when a single mutation changed a few hundred members or more. The event's related resources are now consolidated to one entry per member and per ancestor and capped at the Prefect maximum, so the event is always recorded and membership-driven automations keep firing regardless of how many members change at once. The full member list remains available in the event payload. diff --git a/dev/knowledge/backend/events.md b/dev/knowledge/backend/events.md index ec2527b3b81..295d2d53ac7 100644 --- a/dev/knowledge/backend/events.md +++ b/dev/knowledge/backend/events.md @@ -38,6 +38,19 @@ with a very large cardinality-many relationship therefore keeps its event, but not every peer is represented in `related`; the full peer list remains available in the event payload's changelog. +Group mutation events (`member_added` / `member_removed`) follow the same rule. +Each member and each ancestor is a single related resource carrying its own +role (`infrahub.group.member` / `infrahub.group.ancestor`) rather than a +role-plus-duplicate pair, so the list grows by one per member instead of two. +The fixed group-scoped entries come first and members/ancestors come last, so +the same ordered truncation keeps the event within the maximum. Group +automations match the primary group resource and read the changed members from +the payload, so truncating overflow members only trims the event-query display; +the event is always recorded and automations always fire. The event query +API treats members and ancestors as related nodes (matching all three roles and +deduplicating by id), which keeps its output stable across the consolidated +format and any older events still carrying the duplicate related-node role. + ## Event Types Events are organized by domain in `backend/infrahub/events/`: From b8deb2660c759b39341698665905cc46fe4b1d42 Mon Sep 17 00:00:00 2001 From: Guillaume Mazoyer Date: Mon, 10 Aug 2026 10:19:35 +0200 Subject: [PATCH 02/48] fix(pytest-plugin): run repository smoke tests first (#10171) The resource loop in sort_key wrote the resource priority to type_cost instead of item_cost. Every item carries both a type marker and a resource marker, so that loop always ran and always erased the type priority the first loop had computed. Items were therefore sorted by resource kind only, and an integration test could run before the smoke test of the same resource. Nothing covered the ordering, so add unit tests on the collection hook. --- backend/infrahub/pytest_plugin.py | 2 +- backend/tests/unit/test_pytest_plugin.py | 74 ++++++++++++++++++++++++ changelog/10170.fixed.md | 1 + 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 backend/tests/unit/test_pytest_plugin.py create mode 100644 changelog/10170.fixed.md diff --git a/backend/infrahub/pytest_plugin.py b/backend/infrahub/pytest_plugin.py index 398e75fce1e..a9bf6fe1c7d 100644 --- a/backend/infrahub/pytest_plugin.py +++ b/backend/infrahub/pytest_plugin.py @@ -77,7 +77,7 @@ def sort_key(item: pytest.Item) -> tuple[int, int]: item_cost = 99 for marker_name, priority in ORDER_ITEM_MAP.items(): if item.get_closest_marker(marker_name): - type_cost = priority + item_cost = priority break return type_cost, item_cost diff --git a/backend/tests/unit/test_pytest_plugin.py b/backend/tests/unit/test_pytest_plugin.py new file mode 100644 index 00000000000..2cb01c3afe9 --- /dev/null +++ b/backend/tests/unit/test_pytest_plugin.py @@ -0,0 +1,74 @@ +import pytest +from infrahub_sdk.client import Config as InfrahubClientConfig + +from infrahub.pytest_plugin import InfrahubBackendPlugin + + +class OrderingItem(pytest.Item): + def runtest(self) -> None: + raise NotImplementedError + + +def build_item(session: pytest.Session, name: str, markers: list[str]) -> pytest.Item: + item = OrderingItem.from_parent(session, name=name) + for marker in markers: + item.add_marker(getattr(pytest.mark, marker)) + return item + + +@pytest.fixture +def plugin() -> InfrahubBackendPlugin: + return InfrahubBackendPlugin( + config=InfrahubClientConfig(address="http://localhost:8000", api_token="token"), + repository_id="11111111-1111-1111-1111-111111111111", + proposed_change_id="22222222-2222-2222-2222-222222222222", + ) + + +def test_items_are_ordered_by_type_then_by_resource( + request: pytest.FixtureRequest, plugin: InfrahubBackendPlugin +) -> None: + items = [ + build_item(request.session, name, ["infrahub", *markers]) + for name, markers in ( + ("integration_check", ["infrahub_integration", "infrahub_check"]), + ("unit_python_transform", ["infrahub_unit", "infrahub_python_transform"]), + ("smoke_python_transform", ["infrahub_smoke", "infrahub_python_transform"]), + ("smoke_check", ["infrahub_smoke", "infrahub_check"]), + ) + ] + + plugin.pytest_collection_modifyitems(session=request.session, config=request.config, items=items) + + assert [item.name for item in items] == [ + "smoke_check", + "smoke_python_transform", + "unit_python_transform", + "integration_check", + ] + + +def test_items_without_the_infrahub_marker_are_discarded( + request: pytest.FixtureRequest, plugin: InfrahubBackendPlugin +) -> None: + items = [ + build_item(request.session, "unrelated", []), + build_item(request.session, "smoke_check", ["infrahub", "infrahub_smoke", "infrahub_check"]), + ] + + plugin.pytest_collection_modifyitems(session=request.session, config=request.config, items=items) + + assert [item.name for item in items] == ["smoke_check"] + + +def test_items_without_a_known_marker_are_ordered_last( + request: pytest.FixtureRequest, plugin: InfrahubBackendPlugin +) -> None: + items = [ + build_item(request.session, "unknown", ["infrahub"]), + build_item(request.session, "integration_check", ["infrahub", "infrahub_integration", "infrahub_check"]), + ] + + plugin.pytest_collection_modifyitems(session=request.session, config=request.config, items=items) + + assert [item.name for item in items] == ["integration_check", "unknown"] diff --git a/changelog/10170.fixed.md b/changelog/10170.fixed.md new file mode 100644 index 00000000000..c1a06710942 --- /dev/null +++ b/changelog/10170.fixed.md @@ -0,0 +1 @@ +Fixed the ordering of repository tests in a proposed change. Smoke tests now run before unit tests, and unit tests before integration tests, instead of being ordered by resource kind only. From ff8a1fd6b2437ef59daf05934b7d008e053accfa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 09:04:59 +0000 Subject: [PATCH 03/48] chore(deps): bump dorny/paths-filter from 4.0.2 to 4.0.3 Bumps [dorny/paths-filter](https://github.com/dorny/paths-filter) from 4.0.2 to 4.0.3. - [Release notes](https://github.com/dorny/paths-filter/releases) - [Changelog](https://github.com/dorny/paths-filter/blob/master/CHANGELOG.md) - [Commits](https://github.com/dorny/paths-filter/compare/v4.0.2...v4.0.3) --- updated-dependencies: - dependency-name: dorny/paths-filter dependency-version: 4.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c19a715aa79..f9ac1bfee8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: with: submodules: true - name: Check for file changes - uses: dorny/paths-filter@v4.0.2 + uses: dorny/paths-filter@v4.0.3 id: changes with: token: ${{ github.token }} From 487e55d25eb3b0f4d38a692c4dfffe5304dfc21e Mon Sep 17 00:00:00 2001 From: Saltaferis Dimitrios Date: Mon, 10 Aug 2026 12:35:15 +0300 Subject: [PATCH 04/48] fix(frontend): render date-format previews in the timezone being edited MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "Example:" preview and the (i) source tooltip in the preferences forms formatted through an ad-hoc date-fns format() call that took no timezone, so they showed browser-local time — off by the zone offset, and at some hours by a whole calendar day (with ISO_8601 they also stated a false offset). The duplicate formatter is deleted; previews now render through the shared preference-aware mechanism, exposed as formatWithPreferences, using the unsaved timezone the form currently holds. The user form falls back to the effective inherited zone, while the global editor falls back to the browser zone, since an unset global timezone means "browser default" for every viewer. Fixes #10175 Co-Authored-By: Claude Fable 5 --- changelog/10175.fixed.md | 1 + .../preferences/domain/rules/date-format.ts | 8 +-- .../ui/global-preferences-editor.test.tsx | 37 ++++++++-- .../preferences/ui/preference-fields.test.tsx | 54 +++++++++++++++ .../preferences/ui/preference-fields.tsx | 26 +++++-- .../preferences/ui/preferences-form.tsx | 6 +- .../ui/user-preferences-card.test.tsx | 69 +++++++++++++++++-- .../context/date-preferences-context.tsx | 54 +++++++++------ 8 files changed, 210 insertions(+), 45 deletions(-) create mode 100644 changelog/10175.fixed.md create mode 100644 frontend/app/src/entities/preferences/ui/preference-fields.test.tsx diff --git a/changelog/10175.fixed.md b/changelog/10175.fixed.md new file mode 100644 index 00000000000..60820e3d896 --- /dev/null +++ b/changelog/10175.fixed.md @@ -0,0 +1 @@ +Fixed the date-format "Example" preview (and the source info tooltip) in the preferences forms rendering in the browser's timezone instead of the timezone preference being edited, which could show a time off by the zone offset or even the wrong calendar day. diff --git a/frontend/app/src/entities/preferences/domain/rules/date-format.ts b/frontend/app/src/entities/preferences/domain/rules/date-format.ts index 23214597adf..547beabea9b 100644 --- a/frontend/app/src/entities/preferences/domain/rules/date-format.ts +++ b/frontend/app/src/entities/preferences/domain/rules/date-format.ts @@ -1,5 +1,3 @@ -import { format } from "date-fns"; - import { DATE_FORMAT_KEYS, DATE_FORMAT_PRESETS, @@ -17,6 +15,7 @@ export function buildDateFormatPresets(): Array { return DATE_FORMAT_KEYS.map((key) => ({ key, label: DATE_FORMAT_PRESETS[key].label })); } +// An unknown/invalid key (e.g. written by an out-of-date client or the SDK) falls back to the default pattern so dates still render. export function dateFormatPattern(key: string): string { return ( (DATE_FORMAT_PRESETS as Record)[key]?.pattern ?? @@ -27,8 +26,3 @@ export function dateFormatPattern(key: string): string { export function dateFormatLabel(key: string): string { return (DATE_FORMAT_PRESETS as Record)[key]?.label ?? key; } - -// An unknown/invalid key (e.g. written by an out-of-date client or the SDK) falls back to the default pattern so it still yields a real example. -export function formatDateFormatExample(key: string, referenceDate: Date = new Date()): string { - return format(referenceDate, dateFormatPattern(key)); -} diff --git a/frontend/app/src/entities/preferences/ui/global-preferences-editor.test.tsx b/frontend/app/src/entities/preferences/ui/global-preferences-editor.test.tsx index d4357fe59ef..63db8bc3009 100644 --- a/frontend/app/src/entities/preferences/ui/global-preferences-editor.test.tsx +++ b/frontend/app/src/entities/preferences/ui/global-preferences-editor.test.tsx @@ -1,3 +1,4 @@ +import { format } from "date-fns"; import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; import type { GlobalPreferences } from "@/entities/preferences/domain/model/preference"; @@ -10,12 +11,16 @@ import { GlobalPreferencesEditor } from "./global-preferences-editor"; vi.mock("@/entities/preferences/domain/use-cases/get-global-preferences"); vi.mock("@/entities/preferences/domain/use-cases/update-global-preference"); -const baseGlobal: GlobalPreferences = { dateFormat: null, timezone: "Europe/Paris" }; +// A late-evening UTC instant: rendered in the global zone (UTC+9) it lands on the NEXT calendar day, +// so an example that ignored the timezone being edited could not accidentally match. +const FIXED_INSTANT = new Date("2026-06-11T23:30:00Z"); + +const baseGlobal: GlobalPreferences = { dateFormat: null, timezone: "Asia/Tokyo" }; describe("GlobalPreferencesEditor", () => { beforeEach(() => { vi.useFakeTimers({ toFake: ["Date"] }); - vi.setSystemTime(new Date("2026-06-30T14:30:00")); + vi.setSystemTime(FIXED_INSTANT); vi.clearAllMocks(); vi.mocked(getGlobalPreferences).mockResolvedValue(baseGlobal); vi.mocked(updateGlobalPreference).mockResolvedValue(); @@ -50,13 +55,37 @@ describe("GlobalPreferencesEditor", () => { await component.getByRole("option", { name: "yyyy-MM-dd HH:mm", exact: true }).click(); const combobox = component.getByRole("button", { name: /date format/i }).element(); - const example = component.getByText("Example: 2026-06-30 14:30").element(); + const example = component.getByText("Example: 2026-06-12 08:30").element(); const row = combobox.closest("div.flex.items-center") as HTMLElement; expect(row).not.toBeNull(); expect(row.contains(example)).toBe(true); }); + test("renders the example in the global timezone being edited", async () => { + const component = await render(); + + await component.getByRole("button", { name: /date format/i }).click(); + await component.getByRole("option", { name: "yyyy-MM-dd HH:mm", exact: true }).click(); + + await expect.element(component.getByText("Example: 2026-06-12 08:30")).toBeVisible(); + }); + + test("renders the example in the browser zone when the global timezone is unset", async () => { + vi.mocked(getGlobalPreferences).mockResolvedValue({ dateFormat: null, timezone: null }); + + const component = await render(); + + await component.getByRole("button", { name: /date format/i }).click(); + await component.getByRole("option", { name: "yyyy-MM-dd HH:mm", exact: true }).click(); + + // An unset global timezone means "the browser's" for every viewer, so the preview must not + // borrow a zone from anywhere else — least of all the editing admin's own preference. + await expect + .element(component.getByText(`Example: ${format(FIXED_INSTANT, "yyyy-MM-dd HH:mm")}`)) + .toBeVisible(); + }); + test("edits the raw global values via the global mutation", async () => { const component = await render(); @@ -68,7 +97,7 @@ describe("GlobalPreferencesEditor", () => { await vi.waitFor(() => { expect(vi.mocked(updateGlobalPreference).mock.calls[0]?.[0]).toEqual({ dateFormat: "EU_DATETIME", - timezone: "Europe/Paris", + timezone: "Asia/Tokyo", }); }); }); diff --git a/frontend/app/src/entities/preferences/ui/preference-fields.test.tsx b/frontend/app/src/entities/preferences/ui/preference-fields.test.tsx new file mode 100644 index 00000000000..df3c23391a0 --- /dev/null +++ b/frontend/app/src/entities/preferences/ui/preference-fields.test.tsx @@ -0,0 +1,54 @@ +import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; + +import { Form } from "@/shared/components/ui/form"; + +import { DateFormatField, toFieldValue } from "@/entities/preferences/ui/preference-fields"; + +import { render } from "../../../../tests/components/render"; + +// A late-evening UTC instant: rendered east of UTC it lands on the NEXT calendar day, so an example +// that ignored the timezone could not accidentally match. +const FIXED_INSTANT = new Date("2026-06-11T23:30:00Z"); + +function renderField({ + timezone, + fallbackTimezone, +}: { + timezone: string | null; + fallbackTimezone?: string | null; +}) { + return render( +
{}} + > + + + ); +} + +describe("DateFormatField example", () => { + beforeEach(() => { + vi.useFakeTimers({ toFake: ["Date"] }); + vi.setSystemTime(FIXED_INSTANT); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + test("renders in the timezone held by the form, not the one it would fall back to", async () => { + const component = await renderField({ timezone: "UTC", fallbackTimezone: "Asia/Tokyo" }); + + await expect.element(component.getByText("Example: 2026-06-11 23:30")).toBeVisible(); + }); + + test("renders in the fallback timezone while the form's timezone field is empty", async () => { + const component = await renderField({ timezone: null, fallbackTimezone: "Asia/Tokyo" }); + + await expect.element(component.getByText("Example: 2026-06-12 08:30")).toBeVisible(); + }); +}); diff --git a/frontend/app/src/entities/preferences/ui/preference-fields.tsx b/frontend/app/src/entities/preferences/ui/preference-fields.tsx index ff1e603f25b..a16d2395d4e 100644 --- a/frontend/app/src/entities/preferences/ui/preference-fields.tsx +++ b/frontend/app/src/entities/preferences/ui/preference-fields.tsx @@ -9,12 +9,13 @@ import { DEFAULT_FORM_FIELD_VALUE } from "@/shared/components/form/constants"; import type { FormAttributeValue } from "@/shared/components/form/type"; import { Combobox, type ComboboxItem } from "@/shared/components/inputs/combobox"; import { FormField } from "@/shared/components/ui/form"; +import { formatWithPreferences } from "@/shared/context/date-preferences-context"; import type { Preference } from "@/entities/preferences/domain/model/preference"; import { buildDateFormatPresets, dateFormatLabel, - formatDateFormatExample, + dateFormatPattern, } from "@/entities/preferences/domain/rules/date-format"; const EMPTY_VALUE_LABEL = "Automatic (inherited)"; @@ -74,10 +75,17 @@ interface PreferenceFieldProps { emptyValueLabel?: string; } +interface DateFormatFieldProps extends PreferenceFieldProps { + /** Zone the examples fall back to while the form's timezone field is empty. Omit to fall back to + * the browser's — which is what an empty *global* timezone means for every viewer. */ + fallbackTimezone?: string | null; +} + export function DateFormatField({ preference, emptyValueLabel = EMPTY_VALUE_LABEL, -}: PreferenceFieldProps) { + fallbackTimezone, +}: DateFormatFieldProps) { const now = new Date(); const exampleId = React.useId(); const items = buildDateFormatPresets().map(({ key, label }) => ({ value: key, label })); @@ -85,11 +93,17 @@ export function DateFormatField({ const fieldValue = useWatch({ name: "date_format" }) as FormAttributeValue | undefined; const selected = (fieldValue?.value as string | null | undefined) ?? null; + // The examples preview what the timestamps will look like once saved, so they render in the zone + // the form currently holds — including an unsaved edit to the timezone field. + const timezoneValue = useWatch({ name: "timezone" }) as FormAttributeValue | undefined; + const timezone = (timezoneValue?.value as string | null | undefined) ?? fallbackTimezone ?? null; + const example = (key: string) => + formatWithPreferences(now, { pattern: dateFormatPattern(key), timezone }); + const message = preference ? sourceMessage(preference, { - formatGlobalValue: (value) => - `${formatDateFormatExample(value, now)} (${dateFormatLabel(value)})`, - browserValue: now.toLocaleString(), + formatGlobalValue: (value) => `${example(value)} (${dateFormatLabel(value)})`, + browserValue: formatWithPreferences(now, { pattern: null, timezone }), }) : null; @@ -116,7 +130,7 @@ export function DateFormatField({
{selected && (

- Example: {formatDateFormatExample(selected, now)} + Example: {example(selected)}

)}
diff --git a/frontend/app/src/entities/preferences/ui/preferences-form.tsx b/frontend/app/src/entities/preferences/ui/preferences-form.tsx index 980288ee8a3..678d7cc6de9 100644 --- a/frontend/app/src/entities/preferences/ui/preferences-form.tsx +++ b/frontend/app/src/entities/preferences/ui/preferences-form.tsx @@ -9,6 +9,7 @@ import { Form, FormSubmit } from "@/shared/components/ui/form"; import type { DateFormatKey } from "@/entities/preferences/domain/model/date-format"; import type { Preference, PreferenceValues } from "@/entities/preferences/domain/model/preference"; +import { resolveDatePreferences } from "@/entities/preferences/domain/rules/resolve-date-preferences"; import { DateFormatField, TimezoneField, @@ -75,7 +76,10 @@ export function PreferencesForm() { }} className="space-y-0 divide-y divide-gray-200" > - + diff --git a/frontend/app/src/entities/preferences/ui/user-preferences-card.test.tsx b/frontend/app/src/entities/preferences/ui/user-preferences-card.test.tsx index 8a2f32a3c2a..021d5654a94 100644 --- a/frontend/app/src/entities/preferences/ui/user-preferences-card.test.tsx +++ b/frontend/app/src/entities/preferences/ui/user-preferences-card.test.tsx @@ -11,16 +11,22 @@ import { UserPreferencesCard } from "./user-preferences-card"; vi.mock("@/entities/preferences/domain/use-cases/get-effective-preferences"); vi.mock("@/entities/preferences/domain/use-cases/upsert-user-preferences"); +// A late-evening UTC instant: rendered in the effective zone (UTC+9) it lands on the NEXT calendar +// day, so an example that ignored the timezone preference could not accidentally match. A zone-less +// literal would be parsed as browser-local and make every assertion below zone-agnostic. +const FIXED_INSTANT = new Date("2026-06-11T23:30:00Z"); +const EFFECTIVE_ZONE = "Asia/Tokyo"; + const baseEffective: EffectivePreferences = { dateFormat: { value: "EU_DATETIME", source: "GLOBAL" }, - timezone: { value: "Europe/Paris", source: "GLOBAL" }, + timezone: { value: EFFECTIVE_ZONE, source: "GLOBAL" }, }; describe("UserPreferencesCard", () => { beforeEach(() => { // Freeze the clock: preset labels embed a live date example. vi.useFakeTimers({ toFake: ["Date"] }); - vi.setSystemTime(new Date("2026-06-30T14:30:00")); + vi.setSystemTime(FIXED_INSTANT); vi.clearAllMocks(); vi.mocked(getEffectivePreferences).mockResolvedValue(baseEffective); vi.mocked(upsertUserPreferences).mockResolvedValue(); @@ -92,12 +98,32 @@ describe("UserPreferencesCard", () => { await component.getByRole("button", { name: /date format/i }).click(); await component.getByRole("option", { name: "yyyy-MM-dd HH:mm", exact: true }).click(); - await expect.element(component.getByText("Example: 2026-06-30 14:30")).toBeVisible(); + await expect.element(component.getByText("Example: 2026-06-12 08:30")).toBeVisible(); await component.getByRole("button", { name: /date format/i }).click(); await component.getByRole("option", { name: "dd/MM/yyyy HH:mm", exact: true }).click(); - await expect.element(component.getByText("Example: 30/06/2026 14:30")).toBeVisible(); + await expect.element(component.getByText("Example: 12/06/2026 08:30")).toBeVisible(); + }); + + test("renders the example in the effective timezone, not the browser's", async () => { + const component = await render(); + + await component.getByRole("button", { name: /date format/i }).click(); + await component.getByRole("option", { name: "yyyy-MM-dd HH:mm", exact: true }).click(); + + // 23:30Z is 08:30 the next day in Asia/Tokyo; the browser-zone rendering would still say the 11th. + await expect.element(component.getByText("Example: 2026-06-12 08:30")).toBeVisible(); + expect(component.getByText(/^Example: 2026-06-11/).elements()).toHaveLength(0); + }); + + test("renders the example's offset from the effective timezone, not the browser's", async () => { + const component = await render(); + + await component.getByRole("button", { name: /date format/i }).click(); + await component.getByRole("option", { name: "yyyy-MM-dd'T'HH:mm:ssXXX", exact: true }).click(); + + await expect.element(component.getByText("Example: 2026-06-12T08:30:00+09:00")).toBeVisible(); }); test("renders the live example inline, on the same row as the date-format control", async () => { @@ -108,7 +134,7 @@ describe("UserPreferencesCard", () => { await component.getByRole("option", { name: "dd/MM/yyyy HH:mm", exact: true }).click(); const combobox = component.getByRole("button", { name: /date format/i }).element(); - const example = component.getByText("Example: 30/06/2026 14:30").element(); + const example = component.getByText("Example: 12/06/2026 08:30").element(); const row = combobox.closest("div.flex.items-center") as HTMLElement; expect(row).not.toBeNull(); @@ -127,7 +153,7 @@ describe("UserPreferencesCard", () => { await component.getByRole("button", { name: /date format/i }).click(); await component.getByRole("option", { name: "dd/MM/yyyy HH:mm", exact: true }).click(); - await expect.element(component.getByText("Example: 30/06/2026 14:30")).toBeVisible(); + await expect.element(component.getByText("Example: 12/06/2026 08:30")).toBeVisible(); await component.getByRole("button", { name: /date format/i }).click(); @@ -185,7 +211,7 @@ describe("UserPreferencesCard", () => { await expect .element( component.getByRole("tooltip", { - name: /from the organisation default: 30\/06\/2026 14:30 \(dd\/MM\/yyyy HH:mm\)/i, + name: /from the organisation default: 12\/06\/2026 08:30 \(dd\/MM\/yyyy HH:mm\)/i, }) ) .toBeVisible(); @@ -217,6 +243,35 @@ describe("UserPreferencesCard", () => { await initPointerTracking(component.locator); }); + test("the (i) tooltip's browser-locale example honours the timezone preference", async () => { + vi.mocked(getEffectivePreferences).mockResolvedValue({ + ...baseEffective, + dateFormat: { value: null, source: "DEFAULT" }, + }); + + const component = await render(); + + await expect.element(component.getByRole("button", { name: /date format/i })).toBeVisible(); + + // No date-format preference means the browser's locale renders it — still in the preferred zone. + const expected = FIXED_INSTANT.toLocaleString(undefined, { + dateStyle: "medium", + timeStyle: "short", + timeZone: EFFECTIVE_ZONE, + }); + + const triggers = component.getByRole("button", { name: "Where this value comes from" }); + await initPointerTracking(component.locator); + await triggers.first().hover(); + + await expect + .element(component.getByRole("tooltip", { name: `From your browser: ${expected}.` })) + .toBeVisible(); + + // Park the pointer away from the trigger so the tooltip closes before the next test renders. + await initPointerTracking(component.locator); + }); + test("the (i) tooltip falls back to the browser source when neither user nor global is set", async () => { vi.mocked(getEffectivePreferences).mockResolvedValue({ ...baseEffective, diff --git a/frontend/app/src/shared/context/date-preferences-context.tsx b/frontend/app/src/shared/context/date-preferences-context.tsx index ecd70559c0f..e991b019413 100644 --- a/frontend/app/src/shared/context/date-preferences-context.tsx +++ b/frontend/app/src/shared/context/date-preferences-context.tsx @@ -38,32 +38,46 @@ export interface UseFormatDateResult { timezone: string | null; } -/** Renders dates against the active preferences. `"date"` reuses the datetime pattern's date part. */ -export function useFormatDate(): UseFormatDateResult { - const resolved = React.use(DatePreferencesContext); - const pattern = resolved?.pattern ?? null; - const timezone = resolved?.timezone ?? null; +/** + * Renders a date against a resolved preference pair. `"date"` reuses the datetime pattern's date + * part. Take this over the hook only when the pair is not the active one — previewing preferences + * still being edited, say; everything else must go through the hook so it follows the viewer. + */ +export function formatWithPreferences( + date: DateInput, + { pattern, timezone }: ResolvedDatePreferences, + variant: DateVariant = "datetime" +): string { + if (variant === "relative") { + return formatRelativeTimeFromNow(date); + } - const boundFormat = (date: DateInput, variant: DateVariant = "datetime"): string => { - if (variant === "relative") { - return formatRelativeTimeFromNow(date); - } + if (!pattern) { + return formatWithLocale(date, variant, timezone); + } - if (!pattern) { - return formatWithLocale(date, variant, timezone); - } + if (variant === "date") { + const datePattern = dateOnlyPattern(pattern); + return datePattern + ? formatWithPattern(date, { pattern: datePattern, timezone }) + : formatWithLocale(date, "date", timezone); + } - if (variant === "date") { - const datePattern = dateOnlyPattern(pattern); - return datePattern - ? formatWithPattern(date, { pattern: datePattern, timezone }) - : formatWithLocale(date, "date", timezone); - } + return formatWithPattern(date, { pattern, timezone }); +} - return formatWithPattern(date, { pattern, timezone }); +/** Renders dates against the active preferences. */ +export function useFormatDate(): UseFormatDateResult { + const resolved = React.use(DatePreferencesContext); + const preferences: ResolvedDatePreferences = { + pattern: resolved?.pattern ?? null, + timezone: resolved?.timezone ?? null, }; - return { formatDate: boundFormat, timezone }; + return { + formatDate: (date, variant) => formatWithPreferences(date, preferences, variant), + timezone: preferences.timezone, + }; } // Drops everything from the first time token onward, e.g. "yyyy-MM-dd HH:mm" → "yyyy-MM-dd". From 76f8fe3ec605e8a556a1fff07d98704514433d51 Mon Sep 17 00:00:00 2001 From: Saltaferis Dimitrios Date: Mon, 10 Aug 2026 12:35:27 +0300 Subject: [PATCH 05/48] fix(frontend): show far-future dates in the preferred format, not relative MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DateDisplay's compact/relative heuristic compared the day difference with a signed value, so any future date — however distant — fell through to the relative branch and rendered as "in 4 years" instead of the user's preferred date format. The window is now a week either side of now, keeping the relative phrase where it reads best and the preferred date beyond. Fixes #10173 Co-Authored-By: Claude Fable 5 --- changelog/10173.fixed.md | 1 + dev/knowledge/frontend/date-rendering.md | 15 ++++++++++++--- .../components/display/date-display.test.tsx | 15 +++++++++++++++ .../shared/components/display/date-display.tsx | 5 +++-- 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 changelog/10173.fixed.md diff --git a/changelog/10173.fixed.md b/changelog/10173.fixed.md new file mode 100644 index 00000000000..47850fba35b --- /dev/null +++ b/changelog/10173.fixed.md @@ -0,0 +1 @@ +Fixed future dates more than a week away rendering as a relative phrase (such as "in 4 years") instead of the user's preferred date format. diff --git a/dev/knowledge/frontend/date-rendering.md b/dev/knowledge/frontend/date-rendering.md index 953bf69df70..71fabe9961e 100644 --- a/dev/knowledge/frontend/date-rendering.md +++ b/dev/knowledge/frontend/date-rendering.md @@ -7,8 +7,8 @@ and never a hardcoded pattern. ## Use one of these - **Rendering JSX → ``** (`shared/components/display/date-display.tsx`). - - Default: relative "x ago" for recent dates, a compact date otherwise; the **tooltip** shows the - user's full preferred datetime, rendered in their preferred timezone. + - Default: relative ("x ago" / "in x") within a week either side of now, a compact date beyond; + the **tooltip** shows the user's full preferred datetime, rendered in their preferred timezone. - `fullTimestamp`: render the user's full preferred datetime inline, in their preferred timezone (use for a site that shows a full timestamp). - The value renders *in* the preferred timezone, but an offset/label only shows when the chosen @@ -18,6 +18,15 @@ and never a hardcoded pattern. - **Need a date *string* in code → `useFormatDate()`** (`shared/context/date-preferences-context.tsx`): `const { formatDate } = useFormatDate();` then `formatDate(date, variant?)` with `variant ∈ "datetime" (default) | "date" | "relative"`. +- **Previewing preferences that are not the active ones → `formatWithPreferences()`** (same + module): the hook's underlying pure function, for the rare caller that must render against an + explicit `{ pattern, timezone }` pair — e.g. the preferences forms' "Example:", which previews the + unsaved form values. Anything rendering against the *viewer's* preferences uses the hook, never this. +- **Rendering against preferences that are *not* the viewer's active ones → `formatWithPreferences`** + (same module, backs the hook). Only the preferences editor needs this: its "Example:" preview and + source tooltip must render the pattern *and zone* currently held in the form, including unsaved + edits, so the preview matches what the timestamps will become. An editor of the org-wide default + must not reach for the hook — that would preview everyone's default in the admin's own zone. ## How it's wired (feature-sliced-design safe) @@ -33,7 +42,7 @@ and never a hardcoded pattern. falls back to the **browser locale + zone** (`toLocaleString`) — never a hardcoded pattern. So `DateDisplay`/`useFormatDate` are always safe to use, including in tests/stories. - Timezone rendering uses date-fns v4 + the first-party **`@date-fns/tz`** (`TZDate`). The semantic - `date_format` key → date-fns pattern mapping is `patternForKey` + `date_format` key → date-fns pattern mapping is `dateFormatPattern` (`entities/preferences/domain/rules/date-format.ts`); the `date` variant derives a date-only pattern by stripping the preferred pattern at its first time token. diff --git a/frontend/app/src/shared/components/display/date-display.test.tsx b/frontend/app/src/shared/components/display/date-display.test.tsx index 1c0d910d7f5..f5a719fcdd1 100644 --- a/frontend/app/src/shared/components/display/date-display.test.tsx +++ b/frontend/app/src/shared/components/display/date-display.test.tsx @@ -42,6 +42,13 @@ describe("DateDisplay", () => { await expect.element(component.getByText("2 days ago")).toBeVisible(); }); + test("relative branch: near-future date shows 'in x'", async () => { + // Within a week ahead, a relative phrase still reads best — same window as past dates. + const inThreeDays = new Date("2026-06-14T14:30:00Z"); + const component = await render(withPrefs()); + await expect.element(component.getByText("in 3 days")).toBeVisible(); + }); + test("compact branch: old date renders the preferred date (honouring format + timezone)", async () => { // > 7 days old, so the compact branch fires. It must use the preferred date pattern in the // preferred zone — 2026-01-15 14:30 UTC is still 2026-01-15 in Paris — not a fixed browser-zone @@ -51,6 +58,14 @@ describe("DateDisplay", () => { await expect.element(component.getByText("2026-01-15")).toBeVisible(); }); + test("compact branch: far-future date renders the preferred date, not a relative phrase", async () => { + // > 7 days ahead — beyond the week window in either direction the reader needs the actual + // date in the preferred format, not something like "in 5 months". + const farFuture = new Date("2026-11-18T14:30:00Z"); + const component = await render(withPrefs()); + await expect.element(component.getByText("2026-11-18")).toBeVisible(); + }); + test("compact branch: falls back to the browser-locale date when no preference is set", async () => { const old = new Date("2026-01-15T14:30:00Z"); const component = await render( diff --git a/frontend/app/src/shared/components/display/date-display.tsx b/frontend/app/src/shared/components/display/date-display.tsx index d161f0dcf29..eb5f494ea42 100644 --- a/frontend/app/src/shared/components/display/date-display.tsx +++ b/frontend/app/src/shared/components/display/date-display.tsx @@ -42,8 +42,9 @@ export const DateDisplay = ({ return wrap(tooltipMessage); } - // > 7 days old → preferred date; recent → "x ago". - if (differenceInDays(new Date(), dateData) > 7) { + // Within a week of now — either direction — a relative phrase reads best; farther out it + // loses the precision the reader needs, so the preferred date takes over. + if (Math.abs(differenceInDays(new Date(), dateData)) > 7) { return wrap(formatDate(dateData, "date")); } From 21483b4ec4aa288cc2cceddf1b8f921401322a92 Mon Sep 17 00:00:00 2001 From: Saltaferis Dimitrios Date: Mon, 10 Aug 2026 12:35:38 +0300 Subject: [PATCH 06/48] fix(frontend): keep the time on DateTime attributes on object details The object detail page's DateTime attribute renderer entered DateDisplay's metadata-oriented age heuristic, so user-authored timestamps collapsed to a bare date (or a relative phrase) while the list view showed the full preferred datetime. The detail renderer now opts out with fullTimestamp, matching the table cell. Fixes #10172 Co-Authored-By: Claude Fable 5 --- changelog/10172.fixed.md | 1 + .../nodes/getObjectItemDisplayValue.test.tsx | 69 +++++++++++++++++++ .../nodes/getObjectItemDisplayValue.tsx | 4 +- 3 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 changelog/10172.fixed.md create mode 100644 frontend/app/src/entities/nodes/getObjectItemDisplayValue.test.tsx diff --git a/changelog/10172.fixed.md b/changelog/10172.fixed.md new file mode 100644 index 00000000000..cf38b6508a1 --- /dev/null +++ b/changelog/10172.fixed.md @@ -0,0 +1 @@ +Fixed DateTime attributes on object detail pages dropping the time component of the user's preferred date format (or showing a relative phrase), while the list view rendered the full datetime. diff --git a/frontend/app/src/entities/nodes/getObjectItemDisplayValue.test.tsx b/frontend/app/src/entities/nodes/getObjectItemDisplayValue.test.tsx new file mode 100644 index 00000000000..7158c6dc7c0 --- /dev/null +++ b/frontend/app/src/entities/nodes/getObjectItemDisplayValue.test.tsx @@ -0,0 +1,69 @@ +import type React from "react"; +import { afterEach, beforeEach, describe, expect, test, vi } from "vitest"; + +import { + DatePreferencesContext, + type ResolvedDatePreferences, +} from "@/shared/context/date-preferences-context"; + +import { type FieldSchema, ObjectAttributeValue } from "@/entities/nodes/getObjectItemDisplayValue"; +import type { NodeAttributeWithMetadata } from "@/entities/nodes/object/domain/model/node"; + +import { render } from "../../../tests/components/render"; + +// Fixed "now" so any age-based rendering heuristic is deterministic. +const FIXED_INSTANT = new Date("2026-06-11T14:30:00Z"); + +const TOKYO_PREFS: ResolvedDatePreferences = { + pattern: "yyyy-MM-dd HH:mm:ss", + timezone: "Asia/Tokyo", +}; + +const dateTimeSchema = { name: "expiration", kind: "DateTime" } as FieldSchema; + +function attributeValue(value: string): NodeAttributeWithMetadata { + return { value } as NodeAttributeWithMetadata; +} + +function withPrefs(node: React.ReactElement) { + return {node}; +} + +describe("ObjectAttributeValue", () => { + beforeEach(() => { + vi.useFakeTimers({ toFake: ["Date"] }); + vi.setSystemTime(FIXED_INSTANT); + }); + afterEach(() => { + vi.useRealTimers(); + }); + + test("DateTime attribute renders the full preferred datetime, even months in the past", async () => { + // A DateTime attribute is user data: however old, it must keep the time component and the + // preferred zone — 09:30 UTC is 18:30 in Tokyo — not collapse to a bare date. + const component = await render( + withPrefs( + + ) + ); + + await expect.element(component.getByText("2026-01-15 18:30:00")).toBeVisible(); + }); + + test("DateTime attribute renders the full preferred datetime for recent values too", async () => { + // Two days old — data attributes never degrade to a relative "x ago" phrase. + const component = await render( + withPrefs( + + ) + ); + + await expect.element(component.getByText("2026-06-09 23:30:00")).toBeVisible(); + }); +}); diff --git a/frontend/app/src/entities/nodes/getObjectItemDisplayValue.tsx b/frontend/app/src/entities/nodes/getObjectItemDisplayValue.tsx index 444b1f19dcc..12bd0306135 100644 --- a/frontend/app/src/entities/nodes/getObjectItemDisplayValue.tsx +++ b/frontend/app/src/entities/nodes/getObjectItemDisplayValue.tsx @@ -221,7 +221,9 @@ export const ObjectAttributeValue = ({ case ATTRIBUTE_KIND.CHECKBOX: return attributeData.value ? : ; case ATTRIBUTE_KIND.DATETIME: - return ; + // User-authored data, not metadata: always the full preferred datetime, never an + // age-dependent compact/relative form. + return ; case ATTRIBUTE_KIND.TEXTAREA: return ; case ATTRIBUTE_KIND.PASSWORD: From 3daed1d46a749f935b3c8f72aa8a802c897d7f29 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 12:46:06 +0300 Subject: [PATCH 07/48] chore(deps): bump CodSpeedHQ/action from 4 to 5 (#10191) Bumps [CodSpeedHQ/action](https://github.com/codspeedhq/action) from 4 to 5. - [Release notes](https://github.com/codspeedhq/action/releases) - [Changelog](https://github.com/CodSpeedHQ/action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codspeedhq/action/compare/v4...v5) --- updated-dependencies: - dependency-name: CodSpeedHQ/action dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f9ac1bfee8f..c73a9141eca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1528,7 +1528,7 @@ jobs: - name: Run all benchmarks if: contains(github.event.pull_request.labels.*.name, 'ci/run-intensive-benchmarks') - uses: CodSpeedHQ/action@v4 + uses: CodSpeedHQ/action@v5 with: token: ${{ secrets.CODSPEED_TOKEN }} mode: instrumentation @@ -1537,7 +1537,7 @@ jobs: - name: Run non-intensive benchmarks if: | !contains(github.event.pull_request.labels.*.name, 'ci/run-intensive-benchmarks') - uses: CodSpeedHQ/action@v4 + uses: CodSpeedHQ/action@v5 with: token: ${{ secrets.CODSPEED_TOKEN }} mode: instrumentation From d0f22cf9778f576c694a44a5a2f2974af3ec1694 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 12:47:45 +0300 Subject: [PATCH 08/48] chore(deps): bump actions/stale from 10 to 11 (#10192) Bumps [actions/stale](https://github.com/actions/stale) from 10 to 11. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v10...v11) --- updated-dependencies: - dependency-name: actions/stale dependency-version: '11' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/manage-stale-prs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/manage-stale-prs.yml b/.github/workflows/manage-stale-prs.yml index 9b3375553a8..d9490eeef60 100644 --- a/.github/workflows/manage-stale-prs.yml +++ b/.github/workflows/manage-stale-prs.yml @@ -16,7 +16,7 @@ jobs: if: github.repository == 'opsmill/infrahub' runs-on: ubuntu-latest steps: - - uses: actions/stale@v10 + - uses: actions/stale@v11 with: # Only manage PRs, not issues days-before-issue-stale: -1 From 0f67085c5b4a917b0d745280c94de53c4f89ddbe Mon Sep 17 00:00:00 2001 From: Saltaferis Dimitrios Date: Mon, 10 Aug 2026 16:07:34 +0300 Subject: [PATCH 09/48] fix(frontend): preview the inherited zone when a personal timezone is cleared The date-format example fell back to the caller's effective timezone while the form's timezone field was empty. When that effective value came from the caller's own override, clearing the override previewed the zone being removed rather than the one about to apply. The fallback is now the inherited zone, expressed as a domain rule: a USER source resolves to null, since the API resolves the inherited value away once an override wins and the browser zone is the only honest stand-in. Also trims the comments added across these fixes down to the load-bearing rationale, per the repo's code-documentation style. Co-Authored-By: Claude Fable 5 --- .../nodes/getObjectItemDisplayValue.test.tsx | 4 +--- .../nodes/getObjectItemDisplayValue.tsx | 3 +-- .../rules/resolve-date-preferences.test.ts | 21 ++++++++++++++++++- .../domain/rules/resolve-date-preferences.ts | 15 ++++++++++++- .../ui/global-preferences-editor.test.tsx | 7 +++---- .../preferences/ui/preference-fields.test.tsx | 4 ++-- .../preferences/ui/preference-fields.tsx | 6 ++---- .../preferences/ui/preferences-form.tsx | 4 ++-- .../ui/user-preferences-card.test.tsx | 5 ++--- .../components/display/date-display.test.tsx | 4 +--- .../components/display/date-display.tsx | 3 +-- .../context/date-preferences-context.tsx | 5 ++--- 12 files changed, 51 insertions(+), 30 deletions(-) diff --git a/frontend/app/src/entities/nodes/getObjectItemDisplayValue.test.tsx b/frontend/app/src/entities/nodes/getObjectItemDisplayValue.test.tsx index 7158c6dc7c0..87745a8b40d 100644 --- a/frontend/app/src/entities/nodes/getObjectItemDisplayValue.test.tsx +++ b/frontend/app/src/entities/nodes/getObjectItemDisplayValue.test.tsx @@ -39,8 +39,7 @@ describe("ObjectAttributeValue", () => { }); test("DateTime attribute renders the full preferred datetime, even months in the past", async () => { - // A DateTime attribute is user data: however old, it must keep the time component and the - // preferred zone — 09:30 UTC is 18:30 in Tokyo — not collapse to a bare date. + // 09:30 UTC is 18:30 in Tokyo. const component = await render( withPrefs( { }); test("DateTime attribute renders the full preferred datetime for recent values too", async () => { - // Two days old — data attributes never degrade to a relative "x ago" phrase. const component = await render( withPrefs( : ; case ATTRIBUTE_KIND.DATETIME: - // User-authored data, not metadata: always the full preferred datetime, never an - // age-dependent compact/relative form. + // User-authored data, not metadata: never the age-dependent compact/relative form. return ; case ATTRIBUTE_KIND.TEXTAREA: return ; diff --git a/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.test.ts b/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.test.ts index df23a38e1a1..b8e79a34d6a 100644 --- a/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.test.ts +++ b/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.test.ts @@ -1,7 +1,10 @@ import { describe, expect, test } from "vitest"; import type { EffectivePreferences } from "@/entities/preferences/domain/model/preference"; -import { resolveDatePreferences } from "@/entities/preferences/domain/rules/resolve-date-preferences"; +import { + inheritedTimezone, + resolveDatePreferences, +} from "@/entities/preferences/domain/rules/resolve-date-preferences"; describe("resolveDatePreferences", () => { test("maps a USER date-format key to its date-fns pattern", () => { @@ -83,3 +86,19 @@ describe("resolveDatePreferences", () => { expect(resolved).toEqual({ pattern: null, timezone: null }); }); }); + +describe("inheritedTimezone", () => { + test("returns the GLOBAL value, which is what an unset field inherits", () => { + expect(inheritedTimezone({ value: "Europe/Paris", source: "GLOBAL" })).toBe("Europe/Paris"); + }); + + test("returns null for a DEFAULT source so the browser zone applies", () => { + expect(inheritedTimezone({ value: null, source: "DEFAULT" })).toBeNull(); + }); + + test("discards a USER value: dropping the override is exactly what stops it applying", () => { + // The API resolves the inherited value away once an override wins, so it is unknowable here — + // null (browser zone) is honest, the caller's own zone would be a stale guess. + expect(inheritedTimezone({ value: "Asia/Tokyo", source: "USER" })).toBeNull(); + }); +}); diff --git a/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.ts b/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.ts index 511c99dde7c..3a5a952995d 100644 --- a/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.ts +++ b/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.ts @@ -1,8 +1,21 @@ import type { ResolvedDatePreferences } from "@/shared/context/date-preferences-context"; -import type { EffectivePreferences } from "@/entities/preferences/domain/model/preference"; +import type { + EffectivePreferences, + Preference, +} from "@/entities/preferences/domain/model/preference"; import { dateFormatPattern } from "@/entities/preferences/domain/rules/date-format"; +/** + * The timezone that would apply if the caller held no override of their own. + * A `USER` source yields null: the caller's own value is precisely what an unset field discards, and + * the API resolves the inherited one away once an override wins, so null (browser zone) is the only + * honest answer. + */ +export function inheritedTimezone(timezone: Preference): string | null { + return timezone.source === "USER" ? null : (timezone.value ?? null); +} + // A `DEFAULT` source (or missing value/data) resolves to null so consumers fall back to the browser locale/zone. export function resolveDatePreferences( preferences: EffectivePreferences | undefined diff --git a/frontend/app/src/entities/preferences/ui/global-preferences-editor.test.tsx b/frontend/app/src/entities/preferences/ui/global-preferences-editor.test.tsx index 63db8bc3009..3f760625093 100644 --- a/frontend/app/src/entities/preferences/ui/global-preferences-editor.test.tsx +++ b/frontend/app/src/entities/preferences/ui/global-preferences-editor.test.tsx @@ -11,8 +11,8 @@ import { GlobalPreferencesEditor } from "./global-preferences-editor"; vi.mock("@/entities/preferences/domain/use-cases/get-global-preferences"); vi.mock("@/entities/preferences/domain/use-cases/update-global-preference"); -// A late-evening UTC instant: rendered in the global zone (UTC+9) it lands on the NEXT calendar day, -// so an example that ignored the timezone being edited could not accidentally match. +// Late-evening UTC: in the global zone (UTC+9) this lands on the NEXT calendar day, so an example +// that ignored the timezone being edited cannot match by accident. const FIXED_INSTANT = new Date("2026-06-11T23:30:00Z"); const baseGlobal: GlobalPreferences = { dateFormat: null, timezone: "Asia/Tokyo" }; @@ -79,8 +79,7 @@ describe("GlobalPreferencesEditor", () => { await component.getByRole("button", { name: /date format/i }).click(); await component.getByRole("option", { name: "yyyy-MM-dd HH:mm", exact: true }).click(); - // An unset global timezone means "the browser's" for every viewer, so the preview must not - // borrow a zone from anywhere else — least of all the editing admin's own preference. + // An unset global timezone means "the browser's" for every viewer, not the editing admin's zone. await expect .element(component.getByText(`Example: ${format(FIXED_INSTANT, "yyyy-MM-dd HH:mm")}`)) .toBeVisible(); diff --git a/frontend/app/src/entities/preferences/ui/preference-fields.test.tsx b/frontend/app/src/entities/preferences/ui/preference-fields.test.tsx index df3c23391a0..b9b4592fce3 100644 --- a/frontend/app/src/entities/preferences/ui/preference-fields.test.tsx +++ b/frontend/app/src/entities/preferences/ui/preference-fields.test.tsx @@ -6,8 +6,8 @@ import { DateFormatField, toFieldValue } from "@/entities/preferences/ui/prefere import { render } from "../../../../tests/components/render"; -// A late-evening UTC instant: rendered east of UTC it lands on the NEXT calendar day, so an example -// that ignored the timezone could not accidentally match. +// Late-evening UTC: east of UTC this lands on the NEXT calendar day, so an example that ignored +// the timezone cannot match by accident. const FIXED_INSTANT = new Date("2026-06-11T23:30:00Z"); function renderField({ diff --git a/frontend/app/src/entities/preferences/ui/preference-fields.tsx b/frontend/app/src/entities/preferences/ui/preference-fields.tsx index a16d2395d4e..30b3ba16821 100644 --- a/frontend/app/src/entities/preferences/ui/preference-fields.tsx +++ b/frontend/app/src/entities/preferences/ui/preference-fields.tsx @@ -76,8 +76,7 @@ interface PreferenceFieldProps { } interface DateFormatFieldProps extends PreferenceFieldProps { - /** Zone the examples fall back to while the form's timezone field is empty. Omit to fall back to - * the browser's — which is what an empty *global* timezone means for every viewer. */ + /** Zone the examples use while the form's timezone field is empty; omit for the browser's. */ fallbackTimezone?: string | null; } @@ -93,8 +92,7 @@ export function DateFormatField({ const fieldValue = useWatch({ name: "date_format" }) as FormAttributeValue | undefined; const selected = (fieldValue?.value as string | null | undefined) ?? null; - // The examples preview what the timestamps will look like once saved, so they render in the zone - // the form currently holds — including an unsaved edit to the timezone field. + // Previews what saving would produce, so it follows the form's own (possibly unsaved) zone. const timezoneValue = useWatch({ name: "timezone" }) as FormAttributeValue | undefined; const timezone = (timezoneValue?.value as string | null | undefined) ?? fallbackTimezone ?? null; const example = (key: string) => diff --git a/frontend/app/src/entities/preferences/ui/preferences-form.tsx b/frontend/app/src/entities/preferences/ui/preferences-form.tsx index 678d7cc6de9..ea820262d06 100644 --- a/frontend/app/src/entities/preferences/ui/preferences-form.tsx +++ b/frontend/app/src/entities/preferences/ui/preferences-form.tsx @@ -9,7 +9,7 @@ import { Form, FormSubmit } from "@/shared/components/ui/form"; import type { DateFormatKey } from "@/entities/preferences/domain/model/date-format"; import type { Preference, PreferenceValues } from "@/entities/preferences/domain/model/preference"; -import { resolveDatePreferences } from "@/entities/preferences/domain/rules/resolve-date-preferences"; +import { inheritedTimezone } from "@/entities/preferences/domain/rules/resolve-date-preferences"; import { DateFormatField, TimezoneField, @@ -78,7 +78,7 @@ export function PreferencesForm() { > diff --git a/frontend/app/src/entities/preferences/ui/user-preferences-card.test.tsx b/frontend/app/src/entities/preferences/ui/user-preferences-card.test.tsx index 021d5654a94..2d8c13ab830 100644 --- a/frontend/app/src/entities/preferences/ui/user-preferences-card.test.tsx +++ b/frontend/app/src/entities/preferences/ui/user-preferences-card.test.tsx @@ -11,9 +11,8 @@ import { UserPreferencesCard } from "./user-preferences-card"; vi.mock("@/entities/preferences/domain/use-cases/get-effective-preferences"); vi.mock("@/entities/preferences/domain/use-cases/upsert-user-preferences"); -// A late-evening UTC instant: rendered in the effective zone (UTC+9) it lands on the NEXT calendar -// day, so an example that ignored the timezone preference could not accidentally match. A zone-less -// literal would be parsed as browser-local and make every assertion below zone-agnostic. +// Late-evening UTC: in the effective zone (UTC+9) this lands on the NEXT calendar day, so an example +// that ignored the timezone cannot match by accident. A zone-less literal would be browser-local. const FIXED_INSTANT = new Date("2026-06-11T23:30:00Z"); const EFFECTIVE_ZONE = "Asia/Tokyo"; diff --git a/frontend/app/src/shared/components/display/date-display.test.tsx b/frontend/app/src/shared/components/display/date-display.test.tsx index f5a719fcdd1..988a51c9876 100644 --- a/frontend/app/src/shared/components/display/date-display.test.tsx +++ b/frontend/app/src/shared/components/display/date-display.test.tsx @@ -43,7 +43,6 @@ describe("DateDisplay", () => { }); test("relative branch: near-future date shows 'in x'", async () => { - // Within a week ahead, a relative phrase still reads best — same window as past dates. const inThreeDays = new Date("2026-06-14T14:30:00Z"); const component = await render(withPrefs()); await expect.element(component.getByText("in 3 days")).toBeVisible(); @@ -59,8 +58,7 @@ describe("DateDisplay", () => { }); test("compact branch: far-future date renders the preferred date, not a relative phrase", async () => { - // > 7 days ahead — beyond the week window in either direction the reader needs the actual - // date in the preferred format, not something like "in 5 months". + // 5 months ahead, well outside the week window. const farFuture = new Date("2026-11-18T14:30:00Z"); const component = await render(withPrefs()); await expect.element(component.getByText("2026-11-18")).toBeVisible(); diff --git a/frontend/app/src/shared/components/display/date-display.tsx b/frontend/app/src/shared/components/display/date-display.tsx index eb5f494ea42..2c630dd202d 100644 --- a/frontend/app/src/shared/components/display/date-display.tsx +++ b/frontend/app/src/shared/components/display/date-display.tsx @@ -42,8 +42,7 @@ export const DateDisplay = ({ return wrap(tooltipMessage); } - // Within a week of now — either direction — a relative phrase reads best; farther out it - // loses the precision the reader needs, so the preferred date takes over. + // Beyond a week either side of now a relative phrase loses the precision the reader needs. if (Math.abs(differenceInDays(new Date(), dateData)) > 7) { return wrap(formatDate(dateData, "date")); } diff --git a/frontend/app/src/shared/context/date-preferences-context.tsx b/frontend/app/src/shared/context/date-preferences-context.tsx index e991b019413..f7a1c56551b 100644 --- a/frontend/app/src/shared/context/date-preferences-context.tsx +++ b/frontend/app/src/shared/context/date-preferences-context.tsx @@ -39,9 +39,8 @@ export interface UseFormatDateResult { } /** - * Renders a date against a resolved preference pair. `"date"` reuses the datetime pattern's date - * part. Take this over the hook only when the pair is not the active one — previewing preferences - * still being edited, say; everything else must go through the hook so it follows the viewer. + * Renders a date against a resolved preference pair; `"date"` reuses the datetime pattern's date part. + * Only for a pair that is not the active one (preferences still being edited) — otherwise use the hook. */ export function formatWithPreferences( date: DateInput, From e1f27bf204190d21956f07eefa4fe5bbefb12e6f Mon Sep 17 00:00:00 2001 From: Saltaferis Dimitrios Date: Mon, 10 Aug 2026 16:11:08 +0300 Subject: [PATCH 10/48] docs(frontend): tighten the inheritedTimezone contract comment Co-Authored-By: Claude Fable 5 --- .../preferences/domain/rules/resolve-date-preferences.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.ts b/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.ts index 3a5a952995d..dc80ad88bb3 100644 --- a/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.ts +++ b/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.ts @@ -7,10 +7,9 @@ import type { import { dateFormatPattern } from "@/entities/preferences/domain/rules/date-format"; /** - * The timezone that would apply if the caller held no override of their own. - * A `USER` source yields null: the caller's own value is precisely what an unset field discards, and - * the API resolves the inherited one away once an override wins, so null (browser zone) is the only - * honest answer. + * The timezone that would apply if the caller held no override of their own. A `USER` source yields + * null — that value is exactly what clearing the field discards, and the API resolves the inherited + * one away once an override wins, leaving the browser zone as the only honest stand-in. */ export function inheritedTimezone(timezone: Preference): string | null { return timezone.source === "USER" ? null : (timezone.value ?? null); From 9a4b9c0b0a3372a25a3059c9f43c59ac0077e4b0 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 15:21:53 +0200 Subject: [PATCH 11/48] fix(frontend): link repositories to concrete kind and request profiles only when exposed (#10195) --- .../+edit-form-profiles-generic-kind.fixed.md | 1 + ...homepage-repository-concrete-kind.fixed.md | 1 + .../entities/homepage/ui/git-repository.tsx | 12 ++-- .../generateObjectEditFormQuery.test.ts | 69 +++++++++++++++++++ .../generateObjectEditFormQuery.ts | 3 +- 5 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 changelog/+edit-form-profiles-generic-kind.fixed.md create mode 100644 changelog/+homepage-repository-concrete-kind.fixed.md create mode 100644 frontend/app/src/entities/nodes/object-item-edit/generateObjectEditFormQuery.test.ts diff --git a/changelog/+edit-form-profiles-generic-kind.fixed.md b/changelog/+edit-form-profiles-generic-kind.fixed.md new file mode 100644 index 00000000000..93602955fd5 --- /dev/null +++ b/changelog/+edit-form-profiles-generic-kind.fixed.md @@ -0,0 +1 @@ +Fixed the object edit form failing with a GraphQL `profiles` error when an object is opened through a kind whose GraphQL type has no `profiles` field, such as `CoreGenericRepository`. The form now requests `profiles` only for kinds that expose it. diff --git a/changelog/+homepage-repository-concrete-kind.fixed.md b/changelog/+homepage-repository-concrete-kind.fixed.md new file mode 100644 index 00000000000..b31c7c448b2 --- /dev/null +++ b/changelog/+homepage-repository-concrete-kind.fixed.md @@ -0,0 +1 @@ +Fixed the Git repositories homepage widget linking to the generic `CoreGenericRepository` kind: repositories now open on their own kind, so the details page and edit form show all of their fields. diff --git a/frontend/app/src/entities/homepage/ui/git-repository.tsx b/frontend/app/src/entities/homepage/ui/git-repository.tsx index 3e6dd916cb2..b03c9df9935 100644 --- a/frontend/app/src/entities/homepage/ui/git-repository.tsx +++ b/frontend/app/src/entities/homepage/ui/git-repository.tsx @@ -3,23 +3,21 @@ import { ListBoxItem } from "react-aria-components"; import type { Dropdown } from "@/shared/api/graphql/generated/types"; import { focusVisibleStyle } from "@/shared/components/aria/style-rac"; import { Tooltip } from "@/shared/components/ui/tooltip"; -import { GENERIC_REPOSITORY_KIND } from "@/shared/config/constants"; import { classNames, getTextColor } from "@/shared/utils/common"; +import type { NodeCore } from "@/entities/nodes/types"; import { getObjectDetailsUrl } from "@/entities/nodes/utils"; -export type GitRepositoryData = { - id: string; - display_label?: string | null; +export interface GitRepositoryData extends NodeCore { sync_status?: Dropdown | null; -}; +} export const GitRepositoryItem = ({ repository }: { repository: GitRepositoryData }) => { - const { id, display_label, sync_status } = repository; + const { id, __typename, display_label, sync_status } = repository; return ( { + it("requests the profiles field for a node exposing a profiles relationship", () => { + // GIVEN + const schema = generateNodeSchema({ + generate_profile: true, + relationships: [profilesRelationship], + }); + + // WHEN + const query = generateObjectEditFormQuery({ schema, objectId: "object-id" }); + + // THEN + expect(query).toContain("profiles"); + expect(query).toContain("profile_priority"); + }); + + it("does not request the profiles field for a generic without a profiles relationship", () => { + // GIVEN + const schema = generateGenericSchema({ + generate_profile: true, + relationships: [], + }) as unknown as NodeSchema; + + // WHEN + const query = generateObjectEditFormQuery({ schema, objectId: "object-id" }); + + // THEN + expect(query).not.toContain("profiles"); + }); + + it("requests the profiles field for a generic exposing a profiles relationship", () => { + // GIVEN + const schema = generateGenericSchema({ + generate_profile: true, + relationships: [ + generateRelationshipSchema({ + name: "used_by", + peer: "CoreNode", + identifier: "profile__node", + }), + profilesRelationship, + ], + }) as unknown as NodeSchema; + + // WHEN + const query = generateObjectEditFormQuery({ schema, objectId: "object-id" }); + + // THEN + expect(query).toContain("profiles"); + expect(query).toContain("profile_priority"); + }); +}); diff --git a/frontend/app/src/entities/nodes/object-item-edit/generateObjectEditFormQuery.ts b/frontend/app/src/entities/nodes/object-item-edit/generateObjectEditFormQuery.ts index 3f80452a01a..3e227be8d61 100644 --- a/frontend/app/src/entities/nodes/object-item-edit/generateObjectEditFormQuery.ts +++ b/frontend/app/src/entities/nodes/object-item-edit/generateObjectEditFormQuery.ts @@ -48,7 +48,8 @@ export const generateObjectEditFormQuery = ({ ...addRelationshipsToRequest([...formRelationships, ...extraRelationships], { withMetadata: true, }), - ...("generate_profile" in objectSchema && objectSchema.generate_profile + // `generate_profile` can be true while the GraphQL type exposes no `profiles` field. + ...((objectSchema.relationships ?? []).some((rel) => rel.name === "profiles") ? { profiles: { edges: { From 60facc0c8ebe7ba69a2033d86ed62d787430fd68 Mon Sep 17 00:00:00 2001 From: Saltaferis Dimitrios Date: Mon, 10 Aug 2026 16:55:48 +0300 Subject: [PATCH 12/48] fix(frontend): treat only the global layer as inherited for the zone preview MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit inheritedTimezone excluded a USER source but let any other source through, so a DEFAULT source carrying a value would have been reported as inherited — contradicting the pattern resolver, which discards a DEFAULT value even when one is present. Only GLOBAL is an inherited layer, so whitelist it. No behaviour change against today's API, which always pairs DEFAULT with a null value; this keeps the two rules consistent if that ever loosens. Co-Authored-By: Claude Fable 5 --- .../domain/rules/resolve-date-preferences.test.ts | 4 ++++ .../preferences/domain/rules/resolve-date-preferences.ts | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.test.ts b/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.test.ts index b8e79a34d6a..4fd5bf7e9cd 100644 --- a/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.test.ts +++ b/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.test.ts @@ -96,6 +96,10 @@ describe("inheritedTimezone", () => { expect(inheritedTimezone({ value: null, source: "DEFAULT" })).toBeNull(); }); + test("ignores a value carried on a DEFAULT source, as the pattern resolver does", () => { + expect(inheritedTimezone({ value: "Europe/Paris", source: "DEFAULT" })).toBeNull(); + }); + test("discards a USER value: dropping the override is exactly what stops it applying", () => { // The API resolves the inherited value away once an override wins, so it is unknowable here — // null (browser zone) is honest, the caller's own zone would be a stale guess. diff --git a/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.ts b/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.ts index dc80ad88bb3..a86ea01eec7 100644 --- a/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.ts +++ b/frontend/app/src/entities/preferences/domain/rules/resolve-date-preferences.ts @@ -7,12 +7,12 @@ import type { import { dateFormatPattern } from "@/entities/preferences/domain/rules/date-format"; /** - * The timezone that would apply if the caller held no override of their own. A `USER` source yields - * null — that value is exactly what clearing the field discards, and the API resolves the inherited - * one away once an override wins, leaving the browser zone as the only honest stand-in. + * The timezone a caller inherits when they set none of their own — the global layer, or null for the + * browser zone. A `USER` source yields null: that value is exactly what clearing the field discards, + * and the API resolves the inherited one away once an override wins. */ export function inheritedTimezone(timezone: Preference): string | null { - return timezone.source === "USER" ? null : (timezone.value ?? null); + return timezone.source === "GLOBAL" ? (timezone.value ?? null) : null; } // A `DEFAULT` source (or missing value/data) resolves to null so consumers fall back to the browser locale/zone. From 2504d211c21b5a1cc49a0f23d481d55b5527af7b Mon Sep 17 00:00:00 2001 From: Guillaume Mazoyer Date: Mon, 10 Aug 2026 17:28:36 +0200 Subject: [PATCH 13/48] Clone git repositories on their own default branch (#10141) A worker without a local copy of a repository clones it on demand. That path only carries the repository id and name, so the repository object never learns which git ref to track. It fell back to the platform default branch. Git then failed on any repository whose default branch is not `main`, the repository went to error, and artifact generation failed with it. It takes more than one worker to see this. Each worker keeps its own git directory, so only the worker that added the repository has the clone. `InfrahubRepositoryBase` gets one abstract method, `resolve_checkout_ref`. The re-clone path calls it and passes the result to `create_locally`. `CoreRepository` reads `default_branch`, `CoreReadOnlyRepository` reads `ref`. The lookup only runs when the clone is missing, so an existing clone costs nothing extra. Fixes #8749 --- backend/infrahub/git/base.py | 5 + backend/infrahub/git/integrator.py | 6 +- backend/infrahub/git/repository.py | 24 ++++ .../component/git/test_git_repository.py | 1 + .../git/test_repository_default_branch.py | 107 ++++++++++++++++++ .../test_artifact_composition.py | 23 +++- changelog/8749.fixed.md | 1 + 7 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 backend/tests/functional/git/test_repository_default_branch.py create mode 100644 changelog/8749.fixed.md diff --git a/backend/infrahub/git/base.py b/backend/infrahub/git/base.py index 977958aac95..81cce57d2e1 100644 --- a/backend/infrahub/git/base.py +++ b/backend/infrahub/git/base.py @@ -191,6 +191,11 @@ def sdk(self) -> InfrahubClient: def default_branch(self) -> str: return self.default_branch_name or registry.default_branch + @abstractmethod + async def resolve_checkout_ref(self) -> str: + """Return the git ref the primary clone has to be checked out on, reading it from the graph if needed.""" + raise NotImplementedError() + @property def legacy_directory_root(self) -> Path: """Return the legacy path to the root directory for this repository.""" diff --git a/backend/infrahub/git/integrator.py b/backend/infrahub/git/integrator.py index 217256d2ed2..4b95e381ecb 100644 --- a/backend/infrahub/git/integrator.py +++ b/backend/infrahub/git/integrator.py @@ -197,7 +197,11 @@ async def init(cls, commit: str | None = None, **kwargs: Any) -> Self: self.validate_local_directories() except RepositoryInvalidFileSystemError: await self.ensure_location_is_defined() - await self.create_locally(infrahub_branch_name=self.infrahub_branch_name, update_commit_value=False) + await self.create_locally( + checkout_ref=await self.resolve_checkout_ref(), + infrahub_branch_name=self.infrahub_branch_name, + update_commit_value=False, + ) self.reinitialized = True log.info(f"Initialized the local directory for {self.name} because it was missing.") diff --git a/backend/infrahub/git/repository.py b/backend/infrahub/git/repository.py index 2cd0ed974bb..81effbb0520 100644 --- a/backend/infrahub/git/repository.py +++ b/backend/infrahub/git/repository.py @@ -9,6 +9,7 @@ from cachetools_async import cached from git.exc import BadName, GitCommandError from infrahub_sdk.exceptions import GraphQLError +from infrahub_sdk.protocols import CoreReadOnlyRepository, CoreRepository from prefect import task from prefect.cache_policies import NONE from pydantic import Field @@ -83,6 +84,15 @@ async def new(cls, update_commit_value: bool = True, **kwargs: Any) -> InfrahubR log.info("Created new repository locally.", repository=self.name) return self + async def resolve_checkout_ref(self) -> str: + if not self.default_branch_name: + repository = await self.sdk.get( + kind=CoreRepository, name__value=self.name, exclude=["tags", "credential"], raise_when_missing=True + ) + self.default_branch_name = repository.default_branch.value + + return self.default_branch + def get_commit_value(self, branch_name: str, remote: bool = False) -> str: branches = {} if remote: @@ -356,6 +366,20 @@ async def new(cls, **kwargs: Any) -> InfrahubReadOnlyRepository: log.info("Created new repository locally.", repository=self.name) return self + async def resolve_checkout_ref(self) -> str: + ref = self.ref + if not ref: + repository = await self.sdk.get( + kind=CoreReadOnlyRepository, + name__value=self.name, + exclude=["tags", "credential"], + raise_when_missing=True, + ) + ref = repository.ref.value + self.ref = ref + + return ref + def get_commit_value(self, branch_name: str, remote: bool = False) -> str: # noqa: ARG002 """Always get the latest commit for this repository's ref on the remote. diff --git a/backend/tests/component/git/test_git_repository.py b/backend/tests/component/git/test_git_repository.py index 9489feebba4..d720d37d8db 100644 --- a/backend/tests/component/git/test_git_repository.py +++ b/backend/tests/component/git/test_git_repository.py @@ -1264,6 +1264,7 @@ async def test_init_reinitialized_after_missing_directory( id=repo_id, name=git_upstream_repo_02["name"], location=str(git_upstream_repo_02["path"]), + default_branch_name="main", client=InfrahubClient(config=Config(requester=dummy_async_request)), ) diff --git a/backend/tests/functional/git/test_repository_default_branch.py b/backend/tests/functional/git/test_repository_default_branch.py new file mode 100644 index 00000000000..9aef971e23b --- /dev/null +++ b/backend/tests/functional/git/test_repository_default_branch.py @@ -0,0 +1,107 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from git import Repo + +from infrahub.core.constants import InfrahubKind +from infrahub.core.manager import NodeManager +from infrahub.core.node import Node +from infrahub.core.registry import registry +from infrahub.git.repository import get_initialized_repo +from tests.helpers.test_app import TestInfrahubApp + +if TYPE_CHECKING: + from pathlib import Path + + from infrahub_sdk import InfrahubClient + + from infrahub.core.branch import Branch + from infrahub.core.protocols import CoreRepository + from infrahub.database import InfrahubDatabase + +GIT_BRANCH = "production" +REPOSITORY_NAME = "repository-with-non-main-default-branch" +READ_ONLY_REPOSITORY_NAME = "read-only-repository-with-non-main-ref" + + +def create_upstream_repository(directory: Path, branch: str) -> None: + directory.mkdir() + upstream = Repo.init(directory, initial_branch=branch) + (directory / "file.txt").write_text("content") + upstream.index.add(["file.txt"]) + upstream.index.commit("First commit") + + +class TestRepositoryDefaultBranch(TestInfrahubApp): + async def test_on_demand_clone_checks_out_configured_default_branch( + self, + db: InfrahubDatabase, + client: InfrahubClient, + default_branch: Branch, + initialize_registry: None, + tmp_path: Path, + git_repos_dir: Path, + ) -> None: + """A worker with no local copy of a repository clones it on the repository default branch.""" + assert registry.default_branch != GIT_BRANCH + + source_dir = tmp_path / "upstream" + create_upstream_repository(directory=source_dir, branch=GIT_BRANCH) + + node = await Node.init(db=db, schema=InfrahubKind.REPOSITORY) + await node.new( + db=db, + name=REPOSITORY_NAME, + location=str(source_dir), + default_branch=GIT_BRANCH, + ) + await node.save(db=db) + operational_status_before = node.operational_status.value + + repo = await get_initialized_repo.fn( + client=client, + repository_id=node.id, + name=REPOSITORY_NAME, + repository_kind=InfrahubKind.REPOSITORY, + ) + + assert repo.get_git_repo_main().active_branch.name == GIT_BRANCH + + reloaded: CoreRepository = await NodeManager.get_one( + db=db, id=node.id, kind=InfrahubKind.REPOSITORY, raise_on_error=True + ) + assert reloaded.operational_status.value == operational_status_before + + async def test_on_demand_clone_checks_out_configured_ref( + self, + db: InfrahubDatabase, + client: InfrahubClient, + default_branch: Branch, + initialize_registry: None, + tmp_path: Path, + git_repos_dir: Path, + ) -> None: + """A worker with no local copy of a read-only repository clones it on the ref it tracks.""" + assert registry.default_branch != GIT_BRANCH + + source_dir = tmp_path / "upstream" + create_upstream_repository(directory=source_dir, branch=GIT_BRANCH) + + node = await Node.init(db=db, schema=InfrahubKind.READONLYREPOSITORY) + await node.new( + db=db, + name=READ_ONLY_REPOSITORY_NAME, + location=str(source_dir), + ref=GIT_BRANCH, + ) + await node.save(db=db) + + repo = await get_initialized_repo.fn( + client=client, + repository_id=node.id, + name=READ_ONLY_REPOSITORY_NAME, + repository_kind=InfrahubKind.READONLYREPOSITORY, + ) + + assert repo.get_git_repo_main().active_branch.name == GIT_BRANCH diff --git a/backend/tests/integration_docker/test_artifact_composition.py b/backend/tests/integration_docker/test_artifact_composition.py index dee3fcb6170..baa02f77185 100644 --- a/backend/tests/integration_docker/test_artifact_composition.py +++ b/backend/tests/integration_docker/test_artifact_composition.py @@ -5,19 +5,22 @@ from typing import TYPE_CHECKING import pytest -from infrahub_sdk.protocols import CoreArtifact +from infrahub_sdk.protocols import CoreArtifact, CoreRepository from infrahub_sdk.schema import NodeSchema, SchemaRoot from infrahub_sdk.testing.docker import TestInfrahubDockerClient from infrahub_sdk.testing.repository import GitRepo from infrahub_sdk.testing.schemas.car_person import SchemaCarPerson -from infrahub.core.constants import ArtifactStatus, InfrahubKind +from infrahub.core.constants import ArtifactStatus, InfrahubKind, RepositoryOperationalStatus if TYPE_CHECKING: from infrahub_sdk import InfrahubClient CURRENT_DIRECTORY = Path(__file__).parent.resolve() +# Must stay off "main" to cover repositories whose default branch is not the platform one. +SECTION_GIT_DEFAULT_BRANCH = "production" + async def wait_for_artifacts( client: InfrahubClient, expected_name: str | None = None, interval: int = 3, retries: int = 10 @@ -73,15 +76,22 @@ async def initial_dataset(self, client: InfrahubClient, default_branch: str, ini await group.save() async def test_add_section_repo( - self, client: InfrahubClient, remote_repos_dir: Path, initial_dataset: None + self, client: InfrahubClient, remote_repos_dir: Path, default_branch: str, initial_dataset: None ) -> None: repo = GitRepo( name="section-config", src_directory=CURRENT_DIRECTORY / "test_files/repos/section-config", dst_directory=remote_repos_dir, + initial_branch=SECTION_GIT_DEFAULT_BRANCH, ) - await repo.add_to_infrahub(client=client) - assert await repo.wait_for_sync_to_complete(client=client, retries=12) + repository = await client.create( + kind=CoreRepository, + name=repo.name, + location=f"/remote/{repo.name}", + default_branch=SECTION_GIT_DEFAULT_BRANCH, + ) + await repository.save() + assert await repo.wait_for_sync_to_complete(client=client, branch=default_branch, retries=12) async def test_section_artifacts(self, client: InfrahubClient) -> None: """Section artifacts are generated with the expected content.""" @@ -95,6 +105,9 @@ async def test_section_artifacts(self, client: InfrahubClient) -> None: contents.add(content) assert contents == {"! Section config for John Doe", "! Section config for Jane Doe"} + repository = await client.get(kind=CoreRepository, name__value="section-config") + assert repository.operational_status.value == RepositoryOperationalStatus.ONLINE.value + async def test_add_composite_repo(self, client: InfrahubClient, remote_repos_dir: Path) -> None: repo = GitRepo( name="composite-config", diff --git a/changelog/8749.fixed.md b/changelog/8749.fixed.md new file mode 100644 index 00000000000..6654ec14d12 --- /dev/null +++ b/changelog/8749.fixed.md @@ -0,0 +1 @@ +Fixed artifact generation failing for repositories whose default branch is not named main. From 5589a5d2cf98ae03e3333dbbd9ddea70e3e6e963 Mon Sep 17 00:00:00 2001 From: Saltaferis Dimitrios Date: Mon, 10 Aug 2026 18:38:22 +0300 Subject: [PATCH 14/48] fix(e2e): match the default badge exactly on the branch-details page Both branch-details suites assert that no "default" text is visible on a non-default branch, to prove the default badge is absent. The assertion used a substring match, so renaming the attribute label to "Schema differs from default branch" made it match that label instead and the tests failed for a page that is rendering correctly. The positive assertion was updated with the rename; this negative one was missed. Matching exactly still catches a stray badge, whose text is only "default", without tripping on prose that contains the word. Co-Authored-By: Claude Fable 5 --- frontend/app/tests/e2e/branches/branch-details.spec.ts | 4 +++- tests/e2e/branches/test_branch_details.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/app/tests/e2e/branches/branch-details.spec.ts b/frontend/app/tests/e2e/branches/branch-details.spec.ts index fdd154b6b6f..e052d025d42 100644 --- a/frontend/app/tests/e2e/branches/branch-details.spec.ts +++ b/frontend/app/tests/e2e/branches/branch-details.spec.ts @@ -50,7 +50,9 @@ test.describe("Branch details view", () => { // Header await expect(page.getByRole("heading", { name: BRANCH_NAME })).toBeVisible(); - await expect(page.getByText("default")).not.toBeVisible(); + // exact — the badge's text is only "default"; a substring match also hits the + // "Schema differs from default branch" attribute label below. + await expect(page.getByText("default", { exact: true })).not.toBeVisible(); await expect(page.getByRole("button", { name: "View node metadata" })).toBeVisible(); // Branch attributes diff --git a/tests/e2e/branches/test_branch_details.py b/tests/e2e/branches/test_branch_details.py index 94b2f62067a..f14bea6a9ba 100644 --- a/tests/e2e/branches/test_branch_details.py +++ b/tests/e2e/branches/test_branch_details.py @@ -69,7 +69,9 @@ async def test_display_branch_name_and_no_default_badge( # Header await expect(admin_page.get_by_role("heading", name=NON_DEFAULT_BRANCH)).to_be_visible() - await expect(admin_page.get_by_text("default")).not_to_be_visible() + # exact — the badge's text is only "default"; a substring match also hits the + # "Schema differs from default branch" attribute label below. + await expect(admin_page.get_by_text("default", exact=True)).not_to_be_visible() await expect(admin_page.get_by_role("button", name="View node metadata")).to_be_visible() # Branch attributes From f77cf88b3120cb82a6bb26ab6ac3af50a60c3956 Mon Sep 17 00:00:00 2001 From: Aaron McCarty Date: Mon, 10 Aug 2026 13:18:50 -0500 Subject: [PATCH 15/48] fix(backend): delete branch data in bounded batches (#10132) * fix(backend): delete branch data in bounded batches Deleting a large branch ran as a single Cypher statement whose peak transaction memory scaled with the size of the branch: it collected the element id of every vertex touched by a deleted edge into two lists and concatenated them. On a big enough branch that exceeded dbms.memory.transaction.total.max, and because the inner writes committed in batches the failure left the branch stranded in DELETING -- invisible in the branch list -- with most of its data still in the graph. Replace it with a set of bounded queries driven from Python by a new BranchDeleter component: the agnostic peers of branch-only nodes first, then one batch of edges per relationship type until none are left. Peak transaction memory is now a function of the batch size rather than the branch, and naming the relationship type lets the branch range index serve the match instead of scanning every edge in the database. Each batch also deletes the vertices its edge deletions left bare. That is only sound because every branch edge is removed by the batch's DELETE and both endpoints are re-examined afterwards, so the batch that removes a vertex's last edge is the one that sees it at degree zero. A DETACH DELETE would break it by removing edges that never reach a batch of their own, stranding the vertices on their far side. Branch.delete now raises instead of silently dropping only the Branch vertex; callers use BranchDeleter. On a branch of 1,074,217 edges this deletes the same 416,021 vertices as before, in 27.8s rather than 87.9s, and completes at a 512 MiB transaction limit where the previous implementation ran out of memory. Co-Authored-By: Claude Opus 5 (1M context) * fix(backend): finish deleting branches abandoned in DELETING A branch delete that ran out of transaction memory committed part of its work before failing, leaving the branch with the DELETING status and the rest of its data in the graph. That state was unreachable: the branch is filtered out of the branch list, and Branch.get_by_name hides it by default, so the delete could not be retried and the space could not be reclaimed. Add graph migration 075, which finds every branch still in DELETING and runs it through BranchDeleter, then removes the branch node. It reuses the normal delete path rather than reimplementing it, loading each branch via the ignore_deleting escape hatch. Finding them needs a dedicated query because the shared branch list query filters DELETING out unconditionally. BranchDeleter now returns the number of edges it removed so the migration can report progress on the migration console. `infrahub db migrate` raises the infrahub log level to WARNING, which hides the deleter's own logging, and deleting a large branch takes long enough that silence looks like a hung upgrade. The migration is a no-op where no branch is in DELETING, and safe to re-run: an interrupted delete resumes. Co-Authored-By: Claude Opus 5 (1M context) * fix(backend): allow retrying a branch delete left in DELETING A delete that failed part way through set the DELETING status before doing any cleanup, and both the BranchDelete mutation and the branch-delete flow looked the branch up with the default lookup, which hides that status. The retry reported the branch as missing, so the only way to reclaim the data was the upgrade migration -- which runs once, leaving any later failure stranded for good. Both lookups now pass ignore_deleting=False. Two consequences worth knowing: the mutation will accept a delete for a branch whose delete is still running, and nothing serialises the two runs (each query involved is idempotent); and a retry re-emits BranchDeletedEvent. Making the surrounding flow steps idempotent is left for later. Cap the agnostic cleanup at 500 rows rather than query_size_limit. Those batches count Nodes, and each one can drag an unbounded number of peer vertices into the transaction with it, unlike the edge batches where one row is one edge. 500 is what this phase used before the batching work, and raising it to 5000 was incidental rather than deliberate. Drop the edge accounting comment claiming the vertex cleanup can remove extra edges. That was true while the cleanup used DETACH DELETE; it now only deletes vertices that are already bare. Add the missing tests: Branch.delete refusing, the default/global guards that moved onto BranchDeleter, and retrying a branch left in DELETING. Co-Authored-By: Claude Opus 5 (1M context) * test(backend): own the branch in the delete-retry test The test created the branch with `create_branch`, which leaves created_by as the system user, so the BranchDelete mutation took its permission branch and the assertion depended on how a super-admin grant resolves against a specific DELETE_BRANCH check. That resolved differently in CI and the test failed with PermissionDeniedError. Use the existing first_account / session_first_account fixtures and make that account the branch owner, so created_by matches the requesting account, the permission check is skipped, and the result turns on the DELETING status alone. Co-Authored-By: Claude Opus 5 (1M context) * fix(backend): make the DELETING branch cleanup migration robust Address review findings on migration 075 and the deleter's accounting. The migration's branch lookup suppressed the generated pagination. A read query with no limit of its own is executed page by page, so with the SKIP/LIMIT suppressed every page re-read the whole set and the paging never reached a short page: with at least query_size_limit stranded branches the upgrade would never finish. Pagination is enabled again and the lookup orders by branch name, which is unique, so the pages are disjoint. Each branch is now deleted in its own try. A failure is reported as "branch '': " and the loop continues, so an operator gets the names of everything that still needs a re-run instead of the first exception and an unknown remainder. Failing to list the branches at all still aborts, since there is then nothing to iterate. Cap the agnostic cleanup at min(batch_size, 500) rather than a fixed 500, so lowering the configured batch size to fit a constrained database is not answered with a larger batch than was asked for. Count the agnostic cleanup's edges towards the total the deleter reports. It detaches peer vertices, so leaving it out made the migration's per-branch progress undercount for any branch with agnostic data. The 075 test asserted the stalled branch's node was unreachable from main, which it always was -- it only ever existed on that branch, so the assertion held whether or not the migration ran. It now reads the node on its own branch, before and after. A third case covers one branch of three failing, using a FailingBranchDeleter that delegates to the real deleter for the others so the test proves they were reclaimed rather than merely attempted. Co-Authored-By: Claude Opus 5 (1M context) * fix(backend): only run post-delete work when this attempt removed the branch BranchDeleter.delete returned the number of edges it removed, which only the upgrade migration used, for its progress output. It said nothing about whether this attempt was the one that removed the branch -- so now that a delete can be retried, two attempts on the same branch could both go on to cancel the proposed changes, emit BranchDeletedEvent and delete the Git branch. Return a BranchDeleteResult carrying both branch_deleted and edges_removed. Removing the vertex is itself the claim: two attempts are serialised on it, so exactly one reports nodes_deleted, with no window of the kind a read followed by a delete would leave. The branch-delete flow returns early when it did not make the claim, and the migration reports whether the branch was still there. Skip the DELETING status write when the status is already set. It is a wasted query for a branch whose earlier delete failed part way through, and it fails outright if the branch has meanwhile been removed -- which made delete() unsafe to call twice at all, whatever it returned. A narrower window remains: an attempt that has not yet written the status, and whose write lands after another attempt removed the vertex, still raises rather than reporting false. It fails the run instead of double-processing, and closing it properly wants a branch-scoped lock. Co-Authored-By: Claude Opus 5 (1M context) * make sure branch is deleted from git during concurrent deletes * move delete_branch flow logic to a new component for easier testing * formatting * add default/global branch delete guard at higher level --------- Co-authored-by: Claude Opus 5 (1M context) --- backend/infrahub/core/branch/data_deleter.py | 151 ++++++++++++++ .../core/branch/delete_coordinator.py | 96 +++++++++ backend/infrahub/core/branch/models.py | 18 +- backend/infrahub/core/branch/tasks.py | 45 ++--- backend/infrahub/core/graph/__init__.py | 2 +- ...2_cleanup_orphaned_branch_relationships.py | 7 +- .../graph/m075_finish_deleting_branches.py | 93 +++++++++ backend/infrahub/core/query/branch.py | 158 ++++++++++----- backend/infrahub/graphql/mutations/branch.py | 3 +- .../core/migrations/graph/test_024.py | 3 +- ...7_freeze_orphaned_branch_tracking_diffs.py | 9 +- .../test_075_finish_deleting_branches.py | 184 ++++++++++++++++++ .../test_number_pool_query.py | 5 +- backend/tests/component/core/test_branch.py | 60 +++++- .../diff/test_diff_tree_terminal_branch.py | 3 +- .../graphql/mutations/test_branch.py | 31 +++ .../integration/diff/test_diff_update.py | 3 +- .../core/branch/test_delete_coordinator.py | 151 ++++++++++++++ changelog/9889.fixed.md | 1 + 19 files changed, 918 insertions(+), 105 deletions(-) create mode 100644 backend/infrahub/core/branch/data_deleter.py create mode 100644 backend/infrahub/core/branch/delete_coordinator.py create mode 100644 backend/infrahub/core/migrations/graph/m075_finish_deleting_branches.py create mode 100644 backend/tests/component/core/migrations/graph/test_075_finish_deleting_branches.py create mode 100644 backend/tests/unit/core/branch/test_delete_coordinator.py create mode 100644 changelog/9889.fixed.md diff --git a/backend/infrahub/core/branch/data_deleter.py b/backend/infrahub/core/branch/data_deleter.py new file mode 100644 index 00000000000..477341bbe01 --- /dev/null +++ b/backend/infrahub/core/branch/data_deleter.py @@ -0,0 +1,151 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Protocol + +from infrahub.core.branch.enums import BranchStatus +from infrahub.core.constants.database import DatabaseEdgeType +from infrahub.core.query.branch import ( + DeleteBranchAgnosticAttributesQuery, + DeleteBranchAgnosticRelationshipsQuery, + DeleteBranchEdgesQuery, +) +from infrahub.core.query.standard_node import StandardNodeDeleteQuery +from infrahub.exceptions import ValidationError +from infrahub.log import get_logger + +if TYPE_CHECKING: + from infrahub.core.branch.models import Branch + from infrahub.database import InfrahubDatabase + +# The agnostic cleanup batches Nodes, and each one can drag an unbounded number of peer vertices +# into the transaction with it, so its batch is capped low. +MAX_AGNOSTIC_PEER_BATCH_SIZE = 500 + + +@dataclass(frozen=True) +class BranchDeleteResult: + """What a delete attempt actually did. + + `branch_deleted` is false when the branch had already been removed by the time this attempt got + to it, which is how a caller knows not to repeat the work that follows a delete. + """ + + branch_deleted: bool + edges_removed: int + + +class BranchDataDeleterInterface(Protocol): + """The database side of a branch delete.""" + + async def delete(self, branch: Branch) -> BranchDeleteResult: ... + + +class LoggerInterface(Protocol): + """Just enough of a logger for progress reporting.""" + + def info(self, message: str, /) -> Any: ... + + +class BranchDataDeleter: + """Remove a branch, every edge belonging to it, and the vertices that only it kept alive. + + The graph work is split into one bounded query per batch so that no single transaction has to + hold the whole branch in memory. Each query is its own auto-commit transaction, which also means + an interrupted delete can be resumed by running the whole thing again. + """ + + def __init__(self, db: InfrahubDatabase, batch_size: int, log: LoggerInterface | None = None) -> None: + self.db = db + self.batch_size = batch_size + self.log = log or get_logger() + + async def delete(self, branch: Branch) -> BranchDeleteResult: + """Remove the branch's data and then the branch itself. + + Returns whether the Branch object was actually deleted in case multiple processes try to + delete concurrently so the caller can know which delete really succeeded. + + Raises: + ValidationError: When the branch is the default branch or an internal one. + + """ + if branch.is_default: + raise ValidationError(f"Unable to delete {branch.name} it is the default branch.") + if branch.is_global: + raise ValidationError(f"Unable to delete {branch.name} this is an internal branch.") + + if branch.status != BranchStatus.DELETING: + branch.status = BranchStatus.DELETING + await branch.save(db=self.db) + + edges_removed = await self.delete_branch_data(branch_name=branch.name) + + query = await StandardNodeDeleteQuery.init(db=self.db, node=branch) + await query.execute(db=self.db) + branch_deleted = query.stats.get_counter("nodes_deleted") > 0 + + return BranchDeleteResult(branch_deleted=branch_deleted, edges_removed=edges_removed) + + async def delete_branch_data(self, branch_name: str) -> int: + """Remove a branch's data without requiring the branch itself to still exist. + + Returns the number of edges removed, so a caller whose own logging is the only thing the + operator can see is able to report progress. + """ + agnostic_edges_count = await self._delete_agnostic_peers(branch_name=branch_name) + branch_edges_count = await self._delete_edges(branch_name=branch_name) + return agnostic_edges_count + branch_edges_count + + async def _delete_agnostic_peers(self, branch_name: str) -> int: + """Drop the agnostic attributes and relationships of Nodes that exist on no other branch. + + Both queries locate those Nodes through the branch's IS_PART_OF edges, so this has to + finish before the edge deletion starts removing them. Resuming a delete that failed part + way through this stage is safe for the same reason: no IS_PART_OF edge has been touched yet. + + Returns the number of edges removed, which is every edge of the peers detached here, not + only the agnostic ones that led to them. + """ + batch_size = min(self.batch_size, MAX_AGNOSTIC_PEER_BATCH_SIZE) + + relationships_query = await DeleteBranchAgnosticRelationshipsQuery.init( + db=self.db, branch_name=branch_name, batch_size=batch_size + ) + await relationships_query.execute(db=self.db) + + attributes_query = await DeleteBranchAgnosticAttributesQuery.init( + db=self.db, branch_name=branch_name, batch_size=batch_size + ) + await attributes_query.execute(db=self.db) + + edges_removed = relationships_query.stats.get_counter( + "relationships_deleted" + ) + attributes_query.stats.get_counter("relationships_deleted") + if edges_removed: + self.log.info( + f"Deleted agnostic peers of nodes only on branch '{branch_name}', {edges_removed} edge(s) removed" + ) + return edges_removed + + async def _delete_edges(self, branch_name: str) -> int: + edges_removed = 0 + for edge_type in DatabaseEdgeType: + deleted_total = 0 + while True: + # A fresh query per batch: the stats counters accumulate per instance, so a reused + # one would never report zero again and the loop would not end. + query = await DeleteBranchEdgesQuery.init( + db=self.db, branch_name=branch_name, edge_type=edge_type, batch_size=self.batch_size + ) + await query.execute(db=self.db) + deleted = query.deleted_edge_count() + if not deleted: + break + deleted_total += deleted + + if deleted_total: + edges_removed += deleted_total + self.log.info(f"Deleted {deleted_total} {edge_type.value} edge(s) on branch '{branch_name}'") + + return edges_removed diff --git a/backend/infrahub/core/branch/delete_coordinator.py b/backend/infrahub/core/branch/delete_coordinator.py new file mode 100644 index 00000000000..5e66cbd495e --- /dev/null +++ b/backend/infrahub/core/branch/delete_coordinator.py @@ -0,0 +1,96 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Protocol + +from infrahub.events.branch_action import BranchDeletedEvent +from infrahub.events.models import EventMeta +from infrahub.exceptions import ValidationError +from infrahub.workflows.catalogue import BRANCH_CANCEL_PROPOSED_CHANGES, GIT_REPOSITORIES_DELETE_BRANCH + +if TYPE_CHECKING: + from infrahub.context import InfrahubContext + from infrahub.core.branch.data_deleter import BranchDataDeleterInterface, BranchDeleteResult, LoggerInterface + from infrahub.core.branch.models import Branch + from infrahub.services.adapters.event import InfrahubEventService + from infrahub.services.adapters.workflow import InfrahubWorkflow + + +class DiffFreezerInterface(Protocol): + """Interface for freezing diffs.""" + + async def freeze_diffs_for_branch(self, branch_name: str) -> None: ... + + +class BranchDeleteOrchestrator: + """Delete a branch and do the work that follows from it. + + Holds no database of its own: the deletion is delegated, which is what keeps the ordering and the + post-delete decisions here testable without one. + """ + + def __init__( + self, + data_deleter: BranchDataDeleterInterface, + diff_freezer: DiffFreezerInterface, + event_service: InfrahubEventService, + workflow: InfrahubWorkflow, + log: LoggerInterface, + global_branch: Branch, + delete_git_branch_after_merge: bool, + ) -> None: + self.data_deleter = data_deleter + self.diff_freezer = diff_freezer + self.event_service = event_service + self.workflow = workflow + self.log = log + self.global_branch = global_branch + self.delete_git_branch_after_merge = delete_git_branch_after_merge + + async def delete( + self, + branch: Branch, + context: InfrahubContext, + delete_from_git: bool = False, + proposed_change_id: str | None = None, + ) -> BranchDeleteResult: + """Remove the branch, then cancel its proposed changes, announce it, and drop its Git branch. + + Raises: + ValidationError: When the branch is the default branch or an internal one. + + """ + # Before the freeze, not after: a refused delete has to leave the branch's diffs alone. + if branch.is_default: + raise ValidationError(f"Unable to delete {branch.name} it is the default branch.") + if branch.is_global: + raise ValidationError(f"Unable to delete {branch.name} this is an internal branch.") + + # Freezing has to precede the deletion, which takes away the branch name they are found by. + await self.diff_freezer.freeze_diffs_for_branch(branch_name=branch.name) + + result = await self.data_deleter.delete(branch=branch) + + if result.branch_deleted: + await self.workflow.submit_workflow( + workflow=BRANCH_CANCEL_PROPOSED_CHANGES, context=context, parameters={"branch_name": branch.name} + ) + await self.event_service.send( + event=BranchDeletedEvent( + branch_name=branch.name, + branch_id=str(branch.uuid), + sync_with_git=branch.sync_with_git, + meta=EventMeta.from_context(context=context.to_event_context(), branch=self.global_branch), + proposed_change_id=proposed_change_id, + ) + ) + else: + # Another attempt removed the branch, so the work above is already its responsibility. + self.log.info(f"Branch '{branch.name}' was already deleted") + + # Always execute in case concurrent delete process with delete_from_git=False won the delete race. + if (self.delete_git_branch_after_merge or delete_from_git) and branch.sync_with_git: + await self.workflow.submit_workflow( + workflow=GIT_REPOSITORIES_DELETE_BRANCH, context=context, parameters={"branch": branch.name} + ) + + return result diff --git a/backend/infrahub/core/branch/models.py b/backend/infrahub/core/branch/models.py index 83c68e709e7..f5241bcfdf2 100644 --- a/backend/infrahub/core/branch/models.py +++ b/backend/infrahub/core/branch/models.py @@ -14,7 +14,6 @@ from infrahub.core.query import Query, QueryType from infrahub.core.query.branch import ( BranchNodeGetListQuery, - DeleteBranchRelationshipsQuery, RebaseBranchQuery, ) from infrahub.core.registry import registry @@ -315,17 +314,16 @@ async def create(self, db: InfrahubDatabase, user_id: str = SYSTEM_USER_ID) -> b return await super().create(db=db, user_id=user_id) async def delete(self, db: InfrahubDatabase) -> None: - if self.is_default: - raise ValidationError(f"Unable to delete {self.name} it is the default branch.") - if self.is_global: - raise ValidationError(f"Unable to delete {self.name} this is an internal branch.") + """Not supported on a Branch. - self.status = BranchStatus.DELETING - await self.save(db=db) + The inherited implementation would drop the Branch vertex and silently leave every edge and + vertex belonging to the branch behind, so it is refused rather than overridden. - query = await DeleteBranchRelationshipsQuery.init(db=db, branch_name=self.name) - await query.execute(db=db) - await super().delete(db=db) + Raises: + NotImplementedError: Always. + + """ + raise NotImplementedError("Unable to delete a Branch directly, use BranchDataDeleter instead.") def get_query_filter_relationships( self, rel_labels: list, at: Optional[Timestamp] = None, include_outside_parentheses: bool = False diff --git a/backend/infrahub/core/branch/tasks.py b/backend/infrahub/core/branch/tasks.py index 38258febc3e..35daeaddeaa 100644 --- a/backend/infrahub/core/branch/tasks.py +++ b/backend/infrahub/core/branch/tasks.py @@ -13,6 +13,8 @@ from infrahub.core import registry from infrahub.core.branch import Branch from infrahub.core.branch.creator import BranchCreator +from infrahub.core.branch.data_deleter import BranchDataDeleter +from infrahub.core.branch.delete_coordinator import BranchDeleteOrchestrator from infrahub.core.branch.enums import BranchStatus from infrahub.core.changelog.diff import DiffChangelogCollector, MigrationTracker from infrahub.core.constants import DiffAction, MutationAction @@ -36,7 +38,6 @@ from infrahub.core.validators.tasks import schema_validate_migrations from infrahub.dependencies.registry import get_component_registry from infrahub.events.branch_action import ( - BranchDeletedEvent, BranchMergedEvent, BranchMigratedEvent, BranchRebasedEvent, @@ -54,7 +55,6 @@ BRANCH_MERGE_POST_PROCESS, DIFF_REFRESH_ALL, DIFF_UPDATE, - GIT_REPOSITORIES_DELETE_BRANCH, IPAM_RECONCILIATION, TRIGGER_ARTIFACT_DEFINITION_GENERATE, TRIGGER_GENERATOR_DEFINITION_RUN, @@ -538,37 +538,30 @@ async def delete_branch( ) -> None: await add_tags(branches=[branch], nodes=[proposed_change_id] if proposed_change_id else None) database = await get_database() + workflow = get_workflow() + event_service = await get_event_service() async with database.start_session() as db: - obj = await Branch.get_by_name(db=db, name=str(branch)) + # ignore_deleting=False so that a delete which failed part way through can be run again: + obj = await Branch.get_by_name(db=db, name=str(branch), ignore_deleting=False) component_registry = get_component_registry() diff_repository = await component_registry.get_component(DiffRepository, db=db, branch=obj) - await diff_repository.freeze_diffs_for_branch(branch_name=branch) - - await obj.delete(db=db) - event_context = context.to_event_context() - event = BranchDeletedEvent( - branch_name=branch, - branch_id=str(obj.uuid), - sync_with_git=obj.sync_with_git, - meta=EventMeta.from_context(context=event_context, branch=registry.get_global_branch()), - proposed_change_id=proposed_change_id, - ) - - await get_workflow().submit_workflow( - workflow=BRANCH_CANCEL_PROPOSED_CHANGES, context=context, parameters={"branch_name": branch} + log = get_run_logger() + orchestrator = BranchDeleteOrchestrator( + data_deleter=BranchDataDeleter(db=db, batch_size=config.SETTINGS.database.query_size_limit, log=log), + diff_freezer=diff_repository, + event_service=event_service, + workflow=workflow, + log=log, + global_branch=registry.get_global_branch(), + delete_git_branch_after_merge=config.SETTINGS.git.delete_git_branch_after_merge, ) - - event_service = await get_event_service() - await event_service.send(event=event) - - should_delete_git = (config.SETTINGS.git.delete_git_branch_after_merge or delete_from_git) and obj.sync_with_git - if should_delete_git: - await get_workflow().submit_workflow( - workflow=GIT_REPOSITORIES_DELETE_BRANCH, + await orchestrator.delete( + branch=obj, context=context, - parameters={"branch": branch}, + delete_from_git=delete_from_git, + proposed_change_id=proposed_change_id, ) diff --git a/backend/infrahub/core/graph/__init__.py b/backend/infrahub/core/graph/__init__.py index 947747f069f..6e250218fee 100644 --- a/backend/infrahub/core/graph/__init__.py +++ b/backend/infrahub/core/graph/__init__.py @@ -1 +1 @@ -GRAPH_VERSION = 74 +GRAPH_VERSION = 75 diff --git a/backend/infrahub/core/migrations/graph/m032_cleanup_orphaned_branch_relationships.py b/backend/infrahub/core/migrations/graph/m032_cleanup_orphaned_branch_relationships.py index c77ccfadc39..e43075f18fb 100644 --- a/backend/infrahub/core/migrations/graph/m032_cleanup_orphaned_branch_relationships.py +++ b/backend/infrahub/core/migrations/graph/m032_cleanup_orphaned_branch_relationships.py @@ -2,9 +2,10 @@ from typing import TYPE_CHECKING, Any +from infrahub import config +from infrahub.core.branch.data_deleter import BranchDataDeleter from infrahub.core.migrations.shared import MigrationInput, MigrationResult from infrahub.core.query import Query, QueryType -from infrahub.core.query.branch import DeleteBranchRelationshipsQuery from infrahub.log import get_logger from ..shared import ArbitraryMigration @@ -83,10 +84,10 @@ async def execute(self, migration_input: MigrationInput) -> MigrationResult: log.info(f"Found {len(orphaned_branch_names)} orphaned branch names: {orphaned_branch_names}") + deleter = BranchDataDeleter(db=db, batch_size=config.SETTINGS.database.query_size_limit) for branch_name in orphaned_branch_names: log.info(f"Cleaning up branch '{branch_name}'...") - delete_query = await DeleteBranchRelationshipsQuery.init(db=db, branch_name=branch_name) - await delete_query.execute(db=db) + await deleter.delete_branch_data(branch_name=branch_name) log.info(f"Branch '{branch_name}' cleaned up.") log.info("Deleting orphaned relationships...") diff --git a/backend/infrahub/core/migrations/graph/m075_finish_deleting_branches.py b/backend/infrahub/core/migrations/graph/m075_finish_deleting_branches.py new file mode 100644 index 00000000000..efe96dc0e19 --- /dev/null +++ b/backend/infrahub/core/migrations/graph/m075_finish_deleting_branches.py @@ -0,0 +1,93 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from infrahub import config +from infrahub.core.branch import Branch +from infrahub.core.branch.data_deleter import BranchDataDeleter, BranchDataDeleterInterface +from infrahub.core.branch.enums import BranchStatus +from infrahub.core.migrations.shared import ArbitraryMigration, MigrationInput, MigrationResult, get_migration_console +from infrahub.core.query import Query, QueryType + +if TYPE_CHECKING: + from infrahub.database import InfrahubDatabase + +console = get_migration_console() + + +class DeletingBranchNamesQuery(Query): + """Find the branches whose delete never finished.""" + + name: str = "deleting_branch_names" + insert_return: bool = False + type: QueryType = QueryType.READ + + async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002 + query = """ +MATCH (b:Branch) +WHERE b.status = $deleting_status +RETURN b.name AS branch_name + """ + self.params["deleting_status"] = BranchStatus.DELETING.value + self.add_to_query(query) + self.update_return_labels("branch_name") + self.order_by = ["branch_name"] + + def get_branch_names(self) -> list[str]: + return [result.get_as_type(label="branch_name", return_type=str) for result in self.get_results()] + + +class Migration075(ArbitraryMigration): + """Finish deleting branches that a previous branch delete left unfinished. + + A branch delete used to run as one query whose memory use grew with the size of the branch, so + on a large branch it could exhaust the transaction memory pool and fail part way through. The + branch was left with the DELETING status, which hides it from the branch list, and with however + much of its data the failed run had not yet reached. Nothing retried it, and the branch could not + be deleted again because it was no longer possible to look up. + """ + + name: str = "075_finish_deleting_branches" + description: str = "Finish deleting branches whose delete failed part way through." + minimum_version: int = 74 + + async def validate_migration(self, db: InfrahubDatabase) -> MigrationResult: # noqa: ARG002 + return MigrationResult() + + def build_deleter(self, db: InfrahubDatabase) -> BranchDataDeleterInterface: + return BranchDataDeleter(db=db, batch_size=config.SETTINGS.database.query_size_limit) + + async def execute(self, migration_input: MigrationInput) -> MigrationResult: + db = migration_input.db + + try: + names_query = await DeletingBranchNamesQuery.init(db=db) + await names_query.execute(db=db) + branch_names = names_query.get_branch_names() + except Exception as exc: + return MigrationResult(errors=[f"Unable to look up branches in the DELETING state: {exc}"]) + + if not branch_names: + return MigrationResult() + + console.log(f"Found {len(branch_names)} branch(es) left in the DELETING state: {branch_names}") + + # One branch failing must not hide the others: each is deleted in its own right, and the + # names of those that failed are reported so a re-run has something to act on. + errors: list[str] = [] + deleter = self.build_deleter(db=db) + for branch_name in branch_names: + console.log(f"Cleaning up branch '{branch_name}' left in the DELETING state...") + try: + branch = await Branch.get_by_name(db=db, name=branch_name, ignore_deleting=False) + delete_result = await deleter.delete(branch=branch) + except Exception as exc: + console.log(f"Branch '{branch_name}' could not be deleted: {exc}") + errors.append(f"branch '{branch_name}': {exc}") + continue + if delete_result.branch_deleted: + console.log(f"Branch '{branch_name}' deleted, {delete_result.edges_removed} edge(s) removed.") + else: + console.log(f"Branch '{branch_name}' was already gone, {delete_result.edges_removed} edge(s) removed.") + + return MigrationResult(errors=errors) diff --git a/backend/infrahub/core/query/branch.py b/backend/infrahub/core/query/branch.py index d99ff4d02b1..6a13a11c266 100644 --- a/backend/infrahub/core/query/branch.py +++ b/backend/infrahub/core/query/branch.py @@ -10,83 +10,137 @@ from infrahub.core.timestamp import Timestamp if TYPE_CHECKING: + from infrahub.core.constants.database import DatabaseEdgeType from infrahub.database import InfrahubDatabase -class DeleteBranchRelationshipsQuery(Query): - name: str = "delete_branch_relationships" +class DeleteBranchAgnosticRelationshipsQuery(Query): + """Delete the agnostic Relationship vertices attached to Nodes that only exist on this branch. + + Must run before any IS_PART_OF edge of the branch is deleted: the branch-only determination + reads those edges, so once they are gone the affected Nodes can no longer be found and their + agnostic peers leak. + """ + + name: str = "delete_branch_agnostic_relationships" insert_return: bool = False + insert_limit: bool = False type: QueryType = QueryType.WRITE - def __init__(self, branch_name: str, **kwargs: Any) -> None: + def __init__(self, branch_name: str, batch_size: int, **kwargs: Any) -> None: self.branch_name = branch_name + self.batch_size = batch_size super().__init__(**kwargs) async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002 query = """ -// -------------- -// for every Node that only exists on this branch (it's about to be deleted), -// find any agnostic relationships or attributes connected to the Node and delete them -// -------------- -OPTIONAL MATCH (:Root)<-[e:IS_PART_OF {status: "active"}]-(n:Node) +MATCH (:Root)<-[e:IS_PART_OF {status: "active"}]-(n:Node) WHERE e.branch = $branch_name -// does the node only exist on this branch? -CALL (n) { - OPTIONAL MATCH (n)-[ipo:IS_PART_OF {status: "active"}]->(:Root) +AND NOT EXISTS { + MATCH (n)-[ipo:IS_PART_OF {status: "active"}]->(:Root) WHERE ipo.branch <> $branch_name - LIMIT 1 - RETURN ipo IS NOT NULL AS node_exists_on_other_branch } -// if so, delete any linked agnostic relationships or attributes -CALL (n, node_exists_on_other_branch) { - WITH n, node_exists_on_other_branch - WHERE node_exists_on_other_branch = FALSE - OPTIONAL MATCH (n)-[:IS_RELATED {branch: $global_branch_name}]-(rel:Relationship) +CALL (n) { + MATCH (n)-[:IS_RELATED {branch: $global_branch_name}]-(rel:Relationship) DETACH DELETE rel -} IN TRANSACTIONS OF 500 ROWS -CALL (n, node_exists_on_other_branch) { - WITH n, node_exists_on_other_branch - WHERE node_exists_on_other_branch = FALSE - OPTIONAL MATCH (n)-[:HAS_ATTRIBUTE {branch: $global_branch_name}]-(attr:Attribute) - DETACH DELETE attr -} IN TRANSACTIONS OF 500 ROWS +} IN TRANSACTIONS OF %(batch_size)s ROWS + """ % {"batch_size": self.batch_size} + self.params["branch_name"] = self.branch_name + self.params["global_branch_name"] = GLOBAL_BRANCH_NAME + self.add_to_query(query) -// reduce the results to a single row -WITH 1 AS one -LIMIT 1 -// -------------- -// for every edge on this branch, delete it -// -------------- -MATCH (s)-[r]->(d) -WHERE r.branch = $branch_name -CALL (r) { - DELETE r -} IN TRANSACTIONS OF 500 ROWS +class DeleteBranchAgnosticAttributesQuery(Query): + """Delete the agnostic Attribute vertices attached to Nodes that only exist on this branch. -// -------------- -// get the database IDs of every vertex linked to a deleted edge -// -------------- -WITH DISTINCT elementId(s) AS s_id, elementId(d) AS d_id -WITH collect(s_id) + collect(d_id) AS vertex_ids -UNWIND vertex_ids AS vertex_id + Carries the same ordering requirement as the agnostic Relationship query. + """ -// -------------- -// delete any vertices that are now orphaned -// -------------- -CALL (vertex_id) { - MATCH (n) - WHERE elementId(n) = vertex_id - AND NOT exists((n)--()) - DELETE n -} IN TRANSACTIONS OF 500 ROWS - """ + name: str = "delete_branch_agnostic_attributes" + insert_return: bool = False + insert_limit: bool = False + + type: QueryType = QueryType.WRITE + + def __init__(self, branch_name: str, batch_size: int, **kwargs: Any) -> None: + self.branch_name = branch_name + self.batch_size = batch_size + super().__init__(**kwargs) + + async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002 + query = """ +MATCH (:Root)<-[e:IS_PART_OF {status: "active"}]-(n:Node) +WHERE e.branch = $branch_name +AND NOT EXISTS { + MATCH (n)-[ipo:IS_PART_OF {status: "active"}]->(:Root) + WHERE ipo.branch <> $branch_name +} +CALL (n) { + MATCH (n)-[:HAS_ATTRIBUTE {branch: $global_branch_name}]-(attr:Attribute) + DETACH DELETE attr +} IN TRANSACTIONS OF %(batch_size)s ROWS + """ % {"batch_size": self.batch_size} self.params["branch_name"] = self.branch_name self.params["global_branch_name"] = GLOBAL_BRANCH_NAME self.add_to_query(query) +class DeleteBranchEdgesQuery(Query): + """Delete one batch of edges of a single type belonging to a branch, plus any vertex left bare. + + Every edge on the branch is removed by this query's DELETE, and both endpoints of each one are + then re-examined, so a vertex is examined once per edge it had. The batch that removes its last + edge is therefore the one that sees it at degree zero and deletes it. Nothing can be stranded, + because no edge is ever removed by any other means -- which is why the vertices need no separate + sweep afterwards, and why the vertex delete must not be a DETACH DELETE. A DETACH DELETE would + take out the branch edges the vertex still had, and those edges would then never reach a batch + of their own, leaving the vertices on their far side unexamined and orphaned. + + The DISTINCT is what makes this sound: it forces the whole batch's edge deletes to complete + before the first vertex is examined, so degree zero means degree zero. + + Naming the edge type is what lets the `branch` range index be used for the match; the type + cannot be a query parameter, so it is interpolated from the closed DatabaseEdgeType enum. + + Run repeatedly until it stops deleting edges. + """ + + name: str = "delete_branch_edges" + insert_return: bool = False + insert_limit: bool = False + + type: QueryType = QueryType.WRITE + + def __init__(self, branch_name: str, edge_type: DatabaseEdgeType, batch_size: int, **kwargs: Any) -> None: + self.branch_name = branch_name + self.edge_type = edge_type + self.batch_size = batch_size + super().__init__(**kwargs) + + async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002 + query = """ +MATCH (s)-[r:%(edge_type)s]->(d) +WHERE r.branch = $branch_name +WITH s, r, d +LIMIT $batch_size +DELETE r + +WITH s, d +UNWIND [s, d] AS v +WITH DISTINCT v +WHERE NOT v:Root +AND NOT EXISTS { MATCH (v)--() } +DELETE v + """ % {"edge_type": self.edge_type.value} + self.params["branch_name"] = self.branch_name + self.params["batch_size"] = self.batch_size + self.add_to_query(query) + + def deleted_edge_count(self) -> int: + return self.stats.get_counter("relationships_deleted") + + class RebaseBranchQuery(Query): """Rebase a branch onto the default branch by updating edge timestamps. diff --git a/backend/infrahub/graphql/mutations/branch.py b/backend/infrahub/graphql/mutations/branch.py index a07e23ea089..c98838d0d00 100644 --- a/backend/infrahub/graphql/mutations/branch.py +++ b/backend/infrahub/graphql/mutations/branch.py @@ -148,7 +148,8 @@ async def mutate( wait_until_completion: bool = True, ) -> Self: graphql_context: GraphqlContext = info.context - obj = await Branch.get_by_name(db=graphql_context.db, name=str(data.name)) + # ignore_deleting=False so a delete that failed part way through can be retried: the first + obj = await Branch.get_by_name(db=graphql_context.db, name=str(data.name), ignore_deleting=False) await apply_external_context(graphql_context=graphql_context, context_input=context) parameters = { diff --git a/backend/tests/component/core/migrations/graph/test_024.py b/backend/tests/component/core/migrations/graph/test_024.py index 9beae34f476..eb022e2a513 100644 --- a/backend/tests/component/core/migrations/graph/test_024.py +++ b/backend/tests/component/core/migrations/graph/test_024.py @@ -3,6 +3,7 @@ import pytest from infrahub.core import registry +from infrahub.core.branch.data_deleter import BranchDataDeleter from infrahub.core.branch.models import Branch from infrahub.core.constants import RelationshipHierarchyDirection from infrahub.core.diff.coordinator import DiffCoordinator @@ -42,7 +43,7 @@ async def test_hierarchy_fix_migration( await diff_merger.merge_graph(at=at) # delete the branch - await branch.delete(db=db) + await BranchDataDeleter(db=db, batch_size=5).delete(branch=branch) # remove the hierarchy property on main query = """ diff --git a/backend/tests/component/core/migrations/graph/test_067_freeze_orphaned_branch_tracking_diffs.py b/backend/tests/component/core/migrations/graph/test_067_freeze_orphaned_branch_tracking_diffs.py index d4d06e51729..d68a2888877 100644 --- a/backend/tests/component/core/migrations/graph/test_067_freeze_orphaned_branch_tracking_diffs.py +++ b/backend/tests/component/core/migrations/graph/test_067_freeze_orphaned_branch_tracking_diffs.py @@ -20,6 +20,7 @@ from infrahub_sdk.timestamp import Timestamp from infrahub.core.branch import Branch +from infrahub.core.branch.data_deleter import BranchDataDeleter from infrahub.core.branch.enums import BranchStatus from infrahub.core.diff.model.path import BranchTrackingId, EnrichedDiffs, FrozenTrackingId from infrahub.core.diff.repository.repository import DiffRepository @@ -143,7 +144,7 @@ async def test_migration_067( from_time=deleted_from, to_time=deleted_from.add(seconds=60), ) - await deleted_branch.delete(db=db) + await BranchDataDeleter(db=db, batch_size=5).delete(branch=deleted_branch) expectations.append( DiffExpectation( name="deleted branch frozen", @@ -184,7 +185,7 @@ async def test_migration_067( from_time=v1_from, to_time=v1_from.add(seconds=60), ) - await reused_v1.delete(db=db) + await BranchDataDeleter(db=db, batch_size=5).delete(branch=reused_v1) reused_v2 = await create_branch(db=db, branch_name=reused_name) v2_from = Timestamp(reused_v2.get_branched_from()) reused_v2_diff, reused_v2_base = await self._create_diff_pair( @@ -220,7 +221,7 @@ async def test_migration_067( to_time=frozen_from.add(seconds=60), ) await diff_repository.freeze_diffs_for_branch(branch_name=frozen_branch.name) - await frozen_branch.delete(db=db) + await BranchDataDeleter(db=db, batch_size=5).delete(branch=frozen_branch) expectations.append( DiffExpectation( name="already frozen unchanged", @@ -243,7 +244,7 @@ async def test_migration_067( lifecycle_v1.status = BranchStatus.MERGED await lifecycle_v1.save(db=db) await diff_repository.mark_tracking_ids_merged(tracking_ids=[BranchTrackingId(name=lifecycle_name)]) - await lifecycle_v1.delete(db=db) + await BranchDataDeleter(db=db, batch_size=5).delete(branch=lifecycle_v1) lifecycle_v2 = await create_branch(db=db, branch_name=lifecycle_name) lc_v2_from = Timestamp(lifecycle_v2.get_branched_from()) lc_v2_diff, lc_v2_base = await self._create_diff_pair( diff --git a/backend/tests/component/core/migrations/graph/test_075_finish_deleting_branches.py b/backend/tests/component/core/migrations/graph/test_075_finish_deleting_branches.py new file mode 100644 index 00000000000..f4b2e99bf96 --- /dev/null +++ b/backend/tests/component/core/migrations/graph/test_075_finish_deleting_branches.py @@ -0,0 +1,184 @@ +import pytest + +from infrahub.core.branch.data_deleter import BranchDataDeleter, BranchDataDeleterInterface, BranchDeleteResult +from infrahub.core.branch.enums import BranchStatus +from infrahub.core.branch.models import Branch +from infrahub.core.initialization import create_branch +from infrahub.core.manager import NodeManager +from infrahub.core.migrations.graph.m075_finish_deleting_branches import Migration075 +from infrahub.core.migrations.shared import MigrationInput +from infrahub.core.node import Node +from infrahub.database import InfrahubDatabase +from infrahub.exceptions import BranchNotFoundError, NodeNotFoundError + + +class FailingBranchDeleter: + """Deletes for real, except for one branch, where it raises instead. + + Delegating for the others is what lets a test tell "the loop carried on" apart from "the loop + called delete again but nothing was reclaimed". + """ + + def __init__(self, deleter: BranchDataDeleter, failing_branch_name: str) -> None: + self.deleter = deleter + self.failing_branch_name = failing_branch_name + self.attempted: list[str] = [] + + async def delete(self, branch: Branch) -> BranchDeleteResult: + self.attempted.append(branch.name) + if branch.name == self.failing_branch_name: + raise ValueError("FAILED") + return await self.deleter.delete(branch=branch) + + +class Migration075WithFailingDeleter(Migration075): + """Migration075 wired to a deleter that fails on one nominated branch.""" + + failing_branch_name: str = "" + deleter: FailingBranchDeleter | None = None + + model_config = {"arbitrary_types_allowed": True} + + def build_deleter(self, db: InfrahubDatabase) -> BranchDataDeleterInterface: + self.deleter = FailingBranchDeleter( + deleter=BranchDataDeleter(db=db, batch_size=5), failing_branch_name=self.failing_branch_name + ) + return self.deleter + + +async def _branch_edge_count(db: InfrahubDatabase, branch_name: str) -> int: + results = await db.execute_query( + query="MATCH ()-[e]->() WHERE e.branch = $branch_name RETURN count(e) AS count", + params={"branch_name": branch_name}, + ) + return results[0]["count"] + + +async def _add_tag(db: InfrahubDatabase, branch: Branch, name: str) -> Node: + node = await Node.init(db=db, branch=branch, schema="BuiltinTag") + await node.new(db=db, name=name) + await node.save(db=db) + return node + + +async def test_migration_075(db: InfrahubDatabase, default_branch: Branch, person_tag_schema: None) -> None: + """A branch abandoned in DELETING loses every edge, while the other branches keep all of theirs.""" + healthy_branch = await create_branch(db=db, branch_name="healthy-branch") + stalled_branch = await create_branch(db=db, branch_name="stalled-branch") + + node_on_main = await _add_tag(db=db, branch=default_branch, name="node-on-main") + node_on_healthy = await _add_tag(db=db, branch=healthy_branch, name="node-on-healthy-branch") + node_on_stalled = await _add_tag(db=db, branch=stalled_branch, name="node-on-stalled-branch") + + # Reproduce what a failed delete leaves behind: the status set, the data still present. + stalled_branch.status = BranchStatus.DELETING + await stalled_branch.save(db=db) + + edges_before = { + name: await _branch_edge_count(db=db, branch_name=name) + for name in (default_branch.name, healthy_branch.name, stalled_branch.name) + } + # Every branch has to start with edges, otherwise the assertions below prove nothing. + assert all(count > 0 for count in edges_before.values()), edges_before + # Likewise the stalled branch's node has to be readable to begin with, so that it disappearing + # afterwards is attributable to the migration. + node_before = await NodeManager.get_one(db=db, branch=stalled_branch, id=node_on_stalled.id) + assert node_before is not None + + migration = Migration075() + execution_result = await migration.execute(migration_input=MigrationInput(db=db)) + assert not execution_result.errors + + validation_result = await migration.validate_migration(db=db) + assert not validation_result.errors + + edges_after = { + name: await _branch_edge_count(db=db, branch_name=name) + for name in (default_branch.name, healthy_branch.name, stalled_branch.name) + } + + # The abandoned branch is emptied; the untouched branches keep exactly what they had. + assert edges_after == { + default_branch.name: edges_before[default_branch.name], + healthy_branch.name: edges_before[healthy_branch.name], + stalled_branch.name: 0, + } + + # The branch node is gone too, along with the data that hung off it. + with pytest.raises(BranchNotFoundError): + await Branch.get_by_name(db=db, name=stalled_branch.name, ignore_deleting=False) + with pytest.raises(NodeNotFoundError): + await NodeManager.get_one(db=db, branch=stalled_branch, id=node_on_stalled.id, raise_on_error=True) + + # The surviving branches are still usable, not merely still edged. + reloaded_healthy = await Branch.get_by_name(db=db, name=healthy_branch.name) + assert reloaded_healthy.status == BranchStatus.OPEN + retrieved_on_healthy = await NodeManager.get_one(db=db, branch=healthy_branch, id=node_on_healthy.id) + assert retrieved_on_healthy is not None + assert retrieved_on_healthy.get_attribute("name").value == "node-on-healthy-branch" + retrieved_on_main = await NodeManager.get_one(db=db, branch=default_branch, id=node_on_main.id) + assert retrieved_on_main is not None + assert retrieved_on_main.get_attribute("name").value == "node-on-main" + + +async def test_migration_075_no_deleting_branches( + db: InfrahubDatabase, default_branch: Branch, person_tag_schema: None +) -> None: + """With nothing to finish, every branch keeps every edge.""" + branch = await create_branch(db=db, branch_name="untouched-branch") + await _add_tag(db=db, branch=default_branch, name="node-on-main") + await _add_tag(db=db, branch=branch, name="node-on-untouched-branch") + + edges_before = { + name: await _branch_edge_count(db=db, branch_name=name) for name in (default_branch.name, branch.name) + } + assert all(count > 0 for count in edges_before.values()), edges_before + + migration = Migration075() + execution_result = await migration.execute(migration_input=MigrationInput(db=db)) + assert not execution_result.errors + + edges_after = { + name: await _branch_edge_count(db=db, branch_name=name) for name in (default_branch.name, branch.name) + } + assert edges_after == edges_before + + reloaded = await Branch.get_by_name(db=db, name=branch.name) + assert reloaded.status == BranchStatus.OPEN + + +async def test_migration_075_one_failing_branch_does_not_block_the_others( + db: InfrahubDatabase, default_branch: Branch, person_tag_schema: None +) -> None: + """A branch that cannot be deleted is reported by name; the rest are still reclaimed.""" + branch_names = ["stalled-a", "stalled-b", "stalled-c"] + for branch_name in branch_names: + branch = await create_branch(db=db, branch_name=branch_name) + await _add_tag(db=db, branch=branch, name=f"node-on-{branch_name}") + branch.status = BranchStatus.DELETING + await branch.save(db=db) + + edges_before = {name: await _branch_edge_count(db=db, branch_name=name) for name in branch_names} + assert all(count > 0 for count in edges_before.values()), edges_before + + migration = Migration075WithFailingDeleter(failing_branch_name="stalled-b") + execution_result = await migration.execute(migration_input=MigrationInput(db=db)) + + # The failure is surfaced against the branch it belongs to, not as a bare exception string. + assert execution_result.errors == ["branch 'stalled-b': FAILED"] + + # Every branch was attempted, including the ones queued behind the failure. + assert migration.deleter is not None + assert migration.deleter.attempted == branch_names + + # The other two are genuinely reclaimed; the failed one keeps everything it had. + edges_after = {name: await _branch_edge_count(db=db, branch_name=name) for name in branch_names} + assert edges_after == {"stalled-a": 0, "stalled-b": edges_before["stalled-b"], "stalled-c": 0} + + for deleted_name in ("stalled-a", "stalled-c"): + with pytest.raises(BranchNotFoundError): + await Branch.get_by_name(db=db, name=deleted_name, ignore_deleting=False) + + # The failed branch is left exactly as it was, so a re-run can pick it up. + still_stalled = await Branch.get_by_name(db=db, name="stalled-b", ignore_deleting=False) + assert still_stalled.status == BranchStatus.DELETING diff --git a/backend/tests/component/core/resource_manager/test_number_pool_query.py b/backend/tests/component/core/resource_manager/test_number_pool_query.py index d06500c3175..01bd9dccf94 100644 --- a/backend/tests/component/core/resource_manager/test_number_pool_query.py +++ b/backend/tests/component/core/resource_manager/test_number_pool_query.py @@ -4,6 +4,7 @@ from infrahub.core import registry from infrahub.core.branch import Branch +from infrahub.core.branch.data_deleter import BranchDataDeleter from infrahub.core.constants import InfrahubKind from infrahub.core.diff.coordinator import DiffCoordinator from infrahub.core.diff.data_check_synchronizer import DiffDataCheckSynchronizer @@ -128,7 +129,7 @@ async def test_NumberPoolGetUsed( assert await get_used_numbers_in_pool(db=db, pool=incident_pool, branch=default_branch) == [1, 2, 3, 4, 5, 6, 7] # Delete the branch and validate that the numbers allocated previously are available - await branch2.delete(db=db) + await BranchDataDeleter(db=db, batch_size=5).delete(branch=branch2) assert await get_used_numbers_in_pool(db=db, pool=incident_pool, branch=default_branch) == [1, 2, 3] # Create a new branch and add more incidents @@ -138,7 +139,7 @@ async def test_NumberPoolGetUsed( assert await get_used_numbers_in_pool(db=db, pool=incident_pool, branch=default_branch) == [1, 2, 3, 4, 5, 6] # Delete the branch and validate that the numbers allocated previously are available - await branch3.delete(db=db) + await BranchDataDeleter(db=db, batch_size=5).delete(branch=branch3) assert await get_used_numbers_in_pool(db=db, pool=incident_pool, branch=default_branch) == [1, 2, 3] # Delete nodes in main and ensure the numbers are reallocated diff --git a/backend/tests/component/core/test_branch.py b/backend/tests/component/core/test_branch.py index 0182f4caca8..709654af98f 100644 --- a/backend/tests/component/core/test_branch.py +++ b/backend/tests/component/core/test_branch.py @@ -5,6 +5,8 @@ from pydantic import ValidationError as PydanticValidationError from infrahub.core.branch import Branch +from infrahub.core.branch.data_deleter import BranchDataDeleter +from infrahub.core.branch.enums import BranchStatus from infrahub.core.constants import GLOBAL_BRANCH_NAME from infrahub.core.diff.coordinator import DiffCoordinator from infrahub.core.diff.data_check_synchronizer import DiffDataCheckSynchronizer @@ -295,6 +297,56 @@ async def test_is_isolated(db: InfrahubDatabase, base_dataset_02: dict) -> None: assert cars[0].name.value == "volt" +async def test_branch_delete_method_is_refused(db: InfrahubDatabase, default_branch: Branch) -> None: + """Deleting through the model would drop the Branch node and orphan all of its data.""" + branch = await create_branch(branch_name="refuse-me", db=db) + + with pytest.raises( + NotImplementedError, match=r"^Unable to delete a Branch directly, use BranchDataDeleter instead\.$" + ): + await branch.delete(db=db) + + # The branch is untouched: still listed, still OPEN. + reloaded = await Branch.get_by_name(name="refuse-me", db=db) + assert reloaded.status == BranchStatus.OPEN + + +async def test_branch_deleter_refuses_default_and_global_branches(db: InfrahubDatabase, default_branch: Branch) -> None: + """The guards that used to live on Branch.delete still apply on the deleter.""" + deleter = BranchDataDeleter(db=db, batch_size=5) + + with pytest.raises(ValidationError, match=r"Unable to delete .* it is the default branch\."): + await deleter.delete(branch=default_branch) + + global_branch = registry.get_global_branch() + with pytest.raises(ValidationError, match=r"Unable to delete .* this is an internal branch\."): + await deleter.delete(branch=global_branch) + + # Neither branch was altered before the guard fired. + assert (await Branch.get_by_name(name=default_branch.name, db=db)).status == BranchStatus.OPEN + + +async def test_branch_deleter_reports_who_removed_the_branch( + db: InfrahubDatabase, default_branch: Branch, repos_in_main: dict, car_person_schema: SchemaBranch +) -> None: + """Only the attempt that removes the branch reports having done so.""" + branch = await create_branch(branch_name="claim-me", db=db) + person = await Node.init(schema="TestPerson", branch=branch.name, db=db) + await person.new(name="Bobby", height=175, db=db) + await person.save(db=db) + + deleter = BranchDataDeleter(db=db, batch_size=5) + + first = await deleter.delete(branch=branch) + assert first.branch_deleted is True + assert first.edges_removed > 0 + + # Deleting the same branch again is harmless, but must not claim to have done it. + second = await deleter.delete(branch=branch) + assert second.branch_deleted is False + assert second.edges_removed == 0 + + async def test_delete_branch( db: InfrahubDatabase, default_branch: Branch, repos_in_main: dict, car_person_schema: SchemaBranch ) -> None: @@ -314,7 +366,9 @@ async def test_delete_branch( params = {"branch_name": branch_name} pre_delete = await db.execute_query(query=relationship_query, params=params) - await branch.delete(db=db) + # A batch size well below the number of edges on the branch, so the batching loop has to run + # more than once to finish. + await BranchDataDeleter(db=db, batch_size=5).delete(branch=branch) post_delete = await db.execute_query(query=relationship_query, params=params) assert branch.id == found.id @@ -362,7 +416,7 @@ async def test_delete_branch_with_agnostic_attrs_and_rels( rel_uuid = agnostic_rel.id # Delete the branch - await branch.delete(db=db) + await BranchDataDeleter(db=db, batch_size=5).delete(branch=branch) # Verify the branch is deleted with pytest.raises(BranchNotFoundError): @@ -425,7 +479,7 @@ async def test_delete_branch_after_merge_preserves_node( assert device_on_main.get_attribute("serial_number").value == "SN-67890" # Delete the branch - await branch.delete(db=db) + await BranchDataDeleter(db=db, batch_size=5).delete(branch=branch) # Verify the branch is deleted with pytest.raises(BranchNotFoundError): diff --git a/backend/tests/component/graphql/diff/test_diff_tree_terminal_branch.py b/backend/tests/component/graphql/diff/test_diff_tree_terminal_branch.py index 99427afe1fe..e38684361de 100644 --- a/backend/tests/component/graphql/diff/test_diff_tree_terminal_branch.py +++ b/backend/tests/component/graphql/diff/test_diff_tree_terminal_branch.py @@ -5,6 +5,7 @@ from infrahub.core import registry from infrahub.core.branch import Branch +from infrahub.core.branch.data_deleter import BranchDataDeleter from infrahub.core.branch.enums import BranchStatus from infrahub.core.diff.coordinator import DiffCoordinator from infrahub.core.diff.data_check_synchronizer import DiffDataCheckSynchronizer @@ -390,7 +391,7 @@ async def deleted_branch( merged_branch: Branch, ) -> Branch: """Delete the branch and remove it from registry.""" - await merged_branch.delete(db=db) + await BranchDataDeleter(db=db, batch_size=5).delete(branch=merged_branch) registry.branch.pop(merged_branch.name, None) return merged_branch diff --git a/backend/tests/component/graphql/mutations/test_branch.py b/backend/tests/component/graphql/mutations/test_branch.py index d15144c7490..0658b2a24ea 100644 --- a/backend/tests/component/graphql/mutations/test_branch.py +++ b/backend/tests/component/graphql/mutations/test_branch.py @@ -554,6 +554,37 @@ async def test_branch_delete_own_branch_succeeds( assert delete_result.data["BranchDelete"]["ok"] is True +async def test_branch_delete_retries_a_branch_left_deleting( + db: InfrahubDatabase, + default_branch: Branch, + register_core_models_schema: SchemaBranch, + first_account: Node, + session_first_account: AccountSession, + local_services: InfrahubServices, +) -> None: + """A delete that failed part way through can be retried. + + The first attempt leaves the branch in DELETING, which the default branch lookup hides. Without + accepting that status the retry would report the branch as missing and its data would be unreachable. + """ + branch = await _create_branch(branch_name="stuck-deleting-branch", db=db, owner=first_account) + branch.status = BranchStatus.DELETING + await branch.save(db=db) + + with patch.object(local_services.workflow, "execute_workflow", new=AsyncMock(return_value=None)): + delete_result = await graphql_mutation( + query='mutation { BranchDelete(data: { name: "stuck-deleting-branch" }) { ok } }', + db=db, + branch=default_branch, + account_session=session_first_account, + service=local_services, + ) + + assert delete_result.errors is None + assert delete_result.data + assert delete_result.data["BranchDelete"]["ok"] is True + + async def test_branch_delete_others_branch_denied( db: InfrahubDatabase, default_branch: Branch, diff --git a/backend/tests/integration/diff/test_diff_update.py b/backend/tests/integration/diff/test_diff_update.py index 4d03f654d1e..7ae7197132d 100644 --- a/backend/tests/integration/diff/test_diff_update.py +++ b/backend/tests/integration/diff/test_diff_update.py @@ -7,6 +7,7 @@ from infrahub_sdk.exceptions import GraphQLError from infrahub.core import registry +from infrahub.core.branch.data_deleter import BranchDataDeleter from infrahub.core.constants import NULL_VALUE, BranchConflictKeep, DiffAction, InfrahubKind from infrahub.core.constants.database import DatabaseEdgeType from infrahub.core.diff.model.path import BranchTrackingId, ConflictSelection, EnrichedDiffRoot @@ -215,7 +216,7 @@ async def diff_on_deleted_branch( diff = await self.get_branch_diff(db=db, branch=deleted_branch) assert len(diff.nodes) == 1 - await deleted_branch.delete(db=db) + await BranchDataDeleter(db=db, batch_size=5).delete(branch=deleted_branch) return diff @staticmethod diff --git a/backend/tests/unit/core/branch/test_delete_coordinator.py b/backend/tests/unit/core/branch/test_delete_coordinator.py new file mode 100644 index 00000000000..1e9261e0943 --- /dev/null +++ b/backend/tests/unit/core/branch/test_delete_coordinator.py @@ -0,0 +1,151 @@ +from uuid import uuid4 + +import pytest + +from infrahub.auth.session import AccountSession +from infrahub.auth.types import AuthType +from infrahub.context import BranchContext, InfrahubContext +from infrahub.core.branch import Branch +from infrahub.core.branch.data_deleter import BranchDeleteResult +from infrahub.core.branch.delete_coordinator import BranchDeleteOrchestrator +from infrahub.core.constants import GLOBAL_BRANCH_NAME +from infrahub.events.branch_action import BranchDeletedEvent +from infrahub.workflows.catalogue import BRANCH_CANCEL_PROPOSED_CHANGES, GIT_REPOSITORIES_DELETE_BRANCH +from tests.adapters.event import MemoryInfrahubEvent +from tests.adapters.log import FakeLogger +from tests.adapters.workflow import WorkflowRecorder + + +class RecordingDataDeleter: + """Reports a fixed outcome and remembers which branches it was asked to delete.""" + + def __init__(self, result: BranchDeleteResult) -> None: + self.result = result + self.deleted: list[str] = [] + + async def delete(self, branch: Branch) -> BranchDeleteResult: + self.deleted.append(branch.name) + return self.result + + +class RecordingDiffFreezer: + def __init__(self) -> None: + self.frozen: list[str] = [] + + async def freeze_diffs_for_branch(self, branch_name: str) -> None: + self.frozen.append(branch_name) + + +@pytest.fixture +def context() -> InfrahubContext: + return InfrahubContext( + account=AccountSession(account_id=str(uuid4()), auth_type=AuthType.NONE), + branch=BranchContext(name="main", id="placeholder"), + ) + + +def _build( + *, + branch_deleted: bool, + delete_git_branch_after_merge: bool = False, +) -> tuple[ + BranchDeleteOrchestrator, + RecordingDataDeleter, + RecordingDiffFreezer, + WorkflowRecorder, + MemoryInfrahubEvent, + FakeLogger, +]: + data_deleter = RecordingDataDeleter( + result=BranchDeleteResult(branch_deleted=branch_deleted, edges_removed=7 if branch_deleted else 0) + ) + diff_freezer = RecordingDiffFreezer() + workflow = WorkflowRecorder() + events = MemoryInfrahubEvent() + log = FakeLogger() + orchestrator = BranchDeleteOrchestrator( + data_deleter=data_deleter, + diff_freezer=diff_freezer, + event_service=events, + workflow=workflow, + log=log, + global_branch=Branch(name=GLOBAL_BRANCH_NAME, is_global=True, uuid=uuid4()), + delete_git_branch_after_merge=delete_git_branch_after_merge, + ) + return orchestrator, data_deleter, diff_freezer, workflow, events, log + + +def _branch(name: str = "some-branch", sync_with_git: bool = True) -> Branch: + return Branch(name=name, sync_with_git=sync_with_git, uuid=uuid4()) + + +async def test_delete_runs_post_delete_work(context: InfrahubContext) -> None: + """The attempt that removes the branch cancels its proposed changes and announces it.""" + orchestrator, data_deleter, diff_freezer, workflow, events, _ = _build(branch_deleted=True) + branch = _branch() + + result = await orchestrator.delete(branch=branch, context=context, delete_from_git=True) + + assert result == BranchDeleteResult(branch_deleted=True, edges_removed=7) + # The diffs are frozen before the delete, since they are found by a branch name it takes away. + assert diff_freezer.frozen == [branch.name] + assert data_deleter.deleted == [branch.name] + assert [type(event) for event in events.events] == [BranchDeletedEvent] + assert workflow.get_submit_calls_for(BRANCH_CANCEL_PROPOSED_CHANGES) == [ + {"workflow": BRANCH_CANCEL_PROPOSED_CHANGES, "parameters": {"branch_name": branch.name}} + ] + assert workflow.get_submit_calls_for(GIT_REPOSITORIES_DELETE_BRANCH) == [ + {"workflow": GIT_REPOSITORIES_DELETE_BRANCH, "parameters": {"branch": branch.name}} + ] + + +async def test_delete_skips_post_delete_work_when_another_attempt_won(context: InfrahubContext) -> None: + """An attempt that removed nothing must not repeat what belongs to the one that did.""" + orchestrator, _, _, workflow, events, log = _build(branch_deleted=False) + branch = _branch() + + result = await orchestrator.delete(branch=branch, context=context, delete_from_git=False) + + assert result.branch_deleted is False + assert events.events == [] + assert workflow.submit_calls == [] + assert log.info_logs == [f"Branch '{branch.name}' was already deleted"] + + +async def test_delete_from_git_survives_losing_the_race(context: InfrahubContext) -> None: + """The attempt that won may not have been asked to remove the Git branch.""" + orchestrator, _, _, workflow, events, _ = _build(branch_deleted=False) + branch = _branch() + + await orchestrator.delete(branch=branch, context=context, delete_from_git=True) + + assert workflow.get_submit_calls_for(GIT_REPOSITORIES_DELETE_BRANCH) == [ + {"workflow": GIT_REPOSITORIES_DELETE_BRANCH, "parameters": {"branch": branch.name}} + ] + # Still nothing that belongs to the winning attempt. + assert events.events == [] + assert workflow.get_submit_calls_for(BRANCH_CANCEL_PROPOSED_CHANGES) == [] + + +async def test_delete_from_git_is_ignored_for_a_branch_that_does_not_track_git(context: InfrahubContext) -> None: + orchestrator, _, _, workflow, events, _ = _build(branch_deleted=True) + branch = _branch(sync_with_git=False) + + await orchestrator.delete(branch=branch, context=context, delete_from_git=True) + + assert workflow.get_submit_calls_for(GIT_REPOSITORIES_DELETE_BRANCH) == [] + # The rest of the post-delete work still ran. + assert [type(event) for event in events.events] == [BranchDeletedEvent] + + +async def test_delete_git_branch_after_merge_setting_deletes_without_an_explicit_request( + context: InfrahubContext, +) -> None: + orchestrator, _, _, workflow, _, _ = _build(branch_deleted=True, delete_git_branch_after_merge=True) + branch = _branch() + + await orchestrator.delete(branch=branch, context=context, delete_from_git=False) + + assert workflow.get_submit_calls_for(GIT_REPOSITORIES_DELETE_BRANCH) == [ + {"workflow": GIT_REPOSITORIES_DELETE_BRANCH, "parameters": {"branch": branch.name}} + ] diff --git a/changelog/9889.fixed.md b/changelog/9889.fixed.md new file mode 100644 index 00000000000..0d3e734e99e --- /dev/null +++ b/changelog/9889.fixed.md @@ -0,0 +1 @@ +Fixed deleting a large branch failing with a database out-of-memory error and leaving the branch and its data behind. Branches left behind by an earlier failure are now cleaned up on upgrade. From d2eb74d9e34f878e4fbb7beb85e7f145f1dda8d9 Mon Sep 17 00:00:00 2001 From: Aaron McCarty Date: Mon, 10 Aug 2026 11:34:26 -0700 Subject: [PATCH 16/48] resolve backend test import conflict --- backend/tests/component/core/test_branch.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/backend/tests/component/core/test_branch.py b/backend/tests/component/core/test_branch.py index 4762d536c62..174571d2b71 100644 --- a/backend/tests/component/core/test_branch.py +++ b/backend/tests/component/core/test_branch.py @@ -5,10 +5,7 @@ from pydantic import ValidationError as PydanticValidationError from infrahub.core.branch import Branch -<<<<<<< HEAD -======= from infrahub.core.branch.data_deleter import BranchDataDeleter ->>>>>>> stable from infrahub.core.branch.enums import BranchStatus from infrahub.core.constants import GLOBAL_BRANCH_NAME from infrahub.core.diff.coordinator import DiffCoordinator From 8e2b70a576b42e92c59c36e962a84ff812f2d450 Mon Sep 17 00:00:00 2001 From: Aaron McCarty Date: Mon, 10 Aug 2026 11:34:59 -0700 Subject: [PATCH 17/48] resolve BranchDelete mutation conflict update Branch.get_by_name to include ignore_deleting=True so that a delete can be retried --- backend/infrahub/graphql/mutations/branch.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/backend/infrahub/graphql/mutations/branch.py b/backend/infrahub/graphql/mutations/branch.py index a24c314c548..c9a66759a59 100644 --- a/backend/infrahub/graphql/mutations/branch.py +++ b/backend/infrahub/graphql/mutations/branch.py @@ -152,8 +152,8 @@ async def mutate( wait_until_completion: bool = True, ) -> Self: graphql_context: GraphqlContext = info.context -<<<<<<< HEAD - obj = await Branch.get_by_name(db=graphql_context.db, name=str(data.name)) + # ignore_deleting=False so a delete that failed part way through can be retried: the first + obj = await Branch.get_by_name(db=graphql_context.db, name=str(data.name), ignore_deleting=False) # A branch left in MERGE_FAILED by a died merge must not be deleted until an administrator has # recovered it. @@ -169,10 +169,6 @@ async def mutate( branch=obj ) -======= - # ignore_deleting=False so a delete that failed part way through can be retried: the first - obj = await Branch.get_by_name(db=graphql_context.db, name=str(data.name), ignore_deleting=False) ->>>>>>> stable await apply_external_context(graphql_context=graphql_context, context_input=context) parameters = { From 5be5ac8467ac129a8051458204b1c26f429f4d9f Mon Sep 17 00:00:00 2001 From: Aaron McCarty Date: Mon, 10 Aug 2026 11:35:57 -0700 Subject: [PATCH 18/48] update delete_branch task to use WorkflowPriority.LOW pass the "low_context" into the branch delete orchestrator for follow-up tasks --- backend/infrahub/core/branch/tasks.py | 32 ++------------------------- 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/backend/infrahub/core/branch/tasks.py b/backend/infrahub/core/branch/tasks.py index 3b3a10b55a4..8096c7c5c11 100644 --- a/backend/infrahub/core/branch/tasks.py +++ b/backend/infrahub/core/branch/tasks.py @@ -49,11 +49,6 @@ from infrahub.core.validators.tasks import schema_validate_migrations from infrahub.dependencies.registry import get_component_registry from infrahub.events.branch_action import ( -<<<<<<< HEAD - BranchDeletedEvent, -======= - BranchMergedEvent, ->>>>>>> stable BranchMigratedEvent, BranchRebasedEvent, ) @@ -72,7 +67,6 @@ get_workflow, ) from infrahub.workflows.catalogue import ( - BRANCH_CANCEL_PROPOSED_CHANGES, DIFF_REFRESH_ALL, DIFF_UPDATE, IPAM_RECONCILIATION, @@ -388,14 +382,8 @@ async def delete_branch( ) -> None: await add_tags(branches=[branch], nodes=[proposed_change_id] if proposed_change_id else None) database = await get_database() -<<<<<<< HEAD - - low_context = context.model_copy(update={"priority": WorkflowPriority.LOW}) - -======= workflow = get_workflow() event_service = await get_event_service() ->>>>>>> stable async with database.start_session() as db: # ignore_deleting=False so that a delete which failed part way through can be run again: obj = await Branch.get_by_name(db=db, name=str(branch), ignore_deleting=False) @@ -413,28 +401,12 @@ async def delete_branch( global_branch=registry.get_global_branch(), delete_git_branch_after_merge=config.SETTINGS.git.delete_git_branch_after_merge, ) -<<<<<<< HEAD - - await get_workflow().submit_workflow( - workflow=BRANCH_CANCEL_PROPOSED_CHANGES, context=low_context, parameters={"branch_name": branch} - ) - - event_service = await get_event_service() - await event_service.send(event=event) - - should_delete_git = (config.SETTINGS.git.delete_git_branch_after_merge or delete_from_git) and obj.sync_with_git - if should_delete_git: - await get_workflow().submit_workflow( - workflow=GIT_REPOSITORIES_DELETE_BRANCH, - context=low_context, - parameters={"branch": branch}, -======= + low_context = context.model_copy(update={"priority": WorkflowPriority.LOW}) await orchestrator.delete( branch=obj, - context=context, + context=low_context, delete_from_git=delete_from_git, proposed_change_id=proposed_change_id, ->>>>>>> stable ) From ffc1e0c78870022afca102a8ef81e0ca94f11c78 Mon Sep 17 00:00:00 2001 From: Aaron McCarty Date: Mon, 10 Aug 2026 11:49:59 -0700 Subject: [PATCH 19/48] resolve frontend conflicts from concrete-kind repository links Keep stable's behavior from #10195 on top of the 1.11 frontend architecture: git-repository.tsx links to the node's concrete __typename using the moved NodeCore/object-urls modules, and the profiles-field test is ported onto getObjectForEditingFromApi, which replaced generateObjectEditFormQuery. Co-Authored-By: Claude Opus 5 (1M context) --- .../entities/homepage/ui/git-repository.tsx | 10 +- .../get-object-for-editing-from-api.test.ts | 101 ++++++++++++++++++ .../generateObjectEditFormQuery.test.ts | 69 ------------ 3 files changed, 102 insertions(+), 78 deletions(-) create mode 100644 frontend/app/src/entities/nodes/object/api/get-object-for-editing-from-api.test.ts delete mode 100644 frontend/app/src/entities/nodes/object/generateObjectEditFormQuery.test.ts diff --git a/frontend/app/src/entities/homepage/ui/git-repository.tsx b/frontend/app/src/entities/homepage/ui/git-repository.tsx index 795e35b51c1..6805143461c 100644 --- a/frontend/app/src/entities/homepage/ui/git-repository.tsx +++ b/frontend/app/src/entities/homepage/ui/git-repository.tsx @@ -3,18 +3,10 @@ import { ListBoxItem } from "react-aria-components"; import type { Dropdown } from "@/shared/api/graphql/generated/types"; import { focusVisibleStyle } from "@/shared/components/aria/style-rac"; -<<<<<<< HEAD import { classNames, getTextColor } from "@/shared/utils/common"; +import type { NodeCore } from "@/entities/nodes/object/domain/model/node"; import { getObjectDetailsUrl } from "@/entities/nodes/object/ui/routing/object-urls"; -import { GENERIC_REPOSITORY_KIND } from "@/entities/repository/domain/model/repository"; -======= -import { Tooltip } from "@/shared/components/ui/tooltip"; -import { classNames, getTextColor } from "@/shared/utils/common"; - -import type { NodeCore } from "@/entities/nodes/types"; -import { getObjectDetailsUrl } from "@/entities/nodes/utils"; ->>>>>>> stable export interface GitRepositoryData extends NodeCore { sync_status?: Dropdown | null; diff --git a/frontend/app/src/entities/nodes/object/api/get-object-for-editing-from-api.test.ts b/frontend/app/src/entities/nodes/object/api/get-object-for-editing-from-api.test.ts new file mode 100644 index 00000000000..c269daf535a --- /dev/null +++ b/frontend/app/src/entities/nodes/object/api/get-object-for-editing-from-api.test.ts @@ -0,0 +1,101 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { graphqlClient } from "@/shared/api/graphql/client"; + +import type { NodeSchema } from "@/entities/schema/domain/model/schema"; + +import { + generateGenericSchema, + generateNodeSchema, + generateRelationshipSchema, +} from "../../../../../tests/fake/schema"; +import { getObjectForEditingFromApi } from "./get-object-for-editing-from-api"; + +// `client` also re-exports gql.tada's `graphql` tag, which the module under test uses to build +// the query. Stub it with the identity so the assertions can read the generated query string. +vi.mock("@/shared/api/graphql/client", () => ({ + graphql: (query: string) => query, + graphqlClient: { query: vi.fn() }, +})); + +const profilesRelationship = generateRelationshipSchema({ + name: "profiles", + peer: "CoreProfile", + identifier: "node__profile", +}); + +const getGeneratedQuery = () => + vi.mocked(graphqlClient.query).mock.calls[0]![0].query as unknown as string; + +describe("getObjectForEditingFromApi", () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(graphqlClient.query).mockResolvedValue({ data: {} } as any); + }); + + it("requests the profiles field for a node exposing a profiles relationship", async () => { + // GIVEN + const schema = generateNodeSchema({ + generate_profile: true, + relationships: [profilesRelationship], + }); + + // WHEN + await getObjectForEditingFromApi({ + schema, + objectId: "object-id", + branchName: "main", + atDate: null, + }); + + // THEN + expect(getGeneratedQuery()).toContain("profiles"); + expect(getGeneratedQuery()).toContain("profile_priority"); + }); + + it("does not request the profiles field for a generic without a profiles relationship", async () => { + // GIVEN + const schema = generateGenericSchema({ + generate_profile: true, + relationships: [], + }) as unknown as NodeSchema; + + // WHEN + await getObjectForEditingFromApi({ + schema, + objectId: "object-id", + branchName: "main", + atDate: null, + }); + + // THEN + expect(getGeneratedQuery()).not.toContain("profiles"); + }); + + it("requests the profiles field for a generic exposing a profiles relationship", async () => { + // GIVEN + const schema = generateGenericSchema({ + generate_profile: true, + relationships: [ + generateRelationshipSchema({ + name: "used_by", + peer: "CoreNode", + identifier: "profile__node", + }), + profilesRelationship, + ], + }) as unknown as NodeSchema; + + // WHEN + await getObjectForEditingFromApi({ + schema, + objectId: "object-id", + branchName: "main", + atDate: null, + }); + + // THEN + expect(getGeneratedQuery()).toContain("profiles"); + expect(getGeneratedQuery()).toContain("profile_priority"); + }); +}); diff --git a/frontend/app/src/entities/nodes/object/generateObjectEditFormQuery.test.ts b/frontend/app/src/entities/nodes/object/generateObjectEditFormQuery.test.ts deleted file mode 100644 index a71754ee215..00000000000 --- a/frontend/app/src/entities/nodes/object/generateObjectEditFormQuery.test.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { describe, expect, it } from "vitest"; - -import { generateObjectEditFormQuery } from "@/entities/nodes/object-item-edit/generateObjectEditFormQuery"; -import type { NodeSchema } from "@/entities/schema/types"; - -import { - generateGenericSchema, - generateNodeSchema, - generateRelationshipSchema, -} from "../../../../tests/fake/schema"; - -const profilesRelationship = generateRelationshipSchema({ - name: "profiles", - peer: "CoreProfile", - identifier: "node__profile", -}); - -describe("generateObjectEditFormQuery", () => { - it("requests the profiles field for a node exposing a profiles relationship", () => { - // GIVEN - const schema = generateNodeSchema({ - generate_profile: true, - relationships: [profilesRelationship], - }); - - // WHEN - const query = generateObjectEditFormQuery({ schema, objectId: "object-id" }); - - // THEN - expect(query).toContain("profiles"); - expect(query).toContain("profile_priority"); - }); - - it("does not request the profiles field for a generic without a profiles relationship", () => { - // GIVEN - const schema = generateGenericSchema({ - generate_profile: true, - relationships: [], - }) as unknown as NodeSchema; - - // WHEN - const query = generateObjectEditFormQuery({ schema, objectId: "object-id" }); - - // THEN - expect(query).not.toContain("profiles"); - }); - - it("requests the profiles field for a generic exposing a profiles relationship", () => { - // GIVEN - const schema = generateGenericSchema({ - generate_profile: true, - relationships: [ - generateRelationshipSchema({ - name: "used_by", - peer: "CoreNode", - identifier: "profile__node", - }), - profilesRelationship, - ], - }) as unknown as NodeSchema; - - // WHEN - const query = generateObjectEditFormQuery({ schema, objectId: "object-id" }); - - // THEN - expect(query).toContain("profiles"); - expect(query).toContain("profile_priority"); - }); -}); From ae3dae8a4f42b1240c1e31e58156f1e8f8de1be1 Mon Sep 17 00:00:00 2001 From: Aaron McCarty Date: Mon, 10 Aug 2026 13:13:14 -0700 Subject: [PATCH 20/48] clean up comment --- backend/infrahub/graphql/mutations/branch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/infrahub/graphql/mutations/branch.py b/backend/infrahub/graphql/mutations/branch.py index c9a66759a59..9740a0ef117 100644 --- a/backend/infrahub/graphql/mutations/branch.py +++ b/backend/infrahub/graphql/mutations/branch.py @@ -152,7 +152,7 @@ async def mutate( wait_until_completion: bool = True, ) -> Self: graphql_context: GraphqlContext = info.context - # ignore_deleting=False so a delete that failed part way through can be retried: the first + # ignore_deleting=False so a delete that failed part way through can be retried obj = await Branch.get_by_name(db=graphql_context.db, name=str(data.name), ignore_deleting=False) # A branch left in MERGE_FAILED by a died merge must not be deleted until an administrator has From e35e2d79148f055d07ef7b3969c419009b6bc679 Mon Sep 17 00:00:00 2001 From: Aaron McCarty Date: Mon, 10 Aug 2026 13:14:55 -0700 Subject: [PATCH 21/48] fix BranchDeleteCoordinator tests for expected workflow calls --- .../unit/core/branch/test_delete_coordinator.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/tests/unit/core/branch/test_delete_coordinator.py b/backend/tests/unit/core/branch/test_delete_coordinator.py index 1e9261e0943..cdb6b966217 100644 --- a/backend/tests/unit/core/branch/test_delete_coordinator.py +++ b/backend/tests/unit/core/branch/test_delete_coordinator.py @@ -91,11 +91,11 @@ async def test_delete_runs_post_delete_work(context: InfrahubContext) -> None: assert diff_freezer.frozen == [branch.name] assert data_deleter.deleted == [branch.name] assert [type(event) for event in events.events] == [BranchDeletedEvent] - assert workflow.get_submit_calls_for(BRANCH_CANCEL_PROPOSED_CHANGES) == [ - {"workflow": BRANCH_CANCEL_PROPOSED_CHANGES, "parameters": {"branch_name": branch.name}} + assert [call["parameters"] for call in workflow.get_submit_calls_for(BRANCH_CANCEL_PROPOSED_CHANGES)] == [ + {"branch_name": branch.name} ] - assert workflow.get_submit_calls_for(GIT_REPOSITORIES_DELETE_BRANCH) == [ - {"workflow": GIT_REPOSITORIES_DELETE_BRANCH, "parameters": {"branch": branch.name}} + assert [call["parameters"] for call in workflow.get_submit_calls_for(GIT_REPOSITORIES_DELETE_BRANCH)] == [ + {"branch": branch.name} ] @@ -119,8 +119,8 @@ async def test_delete_from_git_survives_losing_the_race(context: InfrahubContext await orchestrator.delete(branch=branch, context=context, delete_from_git=True) - assert workflow.get_submit_calls_for(GIT_REPOSITORIES_DELETE_BRANCH) == [ - {"workflow": GIT_REPOSITORIES_DELETE_BRANCH, "parameters": {"branch": branch.name}} + assert [call["parameters"] for call in workflow.get_submit_calls_for(GIT_REPOSITORIES_DELETE_BRANCH)] == [ + {"branch": branch.name} ] # Still nothing that belongs to the winning attempt. assert events.events == [] @@ -146,6 +146,6 @@ async def test_delete_git_branch_after_merge_setting_deletes_without_an_explicit await orchestrator.delete(branch=branch, context=context, delete_from_git=False) - assert workflow.get_submit_calls_for(GIT_REPOSITORIES_DELETE_BRANCH) == [ - {"workflow": GIT_REPOSITORIES_DELETE_BRANCH, "parameters": {"branch": branch.name}} + assert [call["parameters"] for call in workflow.get_submit_calls_for(GIT_REPOSITORIES_DELETE_BRANCH)] == [ + {"branch": branch.name} ] From 9b570f6731993eae657438c0f70af8813cb6b0cd Mon Sep 17 00:00:00 2001 From: Aaron McCarty Date: Mon, 10 Aug 2026 13:15:50 -0700 Subject: [PATCH 22/48] match on exact "default" for visibility check --- frontend/app/tests/e2e/branches/branch-details.spec.ts | 2 +- tests/e2e/branches/test_branch_details.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app/tests/e2e/branches/branch-details.spec.ts b/frontend/app/tests/e2e/branches/branch-details.spec.ts index fdd154b6b6f..f45bb8472ec 100644 --- a/frontend/app/tests/e2e/branches/branch-details.spec.ts +++ b/frontend/app/tests/e2e/branches/branch-details.spec.ts @@ -50,7 +50,7 @@ test.describe("Branch details view", () => { // Header await expect(page.getByRole("heading", { name: BRANCH_NAME })).toBeVisible(); - await expect(page.getByText("default")).not.toBeVisible(); + await expect(page.getByText("default", { exact: true })).not.toBeVisible(); await expect(page.getByRole("button", { name: "View node metadata" })).toBeVisible(); // Branch attributes diff --git a/tests/e2e/branches/test_branch_details.py b/tests/e2e/branches/test_branch_details.py index 94b2f62067a..8eb2283f4a2 100644 --- a/tests/e2e/branches/test_branch_details.py +++ b/tests/e2e/branches/test_branch_details.py @@ -69,7 +69,7 @@ async def test_display_branch_name_and_no_default_badge( # Header await expect(admin_page.get_by_role("heading", name=NON_DEFAULT_BRANCH)).to_be_visible() - await expect(admin_page.get_by_text("default")).not_to_be_visible() + await expect(admin_page.get_by_text("default", exact=True)).not_to_be_visible() await expect(admin_page.get_by_role("button", name="View node metadata")).to_be_visible() # Branch attributes From ad4bdf9b32271ee0e82293da59e442b01ce47a06 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Tue, 11 Aug 2026 08:50:26 +0200 Subject: [PATCH 23/48] fix: create branches imported from git with sync_with_git enabled [IFC-3001] The git agent relied on the SDK client default for sync_with_git when creating an imported branch in the graph. That default flipped from True to False in the SDK, so branches discovered on a repository remote were created without the flag. Merging such a branch, running its repository checks and generating its artifacts are all gated on sync_with_git, so the git side of a merge was silently skipped: the git branch was never merged into the repository default branch and the recorded commit never advanced. Closes #10208 --- backend/infrahub/git/base.py | 6 +- .../integration/git/test_sync_branch_flag.py | 59 +++++++++++++++++++ changelog/10208.fixed.md | 1 + 3 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 backend/tests/integration/git/test_sync_branch_flag.py create mode 100644 changelog/10208.fixed.md diff --git a/backend/infrahub/git/base.py b/backend/infrahub/git/base.py index 81cce57d2e1..7ca41bc6bb9 100644 --- a/backend/infrahub/git/base.py +++ b/backend/infrahub/git/base.py @@ -653,11 +653,15 @@ async def update_commit_value(self, branch_name: str, commit: str) -> bool: async def create_branch_in_graph(self, branch_name: str) -> BranchData: """Create a new branch in the graph. + The branch originates from a git repository, so it must sync with git: merging it, running + its repository checks and generating its artifacts are all gated on that flag. It is passed + explicitly rather than left to the client default, which does not sync with git. + NOTE We need to validate that we are not gonna end up with a race condition since a call to the GraphQL API will trigger a new RPC call to add a branch in this repo. """ # TODO need to handle the exception properly - branch = await self.sdk.branch.create(branch_name=branch_name) + branch = await self.sdk.branch.create(branch_name=branch_name, sync_with_git=True) log.debug(f"Branch {branch_name} created in the Graph", repository=self.name, branch=branch_name) return branch diff --git a/backend/tests/integration/git/test_sync_branch_flag.py b/backend/tests/integration/git/test_sync_branch_flag.py new file mode 100644 index 00000000000..21a644674c6 --- /dev/null +++ b/backend/tests/integration/git/test_sync_branch_flag.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from git.repo import Repo + +from infrahub.core.constants import InfrahubKind +from infrahub.core.node import Node +from infrahub.git import InfrahubRepository +from tests.helpers.test_app import TestInfrahubApp + +if TYPE_CHECKING: + from pathlib import Path + + from infrahub_sdk import InfrahubClient + + from infrahub.database import InfrahubDatabase + from tests.helpers.file_repo import FileRepo + +NEW_BRANCH = "branch-from-git" + + +class TestSyncBranchFlag(TestInfrahubApp): + async def test_branch_imported_from_git_syncs_with_git( + self, + db: InfrahubDatabase, + client: InfrahubClient, + git_repo_car_dealership: FileRepo, + git_repos_dir: Path, + ) -> None: + """A branch discovered on the remote must be created with sync_with_git enabled. + + Merging the branch, running its repository checks and generating its artifacts are all gated + on that flag, so a branch imported without it silently skips the git side of a merge. + """ + obj = await Node.init(schema=InfrahubKind.REPOSITORY, db=db) + await obj.new( + db=db, + name=git_repo_car_dealership.name, + description="test repository", + location=git_repo_car_dealership.path, + ) + await obj.save(db=db) + + repo = await InfrahubRepository.new( + id=obj.id, + name=git_repo_car_dealership.name, + location=git_repo_car_dealership.path, + client=client, + ) + + # The branch is created after the clone so the sync treats it as a new remote branch. + Repo(git_repo_car_dealership.path).git.branch(NEW_BRANCH, "main") + + collected = await repo.collect_pending_imports() + assert [pending.infrahub_branch_name for pending in collected.imports] == [NEW_BRANCH] + + branch = await client.branch.get(branch_name=NEW_BRANCH) + assert branch.sync_with_git is True diff --git a/changelog/10208.fixed.md b/changelog/10208.fixed.md new file mode 100644 index 00000000000..a584699ec9e --- /dev/null +++ b/changelog/10208.fixed.md @@ -0,0 +1 @@ +Fixed branches imported from a Git repository being created without the sync with Git flag, which caused merges to skip the Git side. From 1dda41c5c6a413b5346f5ca265f3063280d863fc Mon Sep 17 00:00:00 2001 From: Saltaferis Dimitrios Date: Tue, 11 Aug 2026 10:11:05 +0300 Subject: [PATCH 24/48] test(frontend): pre-bundle react-dropdown-menu so it cannot reload mid-run A dependency discovered after Vite's initial scan triggers a re-optimization reload that resets vi.mock, which surfaces as "mockClear is not a function" in whichever spec happens to be running. CI hit it on two different files across consecutive runs. @radix-ui/react-dropdown-menu is a declared app dependency the scan misses, so pre-bundling it removes that trigger. Two entries already in this list, @dagrejs/dagre and html-to-image, still reload: they belong to a workspace package consumed as source and do not resolve from the app, so pre-bundling silently skips them. Fixing that needs them declared here, which is a dependency change of its own. Co-Authored-By: Claude Fable 5 --- frontend/app/vitest.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/app/vitest.config.ts b/frontend/app/vitest.config.ts index 2647d344387..2abcd0c80de 100644 --- a/frontend/app/vitest.config.ts +++ b/frontend/app/vitest.config.ts @@ -20,6 +20,7 @@ export default mergeConfig( "lucide-react", "tailwind-variants", "tailwind-merge", + "@radix-ui/react-dropdown-menu", "@radix-ui/react-scroll-area", "react-resizable-panels", "@graphiql/plugin-explorer", From 293dba32f8c92c31bb903bd49c69f50c7cb5df58 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Tue, 11 Aug 2026 10:09:08 +0200 Subject: [PATCH 25/48] test: pin import_sync_branch_names so the git sync flag test ignores the ambient env --- backend/tests/integration/git/conftest.py | 13 +++++++++++++ .../tests/integration/git/test_sync_branch_flag.py | 1 + 2 files changed, 14 insertions(+) diff --git a/backend/tests/integration/git/conftest.py b/backend/tests/integration/git/conftest.py index fc0dee7f9f6..1d43c2bb2a4 100644 --- a/backend/tests/integration/git/conftest.py +++ b/backend/tests/integration/git/conftest.py @@ -214,3 +214,16 @@ def delete_git_branch_after_merge_reset_config() -> Generator[None, None, None]: original = config.SETTINGS.git.delete_git_branch_after_merge yield config.SETTINGS.git.delete_git_branch_after_merge = original + + +@pytest.fixture +def import_every_remote_branch() -> Generator[None, None, None]: + """Import every remote branch, whatever INFRAHUB_GIT_IMPORT_SYNC_BRANCH_NAMES holds in the ambient environment. + + An empty list disables branch-name filtering. Without this, a value exported in the developer's + shell leaks into the test process and silently drops the branches the test relies on. + """ + original = config.SETTINGS.git.import_sync_branch_names + config.SETTINGS.git.import_sync_branch_names = [] + yield + config.SETTINGS.git.import_sync_branch_names = original diff --git a/backend/tests/integration/git/test_sync_branch_flag.py b/backend/tests/integration/git/test_sync_branch_flag.py index 21a644674c6..148e9e3a598 100644 --- a/backend/tests/integration/git/test_sync_branch_flag.py +++ b/backend/tests/integration/git/test_sync_branch_flag.py @@ -27,6 +27,7 @@ async def test_branch_imported_from_git_syncs_with_git( client: InfrahubClient, git_repo_car_dealership: FileRepo, git_repos_dir: Path, + import_every_remote_branch: None, ) -> None: """A branch discovered on the remote must be created with sync_with_git enabled. From 3993523b9d47b06bd86cf19188ec0de2c159b084 Mon Sep 17 00:00:00 2001 From: Guillaume Mazoyer Date: Tue, 11 Aug 2026 13:26:44 +0200 Subject: [PATCH 26/48] fix: keep kinds with no read field as a kind-level dependency (#10189) A Python computed attribute whose query follows a relationship to a generic was recomputed on every schema change. The analyzer reports every member kind of the generic. The members the query reads no field from come back with an empty field set. `TransformReadSet.from_read_fields` treated that as unmappable and marked the whole read set imprecise, so `RecomputeScoper` selected the attribute on every schema update. The empty set guard had a reason before. The analyzer could not record an `hfid` read, so an hfid only read arrived empty and had to be caught here. `hfid` is now recorded under its schema name, and `IMPRECISE_READ_FIELDS` covers it, so the guard is no longer needed. A kind with no read field now stays in `read_kinds` and is left out of `read_fields`. If you add or remove that kind, the recompute still runs. If you change a field on that kind, the recompute does not run. `Jinja2DependencyDeriver` already used this shape. The two derivers agree again. --- .../python_transform.py | 18 ++++++++++----- .../test_python_transform_deriver.py | 22 ++++++++++++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/backend/infrahub/core/schema/schema_branch_computed/python_transform.py b/backend/infrahub/core/schema/schema_branch_computed/python_transform.py index 9002dd97b07..97e683cd4d5 100644 --- a/backend/infrahub/core/schema/schema_branch_computed/python_transform.py +++ b/backend/infrahub/core/schema/schema_branch_computed/python_transform.py @@ -50,17 +50,25 @@ def imprecise(cls) -> TransformReadSet: def from_read_fields(cls, read_fields_by_kind: Mapping[str, Iterable[str]]) -> TransformReadSet: """Build the read set from a kind to read-field-names mapping. - The whole set is imprecise when any kind reads a derived field, or reads no field - at all. An imprecise set is recomputed on every schema change. + The whole set is imprecise when any kind reads a derived field. An imprecise set is + recomputed on every schema change. + + A kind the query reaches but reads no field from stays in ``read_kinds`` and is left + out of ``read_fields``: adding or removing that kind still triggers, a field change + on it does not. Traversing a relationship to a generic reports every member kind, + including the ones the query reads nothing from. """ + read_kinds: set[str] = set() read_fields: dict[str, frozenset[str]] = {} for kind, names in read_fields_by_kind.items(): fields = frozenset(names) - if not fields or fields & IMPRECISE_READ_FIELDS: + if fields & IMPRECISE_READ_FIELDS: return cls.imprecise() - read_fields[kind] = fields + read_kinds.add(kind) + if fields: + read_fields[kind] = fields - return cls(read_kinds=frozenset(read_fields), read_fields=read_fields) + return cls(read_kinds=frozenset(read_kinds), read_fields=read_fields) class PythonTransformRegistry: diff --git a/backend/tests/unit/computed_attribute/test_python_transform_deriver.py b/backend/tests/unit/computed_attribute/test_python_transform_deriver.py index 7ac9be85ba6..1961cbe185b 100644 --- a/backend/tests/unit/computed_attribute/test_python_transform_deriver.py +++ b/backend/tests/unit/computed_attribute/test_python_transform_deriver.py @@ -146,9 +146,21 @@ def test_from_read_fields_precise() -> None: } -def test_kind_with_no_mapped_fields_marks_imprecise() -> None: - # A kind the query reaches but reads no field from. Whether that should mark the whole - # set imprecise is unsettled; this pins the current behaviour. - read_set = TransformReadSet.from_read_fields({OWNER_KIND: set()}) +def test_kind_with_no_mapped_fields_is_kind_only() -> None: + # Traversing a relationship to a generic reports every member kind, including the ones + # the query reads nothing from. Those stay a kind-level dependency only. + read_set = TransformReadSet.from_read_fields( + { + "TestPerson": {"name", "cars"}, + "TestElectricCar": {"nbr_engine"}, + OWNER_KIND: set(), + "TestGazCar": set(), + } + ) - assert read_set.depends_on_everything is True + assert read_set.depends_on_everything is False + assert set(read_set.read_kinds) == {"TestPerson", "TestElectricCar", OWNER_KIND, "TestGazCar"} + assert {kind: set(fields) for kind, fields in read_set.read_fields.items()} == { + "TestPerson": {"name", "cars"}, + "TestElectricCar": {"nbr_engine"}, + } From b1c8b7187e4910feaaa7b446d77d2e7dd1799b8e Mon Sep 17 00:00:00 2001 From: Saltaferis Dimitrios Date: Tue, 11 Aug 2026 17:32:03 +0300 Subject: [PATCH 27/48] fix: reject artifacts whose transform returned no payload (closes #5303) (#10187) * test: add failing test for #5303 Co-Authored-By: Claude Opus 5 * fix: reject artifacts whose transform returned no payload Artifact rendering serialized whatever the transform returned without checking it produced anything. A Python transform returning None reached the serialization step, where it missed both isinstance(..., dict) guards and fell through to str(None) -- storing the literal text "None" as the artifact content and marking the artifact Ready. At the revision in the report the text/plain branch instead passed None to bytes(), failing with "encoding without a string argument". The serialization block was duplicated in render_artifact and artifact_generate, so it now lives in serialize_artifact_content(), which rejects a missing payload with a TransformError naming the transform location. Both call sites are validated by construction. The guard is on "is None" rather than falsiness, so an empty string stays a valid payload. Co-Authored-By: Claude Opus 5 * chore: add changelog entry for #5303 Co-Authored-By: Claude Opus 5 * test: cover artifact content serialization Co-Authored-By: Claude Opus 5 * test: assert serialized artifact content against literal values Deriving the expected values with the same library calls the implementation makes asserted nothing about the actual output. The serialized JSON and YAML are now pinned as raw strings. Co-Authored-By: Claude Fable 5 --------- Co-authored-by: Claude Opus 5 --- backend/infrahub/git/integrator.py | 54 ++++++++++---- backend/tests/component/git/conftest.py | 9 +-- .../component/git/test_git_repository.py | 47 ++++++++++++ .../tests/fixtures/transforms/transform03.py | 14 ++++ backend/tests/unit/git/test_integrator.py | 71 +++++++++++++++++++ changelog/5303.fixed.md | 1 + 6 files changed, 179 insertions(+), 17 deletions(-) create mode 100644 backend/tests/fixtures/transforms/transform03.py create mode 100644 backend/tests/unit/git/test_integrator.py create mode 100644 changelog/5303.fixed.md diff --git a/backend/infrahub/git/integrator.py b/backend/infrahub/git/integrator.py index 4b95e381ecb..81841e99b8d 100644 --- a/backend/infrahub/git/integrator.py +++ b/backend/infrahub/git/integrator.py @@ -180,6 +180,31 @@ class ObjectImportPlan: artifact_definitions: dict[str, InfrahubRepositoryArtifactDefinitionConfig] +def serialize_artifact_content( + content: Any, content_type: str, repository_name: str, commit: str, location: str +) -> str: + """Convert the payload returned by a transform into the content stored for an artifact. + + Raises: + TransformError: When the transform returned no payload. + + """ + if content is None: + raise TransformError( + repository_name=repository_name, + commit=commit, + location=location, + message=f"The transform at {location} did not return a payload", + ) + + if content_type == ContentType.APPLICATION_JSON.value and isinstance(content, dict): + return ujson.dumps(content, indent=2) + if content_type == ContentType.APPLICATION_YAML.value and isinstance(content, dict): + return yaml.dump(content, indent=2) + + return str(content) + + class InfrahubRepositoryIntegrator(InfrahubRepositoryBase): """This class provides interfaces to read and process information from .infrahub.yml files and can perform. @@ -1729,9 +1754,10 @@ async def artifact_generate( ) if transformation.typename == InfrahubKind.TRANSFORMJINJA2: + transformation_location = transformation.template_path.value artifact_content = await self.render_jinja2_template.with_options( timeout_seconds=transformation.timeout.value - )(commit=commit, location=transformation.template_path.value, data=response) # type: ignore[call-overload] + )(commit=commit, location=transformation_location, data=response) # type: ignore[call-overload] elif transformation.typename == InfrahubKind.TRANSFORMPYTHON: transformation_location = f"{transformation.file_path.value}::{transformation.class_name.value}" artifact_content = await self.execute_python_transform.with_options( @@ -1745,12 +1771,13 @@ async def artifact_generate( convert_query_response=transformation.convert_query_response.value, ) # type: ignore[call-overload] - if definition.content_type.value == ContentType.APPLICATION_JSON.value and isinstance(artifact_content, dict): - artifact_content_str = ujson.dumps(artifact_content, indent=2) - elif definition.content_type.value == ContentType.APPLICATION_YAML.value and isinstance(artifact_content, dict): - artifact_content_str = yaml.dump(artifact_content, indent=2) - else: - artifact_content_str = str(artifact_content) + artifact_content_str = serialize_artifact_content( + content=artifact_content, + content_type=definition.content_type.value, + repository_name=self.name, + commit=commit, + location=transformation_location, + ) checksum = hashlib.md5(bytes(artifact_content_str, encoding="utf-8"), usedforsecurity=False).hexdigest() @@ -1805,12 +1832,13 @@ async def render_artifact( convert_query_response=message.convert_query_response, ) # type: ignore[call-overload] - if message.content_type == ContentType.APPLICATION_JSON.value and isinstance(artifact_content, dict): - artifact_content_str = ujson.dumps(artifact_content, indent=2) - elif message.content_type == ContentType.APPLICATION_YAML.value and isinstance(artifact_content, dict): - artifact_content_str = yaml.dump(artifact_content, indent=2) - else: - artifact_content_str = str(artifact_content) + artifact_content_str = serialize_artifact_content( + content=artifact_content, + content_type=message.content_type, + repository_name=self.name, + commit=message.commit, + location=message.transform_location, + ) checksum = hashlib.md5(bytes(artifact_content_str, encoding="utf-8"), usedforsecurity=False).hexdigest() diff --git a/backend/tests/component/git/conftest.py b/backend/tests/component/git/conftest.py index 2e2c7ad1bb6..520304b234e 100644 --- a/backend/tests/component/git/conftest.py +++ b/backend/tests/component/git/conftest.py @@ -417,19 +417,20 @@ async def git_repo_transforms( """Git Repository with git_upstream_repo_02 as remote. The repo has 1 local branch: main. - The main branch contains 2 transforms: transform01 and transform02. - Transform01 will change to uppercase the keys in the data dict always and Transform02 is not valid. + The main branch contains 3 transforms: transform01, transform02 and transform03. + Transform01 will change to uppercase the keys in the data dict always, Transform02 is not valid and + Transform03 returns no payload. """ checks_fixture_dir = get_fixtures_dir() / "transforms" upstream = Repo(git_upstream_repo_02["path"]) - files_to_copy = ["transform01.py", "transform02.py"] + files_to_copy = ["transform01.py", "transform02.py", "transform03.py"] for file_to_copy in files_to_copy: shutil.copyfile(checks_fixture_dir / file_to_copy, git_upstream_repo_02["path"] / file_to_copy) upstream.index.add(file_to_copy) - upstream.index.commit("Add 2 Transforms files") + upstream.index.commit("Add 3 Transforms files") return await InfrahubRepository.new( id=UUIDT.new(), diff --git a/backend/tests/component/git/test_git_repository.py b/backend/tests/component/git/test_git_repository.py index d720d37d8db..b0492374c55 100644 --- a/backend/tests/component/git/test_git_repository.py +++ b/backend/tests/component/git/test_git_repository.py @@ -14,6 +14,8 @@ from infrahub_sdk.uuidt import UUIDT from pytest_httpx._httpx_mock import HTTPXMock +from infrahub.auth.session import AnonymousSession +from infrahub.context import BranchContext, InfrahubContext from infrahub.core.branch import Branch from infrahub.core.constants import InfrahubKind from infrahub.core.registry import registry @@ -35,6 +37,7 @@ ArtifactGenerateResult, CheckDefinitionInformation, ) +from infrahub.git.models import RequestArtifactGenerate from infrahub.git.sync import RepositoryFileImporter, RepositorySyncer from infrahub.git.worktree import Worktree from infrahub.lock import InfrahubLockRegistry @@ -917,6 +920,50 @@ async def test_artifact_generate_jinja2_new( assert result == expected_data +async def test_render_artifact_python_without_payload( + client: InfrahubClient, + prefect_test_fixture: None, + git_repo_transforms_w_client: InfrahubRepository, + artifact_node_01: InfrahubNode, + mock_gql_query_03: HTTPXMock, +) -> None: + repo = git_repo_transforms_w_client + commit_main = repo.get_commit_value(branch_name="main", remote=False) + branch = Branch(name="main", uuid=uuid4()) + registry.branch[branch.name] = branch + + message = RequestArtifactGenerate( + artifact_name="myartifact", + artifact_definition="c4908d78-7b24-45e2-9252-96d0fb3e2c78", + artifact_definition_name="artifactdef01", + commit=commit_main, + content_type="text/plain", + transform_type=InfrahubKind.TRANSFORMPYTHON, + transform_location="transform03.py::Transform03", + repository_id=str(repo.id), + repository_name=repo.name, + repository_kind=InfrahubKind.REPOSITORY, + branch_name=branch.name, + target_id="b663d7a4-5f95-48dd-b04d-e03169e7fcf3", + target_kind="TestElectricCar", + target_name="bolt", + query="my_query", + query_id="47800bff-adf1-450d-8388-b04ef2ffb129", + timeout=10, + variables={"name": "bolt"}, + context=InfrahubContext(branch=BranchContext(name=branch.name), account=AnonymousSession()), + ) + + with pytest.raises( + TransformError, match=r"^The transform at transform03\.py::Transform03 did not return a payload$" + ): + await repo.render_artifact(artifact=artifact_node_01, artifact_created=True, message=message) + + assert artifact_node_01.status.value == "Pending" + assert artifact_node_01.checksum.value is None + assert artifact_node_01.storage_id.value is None + + async def test_execute_python_transform_file_missing( client: InfrahubClient, prefect_test_fixture: None, git_repo_transforms: InfrahubRepository ) -> None: diff --git a/backend/tests/fixtures/transforms/transform03.py b/backend/tests/fixtures/transforms/transform03.py new file mode 100644 index 00000000000..0583587f89b --- /dev/null +++ b/backend/tests/fixtures/transforms/transform03.py @@ -0,0 +1,14 @@ +from infrahub_sdk.transforms import InfrahubTransform + + +class Transform03(InfrahubTransform): + """Transform without a payload, as happens when conditional logic returns early.""" + + query = "my_query" + url = "transform03" + + def transform(self, data: dict) -> None: + return None + + +INFRAHUB_TRANSFORMS = [Transform03] diff --git a/backend/tests/unit/git/test_integrator.py b/backend/tests/unit/git/test_integrator.py new file mode 100644 index 00000000000..200c2389b24 --- /dev/null +++ b/backend/tests/unit/git/test_integrator.py @@ -0,0 +1,71 @@ +from dataclasses import dataclass +from typing import Any + +import pytest + +from infrahub.core.constants import ContentType +from infrahub.exceptions import TransformError +from infrahub.git.integrator import serialize_artifact_content + + +@dataclass +class SerializationCase: + name: str + content: Any + content_type: str + expected: str + + +SERIALIZATION_CASES = [ + SerializationCase( + name="dict_as_json", + content={"key1": "value1"}, + content_type=ContentType.APPLICATION_JSON.value, + expected='{\n "key1": "value1"\n}', + ), + SerializationCase( + name="dict_as_yaml", + content={"key1": "value1"}, + content_type=ContentType.APPLICATION_YAML.value, + expected="key1: value1\n", + ), + SerializationCase( + name="string_as_text", + content="Lorem ipsum", + content_type=ContentType.TEXT_PLAIN.value, + expected="Lorem ipsum", + ), + SerializationCase( + name="empty_string_is_a_valid_payload", + content="", + content_type=ContentType.TEXT_PLAIN.value, + expected="", + ), +] + + +@pytest.mark.parametrize("case", SERIALIZATION_CASES, ids=lambda c: c.name) +def test_serialize_artifact_content(case: SerializationCase) -> None: + assert ( + serialize_artifact_content( + content=case.content, + content_type=case.content_type, + repository_name="my-repository", + commit="d9b3b6f9e2c0a1d4e5f60718293a4b5c6d7e8f90", + location="transform01.py::Transform01", + ) + == case.expected + ) + + +def test_serialize_artifact_content_without_payload() -> None: + with pytest.raises( + TransformError, match=r"^The transform at transform01\.py::Transform01 did not return a payload$" + ): + serialize_artifact_content( + content=None, + content_type=ContentType.TEXT_PLAIN.value, + repository_name="my-repository", + commit="d9b3b6f9e2c0a1d4e5f60718293a4b5c6d7e8f90", + location="transform01.py::Transform01", + ) diff --git a/changelog/5303.fixed.md b/changelog/5303.fixed.md new file mode 100644 index 00000000000..a21cb9dcc70 --- /dev/null +++ b/changelog/5303.fixed.md @@ -0,0 +1 @@ +Fixed artifact generation storing the text None when a transform returned no data, it now fails with an error naming the transform instead. From 449be3df4631d712dfa6e6e9eeb1c6c18e13f272 Mon Sep 17 00:00:00 2001 From: Pol Michel <40861490+polmichel@users.noreply.github.com> Date: Tue, 11 Aug 2026 16:32:37 +0200 Subject: [PATCH 28/48] fix: reliably write merge back to non-main default branch on multi-worker pools (closes #9568) (#10204) * test: add failing test for 9568-nonmain-merge-pushdrop Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01TCq7De5CaCV3Y6yKmhBCEK * fix: write merge back to non-main default branch on all workers Push the worktree HEAD to the mapped remote branch instead of a bare branch refspec, and raise when the push is rejected. A clone whose default worktree is checked out on a branch not named after the remote default branch no longer drops the write-back silently. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01TCq7De5CaCV3Y6yKmhBCEK * docs: document push failure mode and add changelog for 9568 Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01TCq7De5CaCV3Y6yKmhBCEK * test: let FileRepo remotes accept pushes to the checked-out branch A non-bare repo rejects pushes to its current branch by default. Now that a rejected push raises instead of being silently dropped, tests that push the default branch back must target a remote that accepts it, as a hosted remote does. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01TCq7De5CaCV3Y6yKmhBCEK * docs: document repository default_branch write-back and FileRepo test helper Document that a read-write repository's default_branch names the git branch merges are pushed back to (Infrahub main maps to it), add the field to the CoreRepository create examples, and note the FileRepo fake-remote test helper. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01TCq7De5CaCV3Y6yKmhBCEK * docs: clarify default_branch semantics (mapping, not tracking) Describe default_branch as the Git branch mapped to Infrahub's default branch (where merges are pushed back and which is imported as the default), and note that a Git branch named main is not imported when default_branch is non-main. Avoids the read-only ref 'track' wording. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01TCq7De5CaCV3Y6yKmhBCEK * docs: use neutral wording for non-main default_branch example Drop the specific 'develop' example in favor of neutral phrasing, since default_branch accepts any Git branch name. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01TCq7De5CaCV3Y6yKmhBCEK * test: assert exact error messages in git live-remote raise tests Expect a raised RepositoryError on a non-fast-forward push rejection (the push helper no longer swallows it) and pin the credentials and merge-conflict tests to their exact messages. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01TCq7De5CaCV3Y6yKmhBCEK * test: assert full push-rejection error message, not just the prefix Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01TCq7De5CaCV3Y6yKmhBCEK --------- Co-authored-by: Claude Opus 4.8 (1M context) --- backend/infrahub/git/repository.py | 19 ++++++++++-- .../component/git/test_git_repository.py | 31 +++++++++++++++++++ backend/tests/helpers/file_repo.py | 11 +++++++ .../integration/git/test_git_live_remote.py | 27 +++++++++------- changelog/9568.fixed.md | 1 + dev/knowledge/backend/testing.md | 1 + .../git-integration/connect-repository.mdx | 3 ++ docs/docs/git-integration/overview.mdx | 2 ++ 8 files changed, 80 insertions(+), 15 deletions(-) create mode 100644 changelog/9568.fixed.md diff --git a/backend/infrahub/git/repository.py b/backend/infrahub/git/repository.py index 81effbb0520..e47218b3bf8 100644 --- a/backend/infrahub/git/repository.py +++ b/backend/infrahub/git/repository.py @@ -284,7 +284,12 @@ async def _collect_staging_imports( return [] async def push(self, branch_name: str) -> bool: - """Push a given branch to the remote Origin repository.""" + """Push a given branch to the remote Origin repository. + + Raises: + RepositoryError: When the remote rejects the push. + + """ if not self.has_origin: return False @@ -292,10 +297,18 @@ async def push(self, branch_name: str) -> bool: f"Pushing the latest update to the remote origin for the branch '{branch_name}'", repository=self.name ) - # TODO Catch potential exceptions coming from origin.push repo = self.get_git_repo_worktree(identifier=branch_name) remote_branch = self._get_mapped_remote_branch(branch_name=branch_name) - repo.remotes.origin.push(remote_branch) + # Push the worktree HEAD, not the bare branch name: the local branch checked out in this + # worktree may not be named after the remote branch (it differs when the repository's + # default branch is not the Infrahub default), so a bare refspec would have no local source. + push_infos = repo.remotes.origin.push(refspec=f"HEAD:refs/heads/{remote_branch}") + for push_info in push_infos: + if push_info.flags & push_info.ERROR: + raise RepositoryError( + identifier=self.name, + message=f"Unable to push the branch {remote_branch} to the remote for repository {self.name}: {push_info.summary.strip()}", + ) return True diff --git a/backend/tests/component/git/test_git_repository.py b/backend/tests/component/git/test_git_repository.py index b0492374c55..c565ca155f2 100644 --- a/backend/tests/component/git/test_git_repository.py +++ b/backend/tests/component/git/test_git_repository.py @@ -520,6 +520,37 @@ async def test_merge_branch01_into_main(git_repo_01: InfrahubRepository, branch0 assert response == str(commit_after) +async def test_merge_writes_back_to_non_main_default_branch( + git_repo_01: InfrahubRepository, + git_upstream_repo_01: dict[str, str | Path], + branch01: BranchData, +) -> None: + """Merging into Infrahub main writes the merge commit back to a non-main git default branch. + + Reproduces a worker whose clone only ever checked out `main`, so it holds `develop` only as a + remote-tracking ref with no local branch of that name. The push that maps Infrahub `main` onto + the configured git default branch must still advance the remote `develop`. + """ + upstream_path = str(git_upstream_repo_01["path"]) + upstream = Repo(upstream_path) + upstream.git.branch("develop", "main") + + repo = git_repo_01 + await repo.fetch() + repo.default_branch_name = "develop" + + local_branch_names = {branch.name for branch in repo.get_git_repo_main().branches} + assert local_branch_names == {"main"} + + await repo.create_branch_in_git(branch_name=branch01.name, branch_id=branch01.id) + + develop_before = Repo(upstream_path).commit("develop").hexsha + merge_commit = await repo.merge(source_branch=branch01.name, dest_branch="main") + + assert merge_commit != develop_before + assert Repo(upstream_path).commit("develop").hexsha == merge_commit + + async def test_rebase(git_repo_01: InfrahubRepository, branch01: BranchData) -> None: repo = git_repo_01 await repo.fetch() diff --git a/backend/tests/helpers/file_repo.py b/backend/tests/helpers/file_repo.py index 2469e8fed24..e6bb60f22b9 100644 --- a/backend/tests/helpers/file_repo.py +++ b/backend/tests/helpers/file_repo.py @@ -29,6 +29,15 @@ def repo(self) -> Repo: return self._repo raise InitializationError + def _accept_pushes_to_current_branch(self) -> None: + """Let this non-bare repo act as a push target, mirroring a real remote. + + A non-bare repo rejects pushes to its checked-out branch by default; a hosted remote does + not, so tests that push the default branch back would otherwise exercise a rejection that + never happens in production. + """ + self.repo.git.config("receive.denyCurrentBranch", "ignore") + def _initial_directory(self, repo_base: Path) -> str: initial_candidates = list(repo_base.glob("initial__*")) assert len(initial_candidates) == 1 @@ -60,6 +69,7 @@ def __post_init__(self) -> None: initial_directory = self._initial_directory(repo_base=repo_base) shutil.copytree(repo_base / initial_directory, self.sources_directory / self.name) self._repo = Repo.init(self.sources_directory / self.name, initial_branch=self._initial_branch) + self._accept_pushes_to_current_branch() for untracked in self.repo.untracked_files: self.repo.index.add(untracked) self.repo.index.commit("First commit") @@ -129,6 +139,7 @@ def __post_init__(self) -> None: shutil.copytree(repo_base / initial_directory, self.sources_directory / self.name) self._repo = Repo.init(self.sources_directory / self.name, initial_branch=self._initial_branch) + self._accept_pushes_to_current_branch() self._setup_initial_branch(directory=repo_base / initial_directory) self._apply_pull_requests(repo_base=repo_base) diff --git a/backend/tests/integration/git/test_git_live_remote.py b/backend/tests/integration/git/test_git_live_remote.py index 326220db4f0..724331de9f4 100644 --- a/backend/tests/integration/git/test_git_live_remote.py +++ b/backend/tests/integration/git/test_git_live_remote.py @@ -147,7 +147,10 @@ async def test_clone_with_wrong_credentials_raises_credentials_error( Uses a fresh clone against a real server so the bad credentials are always presented directly, bypassing any cached credential state. """ - with pytest.raises(RepositoryCredentialsError): + with pytest.raises( + RepositoryCredentialsError, + match=r"^Authentication failed for auth-failure-repo, please validate the credentials\.$", + ): await InfrahubRepository.new( id=auth_failure_dataset["node_id"], name=auth_failure_dataset["repo_name"], @@ -206,14 +209,9 @@ async def test_push_rejected_non_fast_forward( client: InfrahubClient, gogs_server: GogsServer, ) -> None: - """push() returns True despite a non-fast-forward rejection — the failure is silent. + """push() raises RepositoryError when the remote rejects a non-fast-forward push. - InfrahubRepository.push() has no exception handling. GitPython's Remote.push() - does not raise on rejection — it logs a warning and returns a PushInfoList with - error flags. The result is that push() returns True while the local commit was - never actually delivered to the remote. - - This test is expected to fail once proper push-rejection handling is added. + The remote is left unchanged: the rejected local commit is not delivered. """ repo_name = push_rejection_dataset["repo_name"] @@ -238,9 +236,11 @@ async def test_push_rejected_non_fast_forward( git_repo.index.add(["local_diverge.txt"]) local_commit = str(git_repo.index.commit("Local-only commit")) - # GitPython's Remote.push() does not raise on rejection; push() returns True. - result = await infrahub_repo.push("main") - assert result is True + with pytest.raises( + RepositoryError, + match=rf"^Unable to push the branch main to the remote for repository {repo_name}: \[rejected\] \(fetch first\)$", + ): + await infrahub_repo.push("main") git_repo.remotes.origin.fetch() remote_main_commit = str(git_repo.commit("origin/main")) @@ -285,7 +285,10 @@ async def test_merge_conflict_raises_repository_error( branch_b_repo.index.add(["conflict.txt"]) branch_b_repo.index.commit("conflict-branch-b: add conflict.txt") - with pytest.raises(RepositoryError): + with pytest.raises( + RepositoryError, + match=r"^An error occurred with GitRepository 'merge-conflict-repo'\.$", + ): await infrahub_repo.merge( source_branch="conflict-branch-a", dest_branch="conflict-branch-b", diff --git a/changelog/9568.fixed.md b/changelog/9568.fixed.md new file mode 100644 index 00000000000..63791bbdc85 --- /dev/null +++ b/changelog/9568.fixed.md @@ -0,0 +1 @@ +Merging an Infrahub branch now reliably writes the merge back to a repository's non-`main` default branch on the remote, regardless of which task worker executes the merge. diff --git a/dev/knowledge/backend/testing.md b/dev/knowledge/backend/testing.md index 2295de92a75..335c0b83bac 100644 --- a/dev/knowledge/backend/testing.md +++ b/dev/knowledge/backend/testing.md @@ -257,6 +257,7 @@ Test data and fixture files: | `test_client.py` | HTTP test client wrapper | | `utils.py` | Container utilities | | `constants.py` | Port numbers, image names | +| `file_repo.py` | Builds throwaway on-disk Git "remote" repos from `repos/` fixtures (`FileRepo`). The remotes accept pushes to their checked-out branch, so tests exercise push and write-back like a hosted remote would. | ### Test Data (`backend/tests/test_data/`) diff --git a/docs/docs/git-integration/connect-repository.mdx b/docs/docs/git-integration/connect-repository.mdx index 75024945e41..08e6a027267 100644 --- a/docs/docs/git-integration/connect-repository.mdx +++ b/docs/docs/git-integration/connect-repository.mdx @@ -142,6 +142,8 @@ If you are using a **personal access token for authentication**, you should put data: { name: { value: "My Git Repository" }, location: { value: "https://GIT_SERVER/YOUR_GIT_USERNAME/YOUR_REPOSITORY_NAME.git" }, + # Optional: name of the Git branch mapped to Infrahub's default branch, where merges are pushed back (default "main"; set your Git default branch name for a non-main default) + default_branch: { value: "main" }, # The HFID returned in step 2 will be used for the credentials credential: { hfid: ["my-git-credential"] } } @@ -200,6 +202,7 @@ If you are using a **personal access token for authentication**, you should put "CoreRepository", name="My Git repository", location="https://GIT_SERVER/YOUR_GIT_USERNAME/YOUR_REPOSITORY_NAME.git", + default_branch="main", # Optional: Git branch mapped to Infrahub's default branch, where merges are pushed back (default "main"; set your Git default branch name for a non-main default) credential=credential, # The credential object created above ) repository.save() diff --git a/docs/docs/git-integration/overview.mdx b/docs/docs/git-integration/overview.mdx index 08fd22e9ac1..847bec50a6c 100644 --- a/docs/docs/git-integration/overview.mdx +++ b/docs/docs/git-integration/overview.mdx @@ -45,6 +45,8 @@ When you create a Repository connection, Infrahub: The bidirectional nature means that when you merge a Proposed Change between two Infrahub branches that are both linked to Git branches, Infrahub creates a merge commit and automatically pushes it back to the external repository. +A read-write repository has a `default_branch` attribute (defaulting to `main`) that names the Git branch mapped to Infrahub's default branch. Merges into Infrahub's default branch are pushed back to that Git branch on the remote, and that Git branch is imported as Infrahub's default branch, so a repository can use a non-`main` Git default branch. When `default_branch` is not `main`, a Git branch literally named `main` is not imported. + ### Read-only Repository: controlled unidirectional flow The **Read-only Repository** type offers a simpler, unidirectional integration designed for scenarios where you need to consume resources from Git without modifying the external repository. From caf17bf9f07e9cb1abed191010a1930bade01982 Mon Sep 17 00:00:00 2001 From: Pol Michel <40861490+polmichel@users.noreply.github.com> Date: Tue, 11 Aug 2026 16:54:46 +0200 Subject: [PATCH 29/48] docs(knowledge): correct observer fan-out description in api-backpressure (#10214) The admission controller does not fan out through a single `_notify`; it uses one `_observe_*` method per event, each carrying its own per-observer failure containment. Only the slot pool, retry policy, and load tracker use a single `_notify`. Describe both patterns so the doc matches the code. Co-authored-by: Claude Opus 4.8 (1M context) --- dev/knowledge/backend/api-backpressure.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dev/knowledge/backend/api-backpressure.md b/dev/knowledge/backend/api-backpressure.md index 1edd4c0a841..d30bad3ec77 100644 --- a/dev/knowledge/backend/api-backpressure.md +++ b/dev/knowledge/backend/api-backpressure.md @@ -63,8 +63,10 @@ one decision, so a sink implements them together. Separate interfaces belong to which is why the pool, the policy, and the tracker each have their own. The events are named methods rather than a callable protocol, so a sink can carry several of them -and each one says which event fired. Each component fans out through a single private `_notify`, -which is also where the per-observer failure containment lives. +and each one says which event fired. Each component confines its fan-out to sinks behind private +methods where the per-observer failure containment also lives — a single `_notify` in the slot +pool, the retry policy, and the load tracker, and one `_observe_*` method per event in the +admission controller. The concrete sinks in `observers.py` are named only where the object graph is wired: `server.py` passes them to `build_admission_controller`, which takes them as arguments rather than choosing them, From f431abbe33ade77e91525bb2f8aba546d16216a1 Mon Sep 17 00:00:00 2001 From: Pol Michel <40861490+polmichel@users.noreply.github.com> Date: Tue, 11 Aug 2026 16:54:58 +0200 Subject: [PATCH 30/48] docs(guidelines): fix double-attribution in testing adapters example (#10215) BusRecorder is a recording double only; its reply/rpc raises are should-not-be-called guards, not a failing double. Present the recording and failing doubles as the two patterns to write, with BusRecorder as the recording example, rather than claiming BusRecorder illustrates both. Co-authored-by: Claude Opus 4.8 (1M context) --- dev/guidelines/backend/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/guidelines/backend/testing.md b/dev/guidelines/backend/testing.md index 8d9d9b2197b..8ca8cc0d112 100644 --- a/dev/guidelines/backend/testing.md +++ b/dev/guidelines/backend/testing.md @@ -278,7 +278,7 @@ Instead of mocking, design code with explicit boundaries using adapters, interfa Both implement the same `InfrahubMessageBus` protocol. Tests inject the test adapter—no mocking required, and refactoring the RabbitMQ implementation won't silently break tests. -`BusRecorder` illustrates the two doubles worth writing for any injected collaborator. A **recording** double keeps what crossed the boundary, in order, so the test asserts the exact calls and values rather than "was called". A **failing** double raises on every call, to test the path a `Mock` never exercises: that a broken collaborator is handled the way the code claims — the operation still completes, state is intact, and anything queued behind it still runs. Keep both in the shared adapters package or a `helpers.py` beside the test package rather than redefining them per file. +Two doubles are worth writing for any injected collaborator. A **recording** double — like `BusRecorder` — keeps what crossed the boundary, in order, so the test asserts the exact calls and values rather than "was called". A **failing** double raises on every call, to test the path a `Mock` never exercises: that a broken collaborator is handled the way the code claims — the operation still completes, state is intact, and anything queued behind it still runs. Keep both in the shared adapters package or a `helpers.py` beside the test package rather than redefining them per file. ### When mocking seems necessary From 3b826db2507a3627fb5dd74dea7b96f3756b6c57 Mon Sep 17 00:00:00 2001 From: Pol Michel <40861490+polmichel@users.noreply.github.com> Date: Tue, 11 Aug 2026 16:55:08 +0200 Subject: [PATCH 31/48] docs(objects): clarify kind/identifier are required but ignored with --file (#10217) In --file mode, `infrahubctl object update` still requires KIND and IDENTIFIER as positional arguments even though their values are ignored and the file supplies the targets. State both facts so a reader doesn't assume the positionals can be omitted. Co-authored-by: Claude Opus 4.8 (1M context) --- docs/docs/objects/manage-from-cli.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/objects/manage-from-cli.mdx b/docs/docs/objects/manage-from-cli.mdx index 93433d90e11..0877a8f2b9f 100644 --- a/docs/docs/objects/manage-from-cli.mdx +++ b/docs/docs/objects/manage-from-cli.mdx @@ -106,7 +106,7 @@ infrahubctl object update InfraDevice spine01 --set status=active An update only changes the fields you provide. Attributes and relationships you leave out keep their current values — omitting a field does not clear it. -With `--file`, the file defines which objects to update and what to change, so the kind and identifier on the command line are ignored: +With `--file`, the file defines which objects to update and what to change. The kind and identifier are still required on the command line, but their values are ignored — the file supplies the targets: ```bash infrahubctl object update InfraDevice spine01 --file updates.yml From c81961c8e4b4845b94dbdbcb734e55205e6dfd75 Mon Sep 17 00:00:00 2001 From: Aaron McCarty Date: Tue, 11 Aug 2026 10:42:59 -0500 Subject: [PATCH 32/48] Backport rebase common ancestor schema (#10207) * fix(rebase): use the branch-creation schema as the migration baseline This is a backport of the behaviour introduced upstream in fd31b1482, which arrived inside a MergeSchemaAnalyzer refactor that does not apply to stable. Only the behaviour is ported, not the refactor. Co-Authored-By: Claude Opus 5 (1M context) * fix(rebase): restore the branch's own schema when a rebase rolls back SchemaUpdateCoordinator used a single origin_schema for two unrelated jobs: the baseline the migrations compare against, and the schema restored into the registry when the update fails. Those coincide for a plain schema update, but not for a rebase, where the baseline has to be the common ancestor. A rebase that failed during its migrations therefore rolled the branch back to the schema it was created from, silently dropping any schema change made on the branch and persisting a wrong schema hash with it. Split the parameter into migration_baseline_schema and rollback_schema, both required so the two roles cannot be conflated again. The three call sites where they genuinely coincide pass the same value twice; only the rebase differs, capturing the branch's registry schema before the graph is rebased. The graph side already behaves: RollbackQuery reverses edges stamped with the unified timestamp, and the rebase shares that timestamp, so the branch's data is back to its pre-rebase state and the branch's own schema is the consistent thing to pair with it. Covers both schemas the coordinator is handed in one component test, driving the real rebase flow twice, once succeeding to observe the migration baseline and once failing to observe the rollback. The two cases share the fork-before-inheritance setup, which dominates the runtime. WorkflowRecorder gains an execute_results hook so a test can make a workflow report errors without patching. Co-Authored-By: Claude Opus 5 (1M context) * only get the schema from the database when necessary * update SchemaUpdateCoordinator docstring * verify hash is rolled back during failed rebase --------- Co-authored-by: Claude Opus 5 (1M context) --- backend/infrahub/api/schema.py | 3 +- backend/infrahub/cli/db.py | 3 +- backend/infrahub/core/branch/tasks.py | 11 +- .../core/schema/update_coordinator.py | 17 ++- backend/tests/adapters/workflow.py | 3 + backend/tests/component/conftest.py | 12 ++ .../schema_manager/test_schema_rollback.py | 6 +- .../component/core/test_branch_rebase.py | 108 +++++++++++++++++- ...igration-baseline-common-ancestor.fixed.md | 1 + ...e-rollback-restores-branch-schema.fixed.md | 1 + 10 files changed, 151 insertions(+), 14 deletions(-) create mode 100644 changelog/+rebase-migration-baseline-common-ancestor.fixed.md create mode 100644 changelog/+rebase-rollback-restores-branch-schema.fixed.md diff --git a/backend/infrahub/api/schema.py b/backend/infrahub/api/schema.py index 5766b8cbd31..81c31fa9d74 100644 --- a/backend/infrahub/api/schema.py +++ b/backend/infrahub/api/schema.py @@ -388,7 +388,8 @@ async def load_schema( db=db, branch=branch, schema_manager=registry.schema, - origin_schema=origin_schema, + migration_baseline_schema=origin_schema, + rollback_schema=origin_schema, workflow=service.workflow, context=context, migration_executor=MigrationExecutor.WORKFLOW, diff --git a/backend/infrahub/cli/db.py b/backend/infrahub/cli/db.py index 1ea3a1a9d4a..31de5b4f6e7 100644 --- a/backend/infrahub/cli/db.py +++ b/backend/infrahub/cli/db.py @@ -837,7 +837,8 @@ async def update_core_schema(db: InfrahubDatabase, initialize: bool = True, debu db=db, branch=default_branch, schema_manager=registry.schema, - origin_schema=origin_schema, + migration_baseline_schema=origin_schema, + rollback_schema=origin_schema, migration_executor=MigrationExecutor.DIRECT, ) diff --git a/backend/infrahub/core/branch/tasks.py b/backend/infrahub/core/branch/tasks.py index 35daeaddeaa..f4c03446c42 100644 --- a/backend/infrahub/core/branch/tasks.py +++ b/backend/infrahub/core/branch/tasks.py @@ -186,7 +186,6 @@ async def rebase_branch(branch: str, context: InfrahubContext, send_events: bool if error_messages: raise ValidationError(",\n".join(error_messages)) - pre_rebase_schema = merger.destination_schema.duplicate() migrations = [] async with lock.registry.global_graph_lock(): async with db.start_transaction() as dbt: @@ -194,6 +193,10 @@ async def rebase_branch(branch: str, context: InfrahubContext, send_events: bool log.info("Branch graph rebased") if obj.has_schema_changes: + # Use the branch-creation (common-ancestor) schema as the migration baseline + migration_baseline_schema = (await merger.get_common_ancestor_schema()).duplicate() + pre_rebase_schema = registry.schema.get_schema_branch(name=obj.name).duplicate() + # Load the updated schema from DB after rebase log.info("Loading rebased schema") updated_schema = await registry.schema.load_schema_from_db(db=db, branch=obj) @@ -208,7 +211,8 @@ async def rebase_branch(branch: str, context: InfrahubContext, send_events: bool db=db, branch=obj, schema_manager=registry.schema, - origin_schema=pre_rebase_schema, + migration_baseline_schema=migration_baseline_schema, + rollback_schema=pre_rebase_schema, workflow=workflow, context=context, migration_executor=MigrationExecutor.WORKFLOW if send_events else MigrationExecutor.DIRECT, @@ -445,7 +449,8 @@ async def _do_merge_branch( db=db, branch=merger.destination_branch, schema_manager=registry.schema, - origin_schema=pre_merge_schema, + migration_baseline_schema=pre_merge_schema, + rollback_schema=pre_merge_schema, workflow=workflow, context=context, migration_executor=MigrationExecutor.WORKFLOW, diff --git a/backend/infrahub/core/schema/update_coordinator.py b/backend/infrahub/core/schema/update_coordinator.py index 0a4c65c7180..f243778eccf 100644 --- a/backend/infrahub/core/schema/update_coordinator.py +++ b/backend/infrahub/core/schema/update_coordinator.py @@ -49,7 +49,8 @@ def __init__( db: InfrahubDatabase, branch: Branch, schema_manager: SchemaManager, - origin_schema: SchemaBranch, + migration_baseline_schema: SchemaBranch, + rollback_schema: SchemaBranch, workflow: InfrahubWorkflow | None = None, context: InfrahubContext | None = None, migration_executor: MigrationExecutor = MigrationExecutor.WORKFLOW, @@ -61,7 +62,10 @@ def __init__( db: Database connection branch: Branch being updated schema_manager: Schema manager for updating schema in DB and registry - origin_schema: Original schema before update (for rollback) + migration_baseline_schema: Schema the migrations compare the candidate against. + rollback_schema: Schema restored into the registry when the update fails. This is the + schema the branch itself had before the update, which is not the migration baseline + whenever the branch carries changes of its own. workflow: Workflow service for executing migrations (required for WORKFLOW executor) context: Infrahub context (required for WORKFLOW executor) migration_executor: How to execute migrations (DIRECT or WORKFLOW) @@ -74,7 +78,8 @@ def __init__( self.db = db self.branch = branch self.schema_manager = schema_manager - self.origin_schema = origin_schema + self.migration_baseline_schema = migration_baseline_schema + self.rollback_schema = rollback_schema self.workflow = workflow self.context = context self.migration_executor = migration_executor @@ -279,7 +284,7 @@ async def _run_migrations( apply_migration_data = SchemaApplyMigrationData( branch=self.branch, new_schema=candidate_schema, - previous_schema=self.origin_schema, + previous_schema=self.migration_baseline_schema, migrations=migrations, at=at, user_id=user_id, @@ -333,8 +338,8 @@ async def _rollback(self, at: Timestamp) -> None: await rollback_query.execute(db=self.db) async def _restore_registry_state(self) -> None: - """Restore original schema in registry and reset branch hash.""" - self.schema_manager.set_schema_branch(name=self.branch.name, schema=self.origin_schema) + """Restore the branch's pre-update schema in the registry and reset its hash.""" + self.schema_manager.set_schema_branch(name=self.branch.name, schema=self.rollback_schema) self.branch.update_schema_hash() await self.branch.save(db=self.db) diff --git a/backend/tests/adapters/workflow.py b/backend/tests/adapters/workflow.py index 46db44053fc..05ffa93b092 100644 --- a/backend/tests/adapters/workflow.py +++ b/backend/tests/adapters/workflow.py @@ -18,6 +18,7 @@ class WorkflowRecorder(InfrahubWorkflow): def __init__(self) -> None: self.execute_calls: list[dict[str, Any]] = [] self.submit_calls: list[dict[str, Any]] = [] + self.execute_results: dict[str, Any] = {} async def execute_workflow( self, @@ -28,6 +29,8 @@ async def execute_workflow( tags: list[str] | None = None, ) -> Any: self.execute_calls.append({"workflow": workflow, "parameters": parameters or {}}) + if workflow.name in self.execute_results: + return self.execute_results[workflow.name] if expected_return is ValidatorConclusion: return ValidatorConclusion.SUCCESS return None diff --git a/backend/tests/component/conftest.py b/backend/tests/component/conftest.py index ffc9495c629..2fa8b88118a 100644 --- a/backend/tests/component/conftest.py +++ b/backend/tests/component/conftest.py @@ -69,6 +69,7 @@ from infrahub.graphql.registry import registry as graphql_registry from infrahub.services.adapters.workflow.local import WorkflowLocalExecution from infrahub.workers.dependencies import build_workflow +from tests.adapters.workflow import WorkflowRecorder from tests.conftest import TestHelper from tests.helpers.constants import ( PREFECT_FLOW_HEARTBEAT_FREQUENCY_SECONDS, @@ -2964,6 +2965,17 @@ def workflow_local(dependency_provider: Provider) -> Generator[WorkflowLocalExec config.OVERRIDE.workflow = original +@pytest.fixture +def workflow_recorder(dependency_provider: Provider) -> Generator[WorkflowRecorder, None, None]: + """Record workflow submissions instead of running them.""" + original = config.OVERRIDE.workflow + recorder = WorkflowRecorder() + config.OVERRIDE.workflow = recorder + with dependency_provider.scope(build_workflow, lambda: recorder): + yield recorder + config.OVERRIDE.workflow = original + + @pytest.fixture async def generic_car_person_schema(default_branch: Branch, data_schema: None) -> None: schema: dict[str, Any] = { diff --git a/backend/tests/component/core/schema_manager/test_schema_rollback.py b/backend/tests/component/core/schema_manager/test_schema_rollback.py index 13ec5c4a247..3688ed00241 100644 --- a/backend/tests/component/core/schema_manager/test_schema_rollback.py +++ b/backend/tests/component/core/schema_manager/test_schema_rollback.py @@ -125,7 +125,8 @@ async def test_schema_update_with_migrations_and_rollback( db=db, branch=default_branch, schema_manager=registry.schema, - origin_schema=original_schema_copy, + migration_baseline_schema=original_schema_copy, + rollback_schema=original_schema_copy, migration_executor=MigrationExecutor.DIRECT, ) await coordinator.execute( @@ -264,7 +265,8 @@ async def test_schema_update_rolls_back_on_post_write_process_failure( db=db, branch=default_branch, schema_manager=registry.schema, - origin_schema=origin_schema_copy, + migration_baseline_schema=origin_schema_copy, + rollback_schema=origin_schema_copy, migration_executor=MigrationExecutor.DIRECT, ) with pytest.raises(ValueError, match="Unable to find the generic"): diff --git a/backend/tests/component/core/test_branch_rebase.py b/backend/tests/component/core/test_branch_rebase.py index 9fe7c354507..15e05bbfb53 100644 --- a/backend/tests/component/core/test_branch_rebase.py +++ b/backend/tests/component/core/test_branch_rebase.py @@ -6,18 +6,23 @@ from infrahub.auth.session import AccountSession from infrahub.auth.types import AuthType from infrahub.context import InfrahubContext +from infrahub.core import registry from infrahub.core.branch import Branch from infrahub.core.branch.tasks import rebase_branch from infrahub.core.constants import InfrahubKind, MetadataOptions from infrahub.core.initialization import create_branch from infrahub.core.manager import NodeManager from infrahub.core.node import Node +from infrahub.core.schema import AttributeSchema, GenericSchema, NodeSchema, SchemaRoot from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.timestamp import Timestamp from infrahub.database import InfrahubDatabase -from infrahub.exceptions import ValidationError +from infrahub.exceptions import MigrationError, ValidationError from infrahub.services.adapters.workflow.local import WorkflowLocalExecution from infrahub.workers.dependencies import build_database +from infrahub.workflows.catalogue import SCHEMA_APPLY_MIGRATION +from tests.adapters.workflow import WorkflowRecorder +from tests.helpers.schema import load_schema async def test_rebase_graph( @@ -294,3 +299,104 @@ async def test_rebase_preserves_metadata( assert owner_peer._get_created_by() == "person-create-user" assert before_car2_create < owner_peer._get_updated_at() < after_car2_create assert owner_peer._get_updated_by() == "car2-create-user" + + +async def test_rebase_schemas_handed_to_the_update_coordinator( + db: InfrahubDatabase, + default_branch: Branch, + dependency_provider: Provider, + workflow_recorder: WorkflowRecorder, + register_core_models_schema: SchemaBranch, +) -> None: + """The rebase must migrate against the branch-creation schema and roll back to the branch's own. + + Both cases share one fork-before-inheritance setup, which is the expensive part, but they need + separate branches: observing the migration baseline needs a rebase that succeeds, observing the + rollback needs one that fails. + """ + widget_kind = "TestingWidget" + gadget_kind = "TestingGadget" + ownable_kind = "TestingOwnable" + ownable = GenericSchema( + name="Ownable", + namespace="Testing", + attributes=[AttributeSchema(name="owner_name", kind="Text", optional=True)], + ) + widget = NodeSchema( + name="Widget", + namespace="Testing", + default_filter="name__value", + attributes=[AttributeSchema(name="name", kind="Text")], + ) + gadget = NodeSchema( + name="Gadget", + namespace="Testing", + default_filter="name__value", + attributes=[AttributeSchema(name="name", kind="Text")], + ) + await load_schema(db=db, schema=SchemaRoot(generics=[ownable], nodes=[widget, gadget]), update_db=True) + + baseline_branch = await create_branch(db=db, branch_name="baseline-branch") + rollback_branch = await create_branch(db=db, branch_name="rollback-branch") + fork_hash = baseline_branch.active_schema_hash.main + + # A schema change that exists only on the branch being rolled back, on a kind the destination + # never touches so that the rebase does not report a conflict + branch_gadget = gadget.duplicate() + branch_gadget.attributes.append(AttributeSchema(name="serial", kind="Text", optional=True)) + await load_schema( + db=db, + schema=SchemaRoot(nodes=[branch_gadget]), + branch_name=rollback_branch.name, + update_db=True, + limit=[gadget_kind], + ) + rollback_pre_rebase_hash = registry.schema.get_schema_branch(name=rollback_branch.name).get_hash() + + # The destination branch adopts the generic only after both branches forked + inheriting_widget = widget.duplicate() + inheriting_widget.inherit_from = [ownable_kind] + await load_schema( + db=db, + schema=SchemaRoot(nodes=[inheriting_widget]), + update_db=True, + limit=[widget_kind, ownable_kind], + ) + assert set( + registry.schema.get_schema_branch(name=default_branch.name).get_node(name=widget_kind).attribute_names + ) == {"name", "owner_name"} + + context = InfrahubContext.init( + branch=default_branch, + account=AccountSession(account_id=str(uuid4()), auth_type=AuthType.NONE), + ) + + with dependency_provider.scope(build_database, lambda singleton=True: db): # noqa: ARG005 + await rebase_branch(branch=baseline_branch.name, context=context) + + migration_calls = workflow_recorder.get_execute_calls_for(SCHEMA_APPLY_MIGRATION) + assert len(migration_calls) == 1 + baseline_schema = migration_calls[0]["parameters"]["message"].previous_schema + assert isinstance(baseline_schema, SchemaBranch) + + # The whole baseline, not just the widget, must be the schema as it stood at branch creation + assert baseline_schema.get_hash() == fork_hash + assert baseline_schema.get_hash() != registry.schema.get_schema_branch(name=default_branch.name).get_hash() + assert set(baseline_schema.get_node(name=widget_kind).attribute_names) == {"name"} + + # Now make the migrations fail, on the branch that carries a schema change of its own + workflow_recorder.execute_results[SCHEMA_APPLY_MIGRATION.name] = ["migration failed on purpose"] + with pytest.raises(MigrationError) as exc_info: + await rebase_branch(branch=rollback_branch.name, context=context) + assert exc_info.value.message == "migration failed on purpose" + + # The rollback must keep the branch-only change and must not adopt the generic the destination + # picked up after the fork + restored_schema = registry.schema.get_schema_branch(name=rollback_branch.name) + assert set(restored_schema.get_node(name=gadget_kind).attribute_names) == {"name", "serial"} + assert set(restored_schema.get_node(name=widget_kind).attribute_names) == {"name"} + assert restored_schema.get_hash() == rollback_pre_rebase_hash + + # The restored hash has to reach storage, not just the in-memory registry the rollback wrote + reloaded_branch = await Branch.get_by_name(db=db, name=rollback_branch.name) + assert reloaded_branch.active_schema_hash.main == rollback_pre_rebase_hash diff --git a/changelog/+rebase-migration-baseline-common-ancestor.fixed.md b/changelog/+rebase-migration-baseline-common-ancestor.fixed.md new file mode 100644 index 00000000000..09ee769cfba --- /dev/null +++ b/changelog/+rebase-migration-baseline-common-ancestor.fixed.md @@ -0,0 +1 @@ +Fixed schema migrations during a branch rebase using the destination branch's current schema as their baseline instead of the schema the branch was created from. Any schema element that was added on the destination branch after the branch forked already looked pre-existing to the migrations, so the work needed to bring branch data in line was skipped. Migrations now run against the branch-creation schema, which still reflects what changed on either side. diff --git a/changelog/+rebase-rollback-restores-branch-schema.fixed.md b/changelog/+rebase-rollback-restores-branch-schema.fixed.md new file mode 100644 index 00000000000..055e3ef8ca3 --- /dev/null +++ b/changelog/+rebase-rollback-restores-branch-schema.fixed.md @@ -0,0 +1 @@ +Fixed a branch rebase that fails while running its migrations restoring the wrong schema for the branch. The branch was left with the schema it was created from, silently dropping any schema change made on the branch itself, and the recorded schema hash was wrong as a result. The rollback now restores the schema the branch had immediately before the rebase started. From 509e3a69a498b04626eb7b396e26f65d51e5e85f Mon Sep 17 00:00:00 2001 From: Aaron McCarty Date: Tue, 11 Aug 2026 13:13:18 -0500 Subject: [PATCH 33/48] Prep release 1.10.7 (#10221) * chore: release 1.10.7 * add another commit and update changelog/release notes --------- Co-authored-by: Patrick Ogenstad --- CHANGELOG.md | 25 +++++++++++ ...common-parent-relationship-filter.fixed.md | 1 - .../+edit-form-profiles-generic-kind.fixed.md | 1 - ...homepage-repository-concrete-kind.fixed.md | 1 - ...igration-baseline-common-ancestor.fixed.md | 1 - ...e-rollback-restores-branch-schema.fixed.md | 1 - ...e-relationship-id-only-shortcut.changed.md | 1 - changelog/+task-status-branch-link.fixed.md | 1 - changelog/10127.fixed.md | 1 - changelog/10170.fixed.md | 1 - changelog/10208.fixed.md | 1 - changelog/5303.fixed.md | 1 - changelog/7836.fixed.md | 1 - changelog/8749.fixed.md | 1 - changelog/9568.fixed.md | 1 - changelog/9634.fixed.md | 1 - changelog/9889.fixed.md | 1 - changelog/9931.fixed.md | 1 - .../release-notes/infrahub/release-1_10_7.mdx | 45 +++++++++++++++++++ pyproject.toml | 2 +- python_testcontainers/pyproject.toml | 2 +- python_testcontainers/uv.lock | 2 +- uv.lock | 2 +- 23 files changed, 74 insertions(+), 21 deletions(-) delete mode 100644 changelog/+common-parent-relationship-filter.fixed.md delete mode 100644 changelog/+edit-form-profiles-generic-kind.fixed.md delete mode 100644 changelog/+homepage-repository-concrete-kind.fixed.md delete mode 100644 changelog/+rebase-migration-baseline-common-ancestor.fixed.md delete mode 100644 changelog/+rebase-rollback-restores-branch-schema.fixed.md delete mode 100644 changelog/+single-relationship-id-only-shortcut.changed.md delete mode 100644 changelog/+task-status-branch-link.fixed.md delete mode 100644 changelog/10127.fixed.md delete mode 100644 changelog/10170.fixed.md delete mode 100644 changelog/10208.fixed.md delete mode 100644 changelog/5303.fixed.md delete mode 100644 changelog/7836.fixed.md delete mode 100644 changelog/8749.fixed.md delete mode 100644 changelog/9568.fixed.md delete mode 100644 changelog/9634.fixed.md delete mode 100644 changelog/9889.fixed.md delete mode 100644 changelog/9931.fixed.md create mode 100644 docs/docs/release-notes/infrahub/release-1_10_7.mdx diff --git a/CHANGELOG.md b/CHANGELOG.md index ebe44874f3a..10c7902cb3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,31 @@ This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the chang +## [Infrahub - v1.10.7](https://github.com/opsmill/infrahub/tree/infrahub-v1.10.7) - 2026-08-11 + +### Changed + +- Improved the performance of GraphQL queries that only request the `id` of a cardinality-one relationship's peer. When no properties, metadata, or additional node fields are requested, the resolver now returns the peer ID already loaded on the parent instead of hydrating a full peer node, reducing database work on relationship-heavy queries. + +### Fixed + +- Fixed artifact generation storing the text None when a transform returned no data, it now fails with an error naming the transform instead. ([#5303](https://github.com/opsmill/infrahub/issues/5303)) +- Fixed node creation failing when a Jinja2 computed attribute formatted a value sourced from a number pool; the computed attribute now renders once the pool value has been allocated. ([#7836](https://github.com/opsmill/infrahub/issues/7836)) +- Fixed artifact generation failing for repositories whose default branch is not named main. ([#8749](https://github.com/opsmill/infrahub/issues/8749)) +- Merging an Infrahub branch now reliably writes the merge back to a repository's non-`main` default branch on the remote, regardless of which task worker executes the merge. ([#9568](https://github.com/opsmill/infrahub/issues/9568)) +- Fixed the object creation form auto-selecting the first available object in the parent filter of a relationship to a hierarchical node (e.g. the **Device** filter when adding an interface's lag). The parent filter now starts empty and is only pre-filled from an existing relationship value or parent context. ([#9634](https://github.com/opsmill/infrahub/issues/9634)) +- Fixed deleting a large branch failing with a database out-of-memory error and leaving the branch and its data behind. Branches left behind by an earlier failure are now cleaned up on upgrade. ([#9889](https://github.com/opsmill/infrahub/issues/9889)) +- Fixed git repository synchronization halting when a branch that had been merged still existed on the remote. ([#9931](https://github.com/opsmill/infrahub/issues/9931)) +- Fixed group mutation events (`member_added` / `member_removed`) being silently dropped when a single mutation changed a few hundred members or more. The event's related resources are now consolidated to one entry per member and per ancestor and capped at the Prefect maximum, so the event is always recorded and membership-driven automations keep firing regardless of how many members change at once. The full member list remains available in the event payload. ([#10127](https://github.com/opsmill/infrahub/issues/10127)) +- Fixed the ordering of repository tests in a proposed change. Smoke tests now run before unit tests, and unit tests before integration tests, instead of being ordered by resource kind only. ([#10170](https://github.com/opsmill/infrahub/issues/10170)) +- Fixed branches imported from a Git repository being created without the sync with Git flag, which caused merges to skip the Git side. ([#10208](https://github.com/opsmill/infrahub/issues/10208)) +- Fixed relationship selectors in object forms not honoring the `common_parent` schema property. The options are now filtered to peers that share the same parent as the value picked for the referenced relationship in the same form, instead of listing every peer. Changing that parent clears a now-invalid selection, and the inline "Add new" form pre-fills the parent when one is already selected so a created peer stays valid. +- Fixed the Git repositories homepage widget linking to the generic `CoreGenericRepository` kind: repositories now open on their own kind, so the details page and edit form show all of their fields. +- Fixed the object edit form failing with a GraphQL `profiles` error when an object is opened through a kind whose GraphQL type has no `profiles` field, such as `CoreGenericRepository`. The form now requests `profiles` only for kinds that expose it. +- Fixed the task status button in the header sending you to the tasks page on the default branch instead of the branch you were working on. +- Fixed schema migrations during a branch rebase using the destination branch's current schema as their baseline instead of the schema the branch was created from. Any schema element that was added on the destination branch after the branch forked already looked pre-existing to the migrations, so the work needed to bring branch data in line was skipped. Migrations now run against the branch-creation schema, which still reflects what changed on either side. +- Fixed a branch rebase that fails while running its migrations restoring the wrong schema for the branch. The branch was left with the schema it was created from, silently dropping any schema change made on the branch itself, and the recorded schema hash was wrong as a result. The rollback now restores the schema the branch had immediately before the rebase started. + ## [Infrahub - v1.10.6](https://github.com/opsmill/infrahub/tree/infrahub-v1.10.6) - 2026-07-28 ### Changed diff --git a/changelog/+common-parent-relationship-filter.fixed.md b/changelog/+common-parent-relationship-filter.fixed.md deleted file mode 100644 index b5cb214e4b0..00000000000 --- a/changelog/+common-parent-relationship-filter.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed relationship selectors in object forms not honoring the `common_parent` schema property. The options are now filtered to peers that share the same parent as the value picked for the referenced relationship in the same form, instead of listing every peer. Changing that parent clears a now-invalid selection, and the inline "Add new" form pre-fills the parent when one is already selected so a created peer stays valid. diff --git a/changelog/+edit-form-profiles-generic-kind.fixed.md b/changelog/+edit-form-profiles-generic-kind.fixed.md deleted file mode 100644 index 93602955fd5..00000000000 --- a/changelog/+edit-form-profiles-generic-kind.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed the object edit form failing with a GraphQL `profiles` error when an object is opened through a kind whose GraphQL type has no `profiles` field, such as `CoreGenericRepository`. The form now requests `profiles` only for kinds that expose it. diff --git a/changelog/+homepage-repository-concrete-kind.fixed.md b/changelog/+homepage-repository-concrete-kind.fixed.md deleted file mode 100644 index b31c7c448b2..00000000000 --- a/changelog/+homepage-repository-concrete-kind.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed the Git repositories homepage widget linking to the generic `CoreGenericRepository` kind: repositories now open on their own kind, so the details page and edit form show all of their fields. diff --git a/changelog/+rebase-migration-baseline-common-ancestor.fixed.md b/changelog/+rebase-migration-baseline-common-ancestor.fixed.md deleted file mode 100644 index 09ee769cfba..00000000000 --- a/changelog/+rebase-migration-baseline-common-ancestor.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed schema migrations during a branch rebase using the destination branch's current schema as their baseline instead of the schema the branch was created from. Any schema element that was added on the destination branch after the branch forked already looked pre-existing to the migrations, so the work needed to bring branch data in line was skipped. Migrations now run against the branch-creation schema, which still reflects what changed on either side. diff --git a/changelog/+rebase-rollback-restores-branch-schema.fixed.md b/changelog/+rebase-rollback-restores-branch-schema.fixed.md deleted file mode 100644 index 055e3ef8ca3..00000000000 --- a/changelog/+rebase-rollback-restores-branch-schema.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed a branch rebase that fails while running its migrations restoring the wrong schema for the branch. The branch was left with the schema it was created from, silently dropping any schema change made on the branch itself, and the recorded schema hash was wrong as a result. The rollback now restores the schema the branch had immediately before the rebase started. diff --git a/changelog/+single-relationship-id-only-shortcut.changed.md b/changelog/+single-relationship-id-only-shortcut.changed.md deleted file mode 100644 index 8c1d812ab57..00000000000 --- a/changelog/+single-relationship-id-only-shortcut.changed.md +++ /dev/null @@ -1 +0,0 @@ -Improved the performance of GraphQL queries that only request the `id` of a cardinality-one relationship's peer. When no properties, metadata, or additional node fields are requested, the resolver now returns the peer ID already loaded on the parent instead of hydrating a full peer node, reducing database work on relationship-heavy queries. diff --git a/changelog/+task-status-branch-link.fixed.md b/changelog/+task-status-branch-link.fixed.md deleted file mode 100644 index a70c9b711a1..00000000000 --- a/changelog/+task-status-branch-link.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed the task status button in the header sending you to the tasks page on the default branch instead of the branch you were working on. diff --git a/changelog/10127.fixed.md b/changelog/10127.fixed.md deleted file mode 100644 index 421376eedb5..00000000000 --- a/changelog/10127.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed group mutation events (`member_added` / `member_removed`) being silently dropped when a single mutation changed a few hundred members or more. The event's related resources are now consolidated to one entry per member and per ancestor and capped at the Prefect maximum, so the event is always recorded and membership-driven automations keep firing regardless of how many members change at once. The full member list remains available in the event payload. diff --git a/changelog/10170.fixed.md b/changelog/10170.fixed.md deleted file mode 100644 index c1a06710942..00000000000 --- a/changelog/10170.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed the ordering of repository tests in a proposed change. Smoke tests now run before unit tests, and unit tests before integration tests, instead of being ordered by resource kind only. diff --git a/changelog/10208.fixed.md b/changelog/10208.fixed.md deleted file mode 100644 index a584699ec9e..00000000000 --- a/changelog/10208.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed branches imported from a Git repository being created without the sync with Git flag, which caused merges to skip the Git side. diff --git a/changelog/5303.fixed.md b/changelog/5303.fixed.md deleted file mode 100644 index a21cb9dcc70..00000000000 --- a/changelog/5303.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed artifact generation storing the text None when a transform returned no data, it now fails with an error naming the transform instead. diff --git a/changelog/7836.fixed.md b/changelog/7836.fixed.md deleted file mode 100644 index 18ab3b7650b..00000000000 --- a/changelog/7836.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed node creation failing when a Jinja2 computed attribute formatted a value sourced from a number pool; the computed attribute now renders once the pool value has been allocated. diff --git a/changelog/8749.fixed.md b/changelog/8749.fixed.md deleted file mode 100644 index 6654ec14d12..00000000000 --- a/changelog/8749.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed artifact generation failing for repositories whose default branch is not named main. diff --git a/changelog/9568.fixed.md b/changelog/9568.fixed.md deleted file mode 100644 index 63791bbdc85..00000000000 --- a/changelog/9568.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Merging an Infrahub branch now reliably writes the merge back to a repository's non-`main` default branch on the remote, regardless of which task worker executes the merge. diff --git a/changelog/9634.fixed.md b/changelog/9634.fixed.md deleted file mode 100644 index 8489a8017d4..00000000000 --- a/changelog/9634.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed the object creation form auto-selecting the first available object in the parent filter of a relationship to a hierarchical node (e.g. the **Device** filter when adding an interface's lag). The parent filter now starts empty and is only pre-filled from an existing relationship value or parent context. diff --git a/changelog/9889.fixed.md b/changelog/9889.fixed.md deleted file mode 100644 index 0d3e734e99e..00000000000 --- a/changelog/9889.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed deleting a large branch failing with a database out-of-memory error and leaving the branch and its data behind. Branches left behind by an earlier failure are now cleaned up on upgrade. diff --git a/changelog/9931.fixed.md b/changelog/9931.fixed.md deleted file mode 100644 index 9301e913e57..00000000000 --- a/changelog/9931.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed git repository synchronization halting when a branch that had been merged still existed on the remote. diff --git a/docs/docs/release-notes/infrahub/release-1_10_7.mdx b/docs/docs/release-notes/infrahub/release-1_10_7.mdx new file mode 100644 index 00000000000..eb3cb8a1065 --- /dev/null +++ b/docs/docs/release-notes/infrahub/release-1_10_7.mdx @@ -0,0 +1,45 @@ +--- +title: Release 1.10.7 +release_date: 2026-08-11 +release_type: patch +description: "Speeds up GraphQL queries that only request a relationship peer's id, and fixes artifact generation, number-pool computed attributes, non-main default branches, group mutation events, git-sync, and several object form issues." +--- + + + + + + + + + + + + + + + +
Release Number1.10.7
Release DateAugust 11th, 2026
Tag[infrahub-v1.10.7](https://github.com/opsmill/infrahub/releases/tag/infrahub-v1.10.7)
+ +### Changed + +- Improved the performance of GraphQL queries that only request the `id` of a cardinality-one relationship's peer. When no properties, metadata, or additional node fields are requested, the resolver now returns the peer ID already loaded on the parent instead of hydrating a full peer node, reducing database work on relationship-heavy queries. + +### Fixed + +- Fixed artifact generation storing the text None when a transform returned no data, it now fails with an error naming the transform instead. ([#5303](https://github.com/opsmill/infrahub/issues/5303)) +- Fixed node creation failing when a Jinja2 computed attribute formatted a value sourced from a number pool; the computed attribute now renders once the pool value has been allocated. ([#7836](https://github.com/opsmill/infrahub/issues/7836)) +- Fixed artifact generation failing for repositories whose default branch is not named main. ([#8749](https://github.com/opsmill/infrahub/issues/8749)) +- Merging an Infrahub branch now reliably writes the merge back to a repository's non-`main` default branch on the remote, regardless of which task worker executes the merge. ([#9568](https://github.com/opsmill/infrahub/issues/9568)) +- Fixed the object creation form auto-selecting the first available object in the parent filter of a relationship to a hierarchical node (e.g. the **Device** filter when adding an interface's lag). The parent filter now starts empty and is only pre-filled from an existing relationship value or parent context. ([#9634](https://github.com/opsmill/infrahub/issues/9634)) +- Fixed deleting a large branch failing with a database out-of-memory error and leaving the branch and its data behind. Branches left behind by an earlier failure are now cleaned up on upgrade. ([#9889](https://github.com/opsmill/infrahub/issues/9889)) +- Fixed git repository synchronization halting when a branch that had been merged still existed on the remote. ([#9931](https://github.com/opsmill/infrahub/issues/9931)) +- Fixed group mutation events (`member_added` / `member_removed`) being silently dropped when a single mutation changed a few hundred members or more. The event's related resources are now consolidated to one entry per member and per ancestor and capped at the Prefect maximum, so the event is always recorded and membership-driven automations keep firing regardless of how many members change at once. The full member list remains available in the event payload. ([#10127](https://github.com/opsmill/infrahub/issues/10127)) +- Fixed the ordering of repository tests in a proposed change. Smoke tests now run before unit tests, and unit tests before integration tests, instead of being ordered by resource kind only. ([#10170](https://github.com/opsmill/infrahub/issues/10170)) +- Fixed branches imported from a Git repository being created without the sync with Git flag, which caused merges to skip the Git side. ([#10208](https://github.com/opsmill/infrahub/issues/10208)) +- Fixed relationship selectors in object forms not honoring the `common_parent` schema property. The options are now filtered to peers that share the same parent as the value picked for the referenced relationship in the same form, instead of listing every peer. Changing that parent clears a now-invalid selection, and the inline "Add new" form pre-fills the parent when one is already selected so a created peer stays valid. +- Fixed the Git repositories homepage widget linking to the generic `CoreGenericRepository` kind: repositories now open on their own kind, so the details page and edit form show all of their fields. +- Fixed the object edit form failing with a GraphQL `profiles` error when an object is opened through a kind whose GraphQL type has no `profiles` field, such as `CoreGenericRepository`. The form now requests `profiles` only for kinds that expose it. +- Fixed the task status button in the header sending you to the tasks page on the default branch instead of the branch you were working on. +- Fixed schema migrations during a branch rebase using the destination branch's current schema as their baseline instead of the schema the branch was created from. Any schema element that was added on the destination branch after the branch forked already looked pre-existing to the migrations, so the work needed to bring branch data in line was skipped. Migrations now run against the branch-creation schema, which still reflects what changed on either side. +- Fixed a branch rebase that fails while running its migrations restoring the wrong schema for the branch. The branch was left with the schema it was created from, silently dropping any schema change made on the branch itself, and the recorded schema hash was wrong as a result. The rollback now restores the schema the branch had immediately before the rebase started. diff --git a/pyproject.toml b/pyproject.toml index 4692fad868e..21690aa7716 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "infrahub-server" -version = "1.10.6" +version = "1.10.7" description = "Infrahub is taking a new approach to Infrastructure Management by providing a new generation of datastore to organize and control all the data that defines how an infrastructure should run." authors = [{ name = "OpsMill", email = "info@opsmill.com" }] requires-python = ">=3.12,<3.15" diff --git a/python_testcontainers/pyproject.toml b/python_testcontainers/pyproject.toml index 571b05ec9ce..129032590d0 100644 --- a/python_testcontainers/pyproject.toml +++ b/python_testcontainers/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "infrahub-testcontainers" -version = "1.10.6" +version = "1.10.7" requires-python = ">=3.10" description = "Testcontainers instance for Infrahub to easily build integration tests" diff --git a/python_testcontainers/uv.lock b/python_testcontainers/uv.lock index cf9eadfc53e..9765806e2f6 100644 --- a/python_testcontainers/uv.lock +++ b/python_testcontainers/uv.lock @@ -521,7 +521,7 @@ wheels = [ [[package]] name = "infrahub-testcontainers" -version = "1.10.6" +version = "1.10.7" source = { editable = "." } dependencies = [ { name = "httpx" }, diff --git a/uv.lock b/uv.lock index 5d811abe9e9..331b00fbaea 100644 --- a/uv.lock +++ b/uv.lock @@ -1429,7 +1429,7 @@ wheels = [ [[package]] name = "infrahub-server" -version = "1.10.6" +version = "1.10.7" source = { editable = "." } dependencies = [ { name = "aio-pika" }, From 356c8e46d727ef6e9153734e44050b3dda805b03 Mon Sep 17 00:00:00 2001 From: opsmill-bot Date: Tue, 11 Aug 2026 18:14:22 +0000 Subject: [PATCH 34/48] chore: update docker-compose --- docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index f1c5b89d8b0..9e681a31609 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -244,7 +244,7 @@ services: - 6362:6362 task-manager: - image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.10.6}" + image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.10.7}" command: uvicorn --host 0.0.0.0 --port 4200 --factory infrahub.prefect_server.app:create_infrahub_prefect restart: unless-stopped depends_on: @@ -277,7 +277,7 @@ services: retries: 5 infrahub-server: - image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.10.6}" + image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.10.7}" restart: unless-stopped command: > gunicorn --config backend/infrahub/serve/gunicorn_config.py @@ -323,7 +323,7 @@ services: deploy: mode: replicated replicas: 2 - image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.10.6}" + image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-1.10.7}" command: prefect worker start --type infrahubasync --pool infrahub-worker --with-healthcheck restart: unless-stopped depends_on: From 47da2c636b7a9d244b8cf852834b5770586897a6 Mon Sep 17 00:00:00 2001 From: Aaron McCarty Date: Tue, 11 Aug 2026 11:43:31 -0700 Subject: [PATCH 35/48] fix: resolve stable merge conflicts in the schema update coordinator Keep release-1.11's refactored SchemaUpdateCoordinator (GraphRollbacker, timestamp-scoped rollback) and the branch merge orchestrator in place of stable's pre-refactor shape, and forward-port the half of the rebase backport that release-1.11 did not already cover: the schema restored into the registry on failure is now passed separately from the migration baseline, so a failed rebase puts back the schema the branch itself had rather than the common ancestor it migrated against. rollback_schema is required at every call site. WorkflowRecorder keeps the unified calls list and gains the canned execute_results the incoming rebase test relies on. Co-Authored-By: Claude Opus 5 (1M context) --- backend/infrahub/api/schema.py | 6 +- backend/infrahub/cli/db.py | 7 +- backend/infrahub/core/branch/tasks.py | 158 +----------------- backend/infrahub/core/merge/orchestrator.py | 1 + .../core/schema/update_coordinator.py | 54 ++---- backend/tests/adapters/workflow.py | 11 +- .../schema_manager/test_schema_rollback.py | 14 +- 7 files changed, 25 insertions(+), 226 deletions(-) diff --git a/backend/infrahub/api/schema.py b/backend/infrahub/api/schema.py index 6558e9d40aa..0e0c3e5bc1e 100644 --- a/backend/infrahub/api/schema.py +++ b/backend/infrahub/api/schema.py @@ -442,18 +442,14 @@ async def load_schema( coordinator = SchemaUpdateCoordinator( db=db, schema_manager=registry.schema, -<<<<<<< HEAD rollbacker=GraphRollbacker(db=db), -======= - migration_baseline_schema=origin_schema, - rollback_schema=origin_schema, ->>>>>>> stable workflow=service.workflow, ) updated_hash = await coordinator.execute( branch=branch, origin_schema=origin_schema, + rollback_schema=origin_schema, candidate_schema=candidate_schema, at=Timestamp(), # The caller blocks on this request: a priority stamped into the diff --git a/backend/infrahub/cli/db.py b/backend/infrahub/cli/db.py index c9250d6d473..6d5fc58e654 100644 --- a/backend/infrahub/cli/db.py +++ b/backend/infrahub/cli/db.py @@ -837,19 +837,14 @@ async def update_core_schema(db: InfrahubDatabase, initialize: bool = True, debu coordinator = SchemaUpdateCoordinator( db=db, schema_manager=registry.schema, -<<<<<<< HEAD rollbacker=GraphRollbacker(db=db), -======= - migration_baseline_schema=origin_schema, - rollback_schema=origin_schema, - migration_executor=MigrationExecutor.DIRECT, ->>>>>>> stable ) try: await coordinator.execute( branch=default_branch, origin_schema=origin_schema, + rollback_schema=origin_schema, candidate_schema=candidate_schema, at=Timestamp(), migration_executor=MigrationExecutor.DIRECT, diff --git a/backend/infrahub/core/branch/tasks.py b/backend/infrahub/core/branch/tasks.py index 520d11aa224..d62a400475b 100644 --- a/backend/infrahub/core/branch/tasks.py +++ b/backend/infrahub/core/branch/tasks.py @@ -219,55 +219,27 @@ async def rebase_branch(branch: str, context: InfrahubContext, send_events: bool if error_messages: raise ValidationError(",\n".join(error_messages)) -<<<<<<< HEAD - # Use the branch-creation (common-ancestor) schema as the migration baseline: it still contains - # any element removed on either side, so remove migrations can resolve what to close. - pre_rebase_schema = (await schema_analyzer.get_common_ancestor_schema()).duplicate() -======= ->>>>>>> stable migrations = [] async with lock.registry.global_graph_lock(): async with db.start_transaction() as dbt: await user_branch.rebase(db=dbt, user_id=context.account.account_id, at=rebase_at) log.info("Branch graph rebased") -<<<<<<< HEAD if user_branch.schema_differs_from_default_branch: # Update the registry and run migrations after the rebase, with rollback on failure. # Schema nodes were already written by the rebase, so load that schema and apply only # the migrations it implies. log.info("Running migrations") + migration_baseline_schema = (await schema_analyzer.get_common_ancestor_schema()).duplicate() + pre_rebase_schema = registry.schema.get_schema_branch(name=user_branch.name).duplicate() rebased_schema = await registry.schema.load_schema_from_db(db=db, branch=user_branch) migrations = await schema_analyzer.calculate_migrations(target_schema=rebased_schema) await schema_update_coordinator.execute( branch=user_branch, - origin_schema=pre_rebase_schema, + origin_schema=migration_baseline_schema, + rollback_schema=pre_rebase_schema, candidate_schema=rebased_schema, at=rebase_at, -======= - if obj.has_schema_changes: - # Use the branch-creation (common-ancestor) schema as the migration baseline - migration_baseline_schema = (await merger.get_common_ancestor_schema()).duplicate() - pre_rebase_schema = registry.schema.get_schema_branch(name=obj.name).duplicate() - - # Load the updated schema from DB after rebase - log.info("Loading rebased schema") - updated_schema = await registry.schema.load_schema_from_db(db=db, branch=obj) - - # Calculate migrations before updating registry - log.info("Calculating migrations") - migrations = await merger.calculate_migrations(target_schema=updated_schema) - - # Use coordinator to update registry and run migrations with rollback on failure - log.info("Running migrations") - coordinator = SchemaUpdateCoordinator( - db=db, - branch=obj, - schema_manager=registry.schema, - migration_baseline_schema=migration_baseline_schema, - rollback_schema=pre_rebase_schema, - workflow=workflow, ->>>>>>> stable context=context, migration_executor=MigrationExecutor.WORKFLOW if send_events else MigrationExecutor.DIRECT, migrations=migrations, @@ -401,129 +373,7 @@ async def _do_merge_branch( orchestrator = await build_branch_merge_orchestrator( db=db, source_branch=source_branch, destination_branch=destination_branch, logger=log ) -<<<<<<< HEAD await orchestrator.merge(context=context, proposed_change_id=proposed_change_id) -======= - schema_was_updated = False - try: - async with lock.registry.global_graph_lock(): - # Set to MERGING to lock the branch while merge proceeds - branch.status = BranchStatus.MERGING - await branch.save(db=db, user_id=user_id) - registry.branch[branch.name] = branch - await merger.merge(at=merge_at) - - log.info("Loading enriched diff for changelog collection") - branch_diff = await diff_repository.get_one( - diff_branch_name=branch.name, tracking_id=BranchTrackingId(name=branch.name) - ) - changelog_collector = DiffChangelogCollector(diff=branch_diff, branch=branch, db=db) - node_events = changelog_collector.collect_changelogs() - - # Handle schema updates and migrations after merge - if await merger.has_schema_changes(): - # Load the updated schema from DB after merge - log.info("Loading updated schema") - updated_schema = await registry.schema.load_schema_from_db( - db=db, - branch=merger.destination_branch, - ) - log.info("Calculating migrations") - migrations = await merger.calculate_migrations(target_schema=updated_schema) - - # disable the coordinator's internal rollback, it is handled within this function - log.info("Running migrations") - coordinator = SchemaUpdateCoordinator( - db=db, - branch=merger.destination_branch, - schema_manager=registry.schema, - migration_baseline_schema=pre_merge_schema, - rollback_schema=pre_merge_schema, - workflow=workflow, - context=context, - migration_executor=MigrationExecutor.WORKFLOW, - logger=log, - ) - await coordinator.execute( - candidate_schema=updated_schema, - at=merge_at, - migrations=migrations, - update_db=False, # Schema nodes already written by merge - update_registry=True, - user_id=user_id, - manage_rollback=False, - ) - log.info("Migrations completed") - schema_was_updated = True - # ------------------------------------------------------------- - # Trigger the reconciliation of IPAM data after the merge - # ------------------------------------------------------------- - diff_parser = await component_registry.get_component(IpamDiffParser, db=db, branch=branch) - ipam_node_details = await diff_parser.get_changed_ipam_node_details( - source_branch_name=branch.name, - target_branch_name=registry.default_branch, - ) - if ipam_node_details: - await workflow.submit_workflow( - workflow=IPAM_RECONCILIATION, - context=context, - parameters={"branch": registry.default_branch, "ipam_node_details": ipam_node_details}, - ) - except BaseException as exc: - log.error("Merge failed, beginning rollback", extra={"error": str(exc)}) - await _rollback_merge( - db=db, - log=log, - merger=merger, - branch=branch, - pre_merge_schema=pre_merge_schema, - pre_merge_branched_from=pre_merge_branched_from, - user_id=context.account.account_id, - ) - raise - - # ------------------------------------------------------------- - # remove tracking ID from the diff because there is no diff after the merge - # ------------------------------------------------------------- - await diff_repository.mark_tracking_ids_merged(tracking_ids=[BranchTrackingId(name=branch.name)]) - await diff_repository.freeze_diffs_for_branch(branch_name=branch.name) - - # ------------------------------------------------------------- - # Point of no return: merge fully succeeded. Advance to MERGED. - # ------------------------------------------------------------- - branch.status = BranchStatus.MERGED - await branch.save(db=db, user_id=user_id) - registry.branch[branch.name] = branch - - # ------------------------------------------------------------- - # Cancel any remaining open proposed changes for this merged branch - # ------------------------------------------------------------- - await workflow.submit_workflow( - workflow=BRANCH_CANCEL_PROPOSED_CHANGES, - context=context, - parameters={"branch_name": branch.name}, - ) - - if config.SETTINGS.main.delete_branch_after_merge and not branch.is_default: - await get_workflow().submit_workflow( - workflow=BRANCH_DELETE, - context=context, - parameters={"branch": branch.name, "proposed_change_id": proposed_change_id}, - ) - - # ------------------------------------------------------------- - # Generate an event to indicate that a branch has been merged - # NOTE: we still need to convert this event and potentially pull - # some tasks currently executed based on the event into this workflow - # ------------------------------------------------------------- - await workflow.submit_workflow( - workflow=BRANCH_MERGE_POST_PROCESS, - context=context, - parameters={"source_branch": branch.name, "target_branch": registry.default_branch}, - ) - - return MergeBranchResult(node_events=node_events, schema_was_updated=schema_was_updated) ->>>>>>> stable @flow(name="branch-delete", flow_run_name="Delete branch {branch}") diff --git a/backend/infrahub/core/merge/orchestrator.py b/backend/infrahub/core/merge/orchestrator.py index 7a7aa5b2b95..319c9bdf55b 100644 --- a/backend/infrahub/core/merge/orchestrator.py +++ b/backend/infrahub/core/merge/orchestrator.py @@ -130,6 +130,7 @@ async def merge(self, *, context: InfrahubContext, proposed_change_id: str | Non await self.schema_update_coordinator.execute( branch=self.destination_branch, origin_schema=pre_merge_state.destination_schema, + rollback_schema=pre_merge_state.destination_schema, candidate_schema=candidate_schema, at=merge_at, context=context, diff --git a/backend/infrahub/core/schema/update_coordinator.py b/backend/infrahub/core/schema/update_coordinator.py index 6333253ac3b..cfabc470933 100644 --- a/backend/infrahub/core/schema/update_coordinator.py +++ b/backend/infrahub/core/schema/update_coordinator.py @@ -49,12 +49,7 @@ def __init__( self, db: InfrahubDatabase, schema_manager: SchemaManager, -<<<<<<< HEAD rollbacker: GraphRollbacker, -======= - migration_baseline_schema: SchemaBranch, - rollback_schema: SchemaBranch, ->>>>>>> stable workflow: InfrahubWorkflow | None = None, logger: logging.Logger | logging.LoggerAdapter[logging.Logger] | None = None, ) -> None: @@ -63,29 +58,14 @@ def __init__( Args: db: Database connection schema_manager: Schema manager for updating schema in DB and registry -<<<<<<< HEAD rollbacker: Reverses database writes when a schema update fails workflow: Workflow service for executing migrations (required for the WORKFLOW executor) -======= - migration_baseline_schema: Schema the migrations compare the candidate against. - rollback_schema: Schema restored into the registry when the update fails. This is the - schema the branch itself had before the update, which is not the migration baseline - whenever the branch carries changes of its own. - workflow: Workflow service for executing migrations (required for WORKFLOW executor) - context: Infrahub context (required for WORKFLOW executor) - migration_executor: How to execute migrations (DIRECT or WORKFLOW) ->>>>>>> stable logger: Logger to use (defaults to module logger) """ self.db = db self.schema_manager = schema_manager -<<<<<<< HEAD self.rollbacker = rollbacker -======= - self.migration_baseline_schema = migration_baseline_schema - self.rollback_schema = rollback_schema ->>>>>>> stable self.workflow = workflow self.log = logger or _default_log @@ -106,6 +86,7 @@ async def execute( *, branch: Branch, origin_schema: SchemaBranch, + rollback_schema: SchemaBranch, candidate_schema: SchemaBranch, at: Timestamp, context: InfrahubContext | None = ..., @@ -125,6 +106,7 @@ async def execute( *, branch: Branch, origin_schema: SchemaBranch, + rollback_schema: SchemaBranch, candidate_schema: SchemaBranch, at: Timestamp, context: InfrahubContext | None = ..., @@ -144,6 +126,7 @@ async def execute( *, branch: Branch, origin_schema: SchemaBranch, + rollback_schema: SchemaBranch, candidate_schema: SchemaBranch, at: Timestamp, context: InfrahubContext | None = ..., @@ -162,6 +145,7 @@ async def execute( *, branch: Branch, origin_schema: SchemaBranch, + rollback_schema: SchemaBranch, candidate_schema: SchemaBranch, at: Timestamp, context: InfrahubContext | None = None, @@ -178,7 +162,11 @@ async def execute( Args: branch: Branch being updated - origin_schema: Original schema before the update (for rollback) + origin_schema: Schema the migrations compare the candidate against + rollback_schema: Schema restored into the registry when the update fails. This is the + schema the branch itself had before the update, which is not the migration baseline + whenever the branch carries changes of its own: a rebase compares against the common + ancestor but must restore the schema the branch itself had. candidate_schema: New schema to apply at: Timestamp for all operations (enables atomic rollback) context: Infrahub context (required for the WORKFLOW executor) @@ -232,7 +220,7 @@ async def execute( if manage_rollback: await self._handle_failure_and_rollback( branch=branch, - origin_schema=origin_schema, + rollback_schema=rollback_schema, origin_schema_changed_at=origin_schema_changed_at, origin_schema_hash=origin_schema_hash, at=at, @@ -261,7 +249,7 @@ async def execute( if manage_rollback: await self._handle_failure_and_rollback( branch=branch, - origin_schema=origin_schema, + rollback_schema=rollback_schema, origin_schema_changed_at=origin_schema_changed_at, origin_schema_hash=origin_schema_hash, at=at, @@ -336,11 +324,7 @@ async def _run_migrations( apply_migration_data = SchemaApplyMigrationData( branch=branch, new_schema=candidate_schema, -<<<<<<< HEAD previous_schema=origin_schema, -======= - previous_schema=self.migration_baseline_schema, ->>>>>>> stable migrations=migrations, at=at, user_id=user_id, @@ -398,7 +382,6 @@ async def _run_migrations_directly( async def _rollback(self, branch: Branch, at: Timestamp) -> None: """Rollback all changes made at the unified timestamp. -<<<<<<< HEAD Scoped to the exact timestamp: the branch is not write-blocked during a schema update, so other writers may have stamped later writes that must survive the rollback. """ @@ -412,27 +395,20 @@ async def _rollback(self, branch: Branch, at: Timestamp) -> None: async def _restore_registry_state( self, branch: Branch, - origin_schema: SchemaBranch, + rollback_schema: SchemaBranch, origin_schema_changed_at: str | None, origin_schema_hash: SchemaBranchHash | None, ) -> None: """Restore original schema in registry and reset the branch hash to its pre-update value.""" - self.schema_manager.set_schema_branch(name=branch.name, schema=origin_schema) + self.schema_manager.set_schema_branch(name=branch.name, schema=rollback_schema) branch.schema_hash = origin_schema_hash branch.schema_changed_at = origin_schema_changed_at await branch.save(db=self.db) -======= - async def _restore_registry_state(self) -> None: - """Restore the branch's pre-update schema in the registry and reset its hash.""" - self.schema_manager.set_schema_branch(name=self.branch.name, schema=self.rollback_schema) - self.branch.update_schema_hash() - await self.branch.save(db=self.db) ->>>>>>> stable async def _handle_failure_and_rollback( self, branch: Branch, - origin_schema: SchemaBranch, + rollback_schema: SchemaBranch, origin_schema_changed_at: str | None, origin_schema_hash: SchemaBranchHash | None, at: Timestamp, @@ -465,7 +441,7 @@ async def _handle_failure_and_rollback( await self._rollback(branch=branch, at=at) await self._restore_registry_state( branch=branch, - origin_schema=origin_schema, + rollback_schema=rollback_schema, origin_schema_changed_at=origin_schema_changed_at, origin_schema_hash=origin_schema_hash, ) diff --git a/backend/tests/adapters/workflow.py b/backend/tests/adapters/workflow.py index 36bc70ced9c..44816067da3 100644 --- a/backend/tests/adapters/workflow.py +++ b/backend/tests/adapters/workflow.py @@ -17,8 +17,8 @@ class WorkflowRecorder(InfrahubWorkflow): """Records workflow calls without executing them. Use for testing code that submits workflows.""" def __init__(self) -> None: -<<<<<<< HEAD self.calls: list[dict[str, Any]] = [] + self.execute_results: dict[str, Any] = {} def reset(self) -> None: # execute_calls/submit_calls are derived views, so clearing them leaves the backing store @@ -32,11 +32,6 @@ def execute_calls(self) -> list[dict[str, Any]]: @property def submit_calls(self) -> list[dict[str, Any]]: return [call for call in self.calls if call["kind"] == "submit"] -======= - self.execute_calls: list[dict[str, Any]] = [] - self.submit_calls: list[dict[str, Any]] = [] - self.execute_results: dict[str, Any] = {} ->>>>>>> stable async def execute_workflow( self, @@ -47,13 +42,9 @@ async def execute_workflow( tags: list[str] | None = None, priority: WorkflowPriority | None = None, ) -> Any: -<<<<<<< HEAD self.calls.append({"kind": "execute", "workflow": workflow, "parameters": parameters or {}}) -======= - self.execute_calls.append({"workflow": workflow, "parameters": parameters or {}}) if workflow.name in self.execute_results: return self.execute_results[workflow.name] ->>>>>>> stable if expected_return is ValidatorConclusion: return ValidatorConclusion.SUCCESS return None diff --git a/backend/tests/component/core/schema_manager/test_schema_rollback.py b/backend/tests/component/core/schema_manager/test_schema_rollback.py index fb0ca1e5d38..20213445047 100644 --- a/backend/tests/component/core/schema_manager/test_schema_rollback.py +++ b/backend/tests/component/core/schema_manager/test_schema_rollback.py @@ -125,17 +125,12 @@ async def test_schema_update_with_migrations_and_rollback( coordinator = SchemaUpdateCoordinator( db=db, schema_manager=registry.schema, -<<<<<<< HEAD rollbacker=GraphRollbacker(db=db), -======= - migration_baseline_schema=original_schema_copy, - rollback_schema=original_schema_copy, - migration_executor=MigrationExecutor.DIRECT, ->>>>>>> stable ) await coordinator.execute( branch=default_branch, origin_schema=original_schema_copy, + rollback_schema=original_schema_copy, candidate_schema=updated_schema_branch, at=schema_update_at, migration_executor=MigrationExecutor.DIRECT, @@ -271,18 +266,13 @@ async def test_schema_update_rolls_back_on_post_write_process_failure( coordinator = SchemaUpdateCoordinator( db=db, schema_manager=registry.schema, -<<<<<<< HEAD rollbacker=GraphRollbacker(db=db), -======= - migration_baseline_schema=origin_schema_copy, - rollback_schema=origin_schema_copy, - migration_executor=MigrationExecutor.DIRECT, ->>>>>>> stable ) with pytest.raises(ValueError, match="Unable to find the generic"): await coordinator.execute( branch=default_branch, origin_schema=origin_schema_copy, + rollback_schema=origin_schema_copy, candidate_schema=candidate_schema, at=Timestamp(), migration_executor=MigrationExecutor.DIRECT, From 9a1d662c8f9ffee090d753d7b3ac0cebd945fb52 Mon Sep 17 00:00:00 2001 From: Aaron McCarty Date: Tue, 11 Aug 2026 11:43:37 -0700 Subject: [PATCH 36/48] fix(tests): repoint group-action test imports at the task_manager event package The file merged cleanly from stable but kept importing PrefectEventData and InfrahubEventFilter from the modules that the event package split replaced, so nothing resolved at import time. Caught by ty, which resolves imports where ruff and the mypy configuration do not. Co-Authored-By: Claude Opus 5 (1M context) --- backend/tests/unit/event/test_group_action.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/tests/unit/event/test_group_action.py b/backend/tests/unit/event/test_group_action.py index 87407771193..63953afc795 100644 --- a/backend/tests/unit/event/test_group_action.py +++ b/backend/tests/unit/event/test_group_action.py @@ -20,8 +20,8 @@ from infrahub.events.limits import get_prefect_max_related_resources from infrahub.events.models import EventMeta, EventNode from infrahub.external_protocols import ExternalAuthProtocol -from infrahub.task_manager.event import PrefectEventData -from infrahub.task_manager.models import InfrahubEventFilter +from infrahub.task_manager.event.models import InfrahubEventFilter +from infrahub.task_manager.event.query import PrefectEventData def _make_meta(account_id: str = "acct-123") -> EventMeta: From 72f5157486e3efc6bd4ffe8040b19451fe8c40e7 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 11 Aug 2026 21:33:11 +0000 Subject: [PATCH 37/48] chore: retrigger CI From f4e747394ba799b68500876629daec30be4364b9 Mon Sep 17 00:00:00 2001 From: Bilal ABBAD Date: Wed, 12 Aug 2026 10:16:49 +0200 Subject: [PATCH 38/48] Update frontend dependencies (#10225) --- frontend/app/graphql.config.ts | 2 +- frontend/app/package.json | 53 +- .../api/graphql/generated/graphql-cache.d.ts | 270 +- .../src/shared/api/graphql/generated/types.ts | 17 + frontend/packages/graph/oxlint.config.ts | 1 + frontend/packages/graph/package.json | 19 +- frontend/packages/ui/oxlint.config.ts | 1 + frontend/packages/ui/package.json | 14 +- .../packages/ui/src/components/tree/tree.tsx | 2 +- frontend/pnpm-lock.yaml | 6537 ++++++----------- frontend/pnpm-workspace.yaml | 14 +- 11 files changed, 2579 insertions(+), 4351 deletions(-) diff --git a/frontend/app/graphql.config.ts b/frontend/app/graphql.config.ts index 581f9bd56b5..a4be1352525 100644 --- a/frontend/app/graphql.config.ts +++ b/frontend/app/graphql.config.ts @@ -3,7 +3,7 @@ import type { CodegenConfig } from "@graphql-codegen/cli"; const config: CodegenConfig = { overwrite: true, schema: "../../schema/schema.graphql", - documents: ["src/**/*.{ts,tsx}"], + documents: ["src/**/*.{ts,tsx}", "!src/**/*.test.{ts,tsx}"], ignoreNoDocuments: true, // for better experience with the watcher generates: { "src/shared/api/graphql/generated/types.ts": { diff --git a/frontend/app/package.json b/frontend/app/package.json index c1199d5c803..ac255155902 100644 --- a/frontend/app/package.json +++ b/frontend/app/package.json @@ -33,11 +33,11 @@ }, "dependencies": { "@codemirror/commands": "^6.10.4", - "@codemirror/lang-markdown": "^6.5.0", + "@codemirror/lang-markdown": "^6.5.2", "@codemirror/language": "^6.12.4", - "@codemirror/state": "^6.7.0", + "@codemirror/state": "^6.7.1", "@codemirror/theme-one-dark": "^6.1.3", - "@codemirror/view": "^6.43.3", + "@codemirror/view": "^6.43.8", "@date-fns/tz": "^1.5.0", "@graphiql/plugin-explorer": "^5.1.3", "@graphiql/toolkit": "^0.12.1", @@ -46,20 +46,20 @@ "@iconify-json/mdi": "^1.2.3", "@infrahub/graph": "workspace:*", "@infrahub/ui": "workspace:*", - "@radix-ui/react-accordion": "^1.2.14", - "@radix-ui/react-dropdown-menu": "^2.1.18", - "@radix-ui/react-label": "^2.1.10", - "@radix-ui/react-popover": "^1.1.17", - "@radix-ui/react-progress": "^1.1.10", - "@radix-ui/react-slot": "^1.3.0", - "@radix-ui/react-tabs": "^1.1.15", - "@tanstack/react-query": "^5.101.1", - "@tanstack/react-query-devtools": "^5.101.1", + "@radix-ui/react-accordion": "^1.2.20", + "@radix-ui/react-dropdown-menu": "^2.1.24", + "@radix-ui/react-label": "^2.1.15", + "@radix-ui/react-popover": "^1.1.23", + "@radix-ui/react-progress": "^1.1.16", + "@radix-ui/react-slot": "^1.3.3", + "@radix-ui/react-tabs": "^1.1.21", + "@tanstack/react-query": "^5.101.4", + "@tanstack/react-query-devtools": "^5.101.4", "@tanstack/react-table": "^8.21.3", "@uiw/react-color": "^2.10.3", "@urql/core": "^6.0.3", "@urql/exchange-auth": "^3.0.0", - "@xyflow/react": "^12.11.1", + "@xyflow/react": "^12.11.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cm6-graphql": "^0.2.1", @@ -67,16 +67,16 @@ "cmdk": "^1.1.1", "dagre": "^0.8.5", "date-fns": "^4.4.0", - "gql.tada": "^1.11.2", + "gql.tada": "^1.11.3", "graphiql": "^5.2.4", "graphql": "^16.14.2", "infrahub-schema-visualizer": "workspace:*", - "jotai": "^2.20.1", + "jotai": "^2.20.2", "json-to-graphql-query": "^2.3.0", "lucide-react": "catalog:", "monaco-editor": "0.52.2", "monaco-graphql": "^1.8.0", - "nuqs": "^2.8.9", + "nuqs": "^2.9.5", "openapi-fetch": "^0.17.0", "prismjs": "^1.30.0", "react": "catalog:", @@ -93,8 +93,8 @@ "react-simple-code-editor": "^0.14.1", "react-syntax-highlighter": "^16.1.1", "react-toastify": "9.1.3", - "react-zoom-pan-pinch": "^4.0.3", - "recharts": "^3.9.0", + "react-zoom-pan-pinch": "^4.0.4", + "recharts": "^3.10.1", "rehype-mermaid": "^3.0.0", "remark-breaks": "^4.0.0", "remark-gfm": "^4.0.1", @@ -109,9 +109,9 @@ "@betterer/cli": "6.0.0-alpha.1", "@betterer/typescript": "6.0.0-alpha.1", "@biomejs/biome": "^2.5.1", - "@graphql-codegen/cli": "^7.1.3", - "@graphql-codegen/typescript": "^6.0.2", - "@playwright/test": "1.61.1", + "@graphql-codegen/cli": "^7.2.0", + "@graphql-codegen/typescript": "^6.1.0", + "@playwright/test": "1.62.1", "@rolldown/plugin-babel": "^0.2.3", "@tailwindcss/vite": "catalog:", "@types/dagre": "^0.7.54", @@ -122,18 +122,19 @@ "@types/react-syntax-highlighter": "^15.5.13", "@types/sha1": "^1.1.5", "@vitejs/plugin-react": "catalog:", - "@vitest/browser-playwright": "^4.1.9", - "@vitest/coverage-v8": "^4.1.9", + "@vitest/browser-playwright": "^4.1.10", + "@vitest/coverage-v8": "^4.1.10", "babel-plugin-react-compiler": "catalog:", - "knip": "^6.21.0", + "knip": "~6.27.0", "openapi-typescript": "^7.13.0", "tailwindcss": "catalog:", + "ts-node": "^10.9.2", "typescript": "catalog:", "ultracite": "^7.8.3", "vite": "catalog:", - "vite-plugin-monaco-editor-esm": "^2.0.2", + "vite-plugin-monaco-editor-esm": "^2.0.3", "vite-plugin-svgr": "^5.2.0", - "vitest": "^4.1.9", + "vitest": "^4.1.10", "vitest-browser-react": "^2.2.0" }, "engines": { diff --git a/frontend/app/src/shared/api/graphql/generated/graphql-cache.d.ts b/frontend/app/src/shared/api/graphql/generated/graphql-cache.d.ts index 607f4874393..91003e5f90c 100644 --- a/frontend/app/src/shared/api/graphql/generated/graphql-cache.d.ts +++ b/frontend/app/src/shared/api/graphql/generated/graphql-cache.d.ts @@ -30,18 +30,6 @@ declare module 'gql.tada' { /** @gql.tada/hash sha256:124aa4e99b467157d04a260d296d2807 */ "\n mutation BRANCH_DELETE($name: String, $deleteFromGit: Boolean) {\n BranchDelete(data: { name: $name, delete_from_git: $deleteFromGit }) {\n ok\n }\n }\n": TadaDocumentNode<{ BranchDelete: { ok: boolean | null; } | null; }, { deleteFromGit?: boolean | null | undefined; name?: string | null | undefined; }, void>; - /** @gql.tada/hash sha256:208a69e8ea2701e068ec7c4445bf4e2c */ - "\n query GET_BRANCH_ACTION_STATE($branch: String!, $workflow: [String], $state: [StateType]) {\n InfrahubTask(branch: $branch, workflow: $workflow, state: $state) {\n count\n }\n }\n": - TadaDocumentNode<{ InfrahubTask: { count: number; }; }, { state?: ("CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null)[] | null | undefined; workflow?: (string | null)[] | null | undefined; branch: string; }, void>; - /** @gql.tada/hash sha256:0f38c5be613158207df9298328cced57 */ - "\n query GetBranchDetails($branchName: String!) {\n InfrahubBranch(name__value: $branchName) {\n edges {\n node {\n id\n name {\n value\n }\n description {\n value\n }\n origin_branch {\n value\n }\n branched_from {\n value\n }\n status {\n value\n }\n created_at\n sync_with_git {\n value\n }\n is_default {\n value\n }\n schema_differs_from_default_branch {\n value\n }\n }\n }\n }\n }\n": - TadaDocumentNode<{ InfrahubBranch: { edges: { node: { id: string; name: { value: string; }; description: { value: string | null; } | null; origin_branch: { value: string | null; } | null; branched_from: { value: string | null; } | null; status: { value: "DELETING" | "MERGED" | "MERGE_FAILED" | "MERGING" | "NEED_REBASE" | "NEED_UPGRADE_REBASE" | "OPEN"; }; created_at: string | null; sync_with_git: { value: boolean | null; } | null; is_default: { value: boolean | null; } | null; schema_differs_from_default_branch: { value: boolean | null; } | null; }; }[]; }; }, { branchName: string; }, void>; - /** @gql.tada/hash sha256:9dae6d3727951bf76e080ed7e017b025 */ - "\n query GetBranchesCount($nameValue: String, $partialMatch: Boolean, $statusValue: BranchStatus, $createdById: ID, $branchedFromAfter: DateTime, $branchedFromBefore: DateTime, $createdAtAfter: DateTime, $createdAtBefore: DateTime, $updatedAtAfter: DateTime, $updatedAtBefore: DateTime) {\n InfrahubBranch(name__value: $nameValue, partial_match: $partialMatch, status__value: $statusValue, node_metadata__created_by__id: $createdById, branched_from__after: $branchedFromAfter, branched_from__before: $branchedFromBefore, node_metadata__created_at__after: $createdAtAfter, node_metadata__created_at__before: $createdAtBefore, node_metadata__updated_at__after: $updatedAtAfter, node_metadata__updated_at__before: $updatedAtBefore) {\n count\n }\n }\n": - TadaDocumentNode<{ InfrahubBranch: { count: number | null; }; }, { updatedAtBefore?: unknown; updatedAtAfter?: unknown; createdAtBefore?: unknown; createdAtAfter?: unknown; branchedFromBefore?: unknown; branchedFromAfter?: unknown; createdById?: string | null | undefined; statusValue?: "DELETING" | "MERGED" | "MERGE_FAILED" | "MERGING" | "NEED_REBASE" | "NEED_UPGRADE_REBASE" | "OPEN" | null | undefined; partialMatch?: boolean | null | undefined; nameValue?: string | null | undefined; }, void>; - /** @gql.tada/hash sha256:67960ed821204cd83d4b69466536ad58 */ - "\n query GetBranches($limit: Int, $offset: Int, $nameValue: String, $partialMatch: Boolean, $statusValue: BranchStatus, $createdById: ID, $branchedFromAfter: DateTime, $branchedFromBefore: DateTime, $createdAtAfter: DateTime, $createdAtBefore: DateTime, $updatedAtAfter: DateTime, $updatedAtBefore: DateTime) {\n InfrahubBranch(limit: $limit, offset: $offset, name__value: $nameValue, partial_match: $partialMatch, status__value: $statusValue, node_metadata__created_by__id: $createdById, branched_from__after: $branchedFromAfter, branched_from__before: $branchedFromBefore, node_metadata__created_at__after: $createdAtAfter, node_metadata__created_at__before: $createdAtBefore, node_metadata__updated_at__after: $updatedAtAfter, node_metadata__updated_at__before: $updatedAtBefore) {\n edges {\n node {\n id\n name {\n value\n }\n description {\n value\n }\n origin_branch {\n value\n }\n branched_from {\n value\n }\n status {\n value\n }\n created_at\n sync_with_git {\n value\n }\n is_default {\n value\n }\n schema_differs_from_default_branch {\n value\n }\n }\n node_metadata {\n created_at\n created_by {\n id\n display_label\n hfid\n __typename\n }\n updated_at\n updated_by {\n id\n display_label\n hfid\n __typename\n }\n }\n }\n }\n }\n": - TadaDocumentNode<{ InfrahubBranch: { edges: { node: { id: string; name: { value: string; }; description: { value: string | null; } | null; origin_branch: { value: string | null; } | null; branched_from: { value: string | null; } | null; status: { value: "DELETING" | "MERGED" | "MERGE_FAILED" | "MERGING" | "NEED_REBASE" | "NEED_UPGRADE_REBASE" | "OPEN"; }; created_at: string | null; sync_with_git: { value: boolean | null; } | null; is_default: { value: boolean | null; } | null; schema_differs_from_default_branch: { value: boolean | null; } | null; }; node_metadata: { created_at: unknown; created_by: { __typename: "CoreAccount"; id: string | null; display_label: string | null; hfid: string[] | null; } | null; updated_at: unknown; updated_by: { __typename: "CoreAccount"; id: string | null; display_label: string | null; hfid: string[] | null; } | null; }; }[]; }; }, { updatedAtBefore?: unknown; updatedAtAfter?: unknown; createdAtBefore?: unknown; createdAtAfter?: unknown; branchedFromBefore?: unknown; branchedFromAfter?: unknown; createdById?: string | null | undefined; statusValue?: "DELETING" | "MERGED" | "MERGE_FAILED" | "MERGING" | "NEED_REBASE" | "NEED_UPGRADE_REBASE" | "OPEN" | null | undefined; partialMatch?: boolean | null | undefined; nameValue?: string | null | undefined; offset?: number | null | undefined; limit?: number | null | undefined; }, void>; /** @gql.tada/hash sha256:7f138f31534e44ed04215ae7414cdb67 */ "\n mutation BRANCH_MERGE($name: String) {\n BranchMerge(wait_until_completion: false, data: { name: $name }) {\n ok\n task {\n id\n }\n }\n }\n": TadaDocumentNode<{ BranchMerge: { ok: boolean | null; task: { id: string | null; } | null; } | null; }, { name?: string | null | undefined; }, void>; @@ -51,147 +39,123 @@ declare module 'gql.tada' { /** @gql.tada/hash sha256:f3e56cdd49cd634f3f00d4941f276fbf */ "\n mutation BRANCH_VALIDATE($name: String) {\n BranchValidate(wait_until_completion: false, data: { name: $name }) {\n ok\n task {\n id\n }\n }\n }\n": TadaDocumentNode<{ BranchValidate: { ok: boolean | null; task: { id: string | null; } | null; } | null; }, { name?: string | null | undefined; }, void>; + /** @gql.tada/hash sha256:f6dd9885b2d0fae950f58a237ce5e029 */ + "\n mutation CANCEL_TASK($id: String!) {\n InfrahubTaskCancel(data: { id: $id }) {\n ok\n task {\n id\n }\n }\n }\n": + TadaDocumentNode<{ InfrahubTaskCancel: { ok: boolean | null; task: { id: string | null; } | null; } | null; }, { id: string; }, void>; + /** @gql.tada/hash sha256:20c155a5b455af4e2f3131d03b97a091 */ + "\n mutation CHECK_REPOSITORY_CONNECTIVITY($repositoryId: String!) {\n InfrahubRepositoryConnectivity(data: { id: $repositoryId }) {\n ok\n message\n }\n }\n": + TadaDocumentNode<{ InfrahubRepositoryConnectivity: { ok: boolean; message: string; } | null; }, { repositoryId: string; }, void>; + /** @gql.tada/hash sha256:6018794c1e4b364f7d0aff881a2b29cd */ + "\n mutation CONVERT_OBJECT_MUTATION($nodeId: String!, $targetKind: String!, $fieldsMapping: GenericScalar!) {\n ConvertObjectType(\n data: { node_id: $nodeId, target_kind: $targetKind, fields_mapping: $fieldsMapping }\n ) {\n node\n }\n }\n": + TadaDocumentNode<{ ConvertObjectType: { node: unknown; } | null; }, { fieldsMapping: unknown; targetKind: string; nodeId: string; }, void>; + /** @gql.tada/hash sha256:70970c6312c8158a2a072c54a19e3009 */ + "\n mutation CoreGeneratorDefinitionRun($generatorId: String!, $waitUntilCompletion: Boolean, $targetNodeIds: [String!]) {\n CoreGeneratorDefinitionRun(\n wait_until_completion: $waitUntilCompletion\n data: { id: $generatorId, nodes: $targetNodeIds }\n ) {\n task {\n id\n }\n }\n }\n": + TadaDocumentNode<{ CoreGeneratorDefinitionRun: { task: { id: string | null; } | null; } | null; }, { targetNodeIds?: string[] | null | undefined; waitUntilCompletion?: boolean | null | undefined; generatorId: string; }, void>; + /** @gql.tada/hash sha256:ef221c61c8e9e0554096b05d16c2257e */ + "\n mutation CoreProposedChangeCreate(\n $name: String!\n $isDraft: Boolean\n $description: String\n $source_branch: String!\n $destination_branch: String!\n $reviewers: [RelatedNodeInput!]\n ) {\n CoreProposedChangeCreate(\n data: {\n name: { value: $name }\n is_draft: { value: $isDraft }\n description: { value: $description }\n source_branch: { value: $source_branch }\n destination_branch: { value: $destination_branch }\n reviewers: $reviewers\n }\n ) {\n object {\n id\n display_label\n }\n ok\n }\n }\n": + TadaDocumentNode<{ CoreProposedChangeCreate: { object: { id: string; display_label: string | null; } | null; ok: boolean | null; } | null; }, { reviewers?: { kind?: string | null | undefined; id?: string | null | undefined; hfid?: (string | null)[] | null | undefined; from_pool?: { identifier?: string | null | undefined; id: string; data?: unknown; } | null | undefined; _relation__source?: string | null | undefined; _relation__owner?: string | null | undefined; _relation__is_protected?: boolean | null | undefined; }[] | null | undefined; destination_branch: string; source_branch: string; description?: string | null | undefined; isDraft?: boolean | null | undefined; name: string; }, void>; + /** @gql.tada/hash sha256:0fa8712af1cb29a94014b5e47f9b1b77 */ + "\n mutation DIFF_UPDATE($branchName: String!, $waitUntilCompletion: Boolean) {\n DiffUpdate(data: { branch: $branchName }, wait_until_completion: $waitUntilCompletion) {\n ok\n }\n }\n": + TadaDocumentNode<{ DiffUpdate: { ok: boolean | null; } | null; }, { waitUntilCompletion?: boolean | null | undefined; branchName: string; }, void>; + /** @gql.tada/hash sha256:ff263930e8ff625ad84e82ffbd722b6f */ + "\n mutation DropdownAdd(\n $kind: String!\n $attribute: String!\n $dropdown: String!\n $label: String\n $color: String\n $description: String\n ) {\n SchemaDropdownAdd(\n data: {\n kind: $kind\n attribute: $attribute\n dropdown: $dropdown\n label: $label\n color: $color\n description: $description\n }\n ) {\n ok\n object {\n value\n label\n color\n description\n }\n }\n }\n": + TadaDocumentNode<{ SchemaDropdownAdd: { ok: boolean | null; object: { value: string | null; label: string | null; color: string | null; description: string | null; } | null; } | null; }, { description?: string | null | undefined; color?: string | null | undefined; label?: string | null | undefined; dropdown: string; attribute: string; kind: string; }, void>; + /** @gql.tada/hash sha256:bf0f1a370eec3ac0727f9c12ceb0a57d */ + "\n mutation DropdownDelete($kind: String!, $attribute: String!, $dropdown: String!) {\n SchemaDropdownRemove(data: { kind: $kind, attribute: $attribute, dropdown: $dropdown }) {\n ok\n }\n }\n": + TadaDocumentNode<{ SchemaDropdownRemove: { ok: boolean | null; } | null; }, { dropdown: string; attribute: string; kind: string; }, void>; + /** @gql.tada/hash sha256:589321a1d1f294da75a53e6e25b1fef7 */ + "\n mutation EnumAdd($kind: String!, $attribute: String!, $enum: String!) {\n SchemaEnumAdd(data: { kind: $kind, attribute: $attribute, enum: $enum }) {\n ok\n }\n }\n": + TadaDocumentNode<{ SchemaEnumAdd: { ok: boolean | null; } | null; }, { enum: string; attribute: string; kind: string; }, void>; + /** @gql.tada/hash sha256:608790eeb11144570653ec71ddab2fd1 */ + "\n mutation EnumDelete($kind: String!, $attribute: String!, $enum: String!) {\n SchemaEnumRemove(data: { kind: $kind, attribute: $attribute, enum: $enum }) {\n ok\n }\n }\n": + TadaDocumentNode<{ SchemaEnumRemove: { ok: boolean | null; } | null; }, { enum: string; attribute: string; kind: string; }, void>; + /** @gql.tada/hash sha256:0a7e88d29f3b03a48ef8de5fdb035d0c */ + "\n mutation IMPORT_CURRENT_COMMIT($repositoryId: String!) {\n InfrahubRepositoryProcess(data: { id: $repositoryId }) {\n ok\n task {\n id\n }\n }\n }\n": + TadaDocumentNode<{ InfrahubRepositoryProcess: { ok: boolean | null; task: { id: string | null; } | null; } | null; }, { repositoryId: string; }, void>; + /** @gql.tada/hash sha256:d90877c30f537101da2fc543d65f768d */ + "\n mutation InfrahubAccountTokenCreate($tokenName: String!, $tokenExpirationDate: String) {\n InfrahubAccountTokenCreate(data: { name: $tokenName, expiration: $tokenExpirationDate }) {\n object {\n id\n token {\n value\n }\n }\n ok\n }\n }\n": + TadaDocumentNode<{ InfrahubAccountTokenCreate: { object: { id: string; token: { value: string; } | null; } | null; ok: boolean | null; } | null; }, { tokenExpirationDate?: string | null | undefined; tokenName: string; }, void>; + /** @gql.tada/hash sha256:388f1a037f738e522ecf4f174d1de58f */ + "\n mutation ProposedChangeReview($proposedChangeId: String!, $decision: ProposedChangeApprovalDecision!) {\n CoreProposedChangeReview(data: { id: $proposedChangeId, decision: $decision }) {\n ok\n }\n }\n": + TadaDocumentNode<{ CoreProposedChangeReview: { ok: boolean | null; } | null; }, { decision: "APPROVE" | "CANCEL_APPROVE" | "CANCEL_REJECT" | "REJECT"; proposedChangeId: string; }, void>; + /** @gql.tada/hash sha256:2a7db54f21a3f1d83d3ad20967aa6af4 */ + "\n mutation REIMPORT_LAST_COMMIT($repositoryId: String!) {\n InfrahubReadOnlyRepositoryImportLastCommit(data: { id: $repositoryId }) {\n ok\n task {\n id\n }\n }\n }\n": + TadaDocumentNode<{ InfrahubReadOnlyRepositoryImportLastCommit: { ok: boolean | null; task: { id: string | null; } | null; } | null; }, { repositoryId: string; }, void>; + /** @gql.tada/hash sha256:2f34a1340945f37599c93f19145aa214 */ + "\n mutation RESOLVE_CONFLICT($id: String, $selection: ConflictSelection) {\n ResolveDiffConflict(data: { conflict_id: $id, selected_branch: $selection }) {\n ok\n }\n }\n": + TadaDocumentNode<{ ResolveDiffConflict: { ok: boolean | null; } | null; }, { selection?: "BASE_BRANCH" | "DIFF_BRANCH" | null | undefined; id?: string | null | undefined; }, void>; + /** @gql.tada/hash sha256:6de3d7c14c380a1d17b5cb1cbaa26212 */ + "\n mutation RETRY_TASK($id: String!) {\n InfrahubTaskRetry(data: { id: $id }) {\n ok\n task {\n id\n }\n }\n }\n": + TadaDocumentNode<{ InfrahubTaskRetry: { ok: boolean | null; task: { id: string | null; } | null; } | null; }, { id: string; }, void>; + /** @gql.tada/hash sha256:095b6720088da21e655b57af352cf061 */ + "\n mutation RUN_CHECK($proposedChangeId: String!, $checkType: CheckType) {\n CoreProposedChangeRunCheck(data: { id: $proposedChangeId, check_type: $checkType }) {\n ok\n }\n }\n": + TadaDocumentNode<{ CoreProposedChangeRunCheck: { ok: boolean | null; } | null; }, { checkType?: "ALL" | "ARTIFACT" | "DATA" | "GENERATOR" | "REPOSITORY" | "SCHEMA" | "TEST" | "USER" | null | undefined; proposedChangeId: string; }, void>; + /** @gql.tada/hash sha256:001b22bf46a668a45d1048b7800eec8c */ + "\n mutation RelationshipAdd(\n $objectId: String!\n $relationshipName: String!\n $relationshipIds: [RelatedNodeInput]\n ) {\n RelationshipAdd(data: { id: $objectId, name: $relationshipName, nodes: $relationshipIds }) {\n ok\n }\n }\n": + TadaDocumentNode<{ RelationshipAdd: { ok: boolean | null; } | null; }, { relationshipIds?: ({ kind?: string | null | undefined; id?: string | null | undefined; hfid?: (string | null)[] | null | undefined; from_pool?: { identifier?: string | null | undefined; id: string; data?: unknown; } | null | undefined; _relation__source?: string | null | undefined; _relation__owner?: string | null | undefined; _relation__is_protected?: boolean | null | undefined; } | null)[] | null | undefined; relationshipName: string; objectId: string; }, void>; + /** @gql.tada/hash sha256:61fdc46dd2875e6bebdd9363ab4974fb */ + "\n mutation RelationshipRemove(\n $objectId: String!\n $relationshipName: String!\n $relationshipIds: [RelatedNodeInput]\n ) {\n RelationshipRemove(data: { id: $objectId, name: $relationshipName, nodes: $relationshipIds }) {\n ok\n }\n }\n": + TadaDocumentNode<{ RelationshipRemove: { ok: boolean | null; } | null; }, { relationshipIds?: ({ kind?: string | null | undefined; id?: string | null | undefined; hfid?: (string | null)[] | null | undefined; from_pool?: { identifier?: string | null | undefined; id: string; data?: unknown; } | null | undefined; _relation__source?: string | null | undefined; _relation__owner?: string | null | undefined; _relation__is_protected?: boolean | null | undefined; } | null)[] | null | undefined; relationshipName: string; objectId: string; }, void>; + /** @gql.tada/hash sha256:7e3b3fe850b74326deb289855b4bc3ef */ + "\n mutation UPDATE_ACCOUNT_PASSWORD($password: String!) {\n InfrahubAccountSelfUpdate(data: { password: $password }) {\n ok\n }\n }\n": + TadaDocumentNode<{ InfrahubAccountSelfUpdate: { ok: boolean | null; } | null; }, { password: string; }, void>; + /** @gql.tada/hash sha256:441b17c7ad9e6f4801ffe65729249e69 */ + "\n mutation UpdateGlobalPreference($dateFormat: DateFormat, $timezone: String) {\n InfrahubSetPreferences(scope: GLOBAL, date_format: $dateFormat, timezone: $timezone) {\n ok\n date_format\n timezone\n }\n }\n": + TadaDocumentNode<{ InfrahubSetPreferences: { ok: boolean | null; date_format: "EU_DATETIME" | "ISO_8601" | "ISO_DATETIME" | "ISO_DATETIME_SECONDS" | "US_12H" | null; timezone: string | null; } | null; }, { timezone?: string | null | undefined; dateFormat?: "EU_DATETIME" | "ISO_8601" | "ISO_DATETIME" | "ISO_DATETIME_SECONDS" | "US_12H" | null | undefined; }, void>; + /** @gql.tada/hash sha256:30b73a7750503476ab427bcb611d60da */ + "\n mutation UpsertUserPreference($dateFormat: DateFormat, $timezone: String) {\n InfrahubSetPreferences(scope: USER, date_format: $dateFormat, timezone: $timezone) {\n ok\n date_format\n timezone\n }\n }\n": + TadaDocumentNode<{ InfrahubSetPreferences: { ok: boolean | null; date_format: "EU_DATETIME" | "ISO_8601" | "ISO_DATETIME" | "ISO_DATETIME_SECONDS" | "US_12H" | null; timezone: string | null; } | null; }, { timezone?: string | null | undefined; dateFormat?: "EU_DATETIME" | "ISO_8601" | "ISO_DATETIME" | "ISO_DATETIME_SECONDS" | "US_12H" | null | undefined; }, void>; /** @gql.tada/hash sha256:a7e49cf646db24cb093b62981803abb5 */ "\n query GET_ARTIFACT_THREADS($changeIds: [ID!]) {\n CoreArtifactThread(change__ids: $changeIds) {\n count\n edges {\n node {\n id\n display_label\n __typename\n line_number {\n value\n }\n storage_id {\n value\n }\n resolved {\n value\n }\n comments {\n edges {\n node_metadata {\n created_at\n created_by {\n display_label\n }\n }\n node {\n id\n text {\n value\n }\n }\n }\n }\n }\n }\n }\n }\n": TadaDocumentNode<{ CoreArtifactThread: { count: number; edges: { node: { id: string; display_label: string | null; __typename: "CoreArtifactThread"; line_number: { value: unknown; } | null; storage_id: { value: string | null; } | null; resolved: { value: boolean | null; } | null; comments: { edges: { node_metadata: { created_at: unknown; created_by: { __typename?: "CoreAccount" | undefined; display_label: string | null; } | null; } | null; node: { id: string; text: { value: string | null; } | null; } | null; }[]; }; } | null; }[]; }; }, { changeIds?: string[] | null | undefined; }, void>; + /** @gql.tada/hash sha256:208a69e8ea2701e068ec7c4445bf4e2c */ + "\n query GET_BRANCH_ACTION_STATE($branch: String!, $workflow: [String], $state: [StateType]) {\n InfrahubTask(branch: $branch, workflow: $workflow, state: $state) {\n count\n }\n }\n": + TadaDocumentNode<{ InfrahubTask: { count: number; }; }, { state?: ("CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null)[] | null | undefined; workflow?: (string | null)[] | null | undefined; branch: string; }, void>; /** @gql.tada/hash sha256:89409a35e7707ea4b4bce38bd1eedc0a */ "\n query GET_CHECK_DETAILS($id: ID!) {\n CoreCheck(ids: [$id]) {\n edges {\n node {\n id\n display_label\n name {\n value\n }\n message {\n value\n }\n severity {\n value\n }\n conclusion {\n value\n }\n kind {\n value\n }\n origin {\n value\n }\n created_at {\n value\n }\n ... on CoreDataCheck {\n conflicts {\n value\n }\n keep_branch {\n value\n }\n }\n ... on CoreSchemaCheck {\n conflicts {\n value\n }\n }\n ... on CoreFileCheck {\n files {\n value\n }\n commit {\n value\n }\n }\n ... on CoreArtifactCheck {\n storage_id {\n value\n }\n artifact_id {\n value\n }\n }\n __typename\n }\n }\n }\n }\n": TadaDocumentNode<{ CoreCheck: { edges: { node: { __typename: "CoreArtifactCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; } | { __typename: "CoreDataCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; keep_branch: { value: string | null; } | null; } | { __typename: "CoreFileCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; files: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename: "CoreGeneratorCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | { __typename: "CoreSchemaCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreStandardCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | null; }[]; }; }, { id: string; }, void>; - /** @gql.tada/hash sha256:c7223a03d4f81fd84c2741e232b1e4e9 */ - "\n query GET_OBJECT_THREAD_COMMENTS($changeIds: [ID!], $objectPath: String) {\n CoreObjectThread(change__ids: $changeIds, object_path__value: $objectPath) {\n count\n edges {\n node {\n __typename\n id\n display_label\n resolved {\n value\n }\n comments {\n count\n edges {\n node_metadata {\n created_at\n created_by {\n display_label\n }\n }\n node {\n id\n display_label\n text {\n value\n }\n }\n }\n }\n }\n }\n }\n }\n": - TadaDocumentNode<{ CoreObjectThread: { count: number; edges: { node: { __typename: "CoreObjectThread"; id: string; display_label: string | null; resolved: { value: boolean | null; } | null; comments: { count: number; edges: { node_metadata: { created_at: unknown; created_by: { __typename?: "CoreAccount" | undefined; display_label: string | null; } | null; } | null; node: { id: string; display_label: string | null; text: { value: string | null; } | null; } | null; }[]; }; } | null; }[]; }; }, { objectPath?: string | null | undefined; changeIds?: string[] | null | undefined; }, void>; - /** @gql.tada/hash sha256:d15c41ad76d2e5a73a95f8515bbaddfd */ - "\n query GET_OBJECT_THREADS($changeIds: [ID!], $objectPath: String) {\n CoreObjectThread(change__ids: $changeIds, object_path__value: $objectPath) {\n count\n edges {\n node {\n __typename\n id\n comments {\n count\n }\n }\n }\n permissions {\n edges {\n node {\n kind\n view\n create\n update\n delete\n }\n }\n }\n }\n }\n": - TadaDocumentNode<{ CoreObjectThread: { count: number; edges: { node: { __typename: "CoreObjectThread"; id: string; comments: { count: number; }; } | null; }[]; permissions: { edges: { node: { kind: string; view: "ALLOW" | "ALLOW_DEFAULT" | "ALLOW_OTHER" | "DENY"; create: "ALLOW" | "ALLOW_DEFAULT" | "ALLOW_OTHER" | "DENY"; update: "ALLOW" | "ALLOW_DEFAULT" | "ALLOW_OTHER" | "DENY"; delete: "ALLOW" | "ALLOW_DEFAULT" | "ALLOW_OTHER" | "DENY"; }; }[]; }; }; }, { objectPath?: string | null | undefined; changeIds?: string[] | null | undefined; }, void>; + /** @gql.tada/hash sha256:55e026a46fc574187d2c4ddca85f4101 */ + "\n query GET_CORE_VALIDATORS($id: ID!) {\n CoreValidator(proposed_change__ids: [$id]) {\n edges {\n node {\n id\n display_label\n conclusion {\n value\n }\n started_at {\n value\n }\n completed_at {\n value\n }\n state {\n value\n }\n checks {\n edges {\n node {\n conclusion {\n value\n }\n severity {\n value\n }\n }\n }\n }\n ... on CoreArtifactValidator {\n definition {\n node {\n id\n display_label\n __typename\n }\n }\n }\n __typename\n }\n }\n }\n }\n": + TadaDocumentNode<{ CoreValidator: { edges: { node: { __typename: "CoreArtifactValidator"; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { edges: { node: { __typename?: "CoreArtifactCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreDataCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreFileCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreGeneratorCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreSchemaCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreStandardCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | null; }[] | null; }; definition: { node: { id: string; display_label: string | null; __typename: "CoreArtifactDefinition"; } | null; }; } | { __typename: "CoreDataValidator"; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { edges: { node: { __typename?: "CoreArtifactCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreDataCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreFileCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreGeneratorCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreSchemaCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreStandardCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename: "CoreGeneratorValidator"; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { edges: { node: { __typename?: "CoreArtifactCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreDataCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreFileCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreGeneratorCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreSchemaCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreStandardCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename: "CoreRepositoryValidator"; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { edges: { node: { __typename?: "CoreArtifactCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreDataCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreFileCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreGeneratorCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreSchemaCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreStandardCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename: "CoreSchemaValidator"; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { edges: { node: { __typename?: "CoreArtifactCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreDataCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreFileCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreGeneratorCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreSchemaCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreStandardCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename: "CoreUserValidator"; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { edges: { node: { __typename?: "CoreArtifactCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreDataCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreFileCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreGeneratorCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreSchemaCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreStandardCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | null; }[] | null; }; } | null; }[]; }; }, { id: string; }, void>; /** @gql.tada/hash sha256:52f4fbae0cf2bee9b5e20bf983f25d5d */ "\n query GET_DIFF_TREE($branchName: String, $filters: DiffTreeQueryFilters, $limit: Int, $offset: Int, $proposedChangeId: String) {\n DiffTree(branch: $branchName, filters: $filters, include_parents: true, limit: $limit, offset: $offset, proposed_change_id: $proposedChangeId) {\n nodes {\n uuid\n relationships {\n label\n status\n contains_conflict\n cardinality\n elements {\n conflict {\n base_branch_label\n base_branch_action\n base_branch_changed_at\n base_branch_value\n diff_branch_label\n diff_branch_action\n diff_branch_changed_at\n diff_branch_value\n selected_branch\n uuid\n }\n last_changed_at\n contains_conflict\n peer_id\n properties {\n conflict {\n base_branch_label\n base_branch_action\n base_branch_changed_at\n base_branch_value\n diff_branch_label\n diff_branch_action\n diff_branch_changed_at\n diff_branch_value\n selected_branch\n uuid\n }\n last_changed_at\n new_value\n previous_value\n property_type\n status\n path_identifier\n }\n status\n path_identifier\n peer_label\n }\n last_changed_at\n name\n path_identifier\n }\n conflict {\n base_branch_label\n base_branch_action\n base_branch_changed_at\n diff_branch_action\n diff_branch_label\n base_branch_value\n diff_branch_changed_at\n diff_branch_value\n selected_branch\n uuid\n }\n attributes {\n contains_conflict\n last_changed_at\n name\n conflict {\n base_branch_label\n base_branch_action\n base_branch_changed_at\n base_branch_value\n diff_branch_label\n diff_branch_action\n diff_branch_changed_at\n diff_branch_value\n selected_branch\n uuid\n }\n properties {\n conflict {\n base_branch_label\n base_branch_action\n base_branch_changed_at\n base_branch_value\n diff_branch_label\n diff_branch_action\n diff_branch_changed_at\n diff_branch_value\n selected_branch\n uuid\n }\n last_changed_at\n new_value\n previous_value\n property_type\n status\n path_identifier\n }\n status\n path_identifier\n }\n kind\n contains_conflict\n label\n last_changed_at\n status\n path_identifier\n parent {\n uuid\n relationship_name\n kind\n }\n }\n to_time\n base_branch\n diff_branch\n from_time\n }\n }\n": TadaDocumentNode<{ DiffTree: { nodes: { uuid: string; relationships: { label: string | null; status: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; contains_conflict: boolean; cardinality: "MANY" | "ONE"; elements: { conflict: { base_branch_label: string | null; base_branch_action: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; base_branch_changed_at: unknown; base_branch_value: string | null; diff_branch_label: string | null; diff_branch_action: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; diff_branch_changed_at: unknown; diff_branch_value: string | null; selected_branch: "BASE_BRANCH" | "DIFF_BRANCH" | null; uuid: string; } | null; last_changed_at: unknown; contains_conflict: boolean; peer_id: string; properties: { conflict: { base_branch_label: string | null; base_branch_action: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; base_branch_changed_at: unknown; base_branch_value: string | null; diff_branch_label: string | null; diff_branch_action: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; diff_branch_changed_at: unknown; diff_branch_value: string | null; selected_branch: "BASE_BRANCH" | "DIFF_BRANCH" | null; uuid: string; } | null; last_changed_at: unknown; new_value: string | null; previous_value: string | null; property_type: string; status: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; path_identifier: string; }[] | null; status: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; path_identifier: string; peer_label: string | null; }[]; last_changed_at: unknown; name: string; path_identifier: string; }[]; conflict: { base_branch_label: string | null; base_branch_action: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; base_branch_changed_at: unknown; diff_branch_action: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; diff_branch_label: string | null; base_branch_value: string | null; diff_branch_changed_at: unknown; diff_branch_value: string | null; selected_branch: "BASE_BRANCH" | "DIFF_BRANCH" | null; uuid: string; } | null; attributes: { contains_conflict: boolean; last_changed_at: unknown; name: string; conflict: { base_branch_label: string | null; base_branch_action: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; base_branch_changed_at: unknown; base_branch_value: string | null; diff_branch_label: string | null; diff_branch_action: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; diff_branch_changed_at: unknown; diff_branch_value: string | null; selected_branch: "BASE_BRANCH" | "DIFF_BRANCH" | null; uuid: string; } | null; properties: { conflict: { base_branch_label: string | null; base_branch_action: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; base_branch_changed_at: unknown; base_branch_value: string | null; diff_branch_label: string | null; diff_branch_action: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; diff_branch_changed_at: unknown; diff_branch_value: string | null; selected_branch: "BASE_BRANCH" | "DIFF_BRANCH" | null; uuid: string; } | null; last_changed_at: unknown; new_value: string | null; previous_value: string | null; property_type: string; status: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; path_identifier: string; }[] | null; status: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; path_identifier: string; }[]; kind: string; contains_conflict: boolean; label: string; last_changed_at: unknown; status: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; path_identifier: string; parent: { uuid: string; relationship_name: string | null; kind: string | null; } | null; }[] | null; to_time: unknown; base_branch: string; diff_branch: string; from_time: unknown; } | null; }, { proposedChangeId?: string | null | undefined; offset?: number | null | undefined; limit?: number | null | undefined; filters?: { status?: { includes?: ("ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED" | null)[] | null | undefined; excludes?: ("ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED" | null)[] | null | undefined; } | null | undefined; namespace?: { includes?: (string | null)[] | null | undefined; excludes?: (string | null)[] | null | undefined; } | null | undefined; kind?: { includes?: (string | null)[] | null | undefined; excludes?: (string | null)[] | null | undefined; } | null | undefined; ids?: (string | null)[] | null | undefined; } | null | undefined; branchName?: string | null | undefined; }, void>; /** @gql.tada/hash sha256:16c1329c1a07981207e69d49f3afcf79 */ "\n query GET_DIFF_TREE_SUMMARY($branch: String, $filters: DiffTreeQueryFilters, $proposedChangeId: String) {\n DiffTreeSummary(branch: $branch, filters: $filters, proposed_change_id: $proposedChangeId) {\n num_added\n num_updated\n num_removed\n num_conflicts\n }\n }\n": TadaDocumentNode<{ DiffTreeSummary: { num_added: number; num_updated: number; num_removed: number; num_conflicts: number; } | null; }, { proposedChangeId?: string | null | undefined; filters?: { status?: { includes?: ("ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED" | null)[] | null | undefined; excludes?: ("ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED" | null)[] | null | undefined; } | null | undefined; namespace?: { includes?: (string | null)[] | null | undefined; excludes?: (string | null)[] | null | undefined; } | null | undefined; kind?: { includes?: (string | null)[] | null | undefined; excludes?: (string | null)[] | null | undefined; } | null | undefined; ids?: (string | null)[] | null | undefined; } | null | undefined; branch?: string | null | undefined; }, void>; + /** @gql.tada/hash sha256:4008eb900f182ba09d71114070bc9ffc */ + "\n query GET_FIELDS_MAPPING($sourceKind: String!, $targetKind: String!) {\n FieldsMappingTypeConversion(source_kind: $sourceKind, target_kind: $targetKind) {\n mapping\n }\n }\n": + TadaDocumentNode<{ FieldsMappingTypeConversion: { mapping: unknown; }; }, { targetKind: string; sourceKind: string; }, void>; /** @gql.tada/hash sha256:618ccaf2a9cb71a45639038a948cb67f */ "\n query GET_FILE_THREADS($changeIds: [ID!]) {\n CoreFileThread(change__ids: $changeIds) {\n count\n edges {\n node {\n id\n display_label\n resolved {\n value\n }\n __typename\n file {\n value\n }\n commit {\n value\n }\n repository {\n node {\n id\n }\n }\n line_number {\n value\n }\n comments {\n edges {\n node_metadata {\n created_at\n created_by {\n display_label\n }\n }\n node {\n id\n text {\n value\n }\n }\n }\n }\n }\n }\n }\n }\n": TadaDocumentNode<{ CoreFileThread: { count: number; edges: { node: { id: string; display_label: string | null; resolved: { value: boolean | null; } | null; __typename: "CoreFileThread"; file: { value: string | null; } | null; commit: { value: string | null; } | null; repository: { node: { id: string; } | null; }; line_number: { value: unknown; } | null; comments: { edges: { node_metadata: { created_at: unknown; created_by: { __typename?: "CoreAccount" | undefined; display_label: string | null; } | null; } | null; node: { id: string; text: { value: string | null; } | null; } | null; }[]; }; } | null; }[]; }; }, { changeIds?: string[] | null | undefined; }, void>; - /** @gql.tada/hash sha256:3610b040f6f6b6533b88031726974336 */ - "\n query GET_VALIDATOR_DETAILS($ids: [ID!], $checksOffset: Int, $checksLimit: Int) {\n CoreValidator(ids: $ids) {\n edges {\n node {\n id\n display_label\n conclusion {\n value\n }\n started_at {\n value\n }\n completed_at {\n value\n }\n state {\n value\n }\n ... on CoreRepositoryValidator {\n repository {\n node {\n display_label\n }\n }\n }\n ... on CoreArtifactValidator {\n definition {\n node {\n display_label\n name {\n value\n }\n description {\n value\n }\n }\n }\n }\n checks(offset: $checksOffset, limit: $checksLimit) {\n count\n edges {\n node {\n id\n display_label\n name {\n value\n }\n message {\n value\n }\n severity {\n value\n }\n conclusion {\n value\n }\n kind {\n value\n }\n origin {\n value\n }\n created_at {\n value\n }\n ... on CoreDataCheck {\n conflicts {\n value\n }\n }\n ... on CoreSchemaCheck {\n conflicts {\n value\n }\n }\n ... on CoreFileCheck {\n files {\n value\n }\n commit {\n value\n }\n }\n ... on CoreArtifactCheck {\n storage_id {\n value\n }\n artifact_id {\n value\n }\n }\n __typename\n }\n }\n }\n }\n }\n }\n }\n": - TadaDocumentNode<{ CoreValidator: { edges: { node: { __typename?: "CoreArtifactValidator" | undefined; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; definition: { node: { display_label: string | null; name: { value: string | null; } | null; description: { value: string | null; } | null; } | null; }; checks: { count: number; edges: { node: { __typename: "CoreArtifactCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; } | { __typename: "CoreDataCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreFileCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; files: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename: "CoreGeneratorCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | { __typename: "CoreSchemaCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreStandardCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename?: "CoreDataValidator" | undefined; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { count: number; edges: { node: { __typename: "CoreArtifactCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; } | { __typename: "CoreDataCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreFileCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; files: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename: "CoreGeneratorCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | { __typename: "CoreSchemaCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreStandardCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename?: "CoreGeneratorValidator" | undefined; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { count: number; edges: { node: { __typename: "CoreArtifactCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; } | { __typename: "CoreDataCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreFileCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; files: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename: "CoreGeneratorCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | { __typename: "CoreSchemaCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreStandardCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename?: "CoreRepositoryValidator" | undefined; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; repository: { node: { __typename?: "CoreReadOnlyRepository" | undefined; display_label: string | null; } | { __typename?: "CoreRepository" | undefined; display_label: string | null; } | null; }; checks: { count: number; edges: { node: { __typename: "CoreArtifactCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; } | { __typename: "CoreDataCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreFileCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; files: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename: "CoreGeneratorCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | { __typename: "CoreSchemaCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreStandardCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename?: "CoreSchemaValidator" | undefined; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { count: number; edges: { node: { __typename: "CoreArtifactCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; } | { __typename: "CoreDataCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreFileCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; files: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename: "CoreGeneratorCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | { __typename: "CoreSchemaCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreStandardCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename?: "CoreUserValidator" | undefined; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { count: number; edges: { node: { __typename: "CoreArtifactCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; } | { __typename: "CoreDataCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreFileCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; files: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename: "CoreGeneratorCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | { __typename: "CoreSchemaCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreStandardCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | null; }[] | null; }; } | null; }[]; }; }, { checksLimit?: number | null | undefined; checksOffset?: number | null | undefined; ids?: string[] | null | undefined; }, void>; - /** @gql.tada/hash sha256:55e026a46fc574187d2c4ddca85f4101 */ - "\n query GET_CORE_VALIDATORS($id: ID!) {\n CoreValidator(proposed_change__ids: [$id]) {\n edges {\n node {\n id\n display_label\n conclusion {\n value\n }\n started_at {\n value\n }\n completed_at {\n value\n }\n state {\n value\n }\n checks {\n edges {\n node {\n conclusion {\n value\n }\n severity {\n value\n }\n }\n }\n }\n ... on CoreArtifactValidator {\n definition {\n node {\n id\n display_label\n __typename\n }\n }\n }\n __typename\n }\n }\n }\n }\n": - TadaDocumentNode<{ CoreValidator: { edges: { node: { __typename: "CoreArtifactValidator"; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { edges: { node: { __typename?: "CoreArtifactCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreDataCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreFileCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreGeneratorCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreSchemaCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreStandardCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | null; }[] | null; }; definition: { node: { id: string; display_label: string | null; __typename: "CoreArtifactDefinition"; } | null; }; } | { __typename: "CoreDataValidator"; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { edges: { node: { __typename?: "CoreArtifactCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreDataCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreFileCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreGeneratorCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreSchemaCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreStandardCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename: "CoreGeneratorValidator"; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { edges: { node: { __typename?: "CoreArtifactCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreDataCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreFileCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreGeneratorCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreSchemaCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreStandardCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename: "CoreRepositoryValidator"; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { edges: { node: { __typename?: "CoreArtifactCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreDataCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreFileCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreGeneratorCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreSchemaCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreStandardCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename: "CoreSchemaValidator"; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { edges: { node: { __typename?: "CoreArtifactCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreDataCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreFileCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreGeneratorCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreSchemaCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreStandardCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename: "CoreUserValidator"; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { edges: { node: { __typename?: "CoreArtifactCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreDataCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreFileCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreGeneratorCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreSchemaCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | { __typename?: "CoreStandardCheck" | undefined; conclusion: { value: string | null; } | null; severity: { value: string | null; } | null; } | null; }[] | null; }; } | null; }[]; }; }, { id: string; }, void>; - /** @gql.tada/hash sha256:2f34a1340945f37599c93f19145aa214 */ - "\n mutation RESOLVE_CONFLICT($id: String, $selection: ConflictSelection) {\n ResolveDiffConflict(data: { conflict_id: $id, selected_branch: $selection }) {\n ok\n }\n }\n": - TadaDocumentNode<{ ResolveDiffConflict: { ok: boolean | null; } | null; }, { selection?: "BASE_BRANCH" | "DIFF_BRANCH" | null | undefined; id?: string | null | undefined; }, void>; - /** @gql.tada/hash sha256:095b6720088da21e655b57af352cf061 */ - "\n mutation RUN_CHECK($proposedChangeId: String!, $checkType: CheckType) {\n CoreProposedChangeRunCheck(data: { id: $proposedChangeId, check_type: $checkType }) {\n ok\n }\n }\n": - TadaDocumentNode<{ CoreProposedChangeRunCheck: { ok: boolean | null; } | null; }, { checkType?: "ALL" | "ARTIFACT" | "DATA" | "GENERATOR" | "REPOSITORY" | "SCHEMA" | "TEST" | "USER" | null | undefined; proposedChangeId: string; }, void>; - /** @gql.tada/hash sha256:0fa8712af1cb29a94014b5e47f9b1b77 */ - "\n mutation DIFF_UPDATE($branchName: String!, $waitUntilCompletion: Boolean) {\n DiffUpdate(data: { branch: $branchName }, wait_until_completion: $waitUntilCompletion) {\n ok\n }\n }\n": - TadaDocumentNode<{ DiffUpdate: { ok: boolean | null; } | null; }, { waitUntilCompletion?: boolean | null | undefined; branchName: string; }, void>; /** @gql.tada/hash sha256:b7429e10eca0e06e359a0fff473be4c2 */ "\n query GET_INFRAHUB_EVENTS(\n $ids: [String!]\n $hasChildren: Boolean\n $branches: [String!]\n $eventType: [String!]\n $primaryNodeIds: [String!]\n $relatedNodeIds: [String!]\n $parentIds: [String!]\n $accountIds: [String!]\n $level: Int\n $since: DateTime\n $until: DateTime\n $offset: Int\n $limit: Int\n $order: EventSortOrder\n ) {\n InfrahubEvent(\n ids: $ids\n has_children: $hasChildren\n branches: $branches\n event_type: $eventType\n primary_node__ids: $primaryNodeIds\n related_node__ids: $relatedNodeIds\n parent__ids: $parentIds\n account__ids: $accountIds\n level: $level\n since: $since\n until: $until\n offset: $offset\n limit: $limit\n order: $order\n ) {\n edges {\n node {\n id\n event\n branch\n occurred_at\n level\n account_id\n primary_node {\n id\n kind\n }\n related_nodes {\n id\n kind\n }\n has_children\n __typename\n ... on NodeMutatedEvent {\n attributes {\n action\n kind\n name\n value\n value_previous\n }\n relationships {\n action\n name\n peer {\n id\n kind\n }\n }\n payload\n }\n ... on StandardEvent {\n payload\n }\n ... on BranchCreatedEvent {\n payload\n created_branch\n }\n ... on BranchDeletedEvent {\n payload\n deleted_branch\n }\n ... on BranchRebasedEvent {\n payload\n rebased_branch\n }\n ... on BranchMergedEvent {\n source_branch\n }\n ... on GroupEvent {\n ancestors {\n id\n kind\n }\n members {\n id\n kind\n }\n }\n ... on ArtifactEvent {\n checksum\n storage_id\n artifact_definition_id\n checksum_previous\n storage_id_previous\n }\n ... on AccountLoggedInEventType {\n account_name\n account_type\n auth_method\n session_id\n timestamp\n client_ip\n user_agent\n groups\n roles\n identity_source\n }\n ... on AccountLoggedOutEventType {\n account_name\n logout_type\n session_id\n timestamp\n client_ip\n user_agent\n }\n ... on GroupAutoCreatedEventType {\n idp\n protocol\n triggering_user_id\n triggering_user_name\n group_id\n group_name\n source_pattern\n origin_value\n }\n ... on GroupAutoCreateRejectedEventType {\n idp\n protocol\n triggering_user_id\n triggering_user_name\n rejected_claim_value\n }\n ... on GroupAutoCreateCappedEventType {\n idp\n protocol\n triggering_user_id\n triggering_user_name\n cap_value\n dropped_count\n dropped_claims\n }\n }\n }\n }\n }\n": TadaDocumentNode<{ InfrahubEvent: { edges: { node: { __typename: "AccountLoggedInEventType"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; account_name: string; account_type: string; auth_method: string; session_id: string; timestamp: unknown; client_ip: string | null; user_agent: string | null; groups: string[]; roles: string[]; identity_source: string | null; } | { __typename: "AccountLoggedOutEventType"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; account_name: string; logout_type: string; session_id: string; timestamp: unknown; client_ip: string | null; user_agent: string | null; } | { __typename: "ArtifactEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; checksum: string; storage_id: string; artifact_definition_id: string; checksum_previous: string | null; storage_id_previous: string | null; } | { __typename: "BranchCreatedEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; payload: unknown; created_branch: string; } | { __typename: "BranchDeletedEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; payload: unknown; deleted_branch: string; } | { __typename: "BranchMergedEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; source_branch: string; } | { __typename: "BranchRebasedEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; payload: unknown; rebased_branch: string; } | { __typename: "GroupAutoCreateCappedEventType"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; idp: string; protocol: string; triggering_user_id: string; triggering_user_name: string; cap_value: number; dropped_count: number; dropped_claims: string[]; } | { __typename: "GroupAutoCreateRejectedEventType"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; idp: string; protocol: string; triggering_user_id: string; triggering_user_name: string; rejected_claim_value: string; } | { __typename: "GroupAutoCreatedEventType"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; idp: string; protocol: string; triggering_user_id: string; triggering_user_name: string; group_id: string; group_name: string; source_pattern: string; origin_value: string; } | { __typename: "GroupEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; ancestors: { id: string; kind: string; }[]; members: { id: string; kind: string; }[]; } | { __typename: "NodeMutatedEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; attributes: { action: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; kind: string; name: string; value: string | null; value_previous: string | null; }[]; relationships: { action: "ADDED" | "REMOVED" | "UNCHANGED" | "UPDATED"; name: string; peer: { id: string; kind: string; }; }[]; payload: unknown; } | { __typename: "ProposedChangeApprovalsRevokedEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; } | { __typename: "ProposedChangeMergedEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; } | { __typename: "ProposedChangeReviewEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; } | { __typename: "ProposedChangeReviewRequestedEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; } | { __typename: "ProposedChangeReviewRevokedEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; } | { __typename: "ProposedChangeThreadEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; } | { __typename: "StandardEvent"; id: string; event: string; branch: string | null; occurred_at: unknown; level: number; account_id: string | null; primary_node: { id: string; kind: string; } | null; related_nodes: { id: string; kind: string; }[]; has_children: boolean; payload: unknown; } | null; }[]; }; }, { order?: "ASC" | "DESC" | null | undefined; limit?: number | null | undefined; offset?: number | null | undefined; until?: unknown; since?: unknown; level?: number | null | undefined; accountIds?: string[] | null | undefined; parentIds?: string[] | null | undefined; relatedNodeIds?: string[] | null | undefined; primaryNodeIds?: string[] | null | undefined; eventType?: string[] | null | undefined; branches?: string[] | null | undefined; hasChildren?: boolean | null | undefined; ids?: string[] | null | undefined; }, void>; - /** @gql.tada/hash sha256:70970c6312c8158a2a072c54a19e3009 */ - "\n mutation CoreGeneratorDefinitionRun($generatorId: String!, $waitUntilCompletion: Boolean, $targetNodeIds: [String!]) {\n CoreGeneratorDefinitionRun(\n wait_until_completion: $waitUntilCompletion\n data: { id: $generatorId, nodes: $targetNodeIds }\n ) {\n task {\n id\n }\n }\n }\n": - TadaDocumentNode<{ CoreGeneratorDefinitionRun: { task: { id: string | null; } | null; } | null; }, { targetNodeIds?: string[] | null | undefined; waitUntilCompletion?: boolean | null | undefined; generatorId: string; }, void>; - /** @gql.tada/hash sha256:7ef472d04eaa9bbfc5d3e70a86ff9f5d */ - "\n query getNextIPAddressAvailable($parentPrefixId: String!) {\n InfrahubIPAddressGetNextAvailable(prefix_id: $parentPrefixId) {\n address\n }\n }\n": - TadaDocumentNode<{ InfrahubIPAddressGetNextAvailable: { address: string; }; }, { parentPrefixId: string; }, void>; - /** @gql.tada/hash sha256:b1e9112be17bf990747e22ca64b6f2f8 */ - "\n query getNextIPPrefixAvailable($parentPrefixId: String!) {\n InfrahubIPPrefixGetNextAvailable(prefix_id: $parentPrefixId) {\n prefix\n }\n }\n": - TadaDocumentNode<{ InfrahubIPPrefixGetNextAvailable: { prefix: string; }; }, { parentPrefixId: string; }, void>; /** @gql.tada/hash sha256:958fa9e49e2cfa1fdbc385a9daa5417a */ "\n query GET_IPAM_TREE_NODES(\n $isTopLevel: Boolean\n $parentIds: [ID!]\n $search: String\n $ipNamespaceIds: [ID!]\n $limit: Int\n $offset: Int\n ) {\n BuiltinIPPrefix(\n is_top_level__value: $isTopLevel\n parent__ids: $parentIds\n any__value: $search\n partial_match: true\n ip_namespace__ids: $ipNamespaceIds\n offset: $offset\n limit: $limit\n ) {\n edges {\n node {\n id\n display_label\n descendants {\n count\n }\n }\n }\n }\n }\n": TadaDocumentNode<{ BuiltinIPPrefix: { edges: { node: { __typename?: "InternalIPPrefixAvailable" | undefined; id: string | null; display_label: string | null; descendants: { count: number; }; } | null; }[]; }; }, { offset?: number | null | undefined; limit?: number | null | undefined; ipNamespaceIds?: string[] | null | undefined; search?: string | null | undefined; parentIds?: string[] | null | undefined; isTopLevel?: boolean | null | undefined; }, void>; - /** @gql.tada/hash sha256:7b32b4a094e7e7df8dfa91265eb79121 */ - "\n query Search($search: String!, $caseSensitive: Boolean) {\n InfrahubSearchAnywhere(q: $search, limit: 4, partial_match: true, case_sensitive: $caseSensitive) {\n count\n edges {\n node {\n id\n kind\n }\n }\n parent_prefixes {\n node {\n id\n kind\n }\n }\n }\n }\n": - TadaDocumentNode<{ InfrahubSearchAnywhere: { count: number; edges: { node: { id: string; kind: string; }; }[]; parent_prefixes: { node: { id: string; kind: string; }; }[] | null; }; }, { caseSensitive?: boolean | null | undefined; search: string; }, void>; - /** @gql.tada/hash sha256:6018794c1e4b364f7d0aff881a2b29cd */ - "\n mutation CONVERT_OBJECT_MUTATION($nodeId: String!, $targetKind: String!, $fieldsMapping: GenericScalar!) {\n ConvertObjectType(\n data: { node_id: $nodeId, target_kind: $targetKind, fields_mapping: $fieldsMapping }\n ) {\n node\n }\n }\n": - TadaDocumentNode<{ ConvertObjectType: { node: unknown; } | null; }, { fieldsMapping: unknown; targetKind: string; nodeId: string; }, void>; - /** @gql.tada/hash sha256:4008eb900f182ba09d71114070bc9ffc */ - "\n query GET_FIELDS_MAPPING($sourceKind: String!, $targetKind: String!) {\n FieldsMappingTypeConversion(source_kind: $sourceKind, target_kind: $targetKind) {\n mapping\n }\n }\n": - TadaDocumentNode<{ FieldsMappingTypeConversion: { mapping: unknown; }; }, { targetKind: string; sourceKind: string; }, void>; - /** @gql.tada/hash sha256:001b22bf46a668a45d1048b7800eec8c */ - "\n mutation RelationshipAdd(\n $objectId: String!\n $relationshipName: String!\n $relationshipIds: [RelatedNodeInput]\n ) {\n RelationshipAdd(data: { id: $objectId, name: $relationshipName, nodes: $relationshipIds }) {\n ok\n }\n }\n": - TadaDocumentNode<{ RelationshipAdd: { ok: boolean | null; } | null; }, { relationshipIds?: ({ kind?: string | null | undefined; id?: string | null | undefined; hfid?: (string | null)[] | null | undefined; from_pool?: { identifier?: string | null | undefined; id: string; data?: unknown; } | null | undefined; _relation__source?: string | null | undefined; _relation__owner?: string | null | undefined; _relation__is_protected?: boolean | null | undefined; } | null)[] | null | undefined; relationshipName: string; objectId: string; }, void>; - /** @gql.tada/hash sha256:61fdc46dd2875e6bebdd9363ab4974fb */ - "\n mutation RelationshipRemove(\n $objectId: String!\n $relationshipName: String!\n $relationshipIds: [RelatedNodeInput]\n ) {\n RelationshipRemove(data: { id: $objectId, name: $relationshipName, nodes: $relationshipIds }) {\n ok\n }\n }\n": - TadaDocumentNode<{ RelationshipRemove: { ok: boolean | null; } | null; }, { relationshipIds?: ({ kind?: string | null | undefined; id?: string | null | undefined; hfid?: (string | null)[] | null | undefined; from_pool?: { identifier?: string | null | undefined; id: string; data?: unknown; } | null | undefined; _relation__source?: string | null | undefined; _relation__owner?: string | null | undefined; _relation__is_protected?: boolean | null | undefined; } | null)[] | null | undefined; relationshipName: string; objectId: string; }, void>; - /** @gql.tada/hash sha256:9720c2e718e30cc040b55fe4767f88c3 */ - "\n query InfrahubGlobalPermissions {\n InfrahubPermissions {\n global_permissions {\n edges {\n node {\n action\n decision\n }\n }\n }\n }\n }\n": - TadaDocumentNode<{ InfrahubPermissions: { global_permissions: { edges: { node: { action: string; decision: string; }; }[]; } | null; }; }, {}, void>; - /** @gql.tada/hash sha256:4f04b6c7e8ff5a931bd5a7ee05d0efe2 */ - "\n query InfrahubEffectivePreferences {\n InfrahubEffectivePreferences {\n date_format {\n value\n source\n }\n timezone {\n value\n source\n }\n }\n }\n": - TadaDocumentNode<{ InfrahubEffectivePreferences: { date_format: { value: "EU_DATETIME" | "ISO_8601" | "ISO_DATETIME" | "ISO_DATETIME_SECONDS" | "US_12H" | null; source: "USER" | "DEFAULT" | "GLOBAL"; }; timezone: { value: string | null; source: "USER" | "DEFAULT" | "GLOBAL"; }; }; }, {}, void>; - /** @gql.tada/hash sha256:210ac3ff8dfa4fee3c5d92a266b833fb */ - "\n query InfrahubGlobalPreferences {\n InfrahubGlobalPreferences {\n date_format\n timezone\n }\n }\n": - TadaDocumentNode<{ InfrahubGlobalPreferences: { date_format: "EU_DATETIME" | "ISO_8601" | "ISO_DATETIME" | "ISO_DATETIME_SECONDS" | "US_12H" | null; timezone: string | null; }; }, {}, void>; - /** @gql.tada/hash sha256:441b17c7ad9e6f4801ffe65729249e69 */ - "\n mutation UpdateGlobalPreference($dateFormat: DateFormat, $timezone: String) {\n InfrahubSetPreferences(scope: GLOBAL, date_format: $dateFormat, timezone: $timezone) {\n ok\n date_format\n timezone\n }\n }\n": - TadaDocumentNode<{ InfrahubSetPreferences: { ok: boolean | null; date_format: "EU_DATETIME" | "ISO_8601" | "ISO_DATETIME" | "ISO_DATETIME_SECONDS" | "US_12H" | null; timezone: string | null; } | null; }, { timezone?: string | null | undefined; dateFormat?: "EU_DATETIME" | "ISO_8601" | "ISO_DATETIME" | "ISO_DATETIME_SECONDS" | "US_12H" | null | undefined; }, void>; - /** @gql.tada/hash sha256:30b73a7750503476ab427bcb611d60da */ - "\n mutation UpsertUserPreference($dateFormat: DateFormat, $timezone: String) {\n InfrahubSetPreferences(scope: USER, date_format: $dateFormat, timezone: $timezone) {\n ok\n date_format\n timezone\n }\n }\n": - TadaDocumentNode<{ InfrahubSetPreferences: { ok: boolean | null; date_format: "EU_DATETIME" | "ISO_8601" | "ISO_DATETIME" | "ISO_DATETIME_SECONDS" | "US_12H" | null; timezone: string | null; } | null; }, { timezone?: string | null | undefined; dateFormat?: "EU_DATETIME" | "ISO_8601" | "ISO_DATETIME" | "ISO_DATETIME_SECONDS" | "US_12H" | null | undefined; }, void>; - /** @gql.tada/hash sha256:ef221c61c8e9e0554096b05d16c2257e */ - "\n mutation CoreProposedChangeCreate(\n $name: String!\n $isDraft: Boolean\n $description: String\n $source_branch: String!\n $destination_branch: String!\n $reviewers: [RelatedNodeInput!]\n ) {\n CoreProposedChangeCreate(\n data: {\n name: { value: $name }\n is_draft: { value: $isDraft }\n description: { value: $description }\n source_branch: { value: $source_branch }\n destination_branch: { value: $destination_branch }\n reviewers: $reviewers\n }\n ) {\n object {\n id\n display_label\n }\n ok\n }\n }\n": - TadaDocumentNode<{ CoreProposedChangeCreate: { object: { id: string; display_label: string | null; } | null; ok: boolean | null; } | null; }, { reviewers?: { kind?: string | null | undefined; id?: string | null | undefined; hfid?: (string | null)[] | null | undefined; from_pool?: { identifier?: string | null | undefined; id: string; data?: unknown; } | null | undefined; _relation__source?: string | null | undefined; _relation__owner?: string | null | undefined; _relation__is_protected?: boolean | null | undefined; }[] | null | undefined; destination_branch: string; source_branch: string; description?: string | null | undefined; isDraft?: boolean | null | undefined; name: string; }, void>; - /** @gql.tada/hash sha256:4698c5dc304a3d152c474e83f73abf5b */ - "\n query GET_PROPOSED_CHANGE_DETAILS($proposedChangeId: ID) {\n CoreProposedChange(ids: [$proposedChangeId]) {\n count\n edges {\n node_metadata {\n created_at\n created_by {\n id\n hfid\n display_label\n __typename\n }\n updated_at\n updated_by {\n id\n hfid\n display_label\n __typename\n }\n }\n node {\n id\n display_label\n __typename\n name {\n value\n }\n description {\n value\n updated_at\n }\n source_branch {\n value\n }\n destination_branch {\n value\n }\n state {\n value\n }\n is_draft {\n value\n }\n approved_by {\n edges {\n node {\n id\n display_label\n }\n }\n }\n rejected_by {\n edges {\n node {\n id\n display_label\n }\n }\n }\n reviewers {\n edges {\n node {\n id\n display_label\n }\n }\n }\n comments {\n count\n }\n }\n }\n }\n }\n": - TadaDocumentNode<{ CoreProposedChange: { count: number; edges: { node_metadata: { created_at: unknown; created_by: { __typename: "CoreAccount"; id: string | null; hfid: string[] | null; display_label: string | null; } | null; updated_at: unknown; updated_by: { __typename: "CoreAccount"; id: string | null; hfid: string[] | null; display_label: string | null; } | null; } | null; node: { id: string; display_label: string | null; __typename: "CoreProposedChange"; name: { value: string | null; } | null; description: { value: string | null; updated_at: unknown; } | null; source_branch: { value: string | null; } | null; destination_branch: { value: string | null; } | null; state: { value: string | null; } | null; is_draft: { value: boolean | null; } | null; approved_by: { edges: { node: { __typename?: "CoreAccount" | undefined; id: string | null; display_label: string | null; } | null; }[] | null; }; rejected_by: { edges: { node: { __typename?: "CoreAccount" | undefined; id: string | null; display_label: string | null; } | null; }[] | null; }; reviewers: { edges: { node: { __typename?: "CoreAccount" | undefined; id: string | null; display_label: string | null; } | null; }[] | null; }; comments: { count: number; }; } | null; }[]; }; }, { proposedChangeId?: string | null | undefined; }, void>; - /** @gql.tada/hash sha256:cd8c191aa303a91dc1ba9562d1655a04 */ - "\n query GetCoreThread($ids: [ID]) {\n CoreThread(ids: $ids) {\n edges {\n node {\n id\n display_label\n label {\n value\n }\n resolved {\n value\n }\n comments {\n count\n edges {\n node_metadata {\n created_at\n created_by {\n display_label\n }\n }\n node {\n id\n display_label\n text {\n value\n }\n }\n }\n }\n ... on CoreArtifactThread {\n storage_id {\n value\n }\n artifact_id {\n value\n }\n line_number {\n value\n }\n }\n ... on CoreObjectThread {\n object_path {\n value\n }\n }\n ... on CoreFileThread {\n file {\n value\n }\n line_number {\n value\n }\n commit {\n value\n }\n }\n }\n }\n }\n }\n": - TadaDocumentNode<{ CoreThread: { edges: { node: { __typename?: "CoreArtifactThread" | undefined; id: string | null; display_label: string | null; label: { value: string | null; } | null; resolved: { value: boolean | null; } | null; comments: { count: number; edges: { node_metadata: { created_at: unknown; created_by: { __typename?: "CoreAccount" | undefined; display_label: string | null; } | null; } | null; node: { id: string; display_label: string | null; text: { value: string | null; } | null; } | null; }[]; }; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; line_number: { value: unknown; } | null; } | { __typename?: "CoreChangeThread" | undefined; id: string | null; display_label: string | null; label: { value: string | null; } | null; resolved: { value: boolean | null; } | null; comments: { count: number; edges: { node_metadata: { created_at: unknown; created_by: { __typename?: "CoreAccount" | undefined; display_label: string | null; } | null; } | null; node: { id: string; display_label: string | null; text: { value: string | null; } | null; } | null; }[]; }; } | { __typename?: "CoreFileThread" | undefined; id: string | null; display_label: string | null; label: { value: string | null; } | null; resolved: { value: boolean | null; } | null; comments: { count: number; edges: { node_metadata: { created_at: unknown; created_by: { __typename?: "CoreAccount" | undefined; display_label: string | null; } | null; } | null; node: { id: string; display_label: string | null; text: { value: string | null; } | null; } | null; }[]; }; file: { value: string | null; } | null; line_number: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename?: "CoreObjectThread" | undefined; id: string | null; display_label: string | null; label: { value: string | null; } | null; resolved: { value: boolean | null; } | null; comments: { count: number; edges: { node_metadata: { created_at: unknown; created_by: { __typename?: "CoreAccount" | undefined; display_label: string | null; } | null; } | null; node: { id: string; display_label: string | null; text: { value: string | null; } | null; } | null; }[]; }; object_path: { value: string | null; } | null; } | null; }[]; }; }, { ids?: (string | null)[] | null | undefined; }, void>; - /** @gql.tada/hash sha256:1d92ec936fb68dbb4acb0e99206ad4ac */ - "\n query actions($proposedChangeId: String!) {\n CoreProposedChangeAvailableActions(proposed_change_id: $proposedChangeId) {\n count\n edges {\n node {\n action\n available\n unavailability_reason\n }\n }\n }\n }\n": - TadaDocumentNode<{ CoreProposedChangeAvailableActions: { count: number; edges: { node: { action: string; available: boolean; unavailability_reason: string | null; }; }[]; }; }, { proposedChangeId: string; }, void>; - /** @gql.tada/hash sha256:388f1a037f738e522ecf4f174d1de58f */ - "\n mutation ProposedChangeReview($proposedChangeId: String!, $decision: ProposedChangeApprovalDecision!) {\n CoreProposedChangeReview(data: { id: $proposedChangeId, decision: $decision }) {\n ok\n }\n }\n": - TadaDocumentNode<{ CoreProposedChangeReview: { ok: boolean | null; } | null; }, { decision: "APPROVE" | "CANCEL_APPROVE" | "CANCEL_REJECT" | "REJECT"; proposedChangeId: string; }, void>; - /** @gql.tada/hash sha256:20c155a5b455af4e2f3131d03b97a091 */ - "\n mutation CHECK_REPOSITORY_CONNECTIVITY($repositoryId: String!) {\n InfrahubRepositoryConnectivity(data: { id: $repositoryId }) {\n ok\n message\n }\n }\n": - TadaDocumentNode<{ InfrahubRepositoryConnectivity: { ok: boolean; message: string; } | null; }, { repositoryId: string; }, void>; - /** @gql.tada/hash sha256:3a916ca568f010b38df3f021c11a0e60 */ - "\n query REPOSITORY_GROUP($nodeIds: [ID]) {\n CoreRepositoryGroup(repository__ids: $nodeIds) {\n edges {\n node {\n id\n }\n }\n }\n }\n": - TadaDocumentNode<{ CoreRepositoryGroup: { edges: { node: { id: string; } | null; }[]; }; }, { nodeIds?: (string | null)[] | null | undefined; }, void>; - /** @gql.tada/hash sha256:0a7e88d29f3b03a48ef8de5fdb035d0c */ - "\n mutation IMPORT_CURRENT_COMMIT($repositoryId: String!) {\n InfrahubRepositoryProcess(data: { id: $repositoryId }) {\n ok\n task {\n id\n }\n }\n }\n": - TadaDocumentNode<{ InfrahubRepositoryProcess: { ok: boolean | null; task: { id: string | null; } | null; } | null; }, { repositoryId: string; }, void>; - /** @gql.tada/hash sha256:2a7db54f21a3f1d83d3ad20967aa6af4 */ - "\n mutation REIMPORT_LAST_COMMIT($repositoryId: String!) {\n InfrahubReadOnlyRepositoryImportLastCommit(data: { id: $repositoryId }) {\n ok\n task {\n id\n }\n }\n }\n": - TadaDocumentNode<{ InfrahubReadOnlyRepositoryImportLastCommit: { ok: boolean | null; task: { id: string | null; } | null; } | null; }, { repositoryId: string; }, void>; /** @gql.tada/hash sha256:f41a2c3333b1b4f7ad0e48a3a7b67232 */ "\n query GET_NUMBER_POOLS($objectKinds: [String]) {\n CoreNumberPool(node__values: $objectKinds) {\n edges {\n node {\n id\n hfid\n display_label\n node {\n id\n value\n }\n node_attribute {\n id\n value\n }\n }\n }\n }\n }\n": TadaDocumentNode<{ CoreNumberPool: { edges: { node: { id: string; hfid: string[] | null; display_label: string | null; node: { id: string | null; value: string | null; } | null; node_attribute: { id: string | null; value: string | null; } | null; } | null; }[]; }; }, { objectKinds?: (string | null)[] | null | undefined; }, void>; + /** @gql.tada/hash sha256:d15c41ad76d2e5a73a95f8515bbaddfd */ + "\n query GET_OBJECT_THREADS($changeIds: [ID!], $objectPath: String) {\n CoreObjectThread(change__ids: $changeIds, object_path__value: $objectPath) {\n count\n edges {\n node {\n __typename\n id\n comments {\n count\n }\n }\n }\n permissions {\n edges {\n node {\n kind\n view\n create\n update\n delete\n }\n }\n }\n }\n }\n": + TadaDocumentNode<{ CoreObjectThread: { count: number; edges: { node: { __typename: "CoreObjectThread"; id: string; comments: { count: number; }; } | null; }[]; permissions: { edges: { node: { kind: string; view: "ALLOW" | "ALLOW_DEFAULT" | "ALLOW_OTHER" | "DENY"; create: "ALLOW" | "ALLOW_DEFAULT" | "ALLOW_OTHER" | "DENY"; update: "ALLOW" | "ALLOW_DEFAULT" | "ALLOW_OTHER" | "DENY"; delete: "ALLOW" | "ALLOW_DEFAULT" | "ALLOW_OTHER" | "DENY"; }; }[]; }; }; }, { objectPath?: string | null | undefined; changeIds?: string[] | null | undefined; }, void>; + /** @gql.tada/hash sha256:c7223a03d4f81fd84c2741e232b1e4e9 */ + "\n query GET_OBJECT_THREAD_COMMENTS($changeIds: [ID!], $objectPath: String) {\n CoreObjectThread(change__ids: $changeIds, object_path__value: $objectPath) {\n count\n edges {\n node {\n __typename\n id\n display_label\n resolved {\n value\n }\n comments {\n count\n edges {\n node_metadata {\n created_at\n created_by {\n display_label\n }\n }\n node {\n id\n display_label\n text {\n value\n }\n }\n }\n }\n }\n }\n }\n }\n": + TadaDocumentNode<{ CoreObjectThread: { count: number; edges: { node: { __typename: "CoreObjectThread"; id: string; display_label: string | null; resolved: { value: boolean | null; } | null; comments: { count: number; edges: { node_metadata: { created_at: unknown; created_by: { __typename?: "CoreAccount" | undefined; display_label: string | null; } | null; } | null; node: { id: string; display_label: string | null; text: { value: string | null; } | null; } | null; }[]; }; } | null; }[]; }; }, { objectPath?: string | null | undefined; changeIds?: string[] | null | undefined; }, void>; /** @gql.tada/hash sha256:b97639254161079762e961b350de585a */ "\n query GET_POOL_UTILIZATION($poolId: String!) {\n InfrahubResourcePoolUtilization(pool_id: $poolId) {\n edges {\n node {\n id\n display_label\n kind\n weight\n utilization\n utilization_branches\n utilization_default_branch\n }\n }\n count\n utilization\n utilization_branches\n utilization_default_branch\n }\n }\n": TadaDocumentNode<{ InfrahubResourcePoolUtilization: { edges: { node: { id: string; display_label: string; kind: string; weight: unknown; utilization: number; utilization_branches: number; utilization_default_branch: number; }; }[]; count: unknown; utilization: number; utilization_branches: number; utilization_default_branch: number; }; }, { poolId: string; }, void>; + /** @gql.tada/hash sha256:4698c5dc304a3d152c474e83f73abf5b */ + "\n query GET_PROPOSED_CHANGE_DETAILS($proposedChangeId: ID) {\n CoreProposedChange(ids: [$proposedChangeId]) {\n count\n edges {\n node_metadata {\n created_at\n created_by {\n id\n hfid\n display_label\n __typename\n }\n updated_at\n updated_by {\n id\n hfid\n display_label\n __typename\n }\n }\n node {\n id\n display_label\n __typename\n name {\n value\n }\n description {\n value\n updated_at\n }\n source_branch {\n value\n }\n destination_branch {\n value\n }\n state {\n value\n }\n is_draft {\n value\n }\n approved_by {\n edges {\n node {\n id\n display_label\n }\n }\n }\n rejected_by {\n edges {\n node {\n id\n display_label\n }\n }\n }\n reviewers {\n edges {\n node {\n id\n display_label\n }\n }\n }\n comments {\n count\n }\n }\n }\n }\n }\n": + TadaDocumentNode<{ CoreProposedChange: { count: number; edges: { node_metadata: { created_at: unknown; created_by: { __typename: "CoreAccount"; id: string | null; hfid: string[] | null; display_label: string | null; } | null; updated_at: unknown; updated_by: { __typename: "CoreAccount"; id: string | null; hfid: string[] | null; display_label: string | null; } | null; } | null; node: { id: string; display_label: string | null; __typename: "CoreProposedChange"; name: { value: string | null; } | null; description: { value: string | null; updated_at: unknown; } | null; source_branch: { value: string | null; } | null; destination_branch: { value: string | null; } | null; state: { value: string | null; } | null; is_draft: { value: boolean | null; } | null; approved_by: { edges: { node: { __typename?: "CoreAccount" | undefined; id: string | null; display_label: string | null; } | null; }[] | null; }; rejected_by: { edges: { node: { __typename?: "CoreAccount" | undefined; id: string | null; display_label: string | null; } | null; }[] | null; }; reviewers: { edges: { node: { __typename?: "CoreAccount" | undefined; id: string | null; display_label: string | null; } | null; }[] | null; }; comments: { count: number; }; } | null; }[]; }; }, { proposedChangeId?: string | null | undefined; }, void>; /** @gql.tada/hash sha256:1af629cdd975eae08dfa05c924114d4c */ "\n query GET_RESOURCE_POOL_ALLOCATED(\n $poolId: String!\n $resourceId: String!\n $limit: Int!\n $offset: Int!\n ) {\n InfrahubResourcePoolAllocated(\n pool_id: $poolId\n resource_id: $resourceId\n limit: $limit\n offset: $offset\n ) {\n count\n edges {\n node {\n id\n display_label\n kind\n branch\n identifier\n }\n }\n }\n }\n": TadaDocumentNode<{ InfrahubResourcePoolAllocated: { count: unknown; edges: { node: { id: string; display_label: string; kind: string; branch: string; identifier: string | null; }; }[]; }; }, { offset: number; limit: number; resourceId: string; poolId: string; }, void>; - /** @gql.tada/hash sha256:ff263930e8ff625ad84e82ffbd722b6f */ - "\n mutation DropdownAdd(\n $kind: String!\n $attribute: String!\n $dropdown: String!\n $label: String\n $color: String\n $description: String\n ) {\n SchemaDropdownAdd(\n data: {\n kind: $kind\n attribute: $attribute\n dropdown: $dropdown\n label: $label\n color: $color\n description: $description\n }\n ) {\n ok\n object {\n value\n label\n color\n description\n }\n }\n }\n": - TadaDocumentNode<{ SchemaDropdownAdd: { ok: boolean | null; object: { value: string | null; label: string | null; color: string | null; description: string | null; } | null; } | null; }, { description?: string | null | undefined; color?: string | null | undefined; label?: string | null | undefined; dropdown: string; attribute: string; kind: string; }, void>; - /** @gql.tada/hash sha256:589321a1d1f294da75a53e6e25b1fef7 */ - "\n mutation EnumAdd($kind: String!, $attribute: String!, $enum: String!) {\n SchemaEnumAdd(data: { kind: $kind, attribute: $attribute, enum: $enum }) {\n ok\n }\n }\n": - TadaDocumentNode<{ SchemaEnumAdd: { ok: boolean | null; } | null; }, { enum: string; attribute: string; kind: string; }, void>; - /** @gql.tada/hash sha256:bf0f1a370eec3ac0727f9c12ceb0a57d */ - "\n mutation DropdownDelete($kind: String!, $attribute: String!, $dropdown: String!) {\n SchemaDropdownRemove(data: { kind: $kind, attribute: $attribute, dropdown: $dropdown }) {\n ok\n }\n }\n": - TadaDocumentNode<{ SchemaDropdownRemove: { ok: boolean | null; } | null; }, { dropdown: string; attribute: string; kind: string; }, void>; - /** @gql.tada/hash sha256:608790eeb11144570653ec71ddab2fd1 */ - "\n mutation EnumDelete($kind: String!, $attribute: String!, $enum: String!) {\n SchemaEnumRemove(data: { kind: $kind, attribute: $attribute, enum: $enum }) {\n ok\n }\n }\n": - TadaDocumentNode<{ SchemaEnumRemove: { ok: boolean | null; } | null; }, { enum: string; attribute: string; kind: string; }, void>; - /** @gql.tada/hash sha256:f6dd9885b2d0fae950f58a237ce5e029 */ - "\n mutation CANCEL_TASK($id: String!) {\n InfrahubTaskCancel(data: { id: $id }) {\n ok\n task {\n id\n }\n }\n }\n": - TadaDocumentNode<{ InfrahubTaskCancel: { ok: boolean | null; task: { id: string | null; } | null; } | null; }, { id: string; }, void>; - /** @gql.tada/hash sha256:54f51a9f2ef22b26c5e5eed29e8689ec */ - "\n query TASK_DETAILS_CHECK(\n $ids: [String]\n $branch: String\n $workflow: [String]\n $state: [StateType]\n $relatedNodes: [String]\n ) {\n InfrahubTask(\n ids: $ids\n branch: $branch\n workflow: $workflow\n state: $state\n related_node__ids: $relatedNodes\n ) {\n count\n }\n }\n": - TadaDocumentNode<{ InfrahubTask: { count: number; }; }, { relatedNodes?: (string | null)[] | null | undefined; state?: ("CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null)[] | null | undefined; workflow?: (string | null)[] | null | undefined; branch?: string | null | undefined; ids?: (string | null)[] | null | undefined; }, void>; - /** @gql.tada/hash sha256:32bf68cc18e717ae2ee1188ae013ae2e */ - "\n query TASKS_BRANCH_STATUS_COUNT($branch: String!) {\n InfrahubTaskBranchStatus(branch: $branch) {\n count\n }\n }\n": - TadaDocumentNode<{ InfrahubTaskBranchStatus: { count: number; }; }, { branch: string; }, void>; - /** @gql.tada/hash sha256:6752d3c0f1606d094ce17fafff97788d */ - "\n query TASK_COUNT(\n $search: String\n $branchName: String\n $state: [StateType]\n $relatedNodeIds: [String]\n ) {\n InfrahubTask(\n q: $search\n branch: $branchName\n state: $state\n related_node__ids: $relatedNodeIds\n ) {\n count\n }\n }\n": - TadaDocumentNode<{ InfrahubTask: { count: number; }; }, { relatedNodeIds?: (string | null)[] | null | undefined; state?: ("CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null)[] | null | undefined; branchName?: string | null | undefined; search?: string | null | undefined; }, void>; + /** @gql.tada/hash sha256:eedae705c34ae3f704dcfdf2ad791ecb */ + "\n query GET_TASKS_HOMEPAGE($limit: Int, $branchName: String!, $states: [StateType]) {\n InfrahubTask(limit: $limit, branch: $branchName, state: $states) {\n count\n edges {\n node {\n id\n branch\n title\n updated_at\n state\n related_nodes {\n id\n kind\n }\n }\n }\n }\n }\n": + TadaDocumentNode<{ InfrahubTask: { count: number; edges: { node: { __typename?: "TaskNode" | undefined; id: string; branch: string | null; title: string; updated_at: string; state: "CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null; related_nodes: ({ id: string; kind: string; } | null)[] | null; } | { __typename?: "WebhookDeliveryTask" | undefined; id: string; branch: string | null; title: string; updated_at: string; state: "CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null; related_nodes: ({ id: string; kind: string; } | null)[] | null; } | null; }[]; }; }, { states?: ("CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null)[] | null | undefined; branchName: string; limit?: number | null | undefined; }, void>; /** @gql.tada/hash sha256:d7ff6122381b02cd74b4ac358d6fb768 */ "\n query GET_TASK_DETAILS(\n $ids: [String]\n $branch: String\n $workflow: [String]\n $relatedNodes: [String]\n ) {\n InfrahubTask(\n ids: $ids\n branch: $branch\n workflow: $workflow\n related_node__ids: $relatedNodes\n ) {\n count\n edges {\n node {\n id\n title\n branch\n related_node\n related_nodes {\n id\n kind\n }\n state\n progress\n created_at\n updated_at\n logs {\n edges {\n node {\n id\n message\n severity\n timestamp\n }\n }\n }\n }\n }\n }\n }\n": TadaDocumentNode<{ InfrahubTask: { count: number; edges: { node: { __typename?: "TaskNode" | undefined; id: string; title: string; branch: string | null; related_node: string | null; related_nodes: ({ id: string; kind: string; } | null)[] | null; state: "CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null; progress: number | null; created_at: string; updated_at: string; logs: { edges: { node: { id: string | null; message: string; severity: string; timestamp: string; } | null; }[]; } | null; } | { __typename?: "WebhookDeliveryTask" | undefined; id: string; title: string; branch: string | null; related_node: string | null; related_nodes: ({ id: string; kind: string; } | null)[] | null; state: "CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null; progress: number | null; created_at: string; updated_at: string; logs: { edges: { node: { id: string | null; message: string; severity: string; timestamp: string; } | null; }[]; } | null; } | null; }[]; }; }, { relatedNodes?: (string | null)[] | null | undefined; workflow?: (string | null)[] | null | undefined; branch?: string | null | undefined; ids?: (string | null)[] | null | undefined; }, void>; @@ -201,23 +165,59 @@ declare module 'gql.tada' { /** @gql.tada/hash sha256:ed8311aa64bfdf3a4a08bbf9c093d0dc */ "\n query GET_TASK_LIST(\n $offset: Int\n $limit: Int\n $search: String\n $branchName: String\n $state: [StateType]\n $relatedNodeIds: [String]\n ) {\n InfrahubTask(\n offset: $offset\n limit: $limit\n q: $search\n branch: $branchName\n state: $state\n related_node__ids: $relatedNodeIds\n ) {\n count\n edges {\n node {\n id\n branch\n related_nodes {\n id\n kind\n }\n title\n updated_at\n state\n progress\n workflow\n }\n }\n }\n }\n": TadaDocumentNode<{ InfrahubTask: { count: number; edges: { node: { __typename?: "TaskNode" | undefined; id: string; branch: string | null; related_nodes: ({ id: string; kind: string; } | null)[] | null; title: string; updated_at: string; state: "CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null; progress: number | null; workflow: string | null; } | { __typename?: "WebhookDeliveryTask" | undefined; id: string; branch: string | null; related_nodes: ({ id: string; kind: string; } | null)[] | null; title: string; updated_at: string; state: "CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null; progress: number | null; workflow: string | null; } | null; }[]; }; }, { relatedNodeIds?: (string | null)[] | null | undefined; state?: ("CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null)[] | null | undefined; branchName?: string | null | undefined; search?: string | null | undefined; limit?: number | null | undefined; offset?: number | null | undefined; }, void>; - /** @gql.tada/hash sha256:eedae705c34ae3f704dcfdf2ad791ecb */ - "\n query GET_TASKS_HOMEPAGE($limit: Int, $branchName: String!, $states: [StateType]) {\n InfrahubTask(limit: $limit, branch: $branchName, state: $states) {\n count\n edges {\n node {\n id\n branch\n title\n updated_at\n state\n related_nodes {\n id\n kind\n }\n }\n }\n }\n }\n": - TadaDocumentNode<{ InfrahubTask: { count: number; edges: { node: { __typename?: "TaskNode" | undefined; id: string; branch: string | null; title: string; updated_at: string; state: "CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null; related_nodes: ({ id: string; kind: string; } | null)[] | null; } | { __typename?: "WebhookDeliveryTask" | undefined; id: string; branch: string | null; title: string; updated_at: string; state: "CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null; related_nodes: ({ id: string; kind: string; } | null)[] | null; } | null; }[]; }; }, { states?: ("CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null)[] | null | undefined; branchName: string; limit?: number | null | undefined; }, void>; - /** @gql.tada/hash sha256:6de3d7c14c380a1d17b5cb1cbaa26212 */ - "\n mutation RETRY_TASK($id: String!) {\n InfrahubTaskRetry(data: { id: $id }) {\n ok\n task {\n id\n }\n }\n }\n": - TadaDocumentNode<{ InfrahubTaskRetry: { ok: boolean | null; task: { id: string | null; } | null; } | null; }, { id: string; }, void>; - /** @gql.tada/hash sha256:d90877c30f537101da2fc543d65f768d */ - "\n mutation InfrahubAccountTokenCreate($tokenName: String!, $tokenExpirationDate: String) {\n InfrahubAccountTokenCreate(data: { name: $tokenName, expiration: $tokenExpirationDate }) {\n object {\n id\n token {\n value\n }\n }\n ok\n }\n }\n": - TadaDocumentNode<{ InfrahubAccountTokenCreate: { object: { id: string; token: { value: string; } | null; } | null; ok: boolean | null; } | null; }, { tokenExpirationDate?: string | null | undefined; tokenName: string; }, void>; + /** @gql.tada/hash sha256:3610b040f6f6b6533b88031726974336 */ + "\n query GET_VALIDATOR_DETAILS($ids: [ID!], $checksOffset: Int, $checksLimit: Int) {\n CoreValidator(ids: $ids) {\n edges {\n node {\n id\n display_label\n conclusion {\n value\n }\n started_at {\n value\n }\n completed_at {\n value\n }\n state {\n value\n }\n ... on CoreRepositoryValidator {\n repository {\n node {\n display_label\n }\n }\n }\n ... on CoreArtifactValidator {\n definition {\n node {\n display_label\n name {\n value\n }\n description {\n value\n }\n }\n }\n }\n checks(offset: $checksOffset, limit: $checksLimit) {\n count\n edges {\n node {\n id\n display_label\n name {\n value\n }\n message {\n value\n }\n severity {\n value\n }\n conclusion {\n value\n }\n kind {\n value\n }\n origin {\n value\n }\n created_at {\n value\n }\n ... on CoreDataCheck {\n conflicts {\n value\n }\n }\n ... on CoreSchemaCheck {\n conflicts {\n value\n }\n }\n ... on CoreFileCheck {\n files {\n value\n }\n commit {\n value\n }\n }\n ... on CoreArtifactCheck {\n storage_id {\n value\n }\n artifact_id {\n value\n }\n }\n __typename\n }\n }\n }\n }\n }\n }\n }\n": + TadaDocumentNode<{ CoreValidator: { edges: { node: { __typename?: "CoreArtifactValidator" | undefined; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; definition: { node: { display_label: string | null; name: { value: string | null; } | null; description: { value: string | null; } | null; } | null; }; checks: { count: number; edges: { node: { __typename: "CoreArtifactCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; } | { __typename: "CoreDataCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreFileCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; files: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename: "CoreGeneratorCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | { __typename: "CoreSchemaCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreStandardCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename?: "CoreDataValidator" | undefined; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { count: number; edges: { node: { __typename: "CoreArtifactCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; } | { __typename: "CoreDataCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreFileCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; files: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename: "CoreGeneratorCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | { __typename: "CoreSchemaCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreStandardCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename?: "CoreGeneratorValidator" | undefined; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { count: number; edges: { node: { __typename: "CoreArtifactCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; } | { __typename: "CoreDataCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreFileCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; files: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename: "CoreGeneratorCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | { __typename: "CoreSchemaCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreStandardCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename?: "CoreRepositoryValidator" | undefined; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; repository: { node: { __typename?: "CoreReadOnlyRepository" | undefined; display_label: string | null; } | { __typename?: "CoreRepository" | undefined; display_label: string | null; } | null; }; checks: { count: number; edges: { node: { __typename: "CoreArtifactCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; } | { __typename: "CoreDataCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreFileCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; files: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename: "CoreGeneratorCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | { __typename: "CoreSchemaCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreStandardCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename?: "CoreSchemaValidator" | undefined; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { count: number; edges: { node: { __typename: "CoreArtifactCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; } | { __typename: "CoreDataCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreFileCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; files: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename: "CoreGeneratorCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | { __typename: "CoreSchemaCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreStandardCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | null; }[] | null; }; } | { __typename?: "CoreUserValidator" | undefined; id: string | null; display_label: string | null; conclusion: { value: string | null; } | null; started_at: { value: string | null; } | null; completed_at: { value: string | null; } | null; state: { value: string | null; } | null; checks: { count: number; edges: { node: { __typename: "CoreArtifactCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; } | { __typename: "CoreDataCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreFileCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; files: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename: "CoreGeneratorCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | { __typename: "CoreSchemaCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; conflicts: { value: unknown; } | null; } | { __typename: "CoreStandardCheck"; id: string | null; display_label: string | null; name: { value: string | null; } | null; message: { value: string | null; } | null; severity: { value: string | null; } | null; conclusion: { value: string | null; } | null; kind: { value: string | null; } | null; origin: { value: string | null; } | null; created_at: { value: string | null; } | null; } | null; }[] | null; }; } | null; }[]; }; }, { checksLimit?: number | null | undefined; checksOffset?: number | null | undefined; ids?: string[] | null | undefined; }, void>; /** @gql.tada/hash sha256:d3c51e0b6a283186d7971c1656a20769 */ "\n query GetAccountProfile {\n AccountProfile {\n id\n display_label\n is_externally_managed\n name {\n value\n }\n label {\n value\n }\n description {\n value\n }\n }\n }\n": TadaDocumentNode<{ AccountProfile: { __typename?: "CoreAccount" | undefined; id: string | null; display_label: string | null; is_externally_managed: boolean; name: { value: string | null; } | null; label: { value: string | null; } | null; description: { value: string | null; } | null; } | null; }, {}, void>; + /** @gql.tada/hash sha256:0f38c5be613158207df9298328cced57 */ + "\n query GetBranchDetails($branchName: String!) {\n InfrahubBranch(name__value: $branchName) {\n edges {\n node {\n id\n name {\n value\n }\n description {\n value\n }\n origin_branch {\n value\n }\n branched_from {\n value\n }\n status {\n value\n }\n created_at\n sync_with_git {\n value\n }\n is_default {\n value\n }\n schema_differs_from_default_branch {\n value\n }\n }\n }\n }\n }\n": + TadaDocumentNode<{ InfrahubBranch: { edges: { node: { id: string; name: { value: string; }; description: { value: string | null; } | null; origin_branch: { value: string | null; } | null; branched_from: { value: string | null; } | null; status: { value: "DELETING" | "MERGED" | "MERGE_FAILED" | "MERGING" | "NEED_REBASE" | "NEED_UPGRADE_REBASE" | "OPEN"; }; created_at: string | null; sync_with_git: { value: boolean | null; } | null; is_default: { value: boolean | null; } | null; schema_differs_from_default_branch: { value: boolean | null; } | null; }; }[]; }; }, { branchName: string; }, void>; + /** @gql.tada/hash sha256:67960ed821204cd83d4b69466536ad58 */ + "\n query GetBranches($limit: Int, $offset: Int, $nameValue: String, $partialMatch: Boolean, $statusValue: BranchStatus, $createdById: ID, $branchedFromAfter: DateTime, $branchedFromBefore: DateTime, $createdAtAfter: DateTime, $createdAtBefore: DateTime, $updatedAtAfter: DateTime, $updatedAtBefore: DateTime) {\n InfrahubBranch(limit: $limit, offset: $offset, name__value: $nameValue, partial_match: $partialMatch, status__value: $statusValue, node_metadata__created_by__id: $createdById, branched_from__after: $branchedFromAfter, branched_from__before: $branchedFromBefore, node_metadata__created_at__after: $createdAtAfter, node_metadata__created_at__before: $createdAtBefore, node_metadata__updated_at__after: $updatedAtAfter, node_metadata__updated_at__before: $updatedAtBefore) {\n edges {\n node {\n id\n name {\n value\n }\n description {\n value\n }\n origin_branch {\n value\n }\n branched_from {\n value\n }\n status {\n value\n }\n created_at\n sync_with_git {\n value\n }\n is_default {\n value\n }\n schema_differs_from_default_branch {\n value\n }\n }\n node_metadata {\n created_at\n created_by {\n id\n display_label\n hfid\n __typename\n }\n updated_at\n updated_by {\n id\n display_label\n hfid\n __typename\n }\n }\n }\n }\n }\n": + TadaDocumentNode<{ InfrahubBranch: { edges: { node: { id: string; name: { value: string; }; description: { value: string | null; } | null; origin_branch: { value: string | null; } | null; branched_from: { value: string | null; } | null; status: { value: "DELETING" | "MERGED" | "MERGE_FAILED" | "MERGING" | "NEED_REBASE" | "NEED_UPGRADE_REBASE" | "OPEN"; }; created_at: string | null; sync_with_git: { value: boolean | null; } | null; is_default: { value: boolean | null; } | null; schema_differs_from_default_branch: { value: boolean | null; } | null; }; node_metadata: { created_at: unknown; created_by: { __typename: "CoreAccount"; id: string | null; display_label: string | null; hfid: string[] | null; } | null; updated_at: unknown; updated_by: { __typename: "CoreAccount"; id: string | null; display_label: string | null; hfid: string[] | null; } | null; }; }[]; }; }, { updatedAtBefore?: unknown; updatedAtAfter?: unknown; createdAtBefore?: unknown; createdAtAfter?: unknown; branchedFromBefore?: unknown; branchedFromAfter?: unknown; createdById?: string | null | undefined; statusValue?: "DELETING" | "MERGED" | "MERGE_FAILED" | "MERGING" | "NEED_REBASE" | "NEED_UPGRADE_REBASE" | "OPEN" | null | undefined; partialMatch?: boolean | null | undefined; nameValue?: string | null | undefined; offset?: number | null | undefined; limit?: number | null | undefined; }, void>; + /** @gql.tada/hash sha256:9dae6d3727951bf76e080ed7e017b025 */ + "\n query GetBranchesCount($nameValue: String, $partialMatch: Boolean, $statusValue: BranchStatus, $createdById: ID, $branchedFromAfter: DateTime, $branchedFromBefore: DateTime, $createdAtAfter: DateTime, $createdAtBefore: DateTime, $updatedAtAfter: DateTime, $updatedAtBefore: DateTime) {\n InfrahubBranch(name__value: $nameValue, partial_match: $partialMatch, status__value: $statusValue, node_metadata__created_by__id: $createdById, branched_from__after: $branchedFromAfter, branched_from__before: $branchedFromBefore, node_metadata__created_at__after: $createdAtAfter, node_metadata__created_at__before: $createdAtBefore, node_metadata__updated_at__after: $updatedAtAfter, node_metadata__updated_at__before: $updatedAtBefore) {\n count\n }\n }\n": + TadaDocumentNode<{ InfrahubBranch: { count: number | null; }; }, { updatedAtBefore?: unknown; updatedAtAfter?: unknown; createdAtBefore?: unknown; createdAtAfter?: unknown; branchedFromBefore?: unknown; branchedFromAfter?: unknown; createdById?: string | null | undefined; statusValue?: "DELETING" | "MERGED" | "MERGE_FAILED" | "MERGING" | "NEED_REBASE" | "NEED_UPGRADE_REBASE" | "OPEN" | null | undefined; partialMatch?: boolean | null | undefined; nameValue?: string | null | undefined; }, void>; + /** @gql.tada/hash sha256:cd8c191aa303a91dc1ba9562d1655a04 */ + "\n query GetCoreThread($ids: [ID]) {\n CoreThread(ids: $ids) {\n edges {\n node {\n id\n display_label\n label {\n value\n }\n resolved {\n value\n }\n comments {\n count\n edges {\n node_metadata {\n created_at\n created_by {\n display_label\n }\n }\n node {\n id\n display_label\n text {\n value\n }\n }\n }\n }\n ... on CoreArtifactThread {\n storage_id {\n value\n }\n artifact_id {\n value\n }\n line_number {\n value\n }\n }\n ... on CoreObjectThread {\n object_path {\n value\n }\n }\n ... on CoreFileThread {\n file {\n value\n }\n line_number {\n value\n }\n commit {\n value\n }\n }\n }\n }\n }\n }\n": + TadaDocumentNode<{ CoreThread: { edges: { node: { __typename?: "CoreArtifactThread" | undefined; id: string | null; display_label: string | null; label: { value: string | null; } | null; resolved: { value: boolean | null; } | null; comments: { count: number; edges: { node_metadata: { created_at: unknown; created_by: { __typename?: "CoreAccount" | undefined; display_label: string | null; } | null; } | null; node: { id: string; display_label: string | null; text: { value: string | null; } | null; } | null; }[]; }; storage_id: { value: string | null; } | null; artifact_id: { value: string | null; } | null; line_number: { value: unknown; } | null; } | { __typename?: "CoreChangeThread" | undefined; id: string | null; display_label: string | null; label: { value: string | null; } | null; resolved: { value: boolean | null; } | null; comments: { count: number; edges: { node_metadata: { created_at: unknown; created_by: { __typename?: "CoreAccount" | undefined; display_label: string | null; } | null; } | null; node: { id: string; display_label: string | null; text: { value: string | null; } | null; } | null; }[]; }; } | { __typename?: "CoreFileThread" | undefined; id: string | null; display_label: string | null; label: { value: string | null; } | null; resolved: { value: boolean | null; } | null; comments: { count: number; edges: { node_metadata: { created_at: unknown; created_by: { __typename?: "CoreAccount" | undefined; display_label: string | null; } | null; } | null; node: { id: string; display_label: string | null; text: { value: string | null; } | null; } | null; }[]; }; file: { value: string | null; } | null; line_number: { value: unknown; } | null; commit: { value: string | null; } | null; } | { __typename?: "CoreObjectThread" | undefined; id: string | null; display_label: string | null; label: { value: string | null; } | null; resolved: { value: boolean | null; } | null; comments: { count: number; edges: { node_metadata: { created_at: unknown; created_by: { __typename?: "CoreAccount" | undefined; display_label: string | null; } | null; } | null; node: { id: string; display_label: string | null; text: { value: string | null; } | null; } | null; }[]; }; object_path: { value: string | null; } | null; } | null; }[]; }; }, { ids?: (string | null)[] | null | undefined; }, void>; /** @gql.tada/hash sha256:cf038d1678d2d147af03216eb7766431 */ "\n query InfrahubAccountToken {\n InfrahubAccountToken {\n count\n edges {\n node {\n id\n name\n expiration\n __typename\n }\n }\n }\n }\n": TadaDocumentNode<{ InfrahubAccountToken: { count: number; edges: { node: { id: string; name: string | null; expiration: string | null; __typename: "AccountTokenNode"; }; }[]; }; }, {}, void>; - /** @gql.tada/hash sha256:7e3b3fe850b74326deb289855b4bc3ef */ - "\n mutation UPDATE_ACCOUNT_PASSWORD($password: String!) {\n InfrahubAccountSelfUpdate(data: { password: $password }) {\n ok\n }\n }\n": - TadaDocumentNode<{ InfrahubAccountSelfUpdate: { ok: boolean | null; } | null; }, { password: string; }, void>; + /** @gql.tada/hash sha256:4f04b6c7e8ff5a931bd5a7ee05d0efe2 */ + "\n query InfrahubEffectivePreferences {\n InfrahubEffectivePreferences {\n date_format {\n value\n source\n }\n timezone {\n value\n source\n }\n }\n }\n": + TadaDocumentNode<{ InfrahubEffectivePreferences: { date_format: { value: "EU_DATETIME" | "ISO_8601" | "ISO_DATETIME" | "ISO_DATETIME_SECONDS" | "US_12H" | null; source: "USER" | "DEFAULT" | "GLOBAL"; }; timezone: { value: string | null; source: "USER" | "DEFAULT" | "GLOBAL"; }; }; }, {}, void>; + /** @gql.tada/hash sha256:9720c2e718e30cc040b55fe4767f88c3 */ + "\n query InfrahubGlobalPermissions {\n InfrahubPermissions {\n global_permissions {\n edges {\n node {\n action\n decision\n }\n }\n }\n }\n }\n": + TadaDocumentNode<{ InfrahubPermissions: { global_permissions: { edges: { node: { action: string; decision: string; }; }[]; } | null; }; }, {}, void>; + /** @gql.tada/hash sha256:210ac3ff8dfa4fee3c5d92a266b833fb */ + "\n query InfrahubGlobalPreferences {\n InfrahubGlobalPreferences {\n date_format\n timezone\n }\n }\n": + TadaDocumentNode<{ InfrahubGlobalPreferences: { date_format: "EU_DATETIME" | "ISO_8601" | "ISO_DATETIME" | "ISO_DATETIME_SECONDS" | "US_12H" | null; timezone: string | null; }; }, {}, void>; + /** @gql.tada/hash sha256:3a916ca568f010b38df3f021c11a0e60 */ + "\n query REPOSITORY_GROUP($nodeIds: [ID]) {\n CoreRepositoryGroup(repository__ids: $nodeIds) {\n edges {\n node {\n id\n }\n }\n }\n }\n": + TadaDocumentNode<{ CoreRepositoryGroup: { edges: { node: { id: string; } | null; }[]; }; }, { nodeIds?: (string | null)[] | null | undefined; }, void>; + /** @gql.tada/hash sha256:7b32b4a094e7e7df8dfa91265eb79121 */ + "\n query Search($search: String!, $caseSensitive: Boolean) {\n InfrahubSearchAnywhere(q: $search, limit: 4, partial_match: true, case_sensitive: $caseSensitive) {\n count\n edges {\n node {\n id\n kind\n }\n }\n parent_prefixes {\n node {\n id\n kind\n }\n }\n }\n }\n": + TadaDocumentNode<{ InfrahubSearchAnywhere: { count: number; edges: { node: { id: string; kind: string; }; }[]; parent_prefixes: { node: { id: string; kind: string; }; }[] | null; }; }, { caseSensitive?: boolean | null | undefined; search: string; }, void>; + /** @gql.tada/hash sha256:32bf68cc18e717ae2ee1188ae013ae2e */ + "\n query TASKS_BRANCH_STATUS_COUNT($branch: String!) {\n InfrahubTaskBranchStatus(branch: $branch) {\n count\n }\n }\n": + TadaDocumentNode<{ InfrahubTaskBranchStatus: { count: number; }; }, { branch: string; }, void>; + /** @gql.tada/hash sha256:6752d3c0f1606d094ce17fafff97788d */ + "\n query TASK_COUNT(\n $search: String\n $branchName: String\n $state: [StateType]\n $relatedNodeIds: [String]\n ) {\n InfrahubTask(\n q: $search\n branch: $branchName\n state: $state\n related_node__ids: $relatedNodeIds\n ) {\n count\n }\n }\n": + TadaDocumentNode<{ InfrahubTask: { count: number; }; }, { relatedNodeIds?: (string | null)[] | null | undefined; state?: ("CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null)[] | null | undefined; branchName?: string | null | undefined; search?: string | null | undefined; }, void>; + /** @gql.tada/hash sha256:54f51a9f2ef22b26c5e5eed29e8689ec */ + "\n query TASK_DETAILS_CHECK(\n $ids: [String]\n $branch: String\n $workflow: [String]\n $state: [StateType]\n $relatedNodes: [String]\n ) {\n InfrahubTask(\n ids: $ids\n branch: $branch\n workflow: $workflow\n state: $state\n related_node__ids: $relatedNodes\n ) {\n count\n }\n }\n": + TadaDocumentNode<{ InfrahubTask: { count: number; }; }, { relatedNodes?: (string | null)[] | null | undefined; state?: ("CANCELLED" | "CANCELLING" | "COMPLETED" | "CRASHED" | "FAILED" | "PAUSED" | "PENDING" | "RUNNING" | "SCHEDULED" | null)[] | null | undefined; workflow?: (string | null)[] | null | undefined; branch?: string | null | undefined; ids?: (string | null)[] | null | undefined; }, void>; + /** @gql.tada/hash sha256:1d92ec936fb68dbb4acb0e99206ad4ac */ + "\n query actions($proposedChangeId: String!) {\n CoreProposedChangeAvailableActions(proposed_change_id: $proposedChangeId) {\n count\n edges {\n node {\n action\n available\n unavailability_reason\n }\n }\n }\n }\n": + TadaDocumentNode<{ CoreProposedChangeAvailableActions: { count: number; edges: { node: { action: string; available: boolean; unavailability_reason: string | null; }; }[]; }; }, { proposedChangeId: string; }, void>; + /** @gql.tada/hash sha256:7ef472d04eaa9bbfc5d3e70a86ff9f5d */ + "\n query getNextIPAddressAvailable($parentPrefixId: String!) {\n InfrahubIPAddressGetNextAvailable(prefix_id: $parentPrefixId) {\n address\n }\n }\n": + TadaDocumentNode<{ InfrahubIPAddressGetNextAvailable: { address: string; }; }, { parentPrefixId: string; }, void>; + /** @gql.tada/hash sha256:b1e9112be17bf990747e22ca64b6f2f8 */ + "\n query getNextIPPrefixAvailable($parentPrefixId: String!) {\n InfrahubIPPrefixGetNextAvailable(prefix_id: $parentPrefixId) {\n prefix\n }\n }\n": + TadaDocumentNode<{ InfrahubIPPrefixGetNextAvailable: { prefix: string; }; }, { parentPrefixId: string; }, void>; } } diff --git a/frontend/app/src/shared/api/graphql/generated/types.ts b/frontend/app/src/shared/api/graphql/generated/types.ts index 37e86117b07..13b61aed644 100644 --- a/frontend/app/src/shared/api/graphql/generated/types.ts +++ b/frontend/app/src/shared/api/graphql/generated/types.ts @@ -17946,6 +17946,23 @@ export type HttpResponse = { status_code: Maybe; }; +/** Attribute of type IPAddress */ +export type IpAddress = AttributeInterface & { + __typename: 'IPAddress'; + id: Maybe; + is_default: Maybe; + is_from_profile: Maybe; + is_protected: Maybe; + owner: Maybe; + permissions: Maybe; + source: Maybe; + /** Date/Time when the attribute was last modified by a user or a system task */ + updated_at: Maybe; + updated_by: Maybe; + value: Maybe; + version: Maybe; +}; + export type IpAddressGetNextAvailable = { __typename: 'IPAddressGetNextAvailable'; address: Scalars['String']['output']; diff --git a/frontend/packages/graph/oxlint.config.ts b/frontend/packages/graph/oxlint.config.ts index 42b61e5d71f..9e3fe95a804 100644 --- a/frontend/packages/graph/oxlint.config.ts +++ b/frontend/packages/graph/oxlint.config.ts @@ -13,6 +13,7 @@ export default defineConfig({ }, plugins: ["oxc", "typescript", "react", "react-perf", "jsx-a11y", "vitest", "unicorn"], rules: { + "eslint/one-var": "off", "eslint/no-console": ["error", { allow: ["error"] }], // The React Compiler memoizes; inline values as props are not a re-render hazard here. "react-perf/jsx-no-new-array-as-prop": "off", diff --git a/frontend/packages/graph/package.json b/frontend/packages/graph/package.json index 58a0b99fcd1..04d45e6487d 100644 --- a/frontend/packages/graph/package.json +++ b/frontend/packages/graph/package.json @@ -40,23 +40,22 @@ }, "devDependencies": { "@rolldown/plugin-babel": "^0.2.3", - "@storybook/react-vite": "^10.4.6", - "@types/node": "^25.9.3", + "@storybook/react-vite": "^10.5.7", + "@types/node": "^26.2.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", - "@vitest/browser": "^4.1.9", - "@vitest/browser-playwright": "^4.1.9", + "@vitest/browser": "^4.1.10", + "@vitest/browser-playwright": "^4.1.10", "@xyflow/react": "^12.11.0", "babel-plugin-react-compiler": "^1.0.0", - "globals": "^17.5.0", - "oxfmt": "^0.55.0", + "globals": "^17.10.0", + "oxfmt": "^0.63.0", "oxlint": "^1.70.0", - "playwright": "1.61.0", - "storybook": "^10.4.6", - "typescript": "~6.0.3", + "storybook": "^10.5.7", + "typescript": "catalog:", "vite": "^8.0.10", - "vitest": "^4.1.9", + "vitest": "^4.1.10", "vitest-browser-react": "^2.2.0" }, "peerDependencies": { diff --git a/frontend/packages/ui/oxlint.config.ts b/frontend/packages/ui/oxlint.config.ts index 08625095e97..2182e5971d8 100644 --- a/frontend/packages/ui/oxlint.config.ts +++ b/frontend/packages/ui/oxlint.config.ts @@ -28,6 +28,7 @@ export default defineConfig({ "eslint/no-ternary": "off", "eslint/no-undefined": "off", "eslint/no-use-before-define": "off", + "eslint/one-var": "off", "eslint/sort-imports": "off", "eslint/sort-keys": "off", "jsx-a11y/no-autofocus": "off", diff --git a/frontend/packages/ui/package.json b/frontend/packages/ui/package.json index 93304efd699..963886d90f0 100644 --- a/frontend/packages/ui/package.json +++ b/frontend/packages/ui/package.json @@ -24,25 +24,25 @@ "react-aria-components": "catalog:", "react-resizable-panels": "^4.12.2", "tailwind-merge": "catalog:", - "tailwind-variants": "^3.3.0", + "tailwind-variants": "^3.3.1", "tw-animate-css": "^1.4.0" }, "devDependencies": { "@rolldown/plugin-babel": "^0.2.3", - "@storybook/react-vite": "10.5.5", + "@storybook/react-vite": "10.5.7", "@tailwindcss/vite": "catalog:", "@types/node": "catalog:", "@types/react": "catalog:", "@types/react-dom": "catalog:", "@vitejs/plugin-react": "catalog:", "babel-plugin-react-compiler": "catalog:", - "chromatic": "^18.1.0", - "globals": "^17.8.0", - "oxfmt": "^0.60.0", - "oxlint": "^1.75.0", + "chromatic": "^18.2.0", + "globals": "^17.10.0", + "oxfmt": "^0.63.0", + "oxlint": "^1.78.0", "react": "catalog:", "react-dom": "catalog:", - "storybook": "10.5.5", + "storybook": "10.5.7", "tailwindcss": "catalog:", "typescript": "catalog:", "vite": "catalog:" diff --git a/frontend/packages/ui/src/components/tree/tree.tsx b/frontend/packages/ui/src/components/tree/tree.tsx index 76302867ffa..b29dd73d1d0 100644 --- a/frontend/packages/ui/src/components/tree/tree.tsx +++ b/frontend/packages/ui/src/components/tree/tree.tsx @@ -1,9 +1,9 @@ import type React from "react"; -import type { TreeProps as AriaTreeProps } from "react-aria-components"; import { ChevronRightIcon } from "lucide-react"; import { Tree as AriaTree, + type TreeProps as AriaTreeProps, TreeItem as AriaTreeItem, TreeItemContent as AriaTreeItemContent, type TreeItemContentProps as AriaTreeItemContentProps, diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index c1c8cc2d928..6c0e3b844e2 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -10,29 +10,29 @@ catalogs: specifier: ^4.3.3 version: 4.3.3 '@types/node': - specifier: ^26.1.1 - version: 26.1.1 + specifier: ^26.2.0 + version: 26.2.0 '@types/react': - specifier: ^19.2.17 - version: 19.2.17 + specifier: ^19.2.18 + version: 19.2.18 '@types/react-dom': - specifier: ^19.2.3 - version: 19.2.3 + specifier: ^19.2.4 + version: 19.2.4 '@vitejs/plugin-react': - specifier: ^6.0.4 - version: 6.0.4 + specifier: ^6.0.5 + version: 6.0.5 babel-plugin-react-compiler: specifier: ^1.0.0 version: 1.0.0 lucide-react: - specifier: ^1.27.0 - version: 1.27.0 + specifier: ^1.31.0 + version: 1.31.0 react: specifier: ^19.2.8 version: 19.2.8 react-aria-components: - specifier: ^1.19.0 - version: 1.19.0 + specifier: ^1.20.0 + version: 1.20.0 react-dom: specifier: ^19.2.8 version: 19.2.8 @@ -46,8 +46,8 @@ catalogs: specifier: ^5.9.3 version: 5.9.3 vite: - specifier: ^8.1.5 - version: 8.1.5 + specifier: ^8.2.1 + version: 8.2.1 overrides: playwright: 1.60.0 @@ -63,29 +63,29 @@ importers: specifier: ^6.10.4 version: 6.10.4 '@codemirror/lang-markdown': - specifier: ^6.5.0 - version: 6.5.0 + specifier: ^6.5.2 + version: 6.5.2 '@codemirror/language': specifier: ^6.12.4 version: 6.12.4 '@codemirror/state': - specifier: ^6.7.0 - version: 6.7.0 + specifier: ^6.7.1 + version: 6.7.1 '@codemirror/theme-one-dark': specifier: ^6.1.3 version: 6.1.3 '@codemirror/view': - specifier: ^6.43.3 - version: 6.43.4 + specifier: ^6.43.8 + version: 6.43.8 '@date-fns/tz': specifier: ^1.5.0 version: 1.5.0 '@graphiql/plugin-explorer': specifier: ^5.1.3 - version: 5.1.3(@graphiql/react@0.37.7(@types/node@26.1.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))(graphql@16.14.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 5.1.3(@graphiql/react@0.37.7(@types/node@26.2.0)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))(graphql@16.14.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@graphiql/toolkit': specifier: ^0.12.1 - version: 0.12.1(@types/node@26.1.1)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2) + version: 0.12.1(@types/node@26.2.0)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2) '@headlessui/react': specifier: ^2.2.10 version: 2.2.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -102,38 +102,38 @@ importers: specifier: workspace:* version: link:../packages/ui '@radix-ui/react-accordion': - specifier: ^1.2.14 - version: 1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: ^1.2.20 + version: 1.2.20(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@radix-ui/react-dropdown-menu': - specifier: ^2.1.18 - version: 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: ^2.1.24 + version: 2.1.24(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@radix-ui/react-label': - specifier: ^2.1.10 - version: 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: ^2.1.15 + version: 2.1.15(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@radix-ui/react-popover': - specifier: ^1.1.17 - version: 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: ^1.1.23 + version: 1.1.23(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@radix-ui/react-progress': - specifier: ^1.1.10 - version: 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: ^1.1.16 + version: 1.1.16(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@radix-ui/react-slot': - specifier: ^1.3.0 - version: 1.3.0(@types/react@19.2.17)(react@19.2.8) + specifier: ^1.3.3 + version: 1.3.3(@types/react@19.2.18)(react@19.2.8) '@radix-ui/react-tabs': - specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: ^1.1.21 + version: 1.1.21(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@tanstack/react-query': - specifier: ^5.101.1 - version: 5.101.2(react@19.2.8) + specifier: ^5.101.4 + version: 5.101.4(react@19.2.8) '@tanstack/react-query-devtools': - specifier: ^5.101.1 - version: 5.101.2(@tanstack/react-query@5.101.2(react@19.2.8))(react@19.2.8) + specifier: ^5.101.4 + version: 5.101.4(@tanstack/react-query@5.101.4(react@19.2.8))(react@19.2.8) '@tanstack/react-table': specifier: ^8.21.3 version: 8.21.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@uiw/react-color': specifier: ^2.10.3 - version: 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@urql/core': specifier: ^6.0.3 version: 6.0.3(graphql@16.14.2) @@ -141,8 +141,8 @@ importers: specifier: ^3.0.0 version: 3.0.0(@urql/core@6.0.3(graphql@16.14.2)) '@xyflow/react': - specifier: ^12.11.1 - version: 12.11.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: ^12.11.2 + version: 12.11.2(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -151,13 +151,13 @@ importers: version: 2.1.1 cm6-graphql: specifier: ^0.2.1 - version: 0.2.1(@codemirror/autocomplete@6.20.1)(@codemirror/language@6.12.4)(@codemirror/lint@6.9.5)(@codemirror/state@6.7.0)(@codemirror/view@6.43.4)(@lezer/highlight@1.2.3)(graphql@16.14.2) + version: 0.2.1(@codemirror/autocomplete@6.20.3)(@codemirror/language@6.12.4)(@codemirror/lint@6.9.7)(@codemirror/state@6.7.1)(@codemirror/view@6.43.8)(@lezer/highlight@1.2.3)(graphql@16.14.2) cm6-theme-basic-light: specifier: ^0.2.0 - version: 0.2.0(@codemirror/language@6.12.4)(@codemirror/state@6.7.0)(@codemirror/view@6.43.4)(@lezer/highlight@1.2.3) + version: 0.2.0(@codemirror/language@6.12.4)(@codemirror/state@6.7.1)(@codemirror/view@6.43.8)(@lezer/highlight@1.2.3) cmdk: specifier: ^1.1.1 - version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 1.1.1(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) dagre: specifier: ^0.8.5 version: 0.8.5 @@ -165,11 +165,11 @@ importers: specifier: ^4.4.0 version: 4.4.0 gql.tada: - specifier: ^1.11.2 - version: 1.11.2(graphql@16.14.2)(typescript@5.9.3) + specifier: ^1.11.3 + version: 1.11.3(graphql@16.14.2)(typescript@5.9.3) graphiql: specifier: ^5.2.4 - version: 5.2.4(@types/node@26.1.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) + version: 5.2.4(@types/node@26.2.0)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) graphql: specifier: ^16.14.2 version: 16.14.2 @@ -177,14 +177,14 @@ importers: specifier: workspace:* version: link:../packages/schema-visualizer jotai: - specifier: ^2.20.1 - version: 2.20.1(@babel/core@7.29.7)(@babel/template@7.29.7)(@types/react@19.2.17)(react@19.2.8) + specifier: ^2.20.2 + version: 2.20.2(@babel/core@7.29.7)(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8) json-to-graphql-query: specifier: ^2.3.0 version: 2.3.0 lucide-react: specifier: 'catalog:' - version: 1.27.0(react@19.2.8) + version: 1.31.0(react@19.2.8) monaco-editor: specifier: 0.52.2 version: 0.52.2 @@ -192,8 +192,8 @@ importers: specifier: ^1.8.0 version: 1.8.0(graphql@16.14.2)(monaco-editor@0.52.2)(prettier@3.8.4) nuqs: - specifier: ^2.8.9 - version: 2.8.9(react-router@8.3.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) + specifier: ^2.9.5 + version: 2.9.5(react-router@8.3.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8) openapi-fetch: specifier: ^0.17.0 version: 0.17.0 @@ -205,7 +205,7 @@ importers: version: 19.2.8 react-aria-components: specifier: 'catalog:' - version: 1.19.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 1.20.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react-datepicker: specifier: 8.1.0 version: 8.1.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -223,7 +223,7 @@ importers: version: 7.73.1(react@19.2.8) react-markdown: specifier: ^10.1.0 - version: 10.1.0(@types/react@19.2.17)(react@19.2.8) + version: 10.1.0(@types/react@19.2.18)(react@19.2.8) react-paginate: specifier: ^8.3.0 version: 8.3.0(react@19.2.8) @@ -232,7 +232,7 @@ importers: version: 8.3.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react-scan: specifier: ^0.5.7 - version: 0.5.7(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@types/react@19.2.17)(esbuild@0.28.1)(eslint@10.4.1(jiti@2.7.0))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 0.5.7(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(esbuild@0.28.1)(eslint@10.4.1(jiti@2.7.0))(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react-simple-code-editor: specifier: ^0.14.1 version: 0.14.1(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -243,11 +243,11 @@ importers: specifier: 9.1.3 version: 9.1.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react-zoom-pan-pinch: - specifier: ^4.0.3 - version: 4.0.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: ^4.0.4 + version: 4.0.4(react-dom@19.2.8(react@19.2.8))(react@19.2.8) recharts: - specifier: ^3.9.0 - version: 3.9.0(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react-is@17.0.2)(react@19.2.8)(redux@5.0.1) + specifier: ^3.10.1 + version: 3.10.1(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react-is@17.0.2)(react@19.2.8)(redux@5.0.1) rehype-mermaid: specifier: ^3.0.0 version: 3.0.0(playwright@1.60.0) @@ -286,35 +286,35 @@ importers: specifier: ^2.5.1 version: 2.5.1 '@graphql-codegen/cli': - specifier: ^7.1.3 - version: 7.1.3(@types/node@26.1.1)(graphql@16.14.2)(typescript@5.9.3) + specifier: ^7.2.0 + version: 7.2.0(@types/node@26.2.0)(graphql@16.14.2)(typescript@5.9.3) '@graphql-codegen/typescript': - specifier: ^6.0.2 - version: 6.0.2(graphql@16.14.2) + specifier: ^6.1.0 + version: 6.1.0(graphql@16.14.2) '@playwright/test': - specifier: 1.61.1 - version: 1.61.1 + specifier: 1.62.1 + version: 1.62.1 '@rolldown/plugin-babel': specifier: ^0.2.3 - version: 0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 0.2.3(@babel/core@7.29.7)(@babel/runtime@8.0.0)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) '@tailwindcss/vite': specifier: 'catalog:' - version: 4.3.3(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.3.3(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) '@types/dagre': specifier: ^0.7.54 version: 0.7.54 '@types/node': specifier: 'catalog:' - version: 26.1.1 + version: 26.2.0 '@types/prismjs': specifier: ^1.26.6 version: 1.26.6 '@types/react': specifier: 'catalog:' - version: 19.2.17 + version: 19.2.18 '@types/react-dom': specifier: 'catalog:' - version: 19.2.3(@types/react@19.2.17) + version: 19.2.4(@types/react@19.2.18) '@types/react-syntax-highlighter': specifier: ^15.5.13 version: 15.5.13 @@ -323,46 +323,49 @@ importers: version: 1.1.5 '@vitejs/plugin-react': specifier: 'catalog:' - version: 6.0.4(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 6.0.5(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@8.0.0)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) '@vitest/browser-playwright': - specifier: ^4.1.9 - version: 4.1.9(playwright@1.60.0)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.9) + specifier: ^4.1.10 + version: 4.1.10(playwright@1.60.0)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10) '@vitest/coverage-v8': - specifier: ^4.1.9 - version: 4.1.9(@vitest/browser@4.1.9)(vitest@4.1.9) + specifier: ^4.1.10 + version: 4.1.10(@vitest/browser@4.1.10)(vitest@4.1.10) babel-plugin-react-compiler: specifier: 'catalog:' version: 1.0.0 knip: - specifier: ^6.21.0 - version: 6.23.0 + specifier: ~6.27.0 + version: 6.27.0 openapi-typescript: specifier: ^7.13.0 version: 7.13.0(typescript@5.9.3) tailwindcss: specifier: 'catalog:' version: 4.3.3 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@26.2.0)(typescript@5.9.3) typescript: specifier: 'catalog:' version: 5.9.3 ultracite: specifier: ^7.8.3 - version: 7.8.3(oxfmt@0.60.0)(oxlint@1.75.0) + version: 7.8.3(oxfmt@0.63.0)(oxlint@1.78.0) vite: specifier: 'catalog:' - version: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + version: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) vite-plugin-monaco-editor-esm: - specifier: ^2.0.2 - version: 2.0.2(monaco-editor@0.52.2) + specifier: ^2.0.3 + version: 2.0.3(monaco-editor@0.52.2) vite-plugin-svgr: specifier: ^5.2.0 - version: 5.2.0(typescript@5.9.3)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 5.2.0(typescript@5.9.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) vitest: - specifier: ^4.1.9 - version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + specifier: ^4.1.10 + version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.2.0)(@vitest/browser-playwright@4.1.10)(@vitest/coverage-v8@4.1.10)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.2.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.9) + version: 2.2.0(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) packages/graph: dependencies: @@ -374,22 +377,22 @@ importers: version: link:../ui '@tailwindcss/vite': specifier: ^4.3.1 - version: 4.3.3(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.3.3(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) lucide-react: specifier: ^1.21.0 - version: 1.27.0(react@19.2.8) + version: 1.31.0(react@19.2.8) react: specifier: ^19.2.5 version: 19.2.8 react-aria-components: specifier: ^1.18.0 - version: 1.18.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 1.20.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react-dom: specifier: ^19.2.5 version: 19.2.8(react@19.2.8) tailwind-variants: specifier: ^3.2.2 - version: 3.2.2(tailwind-merge@3.6.0)(tailwindcss@4.3.3) + version: 3.3.1(tailwind-merge@3.6.0)(tailwindcss@4.3.3) tailwindcss: specifier: ^4.3.1 version: 4.3.3 @@ -399,61 +402,58 @@ importers: devDependencies: '@rolldown/plugin-babel': specifier: ^0.2.3 - version: 0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.1.5)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 0.2.3(@babel/core@7.29.7)(@babel/runtime@8.0.0)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) '@storybook/react-vite': - specifier: ^10.4.6 - version: 10.4.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(typescript@6.0.3)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + specifier: ^10.5.7 + version: 10.5.7(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8))(typescript@5.9.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) '@types/node': - specifier: ^25.9.3 - version: 25.9.5 + specifier: ^26.2.0 + version: 26.2.0 '@types/react': specifier: ^19.2.14 - version: 19.2.17 + version: 19.2.18 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.17) + version: 19.2.4(@types/react@19.2.18) '@vitejs/plugin-react': specifier: ^6.0.1 - version: 6.0.4(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.1.5)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 6.0.5(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@8.0.0)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) '@vitest/browser': - specifier: ^4.1.9 - version: 4.1.9(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.9) + specifier: ^4.1.10 + version: 4.1.10(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10) '@vitest/browser-playwright': - specifier: ^4.1.9 - version: 4.1.9(playwright@1.60.0)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.9) + specifier: ^4.1.10 + version: 4.1.10(playwright@1.60.0)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10) '@xyflow/react': specifier: ^12.11.0 - version: 12.11.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 12.11.2(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) babel-plugin-react-compiler: specifier: ^1.0.0 version: 1.0.0 globals: - specifier: ^17.5.0 - version: 17.6.0 + specifier: ^17.10.0 + version: 17.10.0 oxfmt: - specifier: ^0.55.0 - version: 0.55.0 + specifier: ^0.63.0 + version: 0.63.0 oxlint: specifier: ^1.70.0 - version: 1.70.0 - playwright: - specifier: 1.60.0 - version: 1.60.0 + version: 1.78.0 storybook: - specifier: ^10.4.6 - version: 10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: ^10.5.7 + version: 10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8) typescript: - specifier: ~6.0.3 - version: 6.0.3 + specifier: 'catalog:' + version: 5.9.3 vite: specifier: ^8.0.10 - version: 8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + version: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) vitest: - specifier: ^4.1.9 - version: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@25.9.5)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + specifier: ^4.1.10 + version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.2.0)(@vitest/browser-playwright@4.1.10)(@vitest/coverage-v8@4.1.10)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.2.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.9) + version: 2.2.0(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) packages/schema-visualizer: dependencies: @@ -465,7 +465,7 @@ importers: version: 3.0.3(react@19.2.8) '@xyflow/react': specifier: ^12.10.2 - version: 12.10.2(@types/react@19.2.17)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 12.11.2(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -477,32 +477,32 @@ importers: version: 1.11.13 jotai: specifier: ^2.19.1 - version: 2.20.0(@babel/core@7.29.7)(@babel/template@7.29.7)(@types/react@19.2.17)(react@19.2.8) + version: 2.20.2(@babel/core@7.29.7)(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8) tailwind-merge: specifier: ^3.5.0 version: 3.6.0 devDependencies: '@biomejs/biome': specifier: ^2.4.12 - version: 2.4.16 + version: 2.5.1 '@tailwindcss/vite': specifier: ^4.2.4 - version: 4.3.3(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.3.3(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) '@types/react': specifier: ^19.2.14 - version: 19.2.17 + version: 19.2.18 '@types/react-dom': specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.17) + version: 19.2.4(@types/react@19.2.18) '@vitejs/plugin-react': specifier: ^6.0.1 - version: 6.0.4(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 6.0.5(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@8.0.0)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) '@vitest/browser': specifier: ^4.1.5 - version: 4.1.8(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.8) + version: 4.1.10(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10) '@vitest/browser-playwright': specifier: ^4.1.5 - version: 4.1.8(playwright@1.60.0)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.8) + version: 4.1.10(playwright@1.60.0)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10) babel-plugin-react-compiler: specifier: ^1.0.0 version: 1.0.0 @@ -523,25 +523,25 @@ importers: version: 6.0.3 vite: specifier: ^8.0.9 - version: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + version: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) vitest: specifier: ^4.1.5 - version: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.8)(@vitest/coverage-v8@4.1.8)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.2.0)(@vitest/browser-playwright@4.1.10)(@vitest/coverage-v8@4.1.10)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) vitest-browser-react: specifier: ^2.2.0 - version: 2.2.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.8) + version: 2.2.0(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) packages/ui: dependencies: '@radix-ui/react-scroll-area': specifier: ^1.2.18 - version: 1.2.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 1.2.18(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) lucide-react: specifier: 'catalog:' - version: 1.27.0(react@19.2.8) + version: 1.31.0(react@19.2.8) react-aria-components: specifier: 'catalog:' - version: 1.19.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 1.20.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react-resizable-panels: specifier: ^4.12.2 version: 4.12.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -549,48 +549,48 @@ importers: specifier: 'catalog:' version: 3.6.0 tailwind-variants: - specifier: ^3.3.0 - version: 3.3.0(tailwind-merge@3.6.0)(tailwindcss@4.3.3) + specifier: ^3.3.1 + version: 3.3.1(tailwind-merge@3.6.0)(tailwindcss@4.3.3) tw-animate-css: specifier: ^1.4.0 version: 1.4.0 devDependencies: '@rolldown/plugin-babel': specifier: ^0.2.3 - version: 0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 0.2.3(@babel/core@7.29.7)(@babel/runtime@8.0.0)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) '@storybook/react-vite': - specifier: 10.5.5 - version: 10.5.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8))(typescript@5.9.3)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + specifier: 10.5.7 + version: 10.5.7(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8))(typescript@5.9.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) '@tailwindcss/vite': specifier: 'catalog:' - version: 4.3.3(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.3.3(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) '@types/node': specifier: 'catalog:' - version: 26.1.1 + version: 26.2.0 '@types/react': specifier: 'catalog:' - version: 19.2.17 + version: 19.2.18 '@types/react-dom': specifier: 'catalog:' - version: 19.2.3(@types/react@19.2.17) + version: 19.2.4(@types/react@19.2.18) '@vitejs/plugin-react': specifier: 'catalog:' - version: 6.0.4(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 6.0.5(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@8.0.0)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) babel-plugin-react-compiler: specifier: 'catalog:' version: 1.0.0 chromatic: - specifier: ^18.1.0 - version: 18.1.0 + specifier: ^18.2.0 + version: 18.2.0 globals: - specifier: ^17.8.0 - version: 17.8.0 + specifier: ^17.10.0 + version: 17.10.0 oxfmt: - specifier: ^0.60.0 - version: 0.60.0 + specifier: ^0.63.0 + version: 0.63.0 oxlint: - specifier: ^1.75.0 - version: 1.75.0 + specifier: ^1.78.0 + version: 1.78.0 react: specifier: 'catalog:' version: 19.2.8 @@ -598,8 +598,8 @@ importers: specifier: 'catalog:' version: 19.2.8(react@19.2.8) storybook: - specifier: 10.5.5 - version: 10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8) + specifier: 10.5.7 + version: 10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8) tailwindcss: specifier: 'catalog:' version: 4.3.3 @@ -608,12 +608,12 @@ importers: version: 5.9.3 vite: specifier: 'catalog:' - version: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + version: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) packages: - '@0no-co/graphql.web@1.3.2': - resolution: {integrity: sha512-Q1+pRlLhE31GOY/2c9BAEnFTNxO7Awtc6fhhEDlxyCBQ2N0IhD32cPVvPChrK9mwBNSgRdW/sF1kd2e0ojHj1Q==} + '@0no-co/graphql.web@1.3.3': + resolution: {integrity: sha512-4gFGBdyaFmQ6n9euhp5JtIGS4ZeivwDr1tCPENUxTvy5wyv532yOtFCr9zzYAJh1s6uibgC+TRXUcay+mxzCoQ==} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 peerDependenciesMeta: @@ -629,15 +629,11 @@ packages: '@adobe/css-tools@4.5.0': resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} - '@alcalzone/ansi-tokenize@0.3.0': - resolution: {integrity: sha512-p+CMKJ93HFmLkjXKlXiVGlMQEuRb6H0MokBSwUsX+S6BRX8eV5naFZpQJFfJHjRZY0Hmnqy1/r6UWl3x+19zYA==} - engines: {node: '>=18'} - '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@apm-js-collab/code-transformer-bundler-plugins@0.7.1': - resolution: {integrity: sha512-Yidf5GOl60db80UxUtNdKK3pnY7obU/gs0xOfA0SCdnvVLMCvfYIer/egC3TqpPiT0Jg22eg3RlzcO+zKfPMcA==} + '@apm-js-collab/code-transformer-bundler-plugins@0.7.4': + resolution: {integrity: sha512-nAfOeZPSUAQvJa1iFT/5oCrTm5YQhMMrfCNthNnaXHZiOQhu1KGuLoIx7HtbAi3wfwaBYLaICPIeenIaEwcXIg==} engines: {node: '>=18.0.0'} '@apm-js-collab/code-transformer@0.18.1': @@ -647,11 +643,14 @@ packages: '@apm-js-collab/tracing-hooks@0.13.0': resolution: {integrity: sha512-mTvWz9rnQwx1U3h0XPTHaX7bgfkpipLLTQyjlC2cdhQpQEuoLT0AGzoydeoq2NxfEVv6fWOOETcSbb2nptleyw==} - '@ardatan/relay-compiler@13.0.1': - resolution: {integrity: sha512-afG3YPwuSA0E5foouZusz5GlXKs74dObv4cuWyLyfKsYFj2r7oGRNB28v18HvwuLSQtQFCi+DpIe0TZkgQDYyg==} + '@ardatan/relay-compiler@13.0.2': + resolution: {integrity: sha512-VFpv9UP820SiwDUPYtq7PmD3jifzZlevkQ26bhbSzFeruSTys0eHzQCZyKg+IhgmZzwPI9AFjPe26ABNjGeIKg==} peerDependencies: graphql: '*' + '@astrojs/compiler@4.0.0': + resolution: {integrity: sha512-eouss7G8ygdZqHuke033VMcVw5HTZUu+PXd/h06DGDUg/jt5btPYPqh66ENWw/mU78rBrf/oeC4oqoBwMtDMNA==} + '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} @@ -664,8 +663,8 @@ packages: resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.29.7': - resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.29.7': @@ -706,8 +705,8 @@ packages: resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.7': - resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} engines: {node: '>=6.0.0'} hasBin: true @@ -717,24 +716,23 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.29.2': - resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} - engines: {node: '>=6.9.0'} - '@babel/runtime@7.29.7': resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} + '@babel/runtime@8.0.0': + resolution: {integrity: sha512-sL6cvO2IfkSu/iU+zs2S/w01B7A8V7suXSIKEN4hPFFdZoiPGxrj5pAG0lCaqLWiEIrjKzdznIWuaLcxPR53qw==} + '@babel/template@7.29.7': resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.29.7': - resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} engines: {node: '>=6.9.0'} - '@babel/types@7.29.7': - resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@1.0.2': @@ -788,47 +786,23 @@ packages: resolution: {integrity: sha512-JUASLtYzUPdFmm1nSq+ABoVvxUJsU9IhNVY3g8RHM1ZhYs3YTx7IczjqYHc0SjKkqz/S2ejMBrrXyWdFBmv/0g==} engines: {node: '>=16'} - '@biomejs/biome@2.4.16': - resolution: {integrity: sha512-x9ajFh1zChVybCiM3TN6OD4phAqLgtPZjFrZF+aTMYCPjwBO+k529TX7PPsAqtGNLeV4UgzwQnowEgS7bGmzcA==} - engines: {node: '>=14.21.3'} - hasBin: true - '@biomejs/biome@2.5.1': resolution: {integrity: sha512-IXWLCxKmae+rI7LOHS1B3EbVisQ6GRAWbhN9msa6KjNCyFWrvKZWR4oUdinaNssrV852OrSHuSPa95h1GPJc7Q==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.4.16': - resolution: {integrity: sha512-wxPvu4XOA85YJk9ixSWUmq/QBHbid85BISbOAqqBM/5xQpPk9ayjk5375tOlSC0BeCwNSbPFafQBm+vBumXq0A==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [darwin] - '@biomejs/cli-darwin-arm64@2.5.1': resolution: {integrity: sha512-npqDzvqv7vFaWRiNN1Te71siRgPaqS9MpqgYCdP/CrUbkJ7ApezaeaKjueKHRN/JH/6lRjJQAHi8acQDCAz22w==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.4.16': - resolution: {integrity: sha512-xFCqGPwYusQJp4N4NJLi1XJiZqjwFdjhT+KqtNy+Ug3qgfczqnTa6MSDvxJF6TkuDLoYJItMapz6tAf7kCekFw==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [darwin] - '@biomejs/cli-darwin-x64@2.5.1': resolution: {integrity: sha512-RgwTqPAM8g2tn1j+b5oRjF/DbSBX8a4gwojtuG9XuhfK7GgomvZ9+T+tqjXiVbjLEeGJOoL6VEk8mvRTVeSybw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.4.16': - resolution: {integrity: sha512-oYxnW0ARfJkr72ezzF2OR8N/rtkgLUQeYtF8cFhVswbknHxtTcmzSsanVJP8yQKnGpGpc2ck6c5zLvHahL6Cbg==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - libc: [musl] - '@biomejs/cli-linux-arm64-musl@2.5.1': resolution: {integrity: sha512-WMcvMLgByyTqVxGlq918NBBYliq9FRR9GAQVETHb+VjGVqXCZFfHlZHC1FX4ibuYY/Hg6TJE3rHU0xVrdJXNRw==} engines: {node: '>=14.21.3'} @@ -836,13 +810,6 @@ packages: os: [linux] libc: [musl] - '@biomejs/cli-linux-arm64@2.4.16': - resolution: {integrity: sha512-2kFb4//jxfZaP6D+Rj5VkHkxgyD9EoRAVBEQb8PKRv+s4NO2zYNJKXFaJmK1CmhufJOWEfpHKaRbOja7qjmdhQ==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@biomejs/cli-linux-arm64@2.5.1': resolution: {integrity: sha512-yhV35CzZh38VyMvTEXi3JTjxZBs++oCKK9KG8vB6VI5+uvQvZNR3BFWEKKzuOmx9DJJj7sQpZ4LQJcmbGTs3+Q==} engines: {node: '>=14.21.3'} @@ -850,13 +817,6 @@ packages: os: [linux] libc: [glibc] - '@biomejs/cli-linux-x64-musl@2.4.16': - resolution: {integrity: sha512-iHDS+MCM65DPqWGu+ECC3uoALyj2H7F4nVUPxIPjz/PIl94EUu+EDfGZDzFP+NY1EOPVt9NQvwFqq7HdMmowdg==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - libc: [musl] - '@biomejs/cli-linux-x64-musl@2.5.1': resolution: {integrity: sha512-ANTowtlLmPYm5yeMckWY8Xzb9Ix+JJP3tgHR/n6xRj1VWyIzzWtfRfih9hv9VmClwadpBvZduISZIbBsIlYG3A==} engines: {node: '>=14.21.3'} @@ -864,13 +824,6 @@ packages: os: [linux] libc: [musl] - '@biomejs/cli-linux-x64@2.4.16': - resolution: {integrity: sha512-NbcBbi/nJqn5baae6wqRXdS7Gadf2uRpehSh6vMSYpG8OhkXl/Xg8aorWrJ+9VWqAT5ml90alLvorkpMW0nBwQ==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - libc: [glibc] - '@biomejs/cli-linux-x64@2.5.1': resolution: {integrity: sha512-J/7uHSX7NfoYDI7HijAkd8lnQIOrRb2W7j3X+tw4R+N5ExvXGsyXFiGdQcfcxfOmNQmZVSQOCDk757fwpzqQcg==} engines: {node: '>=14.21.3'} @@ -878,24 +831,12 @@ packages: os: [linux] libc: [glibc] - '@biomejs/cli-win32-arm64@2.4.16': - resolution: {integrity: sha512-0rgImMsNb5v/chhkIFe3wu7PEFClS6RBAYUijGL9UsYN3PanSaoK24HSSuSJb1pYbYYVjzAyZTl3gtjJ84BM8A==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [win32] - '@biomejs/cli-win32-arm64@2.5.1': resolution: {integrity: sha512-zgXnKNgWPC4iPF7Y1lR3STUeCUuZRpD6IiOrC7TZTlh0Lx6FiVUT05myuMQHQ9D+1cc7uyMldi4forE6lp0ivQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.4.16': - resolution: {integrity: sha512-Kp85jgoBHa05gix6UIRjfCDiUV3w/8VIdZ247VyyO2gEjaw12WEVhdIjlxp/AMzXxqxQwbxNTDVZ3Mwd2RG5rw==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [win32] - '@biomejs/cli-win32-x64@2.5.1': resolution: {integrity: sha512-6uxpR9hvaglANkZemeSiN/FhYgkGasrEGn267eXIWvjrjJ2LhDlk251IhjVJq6MXzkV2/bcXwLwSroLyPtqRZg==} engines: {node: '>=14.21.3'} @@ -919,8 +860,8 @@ packages: resolution: {integrity: sha512-zccHj2z2oCCO4yrDiRSlFOxWerGqRiysP7a5jPK6uoI9URKAquwY42Dd/iUP8JWHxEzdRe4TlbvZCo8z1/mhrw==} engines: {node: '>= 20.12.0'} - '@codemirror/autocomplete@6.20.1': - resolution: {integrity: sha512-1cvg3Vz1dSSToCNlJfRA2WSI4ht3K+WplO0UMOgmUYPivCyy2oueZY6Lx7M9wThm7SDUBViRmuT+OG/i8+ON9A==} + '@codemirror/autocomplete@6.20.3': + resolution: {integrity: sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==} '@codemirror/commands@6.10.4': resolution: {integrity: sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg==} @@ -928,29 +869,33 @@ packages: '@codemirror/lang-css@6.3.1': resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==} - '@codemirror/lang-html@6.4.11': - resolution: {integrity: sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==} + '@codemirror/lang-html@6.4.12': + resolution: {integrity: sha512-pw2ReWKUqSkbvh76RAT4NYxiogRu+PWkR2ukAwO9uOgrm8uipkzjtKKtNpyeAQwHOqxEeSvAXZ6vr3AfyB9y/w==} '@codemirror/lang-javascript@6.2.5': resolution: {integrity: sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==} - '@codemirror/lang-markdown@6.5.0': - resolution: {integrity: sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==} + '@codemirror/lang-markdown@6.5.2': + resolution: {integrity: sha512-AwBOdkWYuA//WcM0xO5PfHPUcmz/O2i5o0Nsg1U69SII/loCJlFI1Romd9xp2HYb1kYJRGZotyqRghuHH5n8Kw==} '@codemirror/language@6.12.4': resolution: {integrity: sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==} - '@codemirror/lint@6.9.5': - resolution: {integrity: sha512-GElsbU9G7QT9xXhpUg1zWGmftA/7jamh+7+ydKRuT0ORpWS3wOSP0yT1FOlIZa7mIJjpVPipErsyvVqB9cfTFA==} + '@codemirror/lint@6.9.7': + resolution: {integrity: sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==} - '@codemirror/state@6.7.0': - resolution: {integrity: sha512-Zbl9NyscLMZkfXPQnNAIIAFftidrA1UbcJEIMp24C0Bukc2I5T8wJS0wsXYsnDOqCFJUeJ1BITGNs5CqPDSmSg==} + '@codemirror/state@6.7.1': + resolution: {integrity: sha512-9QzNDgE4EYDnAHfrTlR2lwiPciiOymLtwKK+8yHQzCc7GXhAP9xdEbEJFy2IWB1j9UGUl9BsgMmTo/ImA02T7A==} '@codemirror/theme-one-dark@6.1.3': resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==} - '@codemirror/view@6.43.4': - resolution: {integrity: sha512-YImu23iyKfncJzT7sRy+rEqEhSc8RhOHqDxwy4WzXRKJwYm6iwf/9OJk5ctCAdZ6yi2ZqaGEvmf55fSVqMDrgg==} + '@codemirror/view@6.43.8': + resolution: {integrity: sha512-qtItTDssZ/5GFfi94hrILu9j/VUeFPDPkhovEfmWFj2ipTxnzPB8DdHgfbb8HYTzLTYhrndKmyQxXUz/PDLenw==} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} '@dagrejs/dagre@1.1.8': resolution: {integrity: sha512-5SEDlndt4W/LaVzPYJW+bSmSEZc9EzTf8rJ20WCKvjS5EAZAN0b+x0Yww7VMT4R3Wootkg+X9bUfUxazYw6Blw==} @@ -1004,312 +949,156 @@ packages: resolution: {integrity: sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg==} engines: {node: '>=18.0.0'} - '@esbuild/aix-ppc64@0.28.0': - resolution: {integrity: sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.28.1': resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.28.0': - resolution: {integrity: sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.28.1': resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.28.0': - resolution: {integrity: sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.28.1': resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.28.0': - resolution: {integrity: sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.28.1': resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.28.0': - resolution: {integrity: sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.28.1': resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.28.0': - resolution: {integrity: sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.28.1': resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.28.0': - resolution: {integrity: sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.28.1': resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.28.0': - resolution: {integrity: sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.28.1': resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.28.0': - resolution: {integrity: sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.28.1': resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.28.0': - resolution: {integrity: sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.28.1': resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.28.0': - resolution: {integrity: sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.28.1': resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.28.0': - resolution: {integrity: sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.28.1': resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.28.0': - resolution: {integrity: sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.28.1': resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.28.0': - resolution: {integrity: sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.28.1': resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.28.0': - resolution: {integrity: sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.28.1': resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.28.0': - resolution: {integrity: sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.28.1': resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.28.0': - resolution: {integrity: sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.28.1': resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.28.0': - resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-arm64@0.28.1': resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.28.0': - resolution: {integrity: sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.28.1': resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.28.0': - resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.28.1': resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.28.0': - resolution: {integrity: sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.28.1': resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.28.0': - resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - '@esbuild/openharmony-arm64@0.28.1': resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.28.0': - resolution: {integrity: sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.28.1': resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.28.0': - resolution: {integrity: sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.28.1': resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.28.0': - resolution: {integrity: sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.28.1': resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.28.0': - resolution: {integrity: sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.28.1': resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} engines: {node: '>=18'} @@ -1349,14 +1138,14 @@ packages: '@fastify/busboy@3.2.0': resolution: {integrity: sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==} - '@floating-ui/core@1.7.5': - resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} - '@floating-ui/dom@1.7.6': - resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} - '@floating-ui/react-dom@2.1.8': - resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} + '@floating-ui/react-dom@2.1.9': + resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' @@ -1373,15 +1162,15 @@ packages: react: '>=17.0.0' react-dom: '>=17.0.0' - '@floating-ui/utils@0.2.11': - resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} '@fortawesome/fontawesome-free@6.7.2': resolution: {integrity: sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA==} engines: {node: '>=6'} - '@gql.tada/cli-utils@1.9.2': - resolution: {integrity: sha512-cVNs4v8ewLRYJfyAsaHbiAmd5Hm+zXEMvMhBksH58ZU87d6f8crsp2CQG6QtIqnJJw1q0CBWfWTZepGWNcL3QA==} + '@gql.tada/cli-utils@1.9.3': + resolution: {integrity: sha512-P1TiXErpJwIi73sei5fzwGA/SOeCaIHFWFR4RdZPLwqxZzQN0T6MAUivzXBgKCORL67rvYnLaaPoWeqWq/61ug==} peerDependencies: '@0no-co/graphqlsp': ^1.16.0 '@gql.tada/svelte-support': 1.0.3 @@ -1394,8 +1183,8 @@ packages: '@gql.tada/vue-support': optional: true - '@gql.tada/internal@1.2.1': - resolution: {integrity: sha512-1kPMv9KRpD6mfVwtXK+iy43U/gi4bpr4ganfhPLD0TjxpbuVJm7CtZ9wMFdwy3FjLBFN2QhwscNnL7lRPHg4vg==} + '@gql.tada/internal@1.2.2': + resolution: {integrity: sha512-4lZcElPP6MC8Ct8KN70LR2WQsHjbAsyAmTGG09VsOPhx738UFIYaL0S1XiI2pqq2tj/sDs/y3b9jvKoWGL7iuQ==} peerDependencies: graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1439,109 +1228,109 @@ packages: graphql-ws: optional: true - '@graphql-codegen/add@7.0.1': - resolution: {integrity: sha512-kWw6RMu9ysBw1wcgcgf9mOnswc5M3ekOApDTiaJC/UZNTEYins01srZHYTP7z3P/WlGGC844BRtjwh3U2kNd/A==} + '@graphql-codegen/add@7.1.0': + resolution: {integrity: sha512-bytJg1kel5zfgK3JSYbGwtpbNe6F9OPZSR6DiMDe9RVxblAgl6w4zEEPd/mM3rhNJ1VmGYLbNnf5e1eUfXQEbg==} engines: {node: '>=16'} peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-codegen/cli@7.1.3': - resolution: {integrity: sha512-mMYwpvpqJjjHoA/c6HBjdlbT8JqFC6W85RB80tpHACapufBnLlyNtYHYeOYAoUuU1n3cGQi1if1pKHnjLgS/eQ==} + '@graphql-codegen/cli@7.2.0': + resolution: {integrity: sha512-JPJw2vquEIpO3b8XJyxFVTrYi6WRn/OKu/SlzQA+IwAVT7GZPeG+AHmfRXAvpVMj31899nTpQYEQGUxx3ZqubQ==} engines: {node: '>=16'} hasBin: true peerDependencies: '@parcel/watcher': ^2.1.0 - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 peerDependenciesMeta: '@parcel/watcher': optional: true - '@graphql-codegen/client-preset@6.0.1': - resolution: {integrity: sha512-6wh0ZHG9WzBD6bE4AVOO6VCCMXK2orxHuXxaNKj+sj1w0qZ3Y3WIjZnqZLg6JZrHCIs/e+gy3T15Dc2pH8IbHA==} + '@graphql-codegen/client-preset@6.1.2': + resolution: {integrity: sha512-1ZxyQXoTyK2Q0i46PBAa7meQ+Ds/tJxBHhTkqDbPhNdNeeabcvznPksIek2pHx4wSwc8jvIT87CktrWj5n5Cqg==} engines: {node: '>=16'} peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 graphql-sock: ^1.0.0 peerDependenciesMeta: graphql-sock: optional: true - '@graphql-codegen/core@6.1.0': - resolution: {integrity: sha512-jReAzuCYlrSBJHW2bfBpDl/vMRCw0yQEoTvGi9K+3OTsazDXEQGOpCVfj8p/xO2h7ynu5Yrvzo0sUylVv0CnwA==} + '@graphql-codegen/core@6.2.0': + resolution: {integrity: sha512-RZadhhwYhuy2ZdIGK40vYVBMzXEFGkCC+58MUC/F2af/gKznEYNzHgmNBUBCk/BTklyUsNu0mIXmyGE4tTA0PA==} engines: {node: '>=16'} peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-codegen/gql-tag-operations@6.0.1': - resolution: {integrity: sha512-eHYUIchZLG6G+kafeKnUByL2Nkmb8Uj2vg33UVLFj8XJ2coC4b1iRDWxCdTXbupZrN0FaM0QRRBLs3zBEAzcJg==} + '@graphql-codegen/gql-tag-operations@6.1.0': + resolution: {integrity: sha512-AmMcZFwonufvWJnQm7I0lBxKpAm+35BcCrOOvUlBoviohiR17aPoTGAOaNAEtpcpI86lnZ9m9AXUdiKMdm8nnQ==} engines: {node: '>=16'} peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-codegen/plugin-helpers@7.0.1': - resolution: {integrity: sha512-S2X0YT3XQbP2haqhIeku8GOXo2j8QuBu7BrLsOEHz4UeMu78y3rja1Q4ri3oJ0jq4dMgaQlazoVHI/A+FAKMGw==} + '@graphql-codegen/plugin-helpers@7.1.0': + resolution: {integrity: sha512-ieJH7kZ5oSZKBPJs7CvHMrFY/CLYLklqv74ir93qMwRna6geZsbIMoJzTDBXohxcQTITiProiYSGrEtZjIpYGg==} engines: {node: '>=16'} peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-codegen/schema-ast@6.0.1': - resolution: {integrity: sha512-P16b6XCWXfcrA4fkuAyqoy883USAULifv8YWgEOrNKDAnr2DR+Kr85jSomknIUTY39wiuvisv4/lrdXobwK6sA==} + '@graphql-codegen/schema-ast@6.1.0': + resolution: {integrity: sha512-/xuGkM5gUNFRoaQLumKbENdX7Hc8ha49z9OXsEZY8E+46mMjqzXGF0NtCJ892cmoX7EUgI5c8T+LZqS2upx2Aw==} engines: {node: '>=16'} peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-codegen/typed-document-node@7.0.3': - resolution: {integrity: sha512-l/4KenYJG5D8Aj6Aa2KPeS0fdMIIi04Qx28d4SLwMWuyFU9WXspU5mR9YMNDRZzaTgBtGR8aMIl9RzyiWf5uUw==} + '@graphql-codegen/typed-document-node@7.1.0': + resolution: {integrity: sha512-V6H+ItyqXtYY+JQb76LAoN627Xfzpn29/ifwCFAv61iEepzNzh86sa+yZclflr0G8LDmhcVY5hpPJd3a1qbOfw==} engines: {node: '>=16'} peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-codegen/typescript-operations@6.0.4': - resolution: {integrity: sha512-YUmnmZcJ6wMcC2VmTE7gw18zoG4S1VF1aJmaupDrBnwfKGA5BmdKrA6k2Wj6ZkUDOduHI60l/cmZBRtqel4VtQ==} + '@graphql-codegen/typescript-operations@6.1.5': + resolution: {integrity: sha512-ZiQ2CB6jiYYxFetdrutSsbNsiukh47UbVY9y3NjwWI8IUlslD+rDYN7MLDJrPFA5YXpv38ZBxh6q8PywRf1KfA==} engines: {node: '>=16'} peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 graphql-sock: ^1.0.0 peerDependenciesMeta: graphql-sock: optional: true - '@graphql-codegen/typescript@6.0.2': - resolution: {integrity: sha512-zyLfKsFJ7TRkQ0PyaUVuiAek9TSbtVJwwBoOuaE9RAWr45+9Y5W1LYldpiSTcyfxKVSIniE7Gj0V87qzrpdyYw==} + '@graphql-codegen/typescript@6.1.0': + resolution: {integrity: sha512-2Hu3111O/AwV28Ap7tNsixlmXSAJuQbQArQklx+IC/tNswpckZnCfmlcBtTJrGU1+mJXEneJXGfb2XWvKjbhlQ==} engines: {node: '>=16'} peerDependencies: - graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql: ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-codegen/visitor-plugin-common@7.1.0': - resolution: {integrity: sha512-CO4fJyflbYBuAwbQD16bAuWBIXkz9il3JwyC+pQzXh8NJ+BZZDXmYjmVeGeJuoMUIQDb+CNo2thCU0bFFamAkg==} + '@graphql-codegen/visitor-plugin-common@7.2.4': + resolution: {integrity: sha512-VMq1LNVLIuG4rmumKhzTeCcTDhQf2PLVWWUGe7LOwLstBy8xQ+JdDWntsaK0FbLm8p1pCr707Sx6Iy+GbE6RTg==} engines: {node: '>=16'} peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 '@graphql-hive/signal@2.0.0': resolution: {integrity: sha512-Pz8wB3K0iU6ae9S1fWfsmJX24CcGeTo6hE7T44ucmV/ALKRj+bxClmqrYcDT7v3f0d12Rh4FAXBb6gon+WkDpQ==} engines: {node: '>=20.0.0'} - '@graphql-tools/apollo-engine-loader@8.0.30': - resolution: {integrity: sha512-hUydKGGECrWloERMmfoMzHZi12X99AM9geCGF5XVsv4iMRl/Iyuet24th4kC9bZ8MlAdCwAwtUsCyv9uRfYwSA==} + '@graphql-tools/apollo-engine-loader@8.0.34': + resolution: {integrity: sha512-pxmrIbUtpiH2/Dx0093EQ0x7dXWdlfAZ97uSY280CkUxIB8EYZeNeV2pvk6HksZzBtHrprLRysrjnegLOmQBRA==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/batch-execute@10.0.8': - resolution: {integrity: sha512-Kobt37qrVTFhX4HUK5/vPgMXFw/5f97AzmAlfmDBSRh/GnoAmLKCb48FrEI3gdeIwZB2fEhVHJyDqsojldnLQA==} + '@graphql-tools/batch-execute@10.0.9': + resolution: {integrity: sha512-khIgAPlyaWJ3dVX6SsqOkABZCH1Gii32WHn3xMzavupsxPCfb/9G3zjdswptzTFrOcZ92dWo7MXvwNFkRfNN4w==} engines: {node: '>=20.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/code-file-loader@8.1.32': - resolution: {integrity: sha512-gR5mNQjn0BugDL8a4A+ovS2KEvU52RNOGnbwiq9oWAEHiSv7iqJu77bpWARTzlE1ZFPK5MSQe9218+1t5PbXmQ==} + '@graphql-tools/code-file-loader@8.1.36': + resolution: {integrity: sha512-EAIogV/vUmcrNa4icqnx5Xr5z3uLNSZ6867DEJyizuUfOCnD5JzCBQNsBjOBl3R5rzUiV3+GGSzzo15/jLN4oQ==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/delegate@12.0.17': - resolution: {integrity: sha512-pIVszWEm69rF+bkM0jUyM1KdIxGzygQbIp1GtV1CuEGRB8lN1uFY1eeTzM2nudHXg8cj+XSVO8cnRpph+o8Dmg==} + '@graphql-tools/delegate@12.1.1': + resolution: {integrity: sha512-BiePOU2Nev9KDpAEOw25isTm6y/Ea6Sb3c/aH0P9s25c/6mzluvpEo66nnA9vWeirIRScS7rUtN0Jv3P02h3VA==} engines: {node: '>=20.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 @@ -1570,62 +1359,62 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/executor-legacy-ws@1.1.28': - resolution: {integrity: sha512-O4uj93GG9iUb3s32eyhUohvyfA8mLhN8FvGzEdK628hFQPhZN75yurtVFrR08DHex71mQ3wYCCFkErpwdJbDDQ==} + '@graphql-tools/executor-legacy-ws@1.1.32': + resolution: {integrity: sha512-rmSp846dAgEGtwdQ7ntdgpLJzzRvw5rE8sR7ASPci2QoIn3McxVYwjq52SMNntoH4VaBeI/BrpyQIN5L68zAfA==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/executor@1.5.3': - resolution: {integrity: sha512-mgBFC0bsrZPZLu9EnydpMnAuQ8Iiq0CEbUcsmvXsm2/iYektGHDN/+bmb7hicA6dWZtdPfklYJmr21WD0GnOfA==} + '@graphql-tools/executor@1.5.7': + resolution: {integrity: sha512-UcXVClkBml+qyGEsQxfEaAkboqySVGFUd9ivn7pDc9jZSckgF6zL21cNxuRH5ZA2exneV3PTtcc0I0rDOdE0Tg==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/git-loader@8.0.36': - resolution: {integrity: sha512-PDDakesRu8FJYHJLf9/gkTweh8M19Bymz9i+vOlk9OTs9XmNcCqKM+1S610KX2AodvuBFz/xbesjTtTJIppLPg==} + '@graphql-tools/git-loader@8.0.40': + resolution: {integrity: sha512-aQkcTTymjeQBREoH8/x5JZWt/banq9D7fs8Gqqd2HGirh8JBYGoEbKm45bSdUqCLnqsOJ2oal5A73rsC7ASASw==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/github-loader@9.1.2': - resolution: {integrity: sha512-jhRJncj9Wkr1Cd8Mo3QI2oG6fTw5ILr1/OXcHIqx744NBj8pPwQBXmQzZqh7MXxbekl2EAcum7SJIjq1HpYcPA==} + '@graphql-tools/github-loader@9.1.6': + resolution: {integrity: sha512-hWsCcTZJ5NLKDUYynZjK7kVh/xmc/MpnLkBII+WQHCLOOXimaESZRnUuoo/nMqOwBKghBC0iF1nHiG9PqD9t3w==} engines: {node: '>=20.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/graphql-file-loader@8.1.14': - resolution: {integrity: sha512-CfAcsSEVkkHfEXLFzrd5rUYpcQEGWNV8lfc1Tb1p5m9HnYICzDDH08I5V33iMrEDza3GuujjjRBYqplBkqwIow==} + '@graphql-tools/graphql-file-loader@8.1.18': + resolution: {integrity: sha512-MBbAPFfGZN+jaRQQkqfY1Ztj4ftFgk9m7zh0h1jQC83xsVJ8zvMk2TGwg7g3lEGqa0cLOOEp/a1/78MSdhj2Zg==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/graphql-tag-pluck@8.3.31': - resolution: {integrity: sha512-ema2RRPZGj8TKruNElyDBHVCNFMxioGIVfLBuiA+GdfmRGt95b/i7Uksnj4EwItA6MCmhxokxZoa/fl6mJt3tw==} + '@graphql-tools/graphql-tag-pluck@8.3.35': + resolution: {integrity: sha512-k6udGhRFzf/FnfV/pl+2dxVURHtqdOj8evG3xFJahMS9bSMLkmTRCqTaR8e+ErYZEUODE2zCSPth3JLQEfAEqQ==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/import@7.1.14': - resolution: {integrity: sha512-aqLcu04aEidszbXM6M0PWWL8bP17eX9sxXwjYWpglLvIRd4NFqb3C9QzBY8pleqXNMtWqXktlm9BQjevgSrirQ==} + '@graphql-tools/import@7.1.18': + resolution: {integrity: sha512-/lCEk28rbUiypbX8jl5x0RBiHDsXk3YJ5jAU1nlqXEdxNiNI6p5ts+vt0N7UximIuolwV7BeRxLn5RUejyKZYQ==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/json-file-loader@8.0.28': - resolution: {integrity: sha512-qgCsSkPArnjlNkcYpgGKiXxCTNkrAT9E+l1LhR+Por2jTlKBBeZ8stortkQ/PNDDjuL0WPrLQmHKhNPHabnB3A==} + '@graphql-tools/json-file-loader@8.0.32': + resolution: {integrity: sha512-PJ06nGC836vWWLCAPignLKAnIF+CKNkXlCIe9zkkb02jFPNdG5nA0PUxa1rqt/lqZd/x/ravZQ4yM47pxtLajg==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/load@8.1.10': - resolution: {integrity: sha512-hjcvfEFtwtc8vGi46wtpmGWadNzfEhzbjqinyFIZuIZPlR4aYdWQtqWtY/RMM4Ew4t1USkMNm6xrqC2TH1vCSA==} + '@graphql-tools/load@8.1.15': + resolution: {integrity: sha512-QpCve0kf1IxNOWAk99VjS4CEZinQjKAfsgDDRWGUg9+9TqBbvCdsLAQshykHcfueEUPetVRVqqqOIRKo9eJ2xQ==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/merge@9.1.9': - resolution: {integrity: sha512-iHUWNjRHeQRYdgIMIuChThOwoKzA9vrzYeslgfBo5eUYEyHGZCoDPjAavssoYXLwstYt1dZj2J22jSzc2DrN0Q==} + '@graphql-tools/merge@9.2.2': + resolution: {integrity: sha512-DSLLAztOIQId7QE3m8Ehk5lV+0pjxNSSRDHPzlYQ9E4KJ9AoUMBprC4C+eX3v4srh05S2ujm5/veqAr5yEWFSQ==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 @@ -1636,32 +1425,32 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/relay-operation-optimizer@7.1.4': - resolution: {integrity: sha512-cwOD/GEo/R//1uGCP0/urIxsMFoUgzkJVyMt9BDM2HhQhU6rSgH5l6lFukAFTJyPJVdyeOdYm2i0Jj5vYWbHTw==} + '@graphql-tools/relay-operation-optimizer@7.1.8': + resolution: {integrity: sha512-s16NYT+66VSLCITBURoGNTwh+YvZRSlW3MtFJC2gsvrHZHf6KpCvIR3Qju4yh0FtCoN7Wg7yOI/JT/UzaMEOwQ==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/schema@10.0.33': - resolution: {integrity: sha512-O6P3RIftO0jafnSsFAqpjurUuUxJ43s/AdPVLQsBkI6y4Ic/tKm4C1Qm1KKQsCDTOxXPJClh/v3g7k7yLKCFBQ==} + '@graphql-tools/schema@10.0.38': + resolution: {integrity: sha512-Kckk2/vm+rELJ7ijvFaAn9ouWSVUTD0D4SJIvYF4rKeuQHAfCzE/jzMFonZVpTXleqJjPXLZjVbuaMorw7A5Og==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/url-loader@9.1.2': - resolution: {integrity: sha512-pVSiPrfWQKb3jq23Pl7EjbB2uv3tgZLnWo/axkmg4itAEZ5s/vV/jKa8P1HZzUnSVUTR+8tcEZVeNsUbzFCbkg==} + '@graphql-tools/url-loader@9.1.6': + resolution: {integrity: sha512-BUFafQJv1OVZ/pZvzqXx4oLi2SKeqiWUAIDgz9HGRF/UpJuLY98tYMFzxhYurXg7lAuJMrDjUfl2nFSCX743Nw==} engines: {node: '>=20.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/utils@11.1.0': - resolution: {integrity: sha512-PtFVG4r8Z2LEBSaPYQMusBiB3o6kjLVJyjCLbnWem/SpSuM21v6LTmgpkXfYU1qpBV2UGsFyuEnSJInl8fR1Ag==} + '@graphql-tools/utils@11.2.2': + resolution: {integrity: sha512-Do2A/t6ayqOma8m7PvpYMsCBswL+NB35BQSng4GWSBqafL2/BnfAZy/NSENPwV721Rm2fG0BHETFC0+FJJZmLw==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/wrap@11.1.16': - resolution: {integrity: sha512-JW1XGFTmltXa537J2bAr8dN/n6EWwiBuM9q8V8mWqZ0eWrf++/TT3/mlV3c0M8B8nrS/lqSsotIwPAtVZR8sWQ==} + '@graphql-tools/wrap@11.1.21': + resolution: {integrity: sha512-28a+cTtDONeO+Bg+241biALEHdZuIyFk7libduztuh+Ecwk5hCZgLVFn1S5yJXgvMvkm9+MRVSRQaWILsyougA==} engines: {node: '>=20.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 @@ -1849,14 +1638,14 @@ packages: '@types/node': optional: true - '@internationalized/date@3.12.2': - resolution: {integrity: sha512-FY1Y+H64NDs+HAF6omlnWxm3mEpfgaCSWtL5l551ZZfImA+kGjPFgrnJrGjH6lfmLL0g8Z/mBu1R3kufeCp6Jw==} + '@internationalized/date@3.12.3': + resolution: {integrity: sha512-fuLX+3ZKLsxI73y8b01EG/WjHb6gE6weCqlfawPO27kBWGMh9G1yH6Csv1uU7/cac9H2GHmOMt6CjmuQ1aia4Q==} '@internationalized/number@3.6.7': resolution: {integrity: sha512-3ji1fcrT+FPAK86UqEhB/psHixYo6niWPJtt7+qRaYFynt/BaJG8GhAPimtWUpEiVSTq8ZM8L5psMxGquiB/Vg==} - '@internationalized/string@3.2.9': - resolution: {integrity: sha512-kzP/M/mbQxODlmOt4bIQZ2SBVUWUSqMLXooXixnX7noche8WHaQcA+nwFN1K2KCF/cp+LDUhcJsCicwkvhD1pg==} + '@internationalized/string@3.2.10': + resolution: {integrity: sha512-PDx6//vHSpRnHfxqMqto11zQvhsaU74O3mKv2F/0eicGZcl9NLjQmGlbHz/LsJh5tLKp4A4L7ZVTzN1/MmMTvA==} '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0': resolution: {integrity: sha512-qvsTEwEFefhdirGOPnu9Wp6ChfIwy2dBCRuETU3uE+4cC+PFoxMSiiEhxk4lOluA34eARHA0OxqsEUYDqRMgeQ==} @@ -1883,6 +1672,9 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@kwsites/file-exists@1.1.1': resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} @@ -1892,8 +1684,8 @@ packages: '@lezer/common@1.5.2': resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==} - '@lezer/css@1.3.3': - resolution: {integrity: sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==} + '@lezer/css@1.3.6': + resolution: {integrity: sha512-YJE78Wcg+zX8f10hiHWQ4Az48Qr/c13eId0VtRQYLBpxHDmDeSrXIlkbl+fJGW42rWC/uoUco9mhBZeVWP/A1g==} '@lezer/highlight@1.2.3': resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==} @@ -1907,36 +1699,32 @@ packages: '@lezer/lr@1.4.10': resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==} - '@lezer/markdown@1.6.3': - resolution: {integrity: sha512-jpGm5Ps+XErS+xA4urw7ogEGkeZOahVQF21Z6oECF0sj+2liwZopd2+I8uH5I/vZsRuuze3OxBREIANLf6KKUw==} + '@lezer/markdown@1.7.2': + resolution: {integrity: sha512-iTkYvoVcKt3WkeL7qUDyXHONZEwLio4wj8KTNi2dnjQEXBZKMV63BpQrPqfsM+OkvuRbiSTAcycYAsQzLhRNoQ==} - '@marijn/find-cluster-break@1.0.2': - resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + '@marijn/find-cluster-break@1.0.3': + resolution: {integrity: sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==} - '@mermaid-js/parser@1.1.1': - resolution: {integrity: sha512-VuHdsYMK1bT6X2JbcAaWAhugTRvRBRyuZgd+c22swUeI9g/ntaxF7CY7dYarhZovofCbUNO0G7JesfmNtjYOCw==} + '@mermaid-js/parser@1.2.0': + resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==} '@n1ru4l/push-pull-async-iterable-iterator@3.2.0': resolution: {integrity: sha512-3fkKj25kEjsfObL6IlKPAlHYPq/oYwUkkQ03zsTTiDjD7vg/RxjdiLeCydqtxHZP0JgsXL3D/X5oAkMGzuUp/Q==} engines: {node: '>=12'} - '@napi-rs/wasm-runtime@1.1.4': - resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} - peerDependencies: - '@emnapi/core': ^1.7.1 - '@emnapi/runtime': ^1.7.1 - - '@napi-rs/wasm-runtime@1.1.5': - resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + '@napi-rs/wasm-runtime@1.2.2': + resolution: {integrity: sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} peerDependencies: - '@emnapi/core': ^1.7.1 - '@emnapi/runtime': ^1.7.1 + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.3 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.3 - '@napi-rs/wasm-runtime@1.1.6': - resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + '@napi-rs/wasm-runtime@1.2.3': + resolution: {integrity: sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} peerDependencies: - '@emnapi/core': ^1.7.1 - '@emnapi/runtime': ^1.7.1 + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -2004,8 +1792,8 @@ packages: cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm-eabi@0.141.0': - resolution: {integrity: sha512-jk7086MFvR/T4DG9IY7MKBVt1PMxvSZoz/TvnifodvS0pjghVwJHRttnAExhlwdMOgHv1TmLdENnbNpYk2zjvA==} + '@oxc-parser/binding-android-arm-eabi@0.142.0': + resolution: {integrity: sha512-ZiRGDutGsv1G6bL/ozy/koC0Sv39T1DqyoC4KD1DOy9ZoACm1O5UWhEK2c02Qdk+4lfLVkvFa/mQ0fm/4h1BtQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] @@ -2022,8 +1810,8 @@ packages: cpu: [arm64] os: [android] - '@oxc-parser/binding-android-arm64@0.141.0': - resolution: {integrity: sha512-a4XDQ27ZT7e7zwAlxJDTiCA7IBGWDuy2+MhFq85Of7XlBSmpkfcBFml11q0Zx6f7RMuI0B4xCtt2ytBS4yOptg==} + '@oxc-parser/binding-android-arm64@0.142.0': + resolution: {integrity: sha512-WZkvGRLNQTz8lR9zP5nLjUdlroRCopBu3g9zF1p/laE6DzT1UbQo8Rdz5MWhaJUPYg/6gp+jo7HUgsyKaN1FtQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] @@ -2040,8 +1828,8 @@ packages: cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-arm64@0.141.0': - resolution: {integrity: sha512-m/kVk6rzYmBeHYnz+1Y5fod00AVTTxMbC71azFfm/zjx1j9XxwKtA0+VfkKuVMC8rbghb9TtfevnuWZa9OuPEg==} + '@oxc-parser/binding-darwin-arm64@0.142.0': + resolution: {integrity: sha512-l4khS8LQOOVYsGRVARo1gSaCT/aBSceUVXgtovWc2+drnxVuDr082WA3OCHVdVzIz5JIrP/y9CWsSKxBDNmYGg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] @@ -2058,8 +1846,8 @@ packages: cpu: [x64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.141.0': - resolution: {integrity: sha512-o0X+6KZlfucWU/v5oKRQPwdFXsXAjW8jmpo/Gpw/qyKsbKtlfkHoeH9Bjp/m13TwjewvJnCkwF0DWzgpC4HjTQ==} + '@oxc-parser/binding-darwin-x64@0.142.0': + resolution: {integrity: sha512-QBsNF3nqlXmcH2B1YOPqQYmCJoy4HuIjUxGbBO/k5JAJUl68ghU2psRY2zPk+RyBaWqKP/qfL4oaFgEMCdwskA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] @@ -2076,8 +1864,8 @@ packages: cpu: [x64] os: [freebsd] - '@oxc-parser/binding-freebsd-x64@0.141.0': - resolution: {integrity: sha512-W5KbTnNkTMMMylqj6dYqnsXvkmESVPodPKYLJ5zdzIPdl9fUJtolkpUeSzYEbGGYB4a4A4avl3EePnZ/wLIdJg==} + '@oxc-parser/binding-freebsd-x64@0.142.0': + resolution: {integrity: sha512-b7Q7m4Cqc6XqNhri3R+QhU+GVy646Pn+bkdhrDdWym/Fdi0ZUa+d73H9dm5H91JtbtAQ/z1d8XKMW3oOV8a4tQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] @@ -2094,8 +1882,8 @@ packages: cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-gnueabihf@0.141.0': - resolution: {integrity: sha512-g3dtbJa8zeOGK36Sr9cQavsdi5H/ie2hVjrSjIxsNAR1qZA40ZYVXnfdfoMAlq8CmB9qFL1yhsSCUHeNmdmt8w==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.142.0': + resolution: {integrity: sha512-3riVS5IhdH3uCZj1Y9ftDQlR0dvLsIlw/edrRqk8JhgNd5K0XSs+UBtgh50N13CAlW9/TXj6sVGXaKNBocd0Yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] @@ -2112,8 +1900,8 @@ packages: cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.141.0': - resolution: {integrity: sha512-e6hwQqd+3lvP13G2jxvFpoA7dzHcFLN+Mq47JCVMtdNHbbyBRo756JCtbbJH6ca8inTfyqZoqBmS3vhQlzAK2w==} + '@oxc-parser/binding-linux-arm-musleabihf@0.142.0': + resolution: {integrity: sha512-NmXUOpgpTSkhl795TiXmWppTwmSJ92RC1qvD6e4XOF+slgmo3e6Ah+kEu+6AN8s7NAOEwqGmir58MgSQSWmBSA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] @@ -2132,8 +1920,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-gnu@0.141.0': - resolution: {integrity: sha512-vXz2BLAuypA+4MLyBg94pzEo6THVnzYnCtAjXoihIIQo0t2pnp/AmW+SH1EI+4VbuJnC//KplIJ5yyaCGua4jA==} + '@oxc-parser/binding-linux-arm64-gnu@0.142.0': + resolution: {integrity: sha512-gc0EXsKtXgerujmU2Bql3u1L1HsSQ2774R83idq/FoNMPVV/RY/1ErFsvnit7KoiP/sLvzQixeUo4Ut0ic0wmw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -2153,8 +1941,8 @@ packages: os: [linux] libc: [musl] - '@oxc-parser/binding-linux-arm64-musl@0.141.0': - resolution: {integrity: sha512-jMkS/EztNW34HKsXIaT/SoHcmtocq/vWhwFOVduF9kduuuRIVwfwQ6uxzIO+qPKSXdd2TXt54of0BJ2zFMXnmw==} + '@oxc-parser/binding-linux-arm64-musl@0.142.0': + resolution: {integrity: sha512-F2XvmWSE0uWpie+jHKKIFgdVOe9ypGhkEZxKx5DuW215K6cbAC274yYaPkcM7EqY4Df3Weyhpcz3lsURyH2LVg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -2174,8 +1962,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-ppc64-gnu@0.141.0': - resolution: {integrity: sha512-vo+MR+n3zQJ6Mq92hiP084NZcgDv5iJlVR02gMf28neMvVT1tKVm7VeiW/DxhdqOi3QLeaXIk9cUcLL1qrkngw==} + '@oxc-parser/binding-linux-ppc64-gnu@0.142.0': + resolution: {integrity: sha512-wLMbT21U/QxknQsk+VvNF0b9D2/aGWhcaQQQ+VYlE8FwD5+GoWZIPPXNzyHmkYyhm0KB3itL+TBavjMatqNnYA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] @@ -2195,8 +1983,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.141.0': - resolution: {integrity: sha512-oh80w+7RuiO5gBp9Jnoa/H8Qlt3JsHL2MkW+0dwEdlDMdslVZX/YsekSK6EeyEenY66/mhCfypsNATQ7Ph3qlQ==} + '@oxc-parser/binding-linux-riscv64-gnu@0.142.0': + resolution: {integrity: sha512-+G8F/4ckwT7FCJV4H2bt09xEzJbjNCfuL4Sp1AYNaFtFMVtgIGMuJlteT82U+K0UIZ/DzAR/LDlMFnEuajG7Kw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] @@ -2216,8 +2004,8 @@ packages: os: [linux] libc: [musl] - '@oxc-parser/binding-linux-riscv64-musl@0.141.0': - resolution: {integrity: sha512-LOyEmFA8sCnYbEXP1+iQvCC/P1YXHMA/t6x1Ksp0Y9VwhLFsiBJFzV1zIxrOIE2LKaGGhDjQ29xq9cbq6omDXA==} + '@oxc-parser/binding-linux-riscv64-musl@0.142.0': + resolution: {integrity: sha512-hTsHtTLxMAfCo+rpF5K3qZJKW2NpPN/CHd4mYB3y7XlSdspHkd2gehDIofP64AacA9nWQw2tY3O7wR6UY8IVOA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] @@ -2237,8 +2025,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-s390x-gnu@0.141.0': - resolution: {integrity: sha512-3wnwk/l1CvszVE5TJR1wSl/zSEfydRqrNhn6s7Vr9IzSJpUQIroqVsIoPARHRFA+FQwkxAFDAHDAasa7v8OobQ==} + '@oxc-parser/binding-linux-s390x-gnu@0.142.0': + resolution: {integrity: sha512-6y7qYY3TCUDYjqswImdTGl92y+KA/80twALegQPN27kfY+bG7Ib1+L3jbmrCZQx6wrVnai9IPsEZp07I0hx7JQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] @@ -2258,8 +2046,8 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.141.0': - resolution: {integrity: sha512-qtyQVAAebFq57B2tifTlel3TgGqUtsYNI/e+p6aya9rN9lOZVTDvr215fGYSA9XWooxzMxDiVxkBLk2jQHbsOQ==} + '@oxc-parser/binding-linux-x64-gnu@0.142.0': + resolution: {integrity: sha512-i69kAWU+2LgoH5bR+zWiiu+UzAw7Oxkwv7COeJTeY19pn4e70nKQcr9Pm6cL2Z0Z54d+gl9qADlK/0yyuCPiBA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -2279,8 +2067,8 @@ packages: os: [linux] libc: [musl] - '@oxc-parser/binding-linux-x64-musl@0.141.0': - resolution: {integrity: sha512-SkGV1nKw40roEc94pv5EaaeH2ay14G6+roe8Q0wIUC1LcEKxzKW921h7+ZuZX0D3q2Mb/7aSFmxEVqnko3lPRw==} + '@oxc-parser/binding-linux-x64-musl@0.142.0': + resolution: {integrity: sha512-4SQs678MmjYVrmhAgCWD4o0vpaFszXw9xLX5p2Z9MMFcltxiLkA88wQjh80YHjPrXtpyZ2CWI5m+1yNKM0m2Pw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -2298,8 +2086,8 @@ packages: cpu: [arm64] os: [openharmony] - '@oxc-parser/binding-openharmony-arm64@0.141.0': - resolution: {integrity: sha512-cVgDM7n8QziQqOaP5hNgUYfMG7S/ZeuPxFWXnnHRv7rh025COk0rfQ6eEdKG3j/GaUuyvNZN4ifF1J8KmuXLLA==} + '@oxc-parser/binding-openharmony-arm64@0.142.0': + resolution: {integrity: sha512-YHpx9N7Ln3a++Tc8rv+H7mrK1zyJQOAwCFg8LZ3lTs1T5afGWeZrLPhPT9HLnIwSjCyJqPWVMIrMxbjcmBr2oQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] @@ -2314,8 +2102,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@oxc-parser/binding-wasm32-wasi@0.141.0': - resolution: {integrity: sha512-HggH++Fkn3OilBn+bs3jpgIFQa34oMAyUUHy0vpGum+gt1Eb5nyLc8dNU/RAPSw6lsLrx7ncKtHSZE+3Sp0l2g==} + '@oxc-parser/binding-wasm32-wasi@0.142.0': + resolution: {integrity: sha512-3pLDyY3+oogW73RM5uehNgAiR/Xfb7fvO2Q1Z1gIqZ2+50XDVQmBVlRkHXZTU4gKnQHpwETNsYQVsJ3joVB2iA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] @@ -2331,8 +2119,8 @@ packages: cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-arm64-msvc@0.141.0': - resolution: {integrity: sha512-KLSEH9GwgbrqbJOjtGHt9STw96s+78yDzp7IDN8Lno+7Ut9sNBfZ4jYZIz4mD50qmWUjoOI7i9I6UENbhNbMZQ==} + '@oxc-parser/binding-win32-arm64-msvc@0.142.0': + resolution: {integrity: sha512-Had/VeVY28Oyb0K+Q4FV8KCzoBycIh93oDK6pCbya9lkzdq+ikMHMgBubsdqqlybjJmQRawCQRrnBRHyQwYvcQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] @@ -2349,8 +2137,8 @@ packages: cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.141.0': - resolution: {integrity: sha512-9UVWUOOCI/1YkiSSNjg2zyBJYM9E/t1A/8GNobd48JDn/fQ6mzxcVO3H08jb3rAaW/B1VBf8eCORTvSsO9T08g==} + '@oxc-parser/binding-win32-ia32-msvc@0.142.0': + resolution: {integrity: sha512-GGi3+YphVHavvgs6gum2UXoNCqzHAmPt/nXkn8ZQZstV2Q1qZD1Mn8fz/nWrDkefHQtrG/+1/XrbMxsBTo6Svw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] @@ -2367,8 +2155,8 @@ packages: cpu: [x64] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.141.0': - resolution: {integrity: sha512-HI/wsvbWT5RHHw5c37D0fEgeTd8/1Q4OJs5jUmEBc17VZFG6SsCIe4barq7NsAPPks/JW+3ayi3Rp+PQI5h4Kg==} + '@oxc-parser/binding-win32-x64-msvc@0.142.0': + resolution: {integrity: sha512-Ny/Wv4Us1LGC/ljwNTp+Hx3r/pH15EFfeDF0p+n898gt+TtRd6C9SccHcuUhDiNTb8s5tt7jdeAMDRQZ4Vq6hg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2379,11 +2167,11 @@ packages: '@oxc-project/types@0.137.0': resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==} - '@oxc-project/types@0.139.0': - resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} + '@oxc-project/types@0.142.0': + resolution: {integrity: sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==} - '@oxc-project/types@0.141.0': - resolution: {integrity: sha512-S4as7z0j0xQkXcJlyY5ehntwK8/wRkQb9Cyqw+J/N2rkWGQGK0SxD6X6DhQTc7qsxVTBxXbxZtBJh3mr3PtIzQ==} + '@oxc-project/types@0.143.0': + resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==} '@oxc-resolver/binding-android-arm-eabi@11.21.3': resolution: {integrity: sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==} @@ -2591,612 +2379,368 @@ packages: cpu: [x64] os: [win32] - '@oxfmt/binding-android-arm-eabi@0.55.0': - resolution: {integrity: sha512-+rFDOqQe5LOWgxrAJaZgLRudr6GQm0wGI6gtu7vVkrdLGjNMUSGbAlaCr8j7F2H2Er97vYQCU8WDb30onqMM1g==} + '@oxfmt/binding-android-arm-eabi@0.63.0': + resolution: {integrity: sha512-YmRth4ZPGgEXcgmkhvANbC9uD67dxmSobW7DQuyt5tOBOKvPnIpk5SVHBj88E+7wMNRI2FhqaDbOhQFBix+b8A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.63.0': + resolution: {integrity: sha512-icbahX8X2X3sRamOMecvdYeZXWjPDazRDIfvWfy7Ca1nc/ZDT2Y9k5Nt7s46EqFd7NQPdgk+CM3/SgIT5LPCaQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.63.0': + resolution: {integrity: sha512-WV+Ze5v5gI2qoj8jpAovt8KBTW8pjEz/AiMXXjeTQS+Bmf/MmZXTS40S8xNPDszX+W8WDv2Bbk6qKrMTtUGu1A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.63.0': + resolution: {integrity: sha512-CJGSBdDxXOWIpoFXHpverimCvz084KA7L483rqJ44c3jDtzv6d4qOSoR/V9ywSHfV+Ks1lwIj2P49BFhunLNAA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.63.0': + resolution: {integrity: sha512-BDfKY+KhL2078cgswBBFQPAYuxCy93bS/iC5frdSeSbTLcGrR6VC2hsuPTanoJmg84+wSyWl0wWC1eR+uTnkRg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.63.0': + resolution: {integrity: sha512-Ov1cQEXT4mj7cojAokWSS1eoxkoyvbDfAbxNsGIKY2o36kvdAaFzPxRN6NxFRk9fD72B8oCoTTX/NuYTUWlpsg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.63.0': + resolution: {integrity: sha512-0LE7ro3+6L79jcMANycAZfRaC7zxr9YZ2+vEL5uMD9QlEep+rS/r1kSJsnuLl991NXJZD60euh0PC1GHrR20vw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.63.0': + resolution: {integrity: sha512-izPk+2Z4gjuZK32Fqh5qXoMpT/2NXzLh++ob57HiEiVSQZ1iYXu8EKMzb+K5AvWyIEXhdDIt7ADjGGtFhkT9Bw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-arm64-musl@0.63.0': + resolution: {integrity: sha512-alPmbOuWXFXiSo+lOtv6X71C7SYMEDW2WVvywOvf9BwKgEhSNGhMTLeFVSjKUMCamcjbbgVdsWF8GN1uy8xshg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-ppc64-gnu@0.63.0': + resolution: {integrity: sha512-BdzCPvolJc4AWZ+YMzgUDJcDzbQWrFjYuqBHoNHNqP1aCaluQRJNs4k3vNU5IG7vTpjf9zeD73D7MFM1TecZpg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-gnu@0.63.0': + resolution: {integrity: sha512-7sIgfLzqtNKSkMGsGVyRpHwpjNezRg2XONvUOheFZs95TSZpM0JAuPpA8KrQFsWc4wPU95roX2O69JgH8igOgw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-musl@0.63.0': + resolution: {integrity: sha512-9Tcg0y0WcVa6Mm9AgcgFMseDS+VkFJZpKZ8We9SpDY4gg5jewSwln+0sO04QLcTS1BtfDl9MwR+NfID8L7PUTg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-s390x-gnu@0.63.0': + resolution: {integrity: sha512-qWKC1pEOpx1qYhXaugPhHUeXwSfqEOk2wJH2LqVXGPV5iQYfdAZdt+d2XDiX4DTSWA2QDMUcFB+wEORh3Xn/sA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-gnu@0.63.0': + resolution: {integrity: sha512-S9wXYOiGSqYGS4Fx/TFsY+xDd/7dE5s+rUgbA4TsHiVF9e8J3ZcKmP7dsP/7iqLI9Wz7Ic7TzEr3mdthRCTdrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-musl@0.63.0': + resolution: {integrity: sha512-5eGyTJuMZNwBSHCivXt8Yuta6GeTYksOPXRk2MIhajiyFGQx7bjaHIwY+ZusAoFHhT157A9x6sktLjYo9D5oMQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-openharmony-arm64@0.63.0': + resolution: {integrity: sha512-Rz7hx+Dv3DoW/S6pwVAyjfFXp7/trdQ1zg+vNmsdsdDNlUccugp4XNqambSuEAeP0DaG9k72AtNyfDXCEg0AGw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.63.0': + resolution: {integrity: sha512-T/IuizKN9mr4Xw6YYnptkXRNdLkyIlUZ7c8zfTOBpoytZyJ1BAsMUvsMDEx0X4YvSMpaivm+DR8112rQfzC25g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.63.0': + resolution: {integrity: sha512-XjrO5FJ5Wl9vsAxtCP1G/eaeT6y1K2s9CICUHGE42cEjou32/J6S+B1KnrOAboj6E7uhJnwPbRSvznWcxNdA0g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.63.0': + resolution: {integrity: sha512-sgsHCQy432OTQH4Ikk3tZptp3GqwnhwUDuY0loBH41zyHWfMZY9v8Dy78wsnSofHejvFozZGgJgBB1A0LQRwMQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxlint/binding-android-arm-eabi@1.76.0': + resolution: {integrity: sha512-ZHIE5Zt9AsPDcY4nOlofXt0YfneEeo+QrKMPcPzLf2Z6Q8VtV2W73d7SFJ920WUwyik783u/doKCs3KXdwG+7w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm-eabi@0.60.0': - resolution: {integrity: sha512-1q4q4Jc8FlOMVojEisyFAVyl8h1yawNv6phjgmhGVEDeyeOdsSnSr9x0+D4mOnEKvpO5L4mxKZ/DP9X6U3A/Mw==} + '@oxlint/binding-android-arm-eabi@1.78.0': + resolution: {integrity: sha512-Bu819lmAfZMUHErrpe0cEWj3iaefuUODHSU8+UbXy67V/r7/7f4K3FL0NmbD85E+wiFLDYuhP8Zlv0XnVeXshw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.55.0': - resolution: {integrity: sha512-ctulLq8s3x8Zmvw6+iccB09TIKERAklRSmbJ10gk8mlAn05qZxoyo52dj3Hi9IJcmDSwF54fQaTVh2CbL6PInw==} + '@oxlint/binding-android-arm64@1.76.0': + resolution: {integrity: sha512-shm/ngQilHK6bs+ElJWa4oHfNj5vL1Gl/iVEJldTQjpr0/67oSgr0KUpbmcnLig5Fo0v/l6j2567A7TOL89ONA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-android-arm64@0.60.0': - resolution: {integrity: sha512-tD41I6nCt9k8SQXft0CSjjU9jg6SwG7uMu7PxodSEHXl+GDW0868oy6tTtoJkyUze8YKFgTpz/k5LuPUnFiGLw==} + '@oxlint/binding-android-arm64@1.78.0': + resolution: {integrity: sha512-CDfxZgB61B7buRdY2FJoAYYPPXCZ1EoC1LKscnC5dg3kjobdxiconvAvvN1BmHyW4PyFT3jRLDag/BY/roSNBQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.55.0': - resolution: {integrity: sha512-xDQczLH9pw/RBk1h/GH0qcGMm8hQtmtVHBNLSH3lk1gEIR09hZ4L+mJQl4VqiVAvPK9VG9PYrWWuSQLt7xTbiA==} + '@oxlint/binding-darwin-arm64@1.76.0': + resolution: {integrity: sha512-rvJmrAPKSQ9aWJ6wIS6CK2tJjwzfW0ApQH9qokq6sfDvmHwoyIHxHFMq7z7i7GiV6fdE6s8qvBqWKPTu8RmT6Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-arm64@0.60.0': - resolution: {integrity: sha512-TTpzPug96Zxdyb46KvTyIUQDdsqbumXh2TKG9C23PCT0kF7JkW56Z/quPuG9rqOFKQIi1gpRNZ7DX18LwxXPnw==} + '@oxlint/binding-darwin-arm64@1.78.0': + resolution: {integrity: sha512-2Y2U9Ahrz+OO0Ej88f9SJYq51/jUBp1Mc7iZu0ukrbeeZ3gpRGfzIFnoqfHDY96xr0GEfNrPUBFEy0nN5aD7HA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.55.0': - resolution: {integrity: sha512-JaNoFCkF2CJdGgpPSMbuO9HVyXyoNGIhMHPvp6NYAjeVKw9XEYc0HcUWJLPQa3Q69WV5wMa9m5jPMJPtbLtcRg==} + '@oxlint/binding-darwin-x64@1.76.0': + resolution: {integrity: sha512-U/zYdb7VYKGY6pA9Vd2rYl9O/HlCylcOlb5PGPvVLtg+oLGsk6H3XGKEMHKyqD3nmmtmlmwb/8SwU2vfSAtvMw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.60.0': - resolution: {integrity: sha512-CnOoWgQ7L+JL/YQaRJ+NyATciSfcftncm7y3kqyte1cGtFEGnStaCd1TAyrinkfQ7nRBfHrTs1/vTwUJr3WF2Q==} + '@oxlint/binding-darwin-x64@1.78.0': + resolution: {integrity: sha512-rpych6eJq6m9jDRypTEaPD1xysaEW5h9+xuxhGK/QhOg+/xaqPZrCrTNoIl/f3nEjuJeCEmstNDlrE9rJi/3/g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.55.0': - resolution: {integrity: sha512-DNbszhpg6S2MIzax5azdHFTTBIVkR5xr8yyRZuA4yoDAwOkzIp3tmldgKZM2+VlT+hJIG0xUksA+elISzMEAfA==} + '@oxlint/binding-freebsd-x64@1.76.0': + resolution: {integrity: sha512-WvKG9CAriuo0XNiFzpXjDngUZcRGFNpaK2kLyMUsnJlShxkT96u+BpJQ3KqdQwGOrvI14L6V8bAwXwAYNNY6Jg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-freebsd-x64@0.60.0': - resolution: {integrity: sha512-ychJo7S3hZxdO6eDZ9zM6F2lM9fpJS3EKS5CAUSWyprdLYxTu4gbaUKV/VBPTcMJwQa2Bpo+643y3OJ537pihA==} + '@oxlint/binding-freebsd-x64@1.78.0': + resolution: {integrity: sha512-IcMGrQT3QizkOESUJd5et+rOhVqSkNDfNik1cvrKDqIbzqx9KMtRswpFgkCuNTSwylCFLKhGUu8KmqY1ZnC0Dg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.55.0': - resolution: {integrity: sha512-2snoaoRfFFyGnbOcKUK36rREBYxe/Xgz3uHbiA5zbCB/s6R4DQj4mHqYAaWWhgizCUSDxV8cE9zAZ0XleNpKGw==} + '@oxlint/binding-linux-arm-gnueabihf@1.76.0': + resolution: {integrity: sha512-qJ5+RH99TqFRq3UCDxkW0zJJu9c+OAHFY72vGlxZLEpuO+MpKo3POgqb8sYipL9KYm8XY6ofb0HsOuvY6hQNqQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-gnueabihf@0.60.0': - resolution: {integrity: sha512-36IH5o55T2Fx7E0feDttt+mifxN6yk9pWv4KfhAIsP0dFnUq27331OwbpOsZdoXF9soOLWm7mQUz5+UUmyec4g==} + '@oxlint/binding-linux-arm-gnueabihf@1.78.0': + resolution: {integrity: sha512-/uLdoJ0IXE6vo/0f0LKjinQAp+re+VMaCWaNT8ENIv2EOCkSsc8SGaflXAuW0Jua2dq5+GLVWm1NQK7P3UFSNQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.55.0': - resolution: {integrity: sha512-q1aktHF/WRpSK81BX1dE/9vWrS2jGw1Nax2kb4DBLGAewubCLcoNyp4Zl/NSMgbv3vUS46Z33wIQkBVYOP3PYg==} + '@oxlint/binding-linux-arm-musleabihf@1.76.0': + resolution: {integrity: sha512-PvPCVptkgVARsucgIqFQQcSmJ6xc6GtnVB5bRBekRahTc9eObMtjHfMjy5M+C2tHt5UCMttWM9RuSk/H9NqYeg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.60.0': - resolution: {integrity: sha512-G1Ve7lAa6sFBolVI2LWHfEAqy0YKh4vnioH8uYO9kAEdgM7mR40IksIx9/Zk4+vbYew/sGa4J9Q4tZ3n9gXDHA==} + '@oxlint/binding-linux-arm-musleabihf@1.78.0': + resolution: {integrity: sha512-7xi4Wb/O8NRJhLoUXmDJMUVpNYvB5kefdhFU1Jb8rtae4QoXlTiLwI14X4YvAXVZLNZChP8m5qO9SQAlWQTbkQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.55.0': - resolution: {integrity: sha512-VD0y36aENezl/3tsclA/4G53Cc7iV+7Uoh7gz4yvcOTaEYBtJpQsE6PKDGTtUtOvGS4kv51ybfXY/nWZejO5IA==} + '@oxlint/binding-linux-arm64-gnu@1.76.0': + resolution: {integrity: sha512-3KeFDx8Bu4HPAXbuHZOr/oHvN+QT+JQhMw/NYPz7Z071xLSsG27Jfh9PIQVEY7hk1I+jr43ExqRIeJ6VKk2yLw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-gnu@0.60.0': - resolution: {integrity: sha512-LTQdRBf6uzj/h7Xk6lKzbGD2hrF/fK4YI9LIN1c0509tPUn8wRa3mCmrFQpEWJPLYGFrLFFMTYW1Ljj6VqW2Hw==} + '@oxlint/binding-linux-arm64-gnu@1.78.0': + resolution: {integrity: sha512-4hFW0+fVXa3OIh1Y4A5SPkmvI4wuuBSrCVKzOyE7PTjhc7yEqZ1pmvEEeS5Lj/MaqvegFxXyF33N+6jkehxdyg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.55.0': - resolution: {integrity: sha512-r8xlKJFcsRmn0H5jZrdORae6RX9jDBrZVvOoxF+bCQtampQJClv80aZEHsv+NsLsp2KCE5ql79O7DpPVzYWpXA==} + '@oxlint/binding-linux-arm64-musl@1.76.0': + resolution: {integrity: sha512-oPFkkKTgl0K/EIg9fQ8oA3IGcI05/Mq1en04iFa41mmNPT+6KEiByVazTOZZJiHMBBrbsns1YJ2e1Scqwzesjw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-arm64-musl@0.60.0': - resolution: {integrity: sha512-2JMo3XPxMPx3hiqddSZYyaH+fKJm6cz0u8n1naYjP/CdOQOZW34i8lKBUfmbWiuFvd6KoYXLmhAyBuvojsYS7Q==} + '@oxlint/binding-linux-arm64-musl@1.78.0': + resolution: {integrity: sha512-oC0mvsgBJjlMijSDEhx9KuvR9zYeHXceA9MjbuXB1F8NSR78Yj2unOBrstEvTVaq+pko+kuue6DajC00eqvTdg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.55.0': - resolution: {integrity: sha512-GRKv/HXHcwIVld/WU61rF0g0R16hl5EJ+ScKdpjevT57lnLnagj/U2YUbXf2mT+2Pg1uCzWC+mvGicPV3CDdLQ==} + '@oxlint/binding-linux-ppc64-gnu@1.76.0': + resolution: {integrity: sha512-gN7yZ0eqflA5Fhf1wvHxGUltIV3FsvmB1zhNMDEK9vSHhc7E6qg9CuPeBgPZab66Tjzq6w6kHAtNEvnTHf4cyw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-ppc64-gnu@0.60.0': - resolution: {integrity: sha512-L3C+nBD13lr306tr/PjM3RMll+BVqgFrIgUyoeHuai5oueJrRLgO3j+GO5/Cbhtkf5PSlHYTI1JY7iqBd1qa6A==} + '@oxlint/binding-linux-ppc64-gnu@1.78.0': + resolution: {integrity: sha512-XAllT5SUZS+ohjuZ3/5S0cwe0r7eboiuigeStCZ5DXRYx/2KVM2UvQXvAfyzXEimtQjAB7cDQ2YxDe2Zl2WNQQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.55.0': - resolution: {integrity: sha512-rdv57enTiPtpSYRMKfAiEbQb0Puw5t9N7isVinDoo5qeLDScro2gznmZqSgSWbVZRzLisTeCTW8Qwgw0bOHv3A==} + '@oxlint/binding-linux-riscv64-gnu@1.76.0': + resolution: {integrity: sha512-S/HqMbn22mQrjtErUxEoS/a55u8kIeXvreIxiJu5G7Le3UecEd6SQZxrDIpuhtgaFnsY/nVra3ytP+pRljDilA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.60.0': - resolution: {integrity: sha512-M4MsmvqlxFiPtSRGyBYQSZxchEf463AOyd+Dh4/9xDpjWBsRtDUTDMFN5EdHinjVK1/eDJQ8MLpcYjpYayaCnA==} + '@oxlint/binding-linux-riscv64-gnu@1.78.0': + resolution: {integrity: sha512-trucMER/0QtecoXvc1y/UVqE3kwJipDwrx4oHfj+nNm3dq2zjP44WT0CfHNDPM3G1DXIkx/gY6lAD21NSCZVhA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.55.0': - resolution: {integrity: sha512-7v1nNrlD43VY6+sYQ6efYyb3lE6QY182304PD/768ZxTjOmFd/3dQa3u/nGBUAXYdGSWOQc5N3PnS0QzUXyEIA==} + '@oxlint/binding-linux-riscv64-musl@1.76.0': + resolution: {integrity: sha512-ZIga3097VJZolGZk6SrIAUokIGfRkxRlhiHDUznZptGBfwrhD7pNfD1rzEzsCwvk/1DX0A1bLz+liuNh5QKIVQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-riscv64-musl@0.60.0': - resolution: {integrity: sha512-OH+9UskYuxRB+GxqdGkVN8f5UpwhqG8YscNo1wl8+KJ62cd7wZdGga6iGLJIf8kibF1WBwvlfDUx3cez/VXwFg==} + '@oxlint/binding-linux-riscv64-musl@1.78.0': + resolution: {integrity: sha512-cm3O4F/HQbdzOUX5mKHqG5KDL6E5w0pnlZ+fbBy2rmLryPOowkuLagFHTopQsEIpjcaZoPOrL+BmmAytAG9HFg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.55.0': - resolution: {integrity: sha512-f4lJLUSPOgScjFl9LiflKCTocyNRwE25JmTMbN4XQdDjoZzEHjqf3wA3VESF1/csg7i8m7+EQLbrZyYDqe10UQ==} + '@oxlint/binding-linux-s390x-gnu@1.76.0': + resolution: {integrity: sha512-ZGiiA7pFzMJSyMWYZTVlPgbTsx+Vl8ihLGMIujPwaslUF7kIPPWAbVmAlTc+9lWDV+DCiB8Ikixu+lSHeOIIWQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-s390x-gnu@0.60.0': - resolution: {integrity: sha512-y7AAFutt9wFWBFOAn6+BHaV39usZmcr3YYH2385f+NHgPNpIF9HpqKp0jgUxPaUOCyG3oaX5VhJduL1Nw164rw==} + '@oxlint/binding-linux-s390x-gnu@1.78.0': + resolution: {integrity: sha512-33wRf6HqGNsybJ3qX4cGaQN2ODPxNmc1rMa0mrTmx3eFq1VzOnvQooi9bIGVYakW8a/wmqVx1mgsUm8R2xfTiw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.55.0': - resolution: {integrity: sha512-MihqiPziJNoWy4MqNSV+jVA1g+07iQDjZiR0vaCaDoPgFEiJpCMsxamktzLV07cEeQsSJ04vQaU4CzCQwIvtDA==} + '@oxlint/binding-linux-x64-gnu@1.76.0': + resolution: {integrity: sha512-JLiy5WuvEBFTT6ErIFV35SLzi0R7Iri6MKU6dZbTxfIx8pndbbPs3Mj780nMipBFcPkti+okAPOJ9POKkHFEgg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.60.0': - resolution: {integrity: sha512-yKZ9+CXAI+1RO5nH/4Z/9M6DAsfOzd5bw/gtWk81KB4mpalMaRRSXfouc5/tHxazDmBek55HNPepNYBgaCew0Q==} + '@oxlint/binding-linux-x64-gnu@1.78.0': + resolution: {integrity: sha512-rRdISSYegj6VganMZ9tjRjijowfHJ09IZU01i0toBAqr6n5LEtwHq2IeS4FjW2RoskOHlb6efB26H5izYb3GEQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.55.0': - resolution: {integrity: sha512-Yqghym7KYAVjP9MmSrNZiDeerMuoejNjo0r3ox5H3GDKk8eAfl8VyJm9i+pWCLDCTnAbcTUMMN2ZKjUYXH1v3g==} + '@oxlint/binding-linux-x64-musl@1.76.0': + resolution: {integrity: sha512-z7lgKQtbo/I1NIe8G5NHLesxJDv0tRSUWTpXKb9Pm3E9nKFKfO4IOSDtFroKgXtOYb0jQbcdH+0wzTyMXVes+A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-x64-musl@0.60.0': - resolution: {integrity: sha512-bCUGaF6hJOYnQzLJdHLZbvGsOd5oSvGAyJhPAKum2uyLYUuXmP8vqg690DWi2hqcnIoYpqSqCrjzE5aiUAgwQg==} + '@oxlint/binding-linux-x64-musl@1.78.0': + resolution: {integrity: sha512-GmsP4rW0xTL6u5CVdcDsaN5Fbc7hBc382Wmar1kttbnwSEviM+rSINKOMQ+UQ6iH+AGwC+8gaAiwu134Tgh6Lg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.55.0': - resolution: {integrity: sha512-s5SDvVVSbyQl1V5UU3Yl12M+XLUQ3rl5SglNqgAA2K4PXUtQhyNSS00wivONPEnNo5W01rCou8WkDNyvI/RGHg==} + '@oxlint/binding-openharmony-arm64@1.76.0': + resolution: {integrity: sha512-JOjKymIpb9QcYfEhZsN6h4V9Ivd474W38cNIBRv6bg2TbIvogbMTH0Mg6YWW9TiRDqfcX+/Hyfsbo5vcSE5guQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-openharmony-arm64@0.60.0': - resolution: {integrity: sha512-GrUeZOvzP30ExxfCuQiyofuUGI+OmvAgFwOO5w5p9mGPlxcyuqI+6Sy9fAKFFfLQrqKYWFgc5sYA2Unj/29nPg==} + '@oxlint/binding-openharmony-arm64@1.78.0': + resolution: {integrity: sha512-sy9yeYuADc8a+n4TLBayzMCZiHPW78DcIFVpOXTmdKHWQeM9xe5uzkqIIZmi326D5hY9XVwacipEB1p7tQjPAg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.55.0': - resolution: {integrity: sha512-7p9FB5R32tw2KyyNX3wpQrR2WHwEHvMEiBlGXxeTCaRMCVNx3UtFMAUbaQ/pRNWIrEUZmYhJ6tcUH52uPTRYjQ==} + '@oxlint/binding-win32-arm64-msvc@1.76.0': + resolution: {integrity: sha512-pqDWZiwcmByWUEm1NFUBNiT6aentCcaoMWJv0HbXEmuYermJ4sg8ppVrshubYP2MZ6SHccJJcpr6x469PuDFIw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-arm64-msvc@0.60.0': - resolution: {integrity: sha512-WD4Q954kUl2TDJV/6q7UnE2rlKk047kXLJsr4bJ2mXRaAqNXcmV3nwKUsGCc3mz/jYDBnXtJEaBErJEybK8iQQ==} + '@oxlint/binding-win32-arm64-msvc@1.78.0': + resolution: {integrity: sha512-rjc2hF1KfMi8fZj1X/m3AmnHbdsF3rL0v6KQg0Uc880Yb2khjz+3U14sfdZ7jWTpRnN1m1NQa/TT7uU9lJWPrA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.55.0': - resolution: {integrity: sha512-ZYqj3fDnOT1IaVGMP5kpmkQl4F3tQIm2ZyAxvqkJYmI0xgWWak4ss4XYwv3VDfM+TWXeC9K4uQ/wW5jm/5XABA==} + '@oxlint/binding-win32-ia32-msvc@1.76.0': + resolution: {integrity: sha512-Ba0O659kgMv6pwO3z9PdO+K3aMxQRaw9HnG+e6AtOfgwcKFvYilciQYBoUBmxfQvOCKZe1SwjMkuB542NkuDMQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.60.0': - resolution: {integrity: sha512-HqDekjr8JXzVDUP1YthDZ1Y3CBEcuZT4WX3B+1kaxj8CvZA8Y2YhcEsXqoSop3tVsgjACxjnFQFDkBo0r/jq1Q==} + '@oxlint/binding-win32-ia32-msvc@1.78.0': + resolution: {integrity: sha512-zcuXFVrEFHIafRfkCQT8w/Xe41o07ozl/vwHq7p94vB29xVzsB0sZGYORU1jhcYKv3Lr0J3HbJ2T4fHH5rWmvA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.55.0': - resolution: {integrity: sha512-eEYT5tivGnGbPHuOHuQpi6CGLObhh0re/5jcNQHihD2GRYkTM85dyi5a19zjP8Q00t1uqAx+/QGLUGdHeqzWyg==} + '@oxlint/binding-win32-x64-msvc@1.76.0': + resolution: {integrity: sha512-5qcirPHO8nKfkoowEVWtpAoVTcYDy6g0UT0NGic450Qv8J2NrOqg4uQ8QppRP4MDTC7Xx47lbZnmadTH03CGGA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.60.0': - resolution: {integrity: sha512-tz78yhmGPKboTMHCHSaUqXK8JrmoSejgDcWeqAtg2s07ZGKQ3rH5Jn8NuXPGNG33CDbY2e9NoQWXIVEmKO21Rw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - - '@oxlint/binding-android-arm-eabi@1.70.0': - resolution: {integrity: sha512-zFh0P4cswmRvw6nkyb89dr18rRanuaCPAsEXsFDoQY8WdaquI8Pt4NWFjaMJg6L23cy5NeN8J9cBnREbWzZhaw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [android] - - '@oxlint/binding-android-arm-eabi@1.74.0': - resolution: {integrity: sha512-+gHd12muVI9ZLBaWLPkHt3Fj7jihFjgQ1MGtBaRL8vWrWrI0P7dLUty/cHrHS0oqPYIRgQUJsPu2CExQuMcwNw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [android] - - '@oxlint/binding-android-arm-eabi@1.75.0': - resolution: {integrity: sha512-lutovtFzJqlRaqpZrCqSSGaHZzl9nIxxpjLzhSRLunN6dCLylj0uzlCyQGaQDIys7rrv8kVXiFO+R4Zpn0bX7g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [android] - - '@oxlint/binding-android-arm64@1.70.0': - resolution: {integrity: sha512-qI8o4HZjeGiBrWv+pJv4lH0Yi2Gl/JSp/EumBUApezJprIKa5PS4nU0lQsQngtky8k+SplQIOjv6hwu0SSxeyg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - - '@oxlint/binding-android-arm64@1.74.0': - resolution: {integrity: sha512-xjKdoMB+H+RCOByv/7l7nfIGW9mlOisqYdcyC75UqYuQecLpReAeEYUf2CNeDEI3KtmUgxpRw/+c63y4AeF/Bw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - - '@oxlint/binding-android-arm64@1.75.0': - resolution: {integrity: sha512-hXI0hDgHkw4w5nfru72aG7y+2iQJmC4waH/KV6H/hbgA6yAP5jYNx0P9yug15Hs0tWl/+mda3Jjn/2gmDT48tw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - - '@oxlint/binding-darwin-arm64@1.70.0': - resolution: {integrity: sha512-8KjgVVHI5F9nVwHCRwwA78Ty7zNKP4Wd9OeN5PSv3iu/F/u1RVXoOCgLhWqust6HmwQG6xc8c+RCyaWENy24+w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - - '@oxlint/binding-darwin-arm64@1.74.0': - resolution: {integrity: sha512-iUK7wvc6sejMKsC+Pt67mntoF5weFcyEunhZfLJceU6gL419mexz5wBkSx/EnkFBExMLNtOi9fnDSc5xfK0IzQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - - '@oxlint/binding-darwin-arm64@1.75.0': - resolution: {integrity: sha512-D91BWbK/dMYfCcrghspPIuKs2D9LF4Z/OabVSQjw1AO6PWxArD7teDA48bm0ySFqWDaPVqmQRl5GMWNglTXyrQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - - '@oxlint/binding-darwin-x64@1.70.0': - resolution: {integrity: sha512-WVydssv5PSUBXFJTdNBWlmGkbNmvPGaFt/2SUT/EZRB6bq6bEOHmMlbnupZD5jmlEvi9+mZJHi8TCw15lyfSfQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - - '@oxlint/binding-darwin-x64@1.74.0': - resolution: {integrity: sha512-ggKc/tn5SJ1u2yG2izC6VKODfYKV8MQ2AicJlNzOjuyrC29udvOef6/JzK2r32xqCnBDLFouR1VCkjzEI0/N9Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - - '@oxlint/binding-darwin-x64@1.75.0': - resolution: {integrity: sha512-02mpwzf12BonZ6PT0TuQoomvEh2kVl2WGBIKWezCyToIS+rYkQZ6GXnARBAl9A4Ovm2V+Xe7M4KretyqmmcnJQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - - '@oxlint/binding-freebsd-x64@1.70.0': - resolution: {integrity: sha512-hJucmUf8OlinHNb1R7fI4Fw6WsAstOz7i8nmkWQfiHoZXtbufNm+MxiDTIMk1ggh2Ro4vLzgQ+bKvRY54MZoRA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - - '@oxlint/binding-freebsd-x64@1.74.0': - resolution: {integrity: sha512-u++dH/43jy9hTLbneaWlS0gla/Bp1JdwJ2zgevCl8nDFUh6qRCGMxcL0f0lb7By3A9p/LfFr+7cG4HU1hG856g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - - '@oxlint/binding-freebsd-x64@1.75.0': - resolution: {integrity: sha512-qZJgLnDaBsiL5YESx2t/TZ8eXkL9fEkKoXEdzegROhlz9A0lgyGnZ0dAzJrh7LJAHQl2K9RdRueN2s/9N7+odg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - - '@oxlint/binding-linux-arm-gnueabihf@1.70.0': - resolution: {integrity: sha512-1BnS7wbCYDSXwWzJJ+mc3NURoha6m6m6RT5c6vgAY3oz7C3OVXP+S0awo2mRq97arrJkVvO3qRQfyAHL+76xtQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@oxlint/binding-linux-arm-gnueabihf@1.74.0': - resolution: {integrity: sha512-Sj1zmtFDVTPeIbIz4ZfcXAbFHqCmKCXdCUlAJzvTF7I20NTH1RDpoF2PhkqNODutJzVhJYmm3oz0GwgY+tvE2g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@oxlint/binding-linux-arm-gnueabihf@1.75.0': - resolution: {integrity: sha512-7XlaWA5BJD3XpCfrEqjEe6Zseeb14S7QGa304XfwKignRaKQ+eIj775BQ7nIslggWickl4IsPUFqJ+/gAyNHVg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@oxlint/binding-linux-arm-musleabihf@1.70.0': - resolution: {integrity: sha512-yKy/UdbR55+M2yEcuiV5DCNC/gdQAjr/GioUy50QwBzSrKm8ueWADqyRLS9Xk+qjNeCYGg6A8FvUBds56ttfqg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@oxlint/binding-linux-arm-musleabihf@1.74.0': - resolution: {integrity: sha512-//PKyQb/tQXcHArx2f7z+oVI/eMS2Jpv+edNuAtOrgIhWdGcpHxogveAxzmF2rpH1AIHp4Hq04RF/rgJdiICnQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@oxlint/binding-linux-arm-musleabihf@1.75.0': - resolution: {integrity: sha512-av6Tpv8yrcMMMOadOqENBhlsLRcGFXXwoQ0hzHhsmS9FJ4Wioy8we427GbcMe2XTxmL2e60T67H1Dyr3up+tAA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@oxlint/binding-linux-arm64-gnu@1.70.0': - resolution: {integrity: sha512-0A5XJ4alvmqFUFP/4oYSyaO+qLto/HrKEWTSaegiVl+HOufFngK2BjYw9x4RbwBt/du5QG6l5q1zeWiJYYG5yg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-arm64-gnu@1.74.0': - resolution: {integrity: sha512-/k1Me+aX2tjuH10K62mLS0y8cLkJBHX6Ce0xPK+eWeel4bSdEGZ8dv4+hYMzg0GrSmjwy4yAYsDPeEeKBft/2w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-arm64-gnu@1.75.0': - resolution: {integrity: sha512-WcUhd8fHT5plrA14lANevl+hOl815mVI5t2hU21oFWrZKFXIVV/Sr4rWQV0NzSvzBupbMLNc5ErEA6Ehxh5jMg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-arm64-musl@1.70.0': - resolution: {integrity: sha512-JiylyurlB0CLSedNtx1gzv3FvfWPF1h/2Y3BJszPLNt5XQFlBsH5ke0Jle3iJb3uqu5m2e7A/DwzpuCAHdiU+A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@oxlint/binding-linux-arm64-musl@1.74.0': - resolution: {integrity: sha512-3tFSjBxc5D8/zvjEuLvOqcA8ZXKD0+6NuaVO/edeamNc49MoAsbfaC9s1UiwODwgF6slGaF8yJA2TPkukd77tg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@oxlint/binding-linux-arm64-musl@1.75.0': - resolution: {integrity: sha512-UWzp5wRHFe/ESO3+eEaxXsTkYTGLYjnTsi/I5neEacXSItQ6WNleapfOAeA4x2b8nyhJ4uQxqvtv9pHv8kWJtQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@oxlint/binding-linux-ppc64-gnu@1.70.0': - resolution: {integrity: sha512-J8VPG7I3/HmgaU4u8pNU2kFx2+0U+vPLS1dXFxXOaR/2TQ0f8AC7DRz0SRGRI1bfphnX2hVYTTtLuhL4nYKL+Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-ppc64-gnu@1.74.0': - resolution: {integrity: sha512-9QggtPkSPXOCTu8Szis7auOK/sC7KdQaN+/TujP7YVVhzCAOhgdRfgv8uEz0r2tk5xdgus5rLYUrCDoZNtiRUw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-ppc64-gnu@1.75.0': - resolution: {integrity: sha512-XEVRwGMLKCUKrvhLAz4F6AIh8MJrQVdSZtAmPpRZt9tGPsUnamPOcl3dS/ZQzJnar/Ymgc//+xho0L60Emzuxg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-riscv64-gnu@1.70.0': - resolution: {integrity: sha512-N2+4lV2KLN+oXTIIIwmWDhwkrnvqf5oX7Hw0zPjk+RuIVgiBQSOlJWF7uQoFx2siEYX0ZQ5cfSbEAHm+J3t7Wg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-riscv64-gnu@1.74.0': - resolution: {integrity: sha512-VM5VPUJ4DJIWiK+AZn8FScUqMr6OFrCAYybMYjEEi7W13ParI64MByiXTkKMqZpBmvQ9zxl9Ebq2VUOiZRJYUg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-riscv64-gnu@1.75.0': - resolution: {integrity: sha512-mAG4DUXqfLC8cTjMD2kt3jDmVzFREYtDyeLNdLdsCcBc4Zbl2EMuiFektGBilQwkNjYnMvCqJs55U+Hyb+b+jw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-riscv64-musl@1.70.0': - resolution: {integrity: sha512-1e2L7cFCvx9QDzq6NPP+0tABKb5z6nWHyddWTNKprEsjO9xNrAtPowuCGpjNXxkTdsMiZ4jc8YQ5SstZd4XK6g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@oxlint/binding-linux-riscv64-musl@1.74.0': - resolution: {integrity: sha512-SaDY1gh9rOA592J54g+gu5hkOFFQBZsMmIYHs+NRHG+Uq0OxtuuCXMWQ3vu1830Eugv5uMXyjG+bv2Z9y4IXjw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@oxlint/binding-linux-riscv64-musl@1.75.0': - resolution: {integrity: sha512-95hrAvriAlI+pekSomTFIn0+bawMDlDwTNVmdjsFusTHyL2JWh7TWvRNG/Lkim72uN8OiCcO9wcaC6omLP5E3w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@oxlint/binding-linux-s390x-gnu@1.70.0': - resolution: {integrity: sha512-Kwu/l/8GcYibCWA9m9N5pRXMIKVSsL/YbgpLzYkqDhWTiqdRfnNJ/+nqIKRKQiFbHWsdlHEhzMwruJK+qcEruA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-s390x-gnu@1.74.0': - resolution: {integrity: sha512-ZATQeHZCyr6MbDveg0obD5sxLHFOghtOdC5jwVwYlvFWqtFOxctgFEG6Ef/64hYvZrWyhyCckB10AelqLopeDA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-s390x-gnu@1.75.0': - resolution: {integrity: sha512-4b6f2+FrtruAESrCqIKcrarzfrSx+wk2QNcp+RT91/Prc+pMQMAfyZ1rG1c3tFQNl8Bc616tx40uNXyxNBRPbQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-x64-gnu@1.70.0': - resolution: {integrity: sha512-tap04CsHYOl0nSAQJfPNIuBxqEPB2HnhQqwaOXLg1jnp2XfRo8Fa814dA4QC4zpvTWXCjAAaCY1W5LOORkEQuQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-x64-gnu@1.74.0': - resolution: {integrity: sha512-+aIvJyrdeD7LwCQ2WYLMUWNmnbeDRSPb40aBYtPjD9+PTqUwgJnk+HK5yLfSMeqXrMrDhE9uTmtt2y50tvjhHw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-x64-gnu@1.75.0': - resolution: {integrity: sha512-nshAhrUvXFUWOvqQ2soIw7HFNWvpvEV4o0cYSqPtzLiPF5gKyYTDOOTJ6Rn8g8K/iGvPIrbDA4v8+5MvnjJrrg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@oxlint/binding-linux-x64-musl@1.70.0': - resolution: {integrity: sha512-hzJa/WgvtJpbBD9rgfy0qe+MjbxOXNUT0bfR1S6EQQzfTtBFA9xg5q8KSwRrQ2QfSS+TaP4j+4mVPQrfNc6UNg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@oxlint/binding-linux-x64-musl@1.74.0': - resolution: {integrity: sha512-XyktaR8lhK2qWiCK0Tk8oYD+/cgn+oHA6ddRnxSSXUKkkojkV78CmShZUxQF+yrBFs0SuW+JBOPG6hecyc/iZg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@oxlint/binding-linux-x64-musl@1.75.0': - resolution: {integrity: sha512-e4jNxLKnxLC6sYBQRxrI2pgIIxnmMtF8U/VwNYcjTT/CLS+spH624cYVnj07bTKwaEWT37/e025isOs6j/0xqA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@oxlint/binding-openharmony-arm64@1.70.0': - resolution: {integrity: sha512-xbsaNSNzVSnaJACCUYr1HQMyY/Q/Q1LkePmHG3UvZPvGCYGNxrsZp9OmtA6ick8xH47ltRRbRrPCM1YXYcyC+A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - - '@oxlint/binding-openharmony-arm64@1.74.0': - resolution: {integrity: sha512-mzbjrPl4neaVUiJ1fUiEUxTGaSZBoiKtaoB6jmIpz9S+VOA2vDYmJpihQ82w6178V5jxziclTg8Cgj5yF6tTDg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - - '@oxlint/binding-openharmony-arm64@1.75.0': - resolution: {integrity: sha512-hZ2lH+1qLf/DiEP9UWuQTK2JWj/BgvMB4jhIV4SmNU1wfEiYYX4TynQyAZXx0j9X4qRYizAL042SKaV+8ynh4w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - - '@oxlint/binding-win32-arm64-msvc@1.70.0': - resolution: {integrity: sha512-icAEsUI7JbW1TMRdEXV83mVAInhRVQYuuAlPpxdGwJ95chNdnCzjloRW8GglT0WvzOEZSio6fnYSk2DJ2Hv7LQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - - '@oxlint/binding-win32-arm64-msvc@1.74.0': - resolution: {integrity: sha512-vUAe9okpS2Oa5+lX67lqHMuNUvfkleRKwrUDJ/WJBsgmddvZ1mrsh2HVmuFDRzqFELhaJhFaCNOuR6a7L3rtIA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - - '@oxlint/binding-win32-arm64-msvc@1.75.0': - resolution: {integrity: sha512-Ilj6PNzGDS3bCU0MSJH7Msh0NhH+T/mRp2shwg+q+GHeVlPwP5LEboW96aW+3kVKFk6zYZy1Xi5pZkqZh6X8KQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - - '@oxlint/binding-win32-ia32-msvc@1.70.0': - resolution: {integrity: sha512-FHMSWbVsPVs/f+Jcl04ws4JJ2wUnauyTzlpxWRG/lSO/8GpX08Fo2gQZqdA6CrRFI+zvkxl+N/KwJGWfUwYVZA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - - '@oxlint/binding-win32-ia32-msvc@1.74.0': - resolution: {integrity: sha512-yyXXJyYYSXL4I8K8jAWjJs+J3fa9gH2JmEbo4f5adm+1tNC9itseicBNuwK7BDHvqQ5J534s+yDULu89vYL2ZQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - - '@oxlint/binding-win32-ia32-msvc@1.75.0': - resolution: {integrity: sha512-QVit2nOEOiPhkmsrksPSkoGCdnZRNkspt8fwoYyP09te1VEbnSj4LAxua4rc8FKTmWkySVe05j8iz9GXYfF1AQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - - '@oxlint/binding-win32-x64-msvc@1.70.0': - resolution: {integrity: sha512-ptOlKwCz7n4AKs5VweMqG6DAg677FmKOK+vBkkL9DMNgFATIQ+upqUYBTOEwRQyRAx1ncGlPlXleV2hIcm3z4g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - - '@oxlint/binding-win32-x64-msvc@1.74.0': - resolution: {integrity: sha512-VTC9IYTIMrVUk/i6Ms1ohzzDKZFkWn0KU2OBbPBzgmVZ2V30165T/zK4LztTr0Xgp9fZ1qQZ1rsZAu/rEmySlA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - - '@oxlint/binding-win32-x64-msvc@1.75.0': - resolution: {integrity: sha512-DSxnNkBUAYARPwJtR12Ig3deWr8w0H997xP6jy33i+e0SyYJw8FKuz4+cZtpmPEhQmvlPJE3X/2vNxDmLkd/rA==} + '@oxlint/binding-win32-x64-msvc@1.78.0': + resolution: {integrity: sha512-Sb5ocmLSuYeOuXd+CFOToGKp/gjXUEWDnvIGwhnh8aq8wY4TMmEnKnvbogSW7RdMZv77JSARduS7/gv+khYEjA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -3209,9 +2753,9 @@ packages: '@phenomnomnominal/tstemplate@0.1.0': resolution: {integrity: sha512-/v+GIVNFHAz4+nQtgy9e5ZAXK3xj6TbP5s9JTpnFuqkcLB+gB2lJ6x/nsDhkKhzR6o4REuzhsYoWYnXqKC/UnQ==} - '@playwright/test@1.61.1': - resolution: {integrity: sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==} - engines: {node: '>=18'} + '@playwright/test@1.62.1': + resolution: {integrity: sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==} + engines: {node: '>=20'} hasBin: true '@polka/url@1.0.0-next.29': @@ -3228,17 +2772,14 @@ packages: '@radix-ui/number@1.1.3': resolution: {integrity: sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==} - '@radix-ui/primitive@1.1.3': - resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} - '@radix-ui/primitive@1.1.4': resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==} '@radix-ui/primitive@1.1.7': resolution: {integrity: sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==} - '@radix-ui/react-accordion@1.2.14': - resolution: {integrity: sha512-iE8YB9nmTBH8zd73ofBISZ8JCzgMoMkATJr7qDwa6u5F1+7mTM81V6fa71jgZ65rpjVpecDf1vSnwIFP9Ly1zw==} + '@radix-ui/react-accordion@1.2.20': + resolution: {integrity: sha512-jDhG9FvAEnlhnjrsINbNXcUa4G+L1KqSkJSunkbKEzFRcAb52jvM0PjPxPRvhe1HNc5F5yc0yzzWeeqlH4yBIg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3263,8 +2804,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collapsible@1.1.14': - resolution: {integrity: sha512-9bT+FvifX1FK2Mj6UEsTdyu0cN3JaA3KdfhaBao+ONrYFy/pyOy3TU1TNw7iOk1o+0hOEq67RojlUUmoFGwxyA==} + '@radix-ui/react-arrow@1.1.15': + resolution: {integrity: sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3276,8 +2817,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.10': - resolution: {integrity: sha512-IVVz4EvBcKjrzKgof714qDnz/SzQAkLA2Emh5edlHbgcE6fNd3Un6CJLlaYcnm8N4JmAtzQgse4dOKxcD2yc9g==} + '@radix-ui/react-collapsible@1.1.20': + resolution: {integrity: sha512-mcGesGplBnzN2sbvJETzpCNfSMyPnb29q1GRLU+Ib7bJrpIG2ywmRoh2V5VbA2uNvKikKUlVbAPks7JDjz4A8Q==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3289,14 +2830,18 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-compose-refs@1.1.2': - resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + '@radix-ui/react-collection@1.1.15': + resolution: {integrity: sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==} 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-compose-refs@1.1.3': resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} @@ -3316,15 +2861,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-context@1.1.2': - resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-context@1.1.4': resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==} peerDependencies: @@ -3343,19 +2879,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-dialog@1.1.15': - resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} - 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-dialog@1.1.17': resolution: {integrity: sha512-TDTYmpdq8dI2+Xgvgj9AJ8Ghqq+Eph/TRVEdaFQPDItIY+6QSkU7MJMeevw1568Yw/2Ijz8BTphPSP2XejKphw==} peerDependencies: @@ -3369,15 +2892,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-direction@1.1.2': - resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-direction@1.1.4': resolution: {integrity: sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==} peerDependencies: @@ -3387,8 +2901,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-dismissable-layer@1.1.11': - resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} + '@radix-ui/react-dismissable-layer@1.1.13': + resolution: {integrity: sha512-2v+zNAWWe0ySxgC0D0yeXMPQ23xZVgXZTerTz+JKlmdRj6gfTqmCcR29jb6d290DezXPGgruHWDX/vYUebtErg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3400,8 +2914,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dismissable-layer@1.1.13': - resolution: {integrity: sha512-2v+zNAWWe0ySxgC0D0yeXMPQ23xZVgXZTerTz+JKlmdRj6gfTqmCcR29jb6d290DezXPGgruHWDX/vYUebtErg==} + '@radix-ui/react-dismissable-layer@1.1.19': + resolution: {integrity: sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3413,8 +2927,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.18': - resolution: {integrity: sha512-PZGV82gFk0WltDRI//SsG28ZIjlo9ANTmoNYg0jLNzXXiDsAy5PkOOYQaVD1pPxY6t7gxffb1QMD6qaUvsBZdw==} + '@radix-ui/react-dropdown-menu@2.1.24': + resolution: {integrity: sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3426,8 +2940,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-focus-guards@1.1.3': - resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} + '@radix-ui/react-focus-guards@1.1.4': + resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -3435,8 +2949,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-focus-guards@1.1.4': - resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} + '@radix-ui/react-focus-guards@1.1.6': + resolution: {integrity: sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -3457,8 +2971,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-focus-scope@1.1.7': - resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + '@radix-ui/react-focus-scope@1.1.16': + resolution: {integrity: sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3470,8 +2984,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-id@1.1.1': - resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -3479,8 +2993,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-id@1.1.2': - resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} + '@radix-ui/react-id@1.1.4': + resolution: {integrity: sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -3488,8 +3002,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-label@2.1.10': - resolution: {integrity: sha512-ib0zvq2ZsAqKm5tRnqGJn3vOxSgIts5ToxsXT0q1S/GfLD1Zj7UOEnkw8u2w6sRmn47djpQWuSU1DCL1R29/yw==} + '@radix-ui/react-label@2.1.15': + resolution: {integrity: sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3501,8 +3015,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menu@2.1.18': - resolution: {integrity: sha512-lj8Rxjtn6zJq1oSbE/uDtAwCbB9BnxgHD+8MwJMuTh6u1dPamYhW9iuELr/Z8d0D/UysFblYYHeBPwi7T4k0YQ==} + '@radix-ui/react-menu@2.1.24': + resolution: {integrity: sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3514,8 +3028,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popover@1.1.17': - resolution: {integrity: sha512-/YSAOdJ7YJvdn7bn5sdSx2egW+SKY+u7O5RyAVs94Ymrg2fg5QTSFPMRkzvhGyFuE4/qsmPBdrwYoZMZh/4f+g==} + '@radix-ui/react-popover@1.1.23': + resolution: {integrity: sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3540,8 +3054,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-portal@1.1.12': - resolution: {integrity: sha512-m309havGzsjLHHaIX50G5PlvRs3xkgPCsGk/5PTvYm8D5q33yG0J7w/712PTOhid7NTaFETtnSXjngHQavvhVw==} + '@radix-ui/react-popper@1.3.7': + resolution: {integrity: sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3553,8 +3067,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-portal@1.1.9': - resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + '@radix-ui/react-portal@1.1.12': + resolution: {integrity: sha512-m309havGzsjLHHaIX50G5PlvRs3xkgPCsGk/5PTvYm8D5q33yG0J7w/712PTOhid7NTaFETtnSXjngHQavvhVw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3566,8 +3080,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.10': - resolution: {integrity: sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==} + '@radix-ui/react-portal@1.1.17': + resolution: {integrity: sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3579,8 +3093,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.5': - resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + '@radix-ui/react-presence@1.1.10': + resolution: {integrity: sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3618,32 +3132,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.1.3': - resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} - 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-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-primitive@2.1.6': resolution: {integrity: sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g==} peerDependencies: @@ -3657,8 +3145,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-progress@1.1.10': - resolution: {integrity: sha512-JYzEg60lk79PwKM27WZyKd7PW8O4OM5jOaFfRPfOyeXmMw7tLJh5kSj+CEjVTehszuwml/AdCzPGMXBTGf4BBw==} + '@radix-ui/react-progress@1.1.16': + resolution: {integrity: sha512-5XnomAsoZZCY+KNTxbIghpGqPruZvKFNlvcAljVAOdDRDsH4/OZQxhtwo5wdtoDM5R6MhJBb2sPnDuRFep3lzg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3670,8 +3158,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.1.13': - resolution: {integrity: sha512-9gkwneI0guf8JDmrFxPjJF6Ozzgioyw+/lonYNCwefS9ZHA05er0BVHiXr+LbWGHxUfczvMY6G1oiZZi1VzjRw==} + '@radix-ui/react-roving-focus@1.1.19': + resolution: {integrity: sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3696,24 +3184,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slot@1.2.3': - resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@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-slot@1.3.0': resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==} peerDependencies: @@ -3732,8 +3202,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-tabs@1.1.15': - resolution: {integrity: sha512-kxc9gI6/HfcU4nfMMVS3AmQK414kbU1IE6UCJmMmxjhO3cRPXOyYnmvyKD+ODt7q56nRq9l7Wovi6uaGwKgMlg==} + '@radix-ui/react-tabs@1.1.21': + resolution: {integrity: sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -3758,15 +3228,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-use-callback-ref@1.1.1': - resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-use-callback-ref@1.1.2': resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} peerDependencies: @@ -3785,15 +3246,6 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-controllable-state@1.2.2': - resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-use-controllable-state@1.2.3': resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} peerDependencies: @@ -3803,8 +3255,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-effect-event@0.0.2': - resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + '@radix-ui/react-use-controllable-state@1.2.6': + resolution: {integrity: sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -3821,8 +3273,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-escape-keydown@1.1.1': - resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + '@radix-ui/react-use-effect-event@0.0.5': + resolution: {integrity: sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -3839,8 +3291,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-use-layout-effect@1.1.1': - resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + '@radix-ui/react-use-is-hydrated@0.1.3': + resolution: {integrity: sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -3875,6 +3327,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-rect@1.1.4': + resolution: {integrity: sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-size@1.1.2': resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} peerDependencies: @@ -3884,6 +3345,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-size@1.1.4': + resolution: {integrity: sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-visually-hidden@1.2.6': resolution: {integrity: sha512-jCE0WljWifTI4niIMCll06kGpsJTAPiZVU9H4WR1N6qW7At9ystHbN7dDB+we2xH535roFHj7qKS+RGj0FMDWQ==} peerDependencies: @@ -3900,6 +3370,9 @@ packages: '@radix-ui/rect@1.1.2': resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} + '@radix-ui/rect@1.1.3': + resolution: {integrity: sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==} + '@react-aria/focus@3.22.0': resolution: {integrity: sha512-ZfDOVuVhqDsM9mkNji3QUZ/d40JhlVgXrDkrfXylM1035QCrcTHN7m2DpbE95sU2A8EQb4wikvt5jM6K/73BPg==} peerDependencies: @@ -3916,13 +3389,8 @@ packages: resolution: {integrity: sha512-Px/Hwhhyk2PubCA4ZaRFsfvwxhbxXsetJyvqC6aFFi8WhJhA+oVC33aTzuAeWmM3fhb4/8ce8YsHXI1d6ChcKg==} hasBin: true - '@react-types/shared@3.35.0': - resolution: {integrity: sha512-iNWvuzEwANttpQpdlu8nPBtdHb0mcCMj1ZTH//iRB5E/14IAnyRlR25rxH7pNLyzHINsPGEKnWvpwDMCT6vziQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-types/shared@3.36.0': - resolution: {integrity: sha512-DkP/H0C2YjjS7gZWKNqOmU8a16qHPjQNdzMwmTq9SzplM6Iw0kVMTZ0OIoe6FOgGqa+FwMsE2QbPjh/n3g/jXQ==} + '@react-types/shared@3.36.1': + resolution: {integrity: sha512-AzsuD9OfxTOZMMvTRhlN3oHBwOmFN7tDh27LzqmHt4+uOgPhJT7ZM7/kVs/8/o0WxayMUIk3hBmCFRHv1FUoag==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 @@ -3932,12 +3400,12 @@ packages: '@redocly/config@0.22.0': resolution: {integrity: sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==} - '@redocly/openapi-core@1.34.12': - resolution: {integrity: sha512-b32XWsz6enN6K4bx8xWsqUaXTJR/DnYT3lL1CzDYzIYKw243NNlz6fexmr71q/U4HrEcMoJGBvwAfcxOb8ymQw==} + '@redocly/openapi-core@1.34.19': + resolution: {integrity: sha512-o/0VgsBXgwcY1lyeqcVtSGdTQAPnVggo0fbFVPlxl5XVDKUcVH0OLRqt3CbkwByT5FU305E0iE0O7MzThjDblw==} engines: {node: '>=18.17.0', npm: '>=9.5.0'} - '@reduxjs/toolkit@2.11.2': - resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} + '@reduxjs/toolkit@2.12.0': + resolution: {integrity: sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==} peerDependencies: react: ^16.9.0 || ^17.0.0 || ^18 || ^19 react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 @@ -3950,97 +3418,92 @@ packages: '@repeaterjs/repeater@3.1.0': resolution: {integrity: sha512-TaoVksZRSx2KWYYpyLQtMQXXeS98VsgZImzW65xmiVgbYhXLk+aEsmzPLirqVuE4/XuUapH2iMtxUzaBNDzdSQ==} - '@rolldown/binding-android-arm64@1.1.5': - resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} + '@rolldown/binding-android-arm64@1.2.3': + resolution: {integrity: sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.1.5': - resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} + '@rolldown/binding-darwin-arm64@1.2.3': + resolution: {integrity: sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.1.5': - resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} + '@rolldown/binding-darwin-x64@1.2.3': + resolution: {integrity: sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.1.5': - resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} + '@rolldown/binding-freebsd-x64@1.2.3': + resolution: {integrity: sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.1.5': - resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} + '@rolldown/binding-linux-arm-gnueabihf@1.2.3': + resolution: {integrity: sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.1.5': - resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} + '@rolldown/binding-linux-arm64-gnu@1.2.3': + resolution: {integrity: sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.1.5': - resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} + '@rolldown/binding-linux-arm64-musl@1.2.3': + resolution: {integrity: sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.1.5': - resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} + '@rolldown/binding-linux-ppc64-gnu@1.2.3': + resolution: {integrity: sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.1.5': - resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} + '@rolldown/binding-linux-s390x-gnu@1.2.3': + resolution: {integrity: sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.1.5': - resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} + '@rolldown/binding-linux-x64-gnu@1.2.3': + resolution: {integrity: sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.1.5': - resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} + '@rolldown/binding-linux-x64-musl@1.2.3': + resolution: {integrity: sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.1.5': - resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} + '@rolldown/binding-openharmony-arm64@1.2.3': + resolution: {integrity: sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.1.5': - resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [wasm32] - - '@rolldown/binding-win32-arm64-msvc@1.1.5': - resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==} + '@rolldown/binding-win32-arm64-msvc@1.2.3': + resolution: {integrity: sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.1.5': - resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} + '@rolldown/binding-win32-x64-msvc@1.2.3': + resolution: {integrity: sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -4078,12 +3541,12 @@ packages: resolution: {integrity: sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==} engines: {node: '>=14'} - '@sentry/core@10.68.0': - resolution: {integrity: sha512-5Amhx8ltVz7vb1bRGyf3c4J69/iHW8R/H+SJxTRILHlsSOBrnVVc/IQEYDC6PTRdRdZ3x2u7RVjxZi2Mhe525g==} + '@sentry/core@10.70.0': + resolution: {integrity: sha512-ozhCTDqg89oB4XmWfAwuHshABpvT7AkRpaPnogopPfMAaI61G1t8EKCJ4W7aum8JSBonlfyjPCyW5oYZFm0KvA==} engines: {node: '>=18'} - '@sentry/node-core@10.68.0': - resolution: {integrity: sha512-VreORXnruy8A2SyprZKENAq3ArGwn35KPewLQSQ5dgDXSFtUj2scLuzZoVsZ/Uyt83td0WPvD6Lv0X7MvSYAMQ==} + '@sentry/node-core@10.70.0': + resolution: {integrity: sha512-oPOEVVNxv5WHtckx2i06Wi9FLWyvOg/1DUeX732jZ4iqT2nupINaMH4nF4f4kSvUThFnxkFSRQxwqOxgzMKhKA==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -4103,20 +3566,20 @@ packages: '@opentelemetry/sdk-trace-base': optional: true - '@sentry/node@10.68.0': - resolution: {integrity: sha512-bnvRzEehquG/894DD3BWNCBUbDWsLQfYPU+SNCMd6G4Ext75RthVkRs8R0sRaE6b8Tw9HSEgySHL834Tf8lVsA==} + '@sentry/node@10.70.0': + resolution: {integrity: sha512-SPOOVxmKTVIEtqvOKkQT163e/pOwucjS7OPsCHyRs8sFR4nfBNu0EThplyqnvqd5BWBMTPH6WTBQfo+QWHV+HA==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.68.0': - resolution: {integrity: sha512-JDNH9dacX0MSi7FRvabPCJUAXRMTe16bE/Gajc/7Gfcw7Rwvo4DZ0nd162PCSAW9QoEjUT12upDehEHJuFmsXg==} + '@sentry/opentelemetry@10.70.0': + resolution: {integrity: sha512-UNV/2tqypcUK6FDzerAsFJn1Km/c4VZCYkUZDNbnV5S0cwAq2BYKMo4M5vovaLDBQlxA+Wk9ovbxi5wYjjl9fw==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 '@opentelemetry/core': ^1.30.1 || ^2.1.0 '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 - '@sentry/server-utils@10.68.0': - resolution: {integrity: sha512-lp1ZSs1auw7HrCESSYt/n4dOUaKPVUIAKyVYRk6xVr4bMIN3RPub/H5Wm7QPj9CpXVC5bQFvB6+dHZXz809oMg==} + '@sentry/server-utils@10.70.0': + resolution: {integrity: sha512-rzegZjMFFgCp3o+N8+XU13rfSvz4B+f8rU0ijBGrQcHdMNyfsFDTu1UTm262JofmrV2u+s+D0u0vFTnqtOGkbA==} engines: {node: '>=18'} '@shaderfrog/glsl-parser@7.0.1': @@ -4129,51 +3592,24 @@ packages: '@simple-git/argv-parser@1.1.1': resolution: {integrity: sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw==} - '@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==} - '@storybook/builder-vite@10.4.6': - resolution: {integrity: sha512-BHBtD81HiXUiDQz/CaFynLtWmm7AFUQn8VnXuHipZ8KlnUANopa4yqdVuy/Gwz8ub254uFI5NMZsW/KlgWNgNg==} + '@storybook/builder-vite@10.5.7': + resolution: {integrity: sha512-fShF/aQaITqcJuMCLr42BGNUAbhDi4IboqvlbZqXAwgrrTslnZEUnY8GcEcvpZmjl11VwlmazhMJdH50fIgBPg==} peerDependencies: - storybook: ^10.4.6 + storybook: ^10.5.7 vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - '@storybook/builder-vite@10.5.5': - resolution: {integrity: sha512-dQoJ7gUl8y0z5rV9cE0mz6qTBNmN9R4GOLIZk98rJ8CwduNJOb9eGZXusDzzvnYcp8TnNkqDtyx4tXQSUDInPQ==} - peerDependencies: - storybook: ^10.5.5 - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - - '@storybook/csf-plugin@10.4.6': - resolution: {integrity: sha512-NILLxDqpA/JR/AazGWpsz+4fadJwRU4uhHephGtYpVOWnQA/DkJfKT6zpcJVq8+QA8A2zKMLX3GVKsXIrxjuDA==} + '@storybook/csf-plugin@10.5.7': + resolution: {integrity: sha512-IaX8FlM0H36HNFhJ2+4L9bCldqfvHGqcLg841SJNyK/DhfMlM7JsvY/GDH2ZFuWrUf8FSOx96GRRnHq6XfRKag==} peerDependencies: esbuild: '*' rollup: '*' - storybook: ^10.4.6 - vite: '*' - webpack: '*' - peerDependenciesMeta: - esbuild: - optional: true - rollup: - optional: true - vite: - optional: true - webpack: - optional: true - - '@storybook/csf-plugin@10.5.5': - resolution: {integrity: sha512-/euibhRFqklYCZqUseokojmfYcQpXshVY2QmA1qCuxMz9SzVFD3iSTw+aFLTxpsJGGdcZJk8fnm/rEthLzZ9jA==} - peerDependencies: - esbuild: '*' - rollup: '*' - storybook: ^10.5.5 + storybook: ^10.5.7 vite: '*' webpack: '*' peerDependenciesMeta: @@ -4189,90 +3625,45 @@ packages: '@storybook/global@5.0.0': resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} - '@storybook/icons@2.0.2': - resolution: {integrity: sha512-KZBCpXsshAIjczYNXR/rlxEtCUX/eAbpFNwKi8bcOomrLA4t/SyPz5RF+lVPO2oZBUE4sAkt43mfJUevQDSEEw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@storybook/icons@2.1.0': resolution: {integrity: sha512-Fxh9vYpX9bQqFeHRiY8h2ApeRGDzRSMLwJwNZ/AIRqnyOKHxRKL+yFe+ctEkVJmuptRE9u1Hrn8ZZNHyfDKKNg==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@storybook/react-dom-shim@10.4.6': - resolution: {integrity: sha512-iGNmKzrq9vgl2PDrYAnZKI+yvac3Ym+lJXXuQaqlFRS23zA5MNm4EBX+rAG7WulqchoK6NaZ0KQOs2mAgEpTMg==} + '@storybook/react-dom-shim@10.5.7': + resolution: {integrity: sha512-lxOkyh+wu/MiBXvYQHjZfD+DRKOa4bHBzbuGuiHXnHXmdOcTRdcrQTsoeN2FPtfugmmOG66cZUEgDwNX+k5eRA==} peerDependencies: '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 '@types/react-dom': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.4.6 + storybook: ^10.5.7 peerDependenciesMeta: '@types/react': optional: true '@types/react-dom': optional: true - '@storybook/react-dom-shim@10.5.5': - resolution: {integrity: sha512-PIk7N3LLrZIxfNxmkvmQN1d5UQ70XEedT8n0GhBiXnM6XL09xPGB8n8TZXeJBRYluKhDQcAyQeT0/OZmcDVQJg==} + '@storybook/react-vite@10.5.7': + resolution: {integrity: sha512-eEo3eVa2pvqrzQukKxAzx7YvswDAA1s6k/y+tdMxmRvWyHX6QEOsb9Tda6wcVaa7c8BeJM7Ggq+289cRMTH6Iw==} peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@types/react-dom': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.5.5 - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@storybook/react-vite@10.4.6': - resolution: {integrity: sha512-0arEQtybqGYXHbXpTot+Wv9YtG+V5Vp43QayXavPKQ20M8mpEzhyCPKd0EhqMGSC1Z1UEt0hm365WUBhI9LfKA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.4.6 - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - - '@storybook/react-vite@10.5.5': - resolution: {integrity: sha512-Uy7VV72kVSkw6aDTAPQupXUeZX5LF6e4zqNvTZ+36qxsXAkaFgw7HPEm7L1tsaRfiV+s9anU7UvX47tfJpYGuQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.5.5 - typescript: '>= 4.9.x' - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - - '@storybook/react@10.4.6': - resolution: {integrity: sha512-9Y7YecrVFe1/01KYjfOLxVqTg2Aq+IO6TEv6sC2U0PfD0AWCSCmQ91QqgBpN/XW4aFFWoiZNinyXMUlU8zxy2w==} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@types/react-dom': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.4.6 - typescript: '>= 4.9.x' - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true + storybook: ^10.5.7 + typescript: '>= 4.9.x' + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: typescript: optional: true - '@storybook/react@10.5.5': - resolution: {integrity: sha512-T2Xj0ey7a9RHU6coYLC0L5lhjcdyhLCs9wNv15FvHvgmrRobkynEV72kq5vGW8tFkahNWI1X9+GZPQ6r8Nm38w==} + '@storybook/react@10.5.7': + resolution: {integrity: sha512-uFvty2MMdFXzW5PcQe1JqDAZkz6cQq7q/9G/cbGVnBEvP6zsOVeL+bmrQ0/WBlFQN0Ko9+ZoCTvaQ9s65zBa5g==} peerDependencies: '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 '@types/react-dom': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - storybook: ^10.5.5 + storybook: ^10.5.7 typescript: '>= 4.9.x' peerDependenciesMeta: '@types/react': @@ -4447,20 +3838,20 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 - '@tanstack/query-core@5.101.2': - resolution: {integrity: sha512-hH5MLoJhF7KaIGd7q3xTXGXvslI+GYlM1Z/35aSHHWaCJWB7XvTSHYuV3eM7tw+aE0mT/xMro4M4Q9rCGHT0lw==} + '@tanstack/query-core@5.101.4': + resolution: {integrity: sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==} - '@tanstack/query-devtools@5.101.2': - resolution: {integrity: sha512-o+wHcqgN7Pp0s8v1i0UGq/ZrrEKrxdIiMQmKRdYb2w7NPtylYSJ4+wg/tIn71m9DLstwUwdEGAvROdly6HXP6w==} + '@tanstack/query-devtools@5.101.4': + resolution: {integrity: sha512-z5IPHnDX3aUWeTWlRKLyooBQekaCAw4xRpZqPQ390RiWTDBcTynjpPT221BArw0u2+pnQMdGvPQI9YNNubBcmA==} - '@tanstack/react-query-devtools@5.101.2': - resolution: {integrity: sha512-eU7HctdA9gDjqoERoEdzLbw9DiqnBDfh5+Hu0u26gjqoHJezOpQAuiesDL2VvkU+2cPV76zgv0tMZsOrI4LjnQ==} + '@tanstack/react-query-devtools@5.101.4': + resolution: {integrity: sha512-VeK2gtmfj7kvRBjtxS7TKxt/6qKhn8VzabY4UiYMr7NV9CddjSRYRgeYyld+NpjAkgMV9dd+2Qdr8ah5I03NeA==} peerDependencies: - '@tanstack/react-query': ^5.101.2 + '@tanstack/react-query': ^5.101.4 react: ^18 || ^19 - '@tanstack/react-query@5.101.2': - resolution: {integrity: sha512-seDkr6kzGzX1okaaTtZPtgA688CDPlXUz1C6xSg0ESqn04Vuc8tlrYms1s3de+znBqhPVxFRfpAfUf+6XvfPWg==} + '@tanstack/react-query@5.101.4': + resolution: {integrity: sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==} peerDependencies: react: ^18 || ^19 @@ -4498,11 +3889,17 @@ packages: peerDependencies: '@testing-library/dom': '>=7.21.4' - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@tsconfig/node10@1.0.12': + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - '@tybys/wasm-util@0.10.2': - resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} @@ -4657,25 +4054,22 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@25.9.5': - resolution: {integrity: sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==} - - '@types/node@26.1.1': - resolution: {integrity: sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==} + '@types/node@26.2.0': + resolution: {integrity: sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==} '@types/prismjs@1.26.6': resolution: {integrity: sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==} - '@types/react-dom@19.2.3': - resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + '@types/react-dom@19.2.4': + resolution: {integrity: sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==} peerDependencies: '@types/react': ^19.2.0 '@types/react-syntax-highlighter@15.5.13': resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} - '@types/react@19.2.17': - resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + '@types/react@19.2.18': + resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} '@types/resolve@1.20.6': resolution: {integrity: sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==} @@ -4698,8 +4092,8 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@typescript-eslint/types@8.65.0': - resolution: {integrity: sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==} + '@typescript-eslint/types@8.67.0': + resolution: {integrity: sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@uiw/color-convert@2.10.3': @@ -4867,8 +4261,8 @@ packages: peerDependencies: '@urql/core': ^6.0.0 - '@vitejs/plugin-react@6.0.4': - resolution: {integrity: sha512-XcCQz0TBpBgljhj0gMuuDj49i6Ytqh5q1osT/Gp5uAVJUCTWxyskk/l1jwYYiu2xcNHHipdMz40EGfM1VdamVg==} + '@vitejs/plugin-react@6.0.5': + resolution: {integrity: sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 @@ -4880,42 +4274,22 @@ packages: babel-plugin-react-compiler: optional: true - '@vitest/browser-playwright@4.1.8': - resolution: {integrity: sha512-SR7FqgegaexEg73xvf3ArtygXegagMdXnL0EZMpxrWvvhQxvicD/E8p0ib0J91riPRtQUViyh67Xjw3NqvyhVg==} - peerDependencies: - playwright: 1.60.0 - vitest: 4.1.8 - - '@vitest/browser-playwright@4.1.9': - resolution: {integrity: sha512-Bq1rOGf9waevzG3EOkO/dene6bvKTUsZMVg8S1i+WH3JcMjuXEjiahP9rAqZRELUqjBySOJsvvSWqK/B3wjKQw==} + '@vitest/browser-playwright@4.1.10': + resolution: {integrity: sha512-nMoXGEiRpT7m3W7NsbvrM2aKNwiNHZf+zEpUCvMteGjZFvfT96Q9fh7QyB98dvDWXiKvrLxA7bJ1mCOOv+JQPw==} peerDependencies: playwright: 1.60.0 - vitest: 4.1.9 - - '@vitest/browser@4.1.8': - resolution: {integrity: sha512-u21VzX07HzlJYpFgkxmjEXar/tG2UqWGgyGG/46SrrPc7rSdCTPw5vuowopO9CIqF8UCUQzDFdbVnNpw6N0BfQ==} - peerDependencies: - vitest: 4.1.8 - - '@vitest/browser@4.1.9': - resolution: {integrity: sha512-j1BKtWmPcqpMhmx/L9EPLgAJpCb0zKfwoWLmqBbxaogCXHjOwHFSEoHCBfnGtx93xKQwilZ26m+UOsHqHMkRNg==} - peerDependencies: - vitest: 4.1.9 + vitest: 4.1.10 - '@vitest/coverage-v8@4.1.8': - resolution: {integrity: sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==} + '@vitest/browser@4.1.10': + resolution: {integrity: sha512-UDwuWGwXj646CBx/bQHOaJSX7np0I8JL/UKQYa1e4QrVHH8VdWtx8eaOuf8sy0ShwDgR6NjJAsp5eF6vjF6qng==} peerDependencies: - '@vitest/browser': 4.1.8 - vitest: 4.1.8 - peerDependenciesMeta: - '@vitest/browser': - optional: true + vitest: 4.1.10 - '@vitest/coverage-v8@4.1.9': - resolution: {integrity: sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==} + '@vitest/coverage-v8@4.1.10': + resolution: {integrity: sha512-IM49HmthevbgAO4anp1hwtoT9wYe59w0LR00gr+eagHE+ZJ5lK4sLPeO0ubgoJcwLk6dehU3R24N+FbEEKDc8g==} peerDependencies: - '@vitest/browser': 4.1.9 - vitest: 4.1.9 + '@vitest/browser': 4.1.10 + vitest: 4.1.10 peerDependenciesMeta: '@vitest/browser': optional: true @@ -4923,25 +4297,11 @@ packages: '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@vitest/expect@4.1.8': - resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} + '@vitest/expect@4.1.10': + resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} - '@vitest/expect@4.1.9': - resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} - - '@vitest/mocker@4.1.8': - resolution: {integrity: sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==} - peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - - '@vitest/mocker@4.1.9': - resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==} + '@vitest/mocker@4.1.10': + resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -4954,41 +4314,26 @@ packages: '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/pretty-format@4.1.8': - resolution: {integrity: sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==} - - '@vitest/pretty-format@4.1.9': - resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==} - - '@vitest/runner@4.1.8': - resolution: {integrity: sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==} + '@vitest/pretty-format@4.1.10': + resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} - '@vitest/runner@4.1.9': - resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==} + '@vitest/runner@4.1.10': + resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} - '@vitest/snapshot@4.1.8': - resolution: {integrity: sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==} - - '@vitest/snapshot@4.1.9': - resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==} + '@vitest/snapshot@4.1.10': + resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/spy@4.1.8': - resolution: {integrity: sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==} - - '@vitest/spy@4.1.9': - resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==} + '@vitest/spy@4.1.10': + resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} - '@vitest/utils@4.1.8': - resolution: {integrity: sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==} - - '@vitest/utils@4.1.9': - resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} + '@vitest/utils@4.1.10': + resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} '@webcontainer/env@1.1.1': resolution: {integrity: sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng==} @@ -5009,27 +4354,8 @@ packages: resolution: {integrity: sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==} engines: {node: '>=16.0.0'} - '@xyflow/react@12.10.2': - resolution: {integrity: sha512-CgIi6HwlcHXwlkTpr0fxLv/0sRVNZ8IdwKLzzeCscaYBwpvfcH1QFOCeaTCuEn1FQEs/B8CjnTSjhs8udgmBgQ==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - - '@xyflow/react@12.11.0': - resolution: {integrity: sha512-na4IO33FSs2OS72hASgZDmTYwFAkef7Z74uBUVrong3ARmQQHfnRUVaCFn1kTt5LbS6pK03TbYjCPGLjLFfziA==} - peerDependencies: - '@types/react': '>=17' - '@types/react-dom': '>=17' - react: '>=17' - react-dom: '>=17' - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@xyflow/react@12.11.1': - resolution: {integrity: sha512-L+zBoLGSXham0MnlY8QqjfR7/C5JNw0zxkaey5aZ5XmCgJBAdH4+WRIu8CR40d3l/BdU635V6YbhBK1jMo8/6Q==} + '@xyflow/react@12.11.2': + resolution: {integrity: sha512-eLAlDWJfWnQEhJwGMjlWdAXO9eYllKpliUmPQlAmOLxz6mExXuzMVDUKLMquixgkrtmMFFtug3jGKmYYld12cA==} peerDependencies: '@types/react': '>=17' '@types/react-dom': '>=17' @@ -5041,27 +4367,20 @@ packages: '@types/react-dom': optional: true - '@xyflow/system@0.0.76': - resolution: {integrity: sha512-hvwvnRS1B3REwVDlWexsq7YQaPZeG3/mKo1jv38UmnpWmxihp14bW6VtEOuHEwJX2FvzFw8k77LyKSk/wiZVNA==} - - '@xyflow/system@0.0.77': - resolution: {integrity: sha512-qCDCMCQAAgUu8yHnhloHG9F5mwPX5E+Wl8McpYIOPSSXfzFJJoZcwOcsDiAjitVKIg2de1WmJbCHfpcvxprsgg==} - - '@xyflow/system@0.0.78': - resolution: {integrity: sha512-lY0z2qP33fUhTva9Vaxrk0lqZta2pkbxB1trHAx1omnJqRtPvDlAQYV2r5fhS6AdpkulYmbNW0svy+A4/t4B/g==} + '@xyflow/system@0.0.79': + resolution: {integrity: sha512-czLyOh91NF0hIzbNzwi8I6GlqG23BHh2435OddfI6uiaLH3xdrdygO93gqgH1Bv9mhy8XPFQJOBn1FTq4LvEWA==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.16.0: - resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} engines: {node: '>=0.4.0'} - hasBin: true - acorn@8.17.0: - resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} engines: {node: '>=0.4.0'} hasBin: true @@ -5123,6 +4442,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -5149,9 +4471,6 @@ packages: resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} engines: {node: '>=4'} - ast-v8-to-istanbul@1.0.4: - resolution: {integrity: sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==} - ast-v8-to-istanbul@1.0.5: resolution: {integrity: sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA==} @@ -5198,12 +4517,12 @@ packages: peerDependencies: react: '>=17.0.1' - brace-expansion@2.1.0: - resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} + brace-expansion@2.1.4: + resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==} - brace-expansion@5.0.5: - resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} - engines: {node: 18 || 20 || >=22} + brace-expansion@5.0.9: + resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + engines: {node: 20 || >=22} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -5269,8 +4588,8 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - chardet@2.1.1: - resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + chardet@2.2.0: + resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} charenc@0.0.2: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} @@ -5283,8 +4602,8 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - chromatic@18.1.0: - resolution: {integrity: sha512-I9av8lUc5CQRlYzwKpmOG9cwYvZ4AFmTvtHeNrzOMC1353F9xYw45CHpSNIsNKuOiqqmzci9esXQd5akl0fi6w==} + chromatic@18.2.0: + resolution: {integrity: sha512-xyTKDhBQPDd4qrsXhJK7GAJyIStpRoSTs4b7EtPyliSL0luej3DcqkeCZ9osHxbiLXiViNyWfMxo5Xgzejex8A==} engines: {node: '>=22.0.0'} hasBin: true peerDependencies: @@ -5314,22 +4633,10 @@ packages: classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - cli-boxes@4.0.1: - resolution: {integrity: sha512-5IOn+jcCEHEraYolBPs/sT4BxYCe2nHg374OPiItB1O96KZFseS2gthU4twyYzeDcFew4DaUM/xwc5BQf08JJw==} - engines: {node: '>=18.20 <19 || >=20.10'} - - cli-cursor@4.0.0: - resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - cli-spinners@3.4.0: resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} engines: {node: '>=18.20'} @@ -5338,10 +4645,6 @@ packages: resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} engines: {node: '>=20'} - cli-truncate@6.1.1: - resolution: {integrity: sha512-06p9vyLahLa4zkGcgsGxU6iEkSOiuI4fhCH6Emhe2lPAcoUv73n72DnODsnHA+5wwXGnV0n9M9/qOQJSjYhFhw==} - engines: {node: '>=22'} - cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -5386,10 +4689,6 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc - code-excerpt@4.0.0: - resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -5444,10 +4743,6 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - convert-to-spaces@2.0.1: - resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - cookie-es@3.1.1: resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} @@ -5478,8 +4773,11 @@ packages: typescript: optional: true - crelt@1.0.6: - resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + crelt@1.0.7: + resolution: {integrity: sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==} cross-inspect@1.0.1: resolution: {integrity: sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==} @@ -5730,8 +5028,8 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - deslop-js@0.9.2: - resolution: {integrity: sha512-rGhQ17gHnmsjG5KFJM4+oN4bOxktHiPGqzJxKqWt0Qw/NLZIsEgLH1wIo1tTXRyN/MNwhchKbfUieFiWyAh0pQ==} + deslop-js@0.9.11: + resolution: {integrity: sha512-0hXU8GImv3uJZY25jeXQ1JOcajpuKyzdNTK7FTyxyGR/GtjpGPmiVM+8d+5Tacb702UzVtAHhB5xMBl/pOGxKQ==} detect-indent@7.0.2: resolution: {integrity: sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==} @@ -5754,6 +5052,10 @@ packages: resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + diff@4.0.4: + resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} + engines: {node: '>=0.3.1'} + diff@5.2.2: resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==} engines: {node: '>=0.3.1'} @@ -5776,8 +5078,8 @@ packages: dom-accessibility-api@0.6.3: resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} - dompurify@3.4.10: - resolution: {integrity: sha512-0xzNv0e7oYC6yyuOGZIABPM4qtg3QxLFniDNPP4ZP90wR8Yq3zgwpRbrNiT4N3IKqDbbYFEJLV+JWEs19aZ//w==} + dompurify@3.4.13: + resolution: {integrity: sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==} dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} @@ -5827,23 +5129,12 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@2.1.0: - resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} - es-module-lexer@2.3.1: resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} - es-toolkit@1.45.1: - resolution: {integrity: sha512-/jhoOj/Fx+A+IIyDNOvO3TItGmlMKhtX8ISAHKE90c4b/k1tqaqEZ+uUqfpU8DMnW5cgNJv606zS55jGvza0Xw==} - es-toolkit@1.50.0: resolution: {integrity: sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==} - esbuild@0.28.0: - resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.28.1: resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} engines: {node: '>=18'} @@ -5853,10 +5144,6 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -5930,8 +5217,8 @@ packages: eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} extend@3.0.2: @@ -5959,8 +5246,8 @@ packages: fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-uri@3.1.4: - resolution: {integrity: sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==} + fast-uri@3.1.5: + resolution: {integrity: sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==} fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} @@ -6011,8 +5298,8 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatted@3.4.3: - resolution: {integrity: sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==} + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} format@0.2.2: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} @@ -6092,20 +5379,16 @@ packages: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} - globals@17.6.0: - resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==} - engines: {node: '>=18'} - - globals@17.8.0: - resolution: {integrity: sha512-Zz/LMDZScFmkakeL2cTHzf+PbWKdpU3uclqkZT7TjDG58j5WPt0PpA+n9uPI24fZtlw07q0OtEi84K+umsRzqQ==} + globals@17.10.0: + resolution: {integrity: sha512-V0kztuWST2k8A/VbxAY8+L+7+Rgo3fyA24IHRLrZp7HOzJjV0gHSaZUjK9lpP/IrBSNite2tZ1prhRkinRu1CA==} engines: {node: '>=18'} globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} - gql.tada@1.11.2: - resolution: {integrity: sha512-oBr7ShA5/TmcwOO7BZgN1SynX2rBU+/ltysB0zXc+NCBF+9YOg6MRzJcTfLjIqDKdcE3LGxpOl0l9hBbxmyzmA==} + gql.tada@1.11.3: + resolution: {integrity: sha512-5JCI4j2f0nug8ILaCQys/yjOP78QqqjVUf47OQsME63rZfemsHT3e5vcfbHsnZiG6vxyqpKUJArtXEVwSefUhw==} hasBin: true peerDependencies: typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -6140,31 +5423,25 @@ packages: cosmiconfig-toml-loader: optional: true - graphql-language-service@5.5.1: - resolution: {integrity: sha512-6/sPlE9TFUN8aCFohwo3MWYWn0AgVE+Ze3y+NptK7+ph3QkEryvZq9EruMSeJg6o51x6+ciJC/bm2liJC5dJ2A==} - hasBin: true - peerDependencies: - graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 - graphql-language-service@5.5.2: resolution: {integrity: sha512-NJhgEKTArkyNPcy4NRUFdbpNs5/F99LcvXbNtmGzNGwwruN8tBE3YPMjpYmp8KpBQtOx3uSuvXJlOOE3Vy2KRQ==} hasBin: true peerDependencies: graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 - graphql-tag@2.12.6: - resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + graphql-tag@2.12.7: + resolution: {integrity: sha512-xnE/NFzy+0eIesvAsREJZ284zTl/wYuBAvpsFSDhRGRdRHdnE90M21Q3xAWyYInb0J756c6x0pIQ62+vtvOs1Q==} engines: {node: '>=10'} peerDependencies: - graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - graphql-ws@6.0.8: - resolution: {integrity: sha512-m3EOaNsUBXwAnkBWbzPfe0Nq8pXUfxsWnolC54sru3FzHvhTZL0Ouf/BoQsaGAXqM+YPerXOJ47BUnmgmoupCw==} + graphql-ws@6.2.1: + resolution: {integrity: sha512-NMbPNeTwXpUOxmczdMtzEnynLNbbR267E9hRcJ81SSbQeIvZup3cMjbD1ZT3jpS2xkpxooisitvO7LZNOyz17Q==} engines: {node: '>=20'} peerDependencies: '@fastify/websocket': ^10 || ^11 crossws: ~0.3 - graphql: ^15.10.1 || ^16 + graphql: ^15.10.1 || ^16 || ^17 ws: ^8 peerDependenciesMeta: '@fastify/websocket': @@ -6251,8 +5528,8 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.2: - resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} ignore-walk@7.0.0: @@ -6267,14 +5544,11 @@ packages: resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} engines: {node: '>= 4'} - immer@10.2.0: - resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} - - immer@11.1.4: - resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} + immer@11.1.16: + resolution: {integrity: sha512-Xs7H9rBc+kti1J6RueUvbEBkmOz7jqj11XYgf+YMXAYzu8EeE7hwZ9poLXdVfVnGmJu7QAf41T7H2KuF6QoK6Q==} - immutable@5.1.5: - resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==} + immutable@5.1.9: + resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -6284,8 +5558,8 @@ packages: resolution: {integrity: sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==} engines: {node: '>=12.2'} - import-in-the-middle@3.3.2: - resolution: {integrity: sha512-jTd2FfOgOWOdgjkHuk/1Ms8VKFXkPs15ymYBETw1sAOrO/dY3XeGVRWir9qBbw7pXr0T2eTFwfCZ+N02HmiNGA==} + import-in-the-middle@3.3.3: + resolution: {integrity: sha512-AiohS3H80sXO6owEltjGX+glb7qXaDhBoJb9XcQVH4UI207xu/bDLUcadVKp7Qe576reg9yr/PXZjV5qx8gfbA==} engines: {node: '>=18'} import-meta-resolve@4.2.0: @@ -6299,34 +5573,10 @@ packages: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - indent-string@5.0.0: - resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} - engines: {node: '>=12'} - index-to-position@1.2.0: resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} engines: {node: '>=18'} - ink-spinner@5.0.0: - resolution: {integrity: sha512-EYEasbEjkqLGyPOUc8hBJZNuC5GvXGMLu0w5gdTNskPc7Izc5vO3tdQEYnzvshucyGCBXc86ig0ujXPMWaQCdA==} - engines: {node: '>=14.16'} - peerDependencies: - ink: '>=4.0.0' - react: '>=18.0.0' - - ink@7.1.1: - resolution: {integrity: sha512-Y43xxa1ZSPvpmfLHcN5o+OdP8Rf8ykkNJEuKYOUNZKT8wXVNLFTtEm1nSDMQkfBH+YANF4Xuu0hhZ4ejqAtN2w==} - engines: {node: '>=22'} - peerDependencies: - '@types/react': '>=19.2.0' - react: '>=19.2.0' - react-devtools-core: '>=6.1.2' - peerDependenciesMeta: - '@types/react': - optional: true - react-devtools-core: - optional: true - inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -6384,11 +5634,6 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - is-in-ci@2.0.0: - resolution: {integrity: sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w==} - engines: {node: '>=20'} - hasBin: true - is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} @@ -6475,26 +5720,8 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true - jotai@2.20.0: - resolution: {integrity: sha512-b5GAqgmXmXzB4WPaTH26ppk9Sl7AA9WSQX7yfdM+gJ1rFROiWcVbi97gFuN/yVCojOcbcvop2sfLL+fjxW0JVg==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@babel/core': '>=7.0.0' - '@babel/template': '>=7.0.0' - '@types/react': '>=17.0.0' - react: '>=17.0.0' - peerDependenciesMeta: - '@babel/core': - optional: true - '@babel/template': - optional: true - '@types/react': - optional: true - react: - optional: true - - jotai@2.20.1: - resolution: {integrity: sha512-dnuKfU/GLi8B28RRMjQ3AfoN7kfzP8o41+AX2FmITZqEMY8PHnjABq+VkEooomLwYaGjda+pgy0yFSjaHX/ZPg==} + jotai@2.20.2: + resolution: {integrity: sha512-aHB4CNb9qRcyf0mwSB6EO5bCGAjx8cTwFgOFCE2leOnTzqACbnSWG8XoWB3LxCT1Qoj03I1OWAHszDmN4uHb/w==} engines: {node: '>=12.20.0'} peerDependencies: '@babel/core': '>=7.0.0' @@ -6521,12 +5748,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} - hasBin: true - - js-yaml@4.2.0: - resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} + js-yaml@4.3.1: + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} hasBin: true jsesc@3.1.0: @@ -6581,8 +5804,8 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} - knip@6.23.0: - resolution: {integrity: sha512-2DvAOX2pZWiG4SLvRRxOAU0aWGEn1ZoVblI541xIoXFdHqq2THMZXy66/qcY5WGuW3TXhb9T1x1zd/Hd1u+yqg==} + knip@6.27.0: + resolution: {integrity: sha512-CngYEYrD0n20N06FXA8n3u/0Wnnugoa+B9k14OP+iKIgkCHuzvIdsP3nfwjhByoc1WfogpxfiriMboAXFETDUw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -6602,30 +5825,60 @@ packages: cpu: [arm64] os: [android] + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + lightningcss-darwin-arm64@1.32.0: resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + lightningcss-darwin-x64@1.32.0: resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + lightningcss-freebsd-x64@1.32.0: resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + lightningcss-linux-arm-gnueabihf@1.32.0: resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + lightningcss-linux-arm64-gnu@1.32.0: resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} engines: {node: '>= 12.0.0'} @@ -6633,6 +5886,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} @@ -6640,6 +5900,13 @@ packages: os: [linux] libc: [musl] + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} @@ -6647,6 +5914,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} @@ -6654,22 +5928,45 @@ packages: os: [linux] libc: [musl] + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + lightningcss-win32-x64-msvc@1.32.0: resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + lightningcss@1.32.0: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -6677,11 +5974,11 @@ packages: resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - linkify-it@5.0.1: - resolution: {integrity: sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==} + linkify-it@5.0.2: + resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} - listr2@10.2.1: - resolution: {integrity: sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==} + listr2@10.2.2: + resolution: {integrity: sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==} engines: {node: '>=22.13.0'} locate-path@6.0.0: @@ -6732,8 +6029,8 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-react@1.27.0: - resolution: {integrity: sha512-rJicGl/3Fly/E0rOH1YmPZ6e49JCnKknh1ox1vpHnkfjujAkKA6sqUZvH3MTAaXXjgexyUwgNwTJzTtYuAFYJw==} + lucide-react@1.31.0: + resolution: {integrity: sha512-G8u2eEtoHUnUa9f8lbvqDhCiORMnYLdUEo06EEG9MQvHQrInKcX3Pa2TH39MM5qyzRcWETxB0+aOwAPI1g1kEg==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -6744,13 +6041,16 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - magicast@0.5.3: - resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + magicast@0.5.4: + resolution: {integrity: sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==} make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + map-cache@0.2.2: resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} engines: {node: '>=0.10.0'} @@ -6834,8 +6134,8 @@ packages: playwright: optional: true - mermaid@11.15.0: - resolution: {integrity: sha512-pTMbcf3rWdtLiYGpmoTjHEpeY8seiy6sR+9nD7LOs8KfUbHE4lOUAprTRqRAcWSQ6MQpdX+YEsxShtGsINtPtw==} + mermaid@11.16.1: + resolution: {integrity: sha512-TQsq6u22fAn3rek5VOubrhKPo1g5hwC3FXUN9hiyupTckcYiGuuKGkNQrKYwGJkXUxZdojwRG46gsSCFZMDp4g==} meros@1.3.2: resolution: {integrity: sha512-Q3mobPbvEx7XbwhnC1J1r60+5H6EZyNccdzSz0eGexJRwouUtTZxPVRGdqKtxlpD84ScK4+tIGldkqDtCKdI0A==} @@ -6934,10 +6234,6 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - mimic-function@5.0.1: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} @@ -6950,8 +6246,8 @@ packages: resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==} hasBin: true - minimatch@10.2.5: - resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} minimatch@5.1.9: @@ -6999,8 +6295,8 @@ packages: resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} engines: {node: ^20.17.0 || >=22.9.0} - nanoid@3.3.16: - resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -7033,14 +6329,14 @@ packages: nullthrows@1.1.1: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} - nuqs@2.8.9: - resolution: {integrity: sha512-8ou6AEwsxMWSYo2qkfZtYFVzngwbKmg4c00HVxC1fF6CEJv3Fwm6eoZmfVPALB+vw8Udo7KL5uy96PFcYe1BIQ==} + nuqs@2.9.5: + resolution: {integrity: sha512-Ec+vVwUKKng7E6ya5zZowt3QqDRGqqB1E40Rp2QAyZXGgYekDzmVo7V7jwbLAMkdbiZTH285fOIUyZGrS5zsCg==} peerDependencies: '@remix-run/react': '>=2' '@tanstack/react-router': ^1 next: '>=14.2.0' react: '>=18.2.0 || ^19.0.0-0' - react-router: ^5 || ^6 || ^7 + react-router: ^5 || ^6 || ^7 || ^8 react-router-dom: ^5 || ^6 || ^7 peerDependenciesMeta: '@remix-run/react': @@ -7063,21 +6359,10 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} - - obug@2.1.3: - resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} - engines: {node: '>=12.20.0'} - obug@2.1.4: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} engines: {node: '>=12.20.0'} - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - onetime@7.0.0: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} @@ -7114,8 +6399,8 @@ packages: resolution: {integrity: sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==} engines: {node: ^20.19.0 || >=22.12.0} - oxc-parser@0.141.0: - resolution: {integrity: sha512-uFkGGr1KMWd6aWv9UAqooYrN78trw8MWWmoPvgWokfBEUq1+eiIQ+qfj3wokhy0fxtZWZk+0dHoS7/yRTJtd6w==} + oxc-parser@0.142.0: + resolution: {integrity: sha512-kKR+jPiRJYJDexVoziIg/FVGvr1fT1FZSSJOk6tVoMKKSlsf1Cso+cgGCJkOEDWOP174vRntCPFKg+AS7InWvw==} engines: {node: ^20.19.0 || >=22.12.0} oxc-resolver@11.21.3: @@ -7124,21 +6409,8 @@ packages: oxc-resolver@11.24.2: resolution: {integrity: sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==} - oxfmt@0.55.0: - resolution: {integrity: sha512-jSj2wCTakwgPMxkfiVZX0jf+nX+Nz6xlyAZjqNE0qXTFdCBPYlP6JAN+ODjmealw7DXBjOzYbdsqwBMAZnPZ6A==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - svelte: ^5.0.0 - vite-plus: '*' - peerDependenciesMeta: - svelte: - optional: true - vite-plus: - optional: true - - oxfmt@0.60.0: - resolution: {integrity: sha512-fViX6i+gJuZWY+jI/fnR6WRbRj70GZ9RlCd30MygJrHTUNc4DxvKHWw8vBjMjffv3PgU5qWDR0AzmojQByqaZA==} + oxfmt@0.63.0: + resolution: {integrity: sha512-kgdDwv35wvVf6554U2Ab8Jnd0zTM+TsEQWwaB70RAjK3gICFAFGO+2Hd3Be27GMoXj3XRL9IKSNRVl7KBQL6iw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -7150,29 +6422,16 @@ packages: vite-plus: optional: true - oxlint-plugin-react-doctor@0.9.2: - resolution: {integrity: sha512-fTciSOgAGe/KAgvFDKLz9crjxHyAuu0BvT5sXfSdo2B5QmY5Y/j3nliqX6FaGr7Wud1SYvkAky+/m+58b6K6fQ==} + oxlint-plugin-react-doctor@0.9.11: + resolution: {integrity: sha512-ZhW15wfFjQlUwAO6zG3jVZKse4/PBTCArhbibiidHmTlvOpPHPM1AjdYG13023pfsbbtVdy7Tb7KHfqbKt8rHg==} engines: {node: ^20.19.0 || >=22.13.0} - oxlint@1.70.0: - resolution: {integrity: sha512-D6JgHtzkhRwvEC+A0Nw5AEc5bk8x5i1pHzvZIEf/a0C4hOzmAACNGtkDGPyFaxxX3ZVGxCPeig3P3rMM8XU3/g==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - oxlint-tsgolint: '>=0.22.1' - vite-plus: '*' - peerDependenciesMeta: - oxlint-tsgolint: - optional: true - vite-plus: - optional: true - - oxlint@1.74.0: - resolution: {integrity: sha512-odGl2s2x5IOJoj3A0v1k0PGBXVFBZeZ2+AK/+K2MJur7Ghi3bkyX5NuLUWHKqa4js1wjep3hJeuTQJOlr+4+dA==} + oxlint@1.76.0: + resolution: {integrity: sha512-6QoFioEU4fNdiUx/2Eo6TRd6NG7H7njnRCz8rhB66cZmMHDTqcm1Rjvl8Wry+ZTQMBAmyb4Mlf62Mk5X+eHSOw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - oxlint-tsgolint: '>=0.24.0' + oxlint-tsgolint: '>=7.0.2001' vite-plus: '*' peerDependenciesMeta: oxlint-tsgolint: @@ -7180,8 +6439,8 @@ packages: vite-plus: optional: true - oxlint@1.75.0: - resolution: {integrity: sha512-m9WzjRcRYA/uqIZDa9tclrieoPJ/ln1QYTKdFx6NUOs8uY5DiHlIwRQoCrHT6OM6O3ww3l2skY5gO7G7ZphE7g==} + oxlint@1.78.0: + resolution: {integrity: sha512-QgQePuxIqKOzo1KSjG2EnITEeWvWnKAm77eq8nrMtf6AGoA+zyGc4PFYtDNJSD25g/ibOwfQ851hZ4/SPkMVoA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -7234,10 +6493,6 @@ packages: parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - patch-console@2.0.0: - resolution: {integrity: sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - path-data-parser@0.1.0: resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} @@ -7290,10 +6545,6 @@ packages: resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} - engines: {node: '>=12'} - picomatch@4.0.5: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} @@ -7322,8 +6573,8 @@ packages: points-on-path@0.2.1: resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} - postcss@8.5.23: - resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} engines: {node: ^10 || ^12 || >=14} preact@10.29.2: @@ -7333,11 +6584,6 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.8.3: - resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} - engines: {node: '>=14'} - hasBin: true - prettier@3.8.4: resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==} engines: {node: '>=14'} @@ -7372,14 +6618,8 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - react-aria-components@1.18.0: - resolution: {integrity: sha512-FhRQjuDkH4WhgFv+O2sYTzK3JzdZTGpBeaqfRlfTo+DcSZzD8elJEkytHe7SDpcexVKeire8NVd7OruZHfCVoA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - react-aria-components@1.19.0: - resolution: {integrity: sha512-2smSS5nqJ8cGYMQezuUXveZm7eMyHCqTN6mDpylQBYLYbdF5dxCCuW1DHn1VKLe1DybSfPvX/cZtJlDmvFfn8A==} + react-aria-components@1.20.0: + resolution: {integrity: sha512-BMbpIgoV9aELeBrB0Y120NgoigHb5OdcJwc+4e7uSnbTbamea6lo+gqcc4LAxzMaK3Jf+7LI1oCDE6yANsmxIQ==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 @@ -7390,14 +6630,8 @@ packages: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-aria@3.49.0: - resolution: {integrity: sha512-4+oK9FwJQWYhyA5zLfj/feOGY0zZbkE1muoF4gyxMroHVypjcYaRSTlJwvxph2zIlxt757KX6xIK2wJ5Aw1Kog==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - react-aria@3.50.0: - resolution: {integrity: sha512-S0Os6QZk33fzUAKu1QLT9afoUaCBt1ZNdoiq0n2YMVgKIdNIQS8zxiZ8O9hYE6QyDkHKjD6q39LQZ+qaSAIgjw==} + react-aria@3.51.0: + resolution: {integrity: sha512-AyWLw0XR38cFPwBu/ErgGaVrc5dupLEKmRlMXTGvFKOtbaGRQ2+yQJkjVhpdHhoRhU4+G+tJDFeHDTS8tK3bfQ==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 @@ -7427,8 +6661,8 @@ packages: resolution: {integrity: sha512-aEZ9qP+/M+58x2qgfSFEWH1BxLyHe5+qkLNJOZQb5iGS017jpbRnoKhNRrXPeA6RfBrZO5wZrT9DMC1UqE1f1w==} engines: {node: ^20.9.0 || >=22} - react-doctor@0.9.2: - resolution: {integrity: sha512-A/e21t0y3j7zUTS8lJyNI2pKMfYLDH+zZwqAC7+MQW1vCD3xqWc2x8XCCWKnrQQwtFd6dwSZw2017x1iSLnGTA==} + react-doctor@0.9.11: + resolution: {integrity: sha512-y5DQ+ILL6mawXpKtAvJYrrqznl6nasXd4Xs9JGJsY4GWlplzs5UCozKaZgFS5AWzZ7KNrX88GZTuV4Vj47k+IQ==} engines: {node: ^20.19.0 || >=22.13.0} hasBin: true @@ -7474,14 +6708,8 @@ packages: peerDependencies: react: ^16 || ^17 || ^18 || ^19 - react-reconciler@0.33.0: - resolution: {integrity: sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==} - engines: {node: '>=0.10.0'} - peerDependencies: - react: ^19.2.0 - - react-redux@9.2.0: - resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + react-redux@9.3.0: + resolution: {integrity: sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==} peerDependencies: '@types/react': ^18.2.25 || ^19 react: ^18.0 || ^19 @@ -7550,13 +6778,8 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-stately@3.47.0: - resolution: {integrity: sha512-H3ar+SOWP920EbVg7qWfP3fZjZiwhlEJAEJQqjt+w8oKijCwFgr0+R4941PIHscOXRNRvEOjvWilitImC0DdBg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - react-stately@3.48.0: - resolution: {integrity: sha512-ImicSAG+lTotAe5izcs1fz49Zk48w7pDusqYg04WaPhCoej8BJ24soMu3iLXIrsi273s4P1gZrYGrqReMfgEEA==} + react-stately@3.49.0: + resolution: {integrity: sha512-13iNq2KzBrRAzxRc+n53hgROfIistiYY/sPtIhCw1qUB7/kmo+X1xEU2uiS5zcCIrc55AUPwoHqOIIpKWSwB9A==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 @@ -7582,17 +6805,13 @@ packages: react: '>=16' react-dom: '>=16' - react-zoom-pan-pinch@4.0.3: - resolution: {integrity: sha512-N2Hi6L78fFmhRra+ORpFSW7WST5x6kxpOPplIvtB0b7b+U2anpo1z1wLgaWRPS2kUSqcraRG+JgBCIlDJnqqAg==} + react-zoom-pan-pinch@4.0.4: + resolution: {integrity: sha512-P0D7lfNHyJCNuUozoVdt0WNWcQ34ZbbD71B8pb+UtF7Th8MKBnxYiPApDfPZZmr+Gk0l0WmGTXL8R36JirHcQQ==} engines: {node: '>=8', npm: '>=5'} peerDependencies: react: '*' react-dom: '*' - react@19.2.5: - resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==} - engines: {node: '>=0.10.0'} - react@19.2.8: resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} engines: {node: '>=0.10.0'} @@ -7601,16 +6820,12 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - recast@0.23.11: - resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} - engines: {node: '>= 4'} - recast@0.23.12: resolution: {integrity: sha512-dEWRjcINDu/F4l2dYx57ugBtD7HV9KXESyxhzw/MqWLeglJrsjJKqACPyUPg+6AF8mIgm+Zi0dZ3ACoIg+QtpA==} engines: {node: '>= 4'} - recharts@3.9.0: - resolution: {integrity: sha512-dCEcE9y20c8H2tkVeByrAXhhnBJk6/QLbxKmn+dJUptOfc5NMjwRh1jo0vZPRLD+5dMrHrP+hPEsfbGBMfnf5Q==} + recharts@3.10.1: + resolution: {integrity: sha512-QXFrvt6IVcw7eeZCoyXTwkIJAX3Dv1nyVhMicXJ47GsGDDpcN8z6o644DibE9XjpBTThtsomLKnTV6lc+cVFUA==} engines: {node: '>=18'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -7695,10 +6910,6 @@ packages: engines: {node: '>= 0.4'} hasBin: true - restore-cursor@4.0.0: - resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} @@ -7713,8 +6924,8 @@ packages: robust-predicates@3.0.3: resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} - rolldown@1.1.5: - resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} + rolldown@1.2.3: + resolution: {integrity: sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -7744,11 +6955,6 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.8.4: - resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} - engines: {node: '>=10'} - hasBin: true - semver@7.8.5: resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} @@ -7772,16 +6978,13 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - shell-quote@1.8.4: - resolution: {integrity: sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==} + shell-quote@1.10.0: + resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} engines: {node: '>= 0.4'} siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -7808,12 +7011,8 @@ packages: resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} engines: {node: '>=20'} - slice-ansi@9.0.0: - resolution: {integrity: sha512-SO/3iYL5S3W57LLEniscOGPZgOqZUPCx6d3dB+52B80yJ0XstzsC/eV8gnA4tM3MHDrKz+OCFSLNjswdSC+/bA==} - engines: {node: '>=22'} - - smol-toml@1.6.1: - resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} + smol-toml@1.7.2: + resolution: {integrity: sha512-pXFZ9B2WinEPzxWkMmlYE/oYx2BP+qLrE95wP8tCuK901uLSMGdCb6QSr82z+wnhXkG4+cO+OMLbZB2Cn+97zw==} engines: {node: '>= 18'} snake-case@3.0.4: @@ -7833,16 +7032,9 @@ packages: sponge-case@2.0.3: resolution: {integrity: sha512-i4h9ZGRfxV6Xw3mpZSFOfbXjf0cQcYmssGWutgNIfFZ2VM+YIWfD71N/kjjwK6X/AAHzBr+rciEcn/L34S8TGw==} - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - std-env@4.1.0: - resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} - std-env@4.2.0: resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} @@ -7850,23 +7042,8 @@ packages: resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} engines: {node: '>=18'} - storybook@10.4.6: - resolution: {integrity: sha512-6wkA6LxfDSSilloITsrFOJfsnw0mDUP2h8Ls+lRt8oRsudtz2RWFhLv+Toiwg6NW7hUpdTDc2hzR7DztJid6+A==} - hasBin: true - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - prettier: ^2 || ^3 - vite-plus: ^0.1.15 - peerDependenciesMeta: - '@types/react': - optional: true - prettier: - optional: true - vite-plus: - optional: true - - storybook@10.5.5: - resolution: {integrity: sha512-UscBIBJDloUeqntukHOhP1a5W/vouePDJbzPSxj466WK801FZtzQiMffMtkjzJiWSuj20wfaYlB2QQKh9aOYAg==} + storybook@10.5.7: + resolution: {integrity: sha512-oiKvWIwIoOhFP1i6dASYyMXwPHKEtVZMshqSB7EvIVYjWRh0l9H7gHEt1z4Gh2rLGFMekWdsm4s94rvwpR7gkg==} hasBin: true peerDependencies: '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -7887,10 +7064,6 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} - string-width@8.2.1: - resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} - engines: {node: '>=20'} - string-width@8.2.2: resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} engines: {node: '>=20'} @@ -7968,18 +7141,8 @@ packages: tailwind-merge@3.6.0: resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} - tailwind-variants@3.2.2: - resolution: {integrity: sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==} - engines: {node: '>=16.x', pnpm: '>=7.x'} - peerDependencies: - tailwind-merge: '>=3.0.0' - tailwindcss: '*' - peerDependenciesMeta: - tailwind-merge: - optional: true - - tailwind-variants@3.3.0: - resolution: {integrity: sha512-t1QsB42dcwUdaCEArO0tRZcP0nbCcAmJKRYCs7jmwYSFOCXlGqgO8c0TrCYm9OranYmT6i9YOi8LFo2gxmxnow==} + tailwind-variants@3.3.1: + resolution: {integrity: sha512-4pAvwUtM4HKBiRZftncAbpn6V9Hhwoa5Fl7O2u5zbp7Z5Cvu+/o/6+176WY3WCEES209543quG8zFIcXCsc5Jw==} engines: {node: '>=16.9.x', pnpm: '>=7.x'} peerDependencies: tailwind-merge: '>=3.0.0' @@ -8002,10 +7165,6 @@ packages: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} - terminal-size@4.0.1: - resolution: {integrity: sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ==} - engines: {node: '>=18'} - timeout-signal@2.0.0: resolution: {integrity: sha512-YBGpG4bWsHoPvofT6y/5iqulfXIiIErl5B0LdtHT1mGXDFTAhhRrbUpTvBgYbovr+3cKblya2WAOcpoy90XguA==} engines: {node: '>=16'} @@ -8016,8 +7175,8 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.2.4: - resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} engines: {node: '>=18'} tinyglobby@0.2.17: @@ -8032,8 +7191,8 @@ packages: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} engines: {node: '>=14.0.0'} tinyspy@4.0.4: @@ -8057,10 +7216,6 @@ packages: trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - ts-dedent@2.2.0: - resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} - engines: {node: '>=6.10'} - ts-dedent@2.3.0: resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==} engines: {node: '>=6.10'} @@ -8069,6 +7224,20 @@ packages: resolution: {integrity: sha512-esq6hx2lM66sQV1YcFkIYTqrWWabmqBqobKHyn1CswdI5FgfQhkmiKiRWVGBNlIbdjBxEIkNvMIwLKKPgRYZLQ==} engines: {node: '>=20', npm: '>=10'} + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} @@ -8125,17 +7294,14 @@ packages: oxlint: optional: true - unbash@4.0.1: - resolution: {integrity: sha512-1ajSo3813sDoVIHx4inJdUS4l5L2ic5cFiddemPiyjb/PZEoBAhFwHtbaEdRDFxbAKy7FCG7s5ww3/uCFawuIA==} + unbash@4.0.10: + resolution: {integrity: sha512-b7zoBQvpWp0vuN5q2vK2RRBR2SvuruQAs50DApdDveBSn3eSYd84IaHodFqQIMlvY9K2VnyBUEXgwOBuGU9GBg==} engines: {node: '>=14'} unc-path-regex@0.1.2: resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} engines: {node: '>=0.10.0'} - undici-types@7.24.6: - resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} @@ -8226,6 +7392,9 @@ packages: resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} hasBin: true + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} @@ -8238,8 +7407,8 @@ packages: victory-vendor@37.3.6: resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} - vite-plugin-monaco-editor-esm@2.0.2: - resolution: {integrity: sha512-XVkOpL/r0rw1NpbO30vUwG4S0THkC9KB1vjjV8olGd49h4/EQsKl3DrxB6KRDwyZNC9mKiiZgk2L6njUYj3oKQ==} + vite-plugin-monaco-editor-esm@2.0.3: + resolution: {integrity: sha512-9nA73gcWtO+XDsLmBBQXoY4NMv6D2ZKDZHdHgVl1lfADnXEwdBPr1iL8qGqDKXGgEmYo1F12rFPEnymFe2aibA==} peerDependencies: monaco-editor: '>=0.33.0' @@ -8248,13 +7417,13 @@ packages: peerDependencies: vite: '>=3.0.0' - vite@8.1.5: - resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==} + vite@8.2.1: + resolution: {integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.3.0 + '@vitejs/devtools': ^0.4.0 esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 @@ -8305,61 +7474,20 @@ packages: '@types/react-dom': optional: true - vitest@4.1.8: - resolution: {integrity: sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.8 - '@vitest/browser-preview': 4.1.8 - '@vitest/browser-webdriverio': 4.1.8 - '@vitest/coverage-istanbul': 4.1.8 - '@vitest/coverage-v8': 4.1.8 - '@vitest/ui': 4.1.8 - happy-dom: '*' - jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@opentelemetry/api': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/coverage-istanbul': - optional: true - '@vitest/coverage-v8': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - vitest@4.1.9: - resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==} + vitest@4.1.10: + resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.9 - '@vitest/browser-preview': 4.1.9 - '@vitest/browser-webdriverio': 4.1.9 - '@vitest/coverage-istanbul': 4.1.9 - '@vitest/coverage-v8': 4.1.9 - '@vitest/ui': 4.1.9 + '@vitest/browser-playwright': 4.1.10 + '@vitest/browser-preview': 4.1.10 + '@vitest/browser-webdriverio': 4.1.10 + '@vitest/coverage-istanbul': 4.1.10 + '@vitest/coverage-v8': 4.1.10 + '@vitest/ui': 4.1.10 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -8447,10 +7575,6 @@ packages: engines: {node: '>=8'} hasBin: true - widest-line@6.0.0: - resolution: {integrity: sha512-U89AsyEeAsyoF0zVJBkG9zBgekjgjK7yk9sje3F4IQpXBJ10TF6ByLlIfjMhcmHMJgHZI4KHt4rdNfktzxIAMA==} - engines: {node: '>=20'} - wonka@6.3.6: resolution: {integrity: sha512-MXH+6mDHAZ2GuMpgKS055FR6v0xVP3XwquxIMYXgiW+FejHQlMGlvVRZT4qMCxR+bEo/FCtIdKxwej9WV3YQag==} @@ -8466,20 +7590,8 @@ packages: resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} - ws@8.21.0: - resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.21.1: - resolution: {integrity: sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==} + ws@8.21.3: + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -8517,10 +7629,14 @@ packages: resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} - yargs@18.0.0: - resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} + yargs@18.1.0: + resolution: {integrity: sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -8529,8 +7645,8 @@ packages: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} - yoctocolors@2.1.2: - resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + yoctocolors@2.2.0: + resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==} engines: {node: '>=18'} yoga-layout@3.2.1: @@ -8583,29 +7699,24 @@ packages: snapshots: - '@0no-co/graphql.web@1.3.2(graphql@16.14.2)': + '@0no-co/graphql.web@1.3.3(graphql@16.14.2)': optionalDependencies: graphql: 16.14.2 '@0no-co/graphqlsp@1.17.3(graphql@16.14.2)(typescript@5.9.3)': dependencies: - '@gql.tada/internal': 1.2.1(graphql@16.14.2)(typescript@5.9.3) + '@gql.tada/internal': 1.2.2(graphql@16.14.2)(typescript@5.9.3) graphql: 16.14.2 typescript: 5.9.3 '@adobe/css-tools@4.5.0': {} - '@alcalzone/ansi-tokenize@0.3.0': - dependencies: - ansi-styles: 6.2.3 - is-fullwidth-code-point: 5.1.0 - '@antfu/install-pkg@1.1.0': dependencies: package-manager-detector: 1.8.0 - tinyexec: 1.2.4 + tinyexec: 1.3.0 - '@apm-js-collab/code-transformer-bundler-plugins@0.7.1': + '@apm-js-collab/code-transformer-bundler-plugins@0.7.4': dependencies: '@apm-js-collab/code-transformer': 0.18.1 es-module-lexer: 2.3.1 @@ -8629,13 +7740,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@ardatan/relay-compiler@13.0.1(graphql@16.14.2)': + '@ardatan/relay-compiler@13.0.2(graphql@16.14.2)': dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 8.0.0 graphql: 16.14.2 - immutable: 5.1.5 + immutable: 5.1.9 invariant: 2.2.4 + '@astrojs/compiler@4.0.0': {} + '@babel/code-frame@7.29.7': dependencies: '@babel/helper-validator-identifier': 7.29.7 @@ -8647,14 +7760,14 @@ snapshots: '@babel/core@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.7 + '@babel/generator': 7.29.8 '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) '@babel/helpers': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3(supports-color@10.2.2) @@ -8664,10 +7777,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.29.7': + '@babel/generator@7.29.8': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 @@ -8684,8 +7797,8 @@ snapshots: '@babel/helper-module-imports@7.29.7': dependencies: - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color @@ -8694,7 +7807,7 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-module-imports': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.7 + '@babel/traverse': 7.29.8 transitivePeerDependencies: - supports-color @@ -8709,40 +7822,40 @@ snapshots: '@babel/helpers@7.29.7': dependencies: '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 - '@babel/parser@7.29.7': + '@babel/parser@7.29.8': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@babel/plugin-syntax-import-assertions@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/runtime@7.29.2': {} - '@babel/runtime@7.29.7': {} + '@babel/runtime@8.0.0': {} + '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 - '@babel/traverse@7.29.7': + '@babel/traverse@7.29.8': dependencies: '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.7 + '@babel/generator': 7.29.8 '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/types@7.29.7': + '@babel/types@7.29.8': dependencies: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 @@ -8764,10 +7877,10 @@ snapshots: ignore-walk: 7.0.0 lines-and-columns: 2.0.4 minimatch: 5.1.9 - prettier: 3.8.3 + prettier: 3.8.4 simple-git: 3.36.0 optionalDependencies: - esbuild: 0.28.0 + esbuild: 0.28.1 transitivePeerDependencies: - supports-color @@ -8784,7 +7897,7 @@ snapshots: chalk: 5.6.2 commander: 8.3.0 find-up: 7.0.0 - prettier: 3.8.3 + prettier: 3.8.4 transitivePeerDependencies: - supports-color - typescript @@ -8836,17 +7949,6 @@ snapshots: callsite: 1.0.0 comlink: 4.4.2 - '@biomejs/biome@2.4.16': - optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.4.16 - '@biomejs/cli-darwin-x64': 2.4.16 - '@biomejs/cli-linux-arm64': 2.4.16 - '@biomejs/cli-linux-arm64-musl': 2.4.16 - '@biomejs/cli-linux-x64': 2.4.16 - '@biomejs/cli-linux-x64-musl': 2.4.16 - '@biomejs/cli-win32-arm64': 2.4.16 - '@biomejs/cli-win32-x64': 2.4.16 - '@biomejs/biome@2.5.1': optionalDependencies: '@biomejs/cli-darwin-arm64': 2.5.1 @@ -8858,51 +7960,27 @@ snapshots: '@biomejs/cli-win32-arm64': 2.5.1 '@biomejs/cli-win32-x64': 2.5.1 - '@biomejs/cli-darwin-arm64@2.4.16': - optional: true - '@biomejs/cli-darwin-arm64@2.5.1': optional: true - '@biomejs/cli-darwin-x64@2.4.16': - optional: true - '@biomejs/cli-darwin-x64@2.5.1': optional: true - '@biomejs/cli-linux-arm64-musl@2.4.16': - optional: true - '@biomejs/cli-linux-arm64-musl@2.5.1': optional: true - '@biomejs/cli-linux-arm64@2.4.16': - optional: true - '@biomejs/cli-linux-arm64@2.5.1': optional: true - '@biomejs/cli-linux-x64-musl@2.4.16': - optional: true - '@biomejs/cli-linux-x64-musl@2.5.1': optional: true - '@biomejs/cli-linux-x64@2.4.16': - optional: true - '@biomejs/cli-linux-x64@2.5.1': optional: true - '@biomejs/cli-win32-arm64@2.4.16': - optional: true - '@biomejs/cli-win32-arm64@2.5.1': optional: true - '@biomejs/cli-win32-x64@2.4.16': - optional: true - '@biomejs/cli-win32-x64@2.5.1': optional: true @@ -8924,93 +8002,97 @@ snapshots: fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 - '@codemirror/autocomplete@6.20.1': + '@codemirror/autocomplete@6.20.3': dependencies: '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.4 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.8 '@lezer/common': 1.5.2 '@codemirror/commands@6.10.4': dependencies: '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.4 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.8 '@lezer/common': 1.5.2 '@codemirror/lang-css@6.3.1': dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.0 + '@codemirror/state': 6.7.1 '@lezer/common': 1.5.2 - '@lezer/css': 1.3.3 + '@lezer/css': 1.3.6 - '@codemirror/lang-html@6.4.11': + '@codemirror/lang-html@6.4.12': dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/lang-css': 6.3.1 '@codemirror/lang-javascript': 6.2.5 '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.4 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.8 '@lezer/common': 1.5.2 - '@lezer/css': 1.3.3 + '@lezer/css': 1.3.6 '@lezer/html': 1.3.13 '@codemirror/lang-javascript@6.2.5': dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/language': 6.12.4 - '@codemirror/lint': 6.9.5 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.4 + '@codemirror/lint': 6.9.7 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.8 '@lezer/common': 1.5.2 '@lezer/javascript': 1.5.4 - '@codemirror/lang-markdown@6.5.0': + '@codemirror/lang-markdown@6.5.2': dependencies: - '@codemirror/autocomplete': 6.20.1 - '@codemirror/lang-html': 6.4.11 + '@codemirror/autocomplete': 6.20.3 + '@codemirror/lang-html': 6.4.12 '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.4 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.8 '@lezer/common': 1.5.2 - '@lezer/markdown': 1.6.3 + '@lezer/markdown': 1.7.2 '@codemirror/language@6.12.4': dependencies: - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.4 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.8 '@lezer/common': 1.5.2 '@lezer/highlight': 1.2.3 '@lezer/lr': 1.4.10 style-mod: 4.1.3 - '@codemirror/lint@6.9.5': + '@codemirror/lint@6.9.7': dependencies: - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.4 - crelt: 1.0.6 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.8 + crelt: 1.0.7 - '@codemirror/state@6.7.0': + '@codemirror/state@6.7.1': dependencies: - '@marijn/find-cluster-break': 1.0.2 + '@marijn/find-cluster-break': 1.0.3 '@codemirror/theme-one-dark@6.1.3': dependencies: '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.4 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.8 '@lezer/highlight': 1.2.3 - '@codemirror/view@6.43.4': + '@codemirror/view@6.43.8': dependencies: - '@codemirror/state': 6.7.0 - crelt: 1.0.6 + '@codemirror/state': 6.7.1 + crelt: 1.0.7 style-mod: 4.1.3 w3c-keyname: 2.2.8 + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + '@dagrejs/dagre@1.1.8': dependencies: '@dagrejs/graphlib': 2.2.4 @@ -9090,159 +8172,81 @@ snapshots: '@whatwg-node/promise-helpers': 1.3.2 tslib: 2.8.1 - '@esbuild/aix-ppc64@0.28.0': - optional: true - '@esbuild/aix-ppc64@0.28.1': optional: true - '@esbuild/android-arm64@0.28.0': - optional: true - '@esbuild/android-arm64@0.28.1': optional: true - '@esbuild/android-arm@0.28.0': - optional: true - '@esbuild/android-arm@0.28.1': optional: true - '@esbuild/android-x64@0.28.0': - optional: true - '@esbuild/android-x64@0.28.1': optional: true - '@esbuild/darwin-arm64@0.28.0': - optional: true - '@esbuild/darwin-arm64@0.28.1': optional: true - '@esbuild/darwin-x64@0.28.0': - optional: true - '@esbuild/darwin-x64@0.28.1': optional: true - '@esbuild/freebsd-arm64@0.28.0': - optional: true - '@esbuild/freebsd-arm64@0.28.1': optional: true - '@esbuild/freebsd-x64@0.28.0': - optional: true - '@esbuild/freebsd-x64@0.28.1': optional: true - '@esbuild/linux-arm64@0.28.0': - optional: true - '@esbuild/linux-arm64@0.28.1': optional: true - '@esbuild/linux-arm@0.28.0': - optional: true - '@esbuild/linux-arm@0.28.1': optional: true - '@esbuild/linux-ia32@0.28.0': - optional: true - '@esbuild/linux-ia32@0.28.1': optional: true - '@esbuild/linux-loong64@0.28.0': - optional: true - '@esbuild/linux-loong64@0.28.1': optional: true - '@esbuild/linux-mips64el@0.28.0': - optional: true - '@esbuild/linux-mips64el@0.28.1': optional: true - '@esbuild/linux-ppc64@0.28.0': - optional: true - '@esbuild/linux-ppc64@0.28.1': optional: true - '@esbuild/linux-riscv64@0.28.0': - optional: true - '@esbuild/linux-riscv64@0.28.1': optional: true - '@esbuild/linux-s390x@0.28.0': - optional: true - '@esbuild/linux-s390x@0.28.1': optional: true - '@esbuild/linux-x64@0.28.0': - optional: true - '@esbuild/linux-x64@0.28.1': optional: true - '@esbuild/netbsd-arm64@0.28.0': - optional: true - '@esbuild/netbsd-arm64@0.28.1': optional: true - '@esbuild/netbsd-x64@0.28.0': - optional: true - '@esbuild/netbsd-x64@0.28.1': optional: true - '@esbuild/openbsd-arm64@0.28.0': - optional: true - '@esbuild/openbsd-arm64@0.28.1': optional: true - '@esbuild/openbsd-x64@0.28.0': - optional: true - '@esbuild/openbsd-x64@0.28.1': optional: true - '@esbuild/openharmony-arm64@0.28.0': - optional: true - '@esbuild/openharmony-arm64@0.28.1': optional: true - '@esbuild/sunos-x64@0.28.0': - optional: true - '@esbuild/sunos-x64@0.28.1': optional: true - '@esbuild/win32-arm64@0.28.0': - optional: true - '@esbuild/win32-arm64@0.28.1': optional: true - '@esbuild/win32-ia32@0.28.0': - optional: true - '@esbuild/win32-ia32@0.28.1': optional: true - '@esbuild/win32-x64@0.28.0': - optional: true - '@esbuild/win32-x64@0.28.1': optional: true @@ -9257,7 +8261,7 @@ snapshots: dependencies: '@eslint/object-schema': 3.0.5 debug: 4.4.3(supports-color@10.2.2) - minimatch: 10.2.5 + minimatch: 10.2.6 transitivePeerDependencies: - supports-color @@ -9278,84 +8282,84 @@ snapshots: '@fastify/busboy@3.2.0': {} - '@floating-ui/core@1.7.5': + '@floating-ui/core@1.8.0': dependencies: - '@floating-ui/utils': 0.2.11 + '@floating-ui/utils': 0.2.12 - '@floating-ui/dom@1.7.6': + '@floating-ui/dom@1.8.0': dependencies: - '@floating-ui/core': 1.7.5 - '@floating-ui/utils': 0.2.11 + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 - '@floating-ui/react-dom@2.1.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@floating-ui/react-dom@2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@floating-ui/dom': 1.7.6 + '@floating-ui/dom': 1.8.0 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) '@floating-ui/react@0.26.28(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@floating-ui/utils': 0.2.11 + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@floating-ui/utils': 0.2.12 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) tabbable: 6.4.0 '@floating-ui/react@0.27.19(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@floating-ui/utils': 0.2.11 + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@floating-ui/utils': 0.2.12 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) tabbable: 6.4.0 - '@floating-ui/utils@0.2.11': {} + '@floating-ui/utils@0.2.12': {} '@fortawesome/fontawesome-free@6.7.2': {} - '@gql.tada/cli-utils@1.9.2(@0no-co/graphqlsp@1.17.3(graphql@16.14.2)(typescript@5.9.3))(graphql@16.14.2)(typescript@5.9.3)': + '@gql.tada/cli-utils@1.9.3(@0no-co/graphqlsp@1.17.3(graphql@16.14.2)(typescript@5.9.3))(graphql@16.14.2)(typescript@5.9.3)': dependencies: '@0no-co/graphqlsp': 1.17.3(graphql@16.14.2)(typescript@5.9.3) - '@gql.tada/internal': 1.2.1(graphql@16.14.2)(typescript@5.9.3) + '@gql.tada/internal': 1.2.2(graphql@16.14.2)(typescript@5.9.3) graphql: 16.14.2 typescript: 5.9.3 - '@gql.tada/internal@1.2.1(graphql@16.14.2)(typescript@5.9.3)': + '@gql.tada/internal@1.2.2(graphql@16.14.2)(typescript@5.9.3)': dependencies: - '@0no-co/graphql.web': 1.3.2(graphql@16.14.2) + '@0no-co/graphql.web': 1.3.3(graphql@16.14.2) graphql: 16.14.2 typescript: 5.9.3 - '@graphiql/plugin-doc-explorer@0.4.2(@graphiql/react@0.37.7(@types/node@26.1.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))(@types/react@19.2.17)(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8))': + '@graphiql/plugin-doc-explorer@0.4.2(@graphiql/react@0.37.7(@types/node@26.2.0)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))(@types/react@19.2.18)(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8))': dependencies: - '@graphiql/react': 0.37.7(@types/node@26.1.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) + '@graphiql/react': 0.37.7(@types/node@26.2.0)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) '@headlessui/react': 2.2.10(react-dom@19.2.8(react@19.2.8))(react@19.2.8) graphql: 16.14.2 react: 19.2.8 react-compiler-runtime: 19.1.0-rc.1(react@19.2.8) react-dom: 19.2.8(react@19.2.8) - zustand: 5.0.14(@types/react@19.2.17)(immer@11.1.4)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) + zustand: 5.0.14(@types/react@19.2.18)(immer@11.1.16)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) transitivePeerDependencies: - '@types/react' - immer - use-sync-external-store - '@graphiql/plugin-explorer@5.1.3(@graphiql/react@0.37.7(@types/node@26.1.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))(graphql@16.14.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@graphiql/plugin-explorer@5.1.3(@graphiql/react@0.37.7(@types/node@26.2.0)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))(graphql@16.14.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@graphiql/react': 0.37.7(@types/node@26.1.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) + '@graphiql/react': 0.37.7(@types/node@26.2.0)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) graphiql-explorer: 0.9.0(graphql@16.14.2)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) graphql: 16.14.2 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@graphiql/plugin-history@0.4.2(@graphiql/react@0.37.7(@types/node@26.1.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))(@types/node@26.1.1)(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8))': + '@graphiql/plugin-history@0.4.2(@graphiql/react@0.37.7(@types/node@26.2.0)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))(@types/node@26.2.0)(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8))': dependencies: - '@graphiql/react': 0.37.7(@types/node@26.1.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) - '@graphiql/toolkit': 0.12.1(@types/node@26.1.1)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2) + '@graphiql/react': 0.37.7(@types/node@26.2.0)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) + '@graphiql/toolkit': 0.12.1(@types/node@26.2.0)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2) react: 19.2.8 react-compiler-runtime: 19.1.0-rc.1(react@19.2.8) react-dom: 19.2.8(react@19.2.8) - zustand: 5.0.14(@types/react@19.2.17)(immer@11.1.4)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) + zustand: 5.0.14(@types/react@19.2.18)(immer@11.1.16)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) transitivePeerDependencies: - '@types/node' - '@types/react' @@ -9364,13 +8368,13 @@ snapshots: - immer - use-sync-external-store - '@graphiql/react@0.37.7(@types/node@26.1.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8))': + '@graphiql/react@0.37.7(@types/node@26.2.0)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8))': dependencies: - '@graphiql/toolkit': 0.12.1(@types/node@26.1.1)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2) - '@radix-ui/react-dialog': 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-dropdown-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-tooltip': 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@graphiql/toolkit': 0.12.1(@types/node@26.2.0)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2) + '@radix-ui/react-dialog': 1.1.17(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-dropdown-menu': 2.1.24(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-tooltip': 1.2.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) clsx: 1.2.1 framer-motion: 12.40.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) get-value: 3.0.1 @@ -9385,7 +8389,7 @@ snapshots: react-compiler-runtime: 19.1.0-rc.1(react@19.2.8) react-dom: 19.2.8(react@19.2.8) set-value: 4.1.0 - zustand: 5.0.14(@types/react@19.2.17)(immer@11.1.4)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) + zustand: 5.0.14(@types/react@19.2.18)(immer@11.1.16)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) transitivePeerDependencies: - '@emotion/is-prop-valid' - '@types/node' @@ -9395,60 +8399,60 @@ snapshots: - immer - use-sync-external-store - '@graphiql/toolkit@0.12.1(@types/node@26.1.1)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)': + '@graphiql/toolkit@0.12.1(@types/node@26.2.0)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)': dependencies: '@n1ru4l/push-pull-async-iterable-iterator': 3.2.0 graphql: 16.14.2 - meros: 1.3.2(@types/node@26.1.1) + meros: 1.3.2(@types/node@26.2.0) optionalDependencies: - graphql-ws: 6.0.8(graphql@16.14.2)(ws@8.21.0) + graphql-ws: 6.2.1(graphql@16.14.2)(ws@8.21.3) transitivePeerDependencies: - '@types/node' - '@graphql-codegen/add@7.0.1(graphql@16.14.2)': + '@graphql-codegen/add@7.1.0(graphql@16.14.2)': dependencies: - '@graphql-codegen/plugin-helpers': 7.0.1(graphql@16.14.2) + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) graphql: 16.14.2 tslib: 2.8.1 - '@graphql-codegen/cli@7.1.3(@types/node@26.1.1)(graphql@16.14.2)(typescript@5.9.3)': + '@graphql-codegen/cli@7.2.0(@types/node@26.2.0)(graphql@16.14.2)(typescript@5.9.3)': dependencies: - '@babel/generator': 7.29.7 + '@babel/generator': 7.29.8 '@babel/template': 7.29.7 - '@babel/types': 7.29.7 - '@graphql-codegen/client-preset': 6.0.1(graphql@16.14.2) - '@graphql-codegen/core': 6.1.0(graphql@16.14.2) - '@graphql-codegen/plugin-helpers': 7.0.1(graphql@16.14.2) - '@graphql-tools/apollo-engine-loader': 8.0.30(graphql@16.14.2) - '@graphql-tools/code-file-loader': 8.1.32(graphql@16.14.2) - '@graphql-tools/git-loader': 8.0.36(graphql@16.14.2) - '@graphql-tools/github-loader': 9.1.2(@types/node@26.1.1)(graphql@16.14.2) - '@graphql-tools/graphql-file-loader': 8.1.14(graphql@16.14.2) - '@graphql-tools/json-file-loader': 8.0.28(graphql@16.14.2) - '@graphql-tools/load': 8.1.10(graphql@16.14.2) - '@graphql-tools/merge': 9.1.9(graphql@16.14.2) - '@graphql-tools/url-loader': 9.1.2(@types/node@26.1.1)(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) - '@inquirer/prompts': 8.5.2(@types/node@26.1.1) + '@babel/types': 7.29.8 + '@graphql-codegen/client-preset': 6.1.2(graphql@16.14.2) + '@graphql-codegen/core': 6.2.0(graphql@16.14.2) + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-tools/apollo-engine-loader': 8.0.34(graphql@16.14.2) + '@graphql-tools/code-file-loader': 8.1.36(graphql@16.14.2) + '@graphql-tools/git-loader': 8.0.40(graphql@16.14.2) + '@graphql-tools/github-loader': 9.1.6(@types/node@26.2.0)(graphql@16.14.2) + '@graphql-tools/graphql-file-loader': 8.1.18(graphql@16.14.2) + '@graphql-tools/json-file-loader': 8.0.32(graphql@16.14.2) + '@graphql-tools/load': 8.1.15(graphql@16.14.2) + '@graphql-tools/merge': 9.2.2(graphql@16.14.2) + '@graphql-tools/url-loader': 9.1.6(@types/node@26.2.0)(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) + '@inquirer/prompts': 8.5.2(@types/node@26.2.0) '@whatwg-node/fetch': 0.10.13 chalk: 5.6.2 cosmiconfig: 9.0.2(typescript@5.9.3) debounce: 3.0.0 detect-indent: 7.0.2 graphql: 16.14.2 - graphql-config: 5.1.6(@types/node@26.1.1)(graphql@16.14.2)(typescript@5.9.3) + graphql-config: 5.1.6(@types/node@26.2.0)(graphql@16.14.2)(typescript@5.9.3) is-glob: 4.0.3 jiti: 2.7.0 json-to-pretty-yaml: 1.2.2 - listr2: 10.2.1 + listr2: 10.2.2 log-symbols: 7.0.1 micromatch: 4.0.8 - shell-quote: 1.8.4 + shell-quote: 1.10.0 string-env-interpolation: 1.0.1 ts-log: 3.0.2 tslib: 2.8.1 yaml: 2.9.0 - yargs: 18.0.0 + yargs: 18.1.0 transitivePeerDependencies: - '@fastify/websocket' - '@types/node' @@ -9460,119 +8464,119 @@ snapshots: - typescript - utf-8-validate - '@graphql-codegen/client-preset@6.0.1(graphql@16.14.2)': + '@graphql-codegen/client-preset@6.1.2(graphql@16.14.2)': dependencies: '@babel/helper-plugin-utils': 7.29.7 '@babel/template': 7.29.7 - '@graphql-codegen/add': 7.0.1(graphql@16.14.2) - '@graphql-codegen/gql-tag-operations': 6.0.1(graphql@16.14.2) - '@graphql-codegen/plugin-helpers': 7.0.1(graphql@16.14.2) - '@graphql-codegen/typed-document-node': 7.0.3(graphql@16.14.2) - '@graphql-codegen/typescript': 6.0.2(graphql@16.14.2) - '@graphql-codegen/typescript-operations': 6.0.4(graphql@16.14.2) - '@graphql-codegen/visitor-plugin-common': 7.1.0(graphql@16.14.2) + '@graphql-codegen/add': 7.1.0(graphql@16.14.2) + '@graphql-codegen/gql-tag-operations': 6.1.0(graphql@16.14.2) + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-codegen/typed-document-node': 7.1.0(graphql@16.14.2) + '@graphql-codegen/typescript': 6.1.0(graphql@16.14.2) + '@graphql-codegen/typescript-operations': 6.1.5(graphql@16.14.2) + '@graphql-codegen/visitor-plugin-common': 7.2.4(graphql@16.14.2) '@graphql-tools/documents': 1.0.1(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) '@graphql-typed-document-node/core': 3.2.0(graphql@16.14.2) graphql: 16.14.2 tslib: 2.8.1 - '@graphql-codegen/core@6.1.0(graphql@16.14.2)': + '@graphql-codegen/core@6.2.0(graphql@16.14.2)': dependencies: - '@graphql-codegen/plugin-helpers': 7.0.1(graphql@16.14.2) - '@graphql-tools/schema': 10.0.33(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-tools/schema': 10.0.38(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) graphql: 16.14.2 tslib: 2.8.1 - '@graphql-codegen/gql-tag-operations@6.0.1(graphql@16.14.2)': + '@graphql-codegen/gql-tag-operations@6.1.0(graphql@16.14.2)': dependencies: - '@graphql-codegen/plugin-helpers': 7.0.1(graphql@16.14.2) - '@graphql-codegen/visitor-plugin-common': 7.1.0(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-codegen/visitor-plugin-common': 7.2.4(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) auto-bind: 5.0.1 graphql: 16.14.2 tslib: 2.8.1 - '@graphql-codegen/plugin-helpers@7.0.1(graphql@16.14.2)': + '@graphql-codegen/plugin-helpers@7.1.0(graphql@16.14.2)': dependencies: - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) change-case-all: 2.1.0 common-tags: 1.8.2 graphql: 16.14.2 import-from: 4.0.0 tslib: 2.8.1 - '@graphql-codegen/schema-ast@6.0.1(graphql@16.14.2)': + '@graphql-codegen/schema-ast@6.1.0(graphql@16.14.2)': dependencies: - '@graphql-codegen/plugin-helpers': 7.0.1(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) graphql: 16.14.2 tslib: 2.8.1 - '@graphql-codegen/typed-document-node@7.0.3(graphql@16.14.2)': + '@graphql-codegen/typed-document-node@7.1.0(graphql@16.14.2)': dependencies: - '@graphql-codegen/plugin-helpers': 7.0.1(graphql@16.14.2) - '@graphql-codegen/visitor-plugin-common': 7.1.0(graphql@16.14.2) + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-codegen/visitor-plugin-common': 7.2.4(graphql@16.14.2) auto-bind: 5.0.1 change-case-all: 2.1.0 graphql: 16.14.2 tslib: 2.8.1 - '@graphql-codegen/typescript-operations@6.0.4(graphql@16.14.2)': + '@graphql-codegen/typescript-operations@6.1.5(graphql@16.14.2)': dependencies: - '@graphql-codegen/plugin-helpers': 7.0.1(graphql@16.14.2) - '@graphql-codegen/schema-ast': 6.0.1(graphql@16.14.2) - '@graphql-codegen/visitor-plugin-common': 7.1.0(graphql@16.14.2) + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-codegen/schema-ast': 6.1.0(graphql@16.14.2) + '@graphql-codegen/visitor-plugin-common': 7.2.4(graphql@16.14.2) auto-bind: 5.0.1 graphql: 16.14.2 tslib: 2.8.1 - '@graphql-codegen/typescript@6.0.2(graphql@16.14.2)': + '@graphql-codegen/typescript@6.1.0(graphql@16.14.2)': dependencies: - '@graphql-codegen/plugin-helpers': 7.0.1(graphql@16.14.2) - '@graphql-codegen/schema-ast': 6.0.1(graphql@16.14.2) - '@graphql-codegen/visitor-plugin-common': 7.1.0(graphql@16.14.2) + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) + '@graphql-codegen/schema-ast': 6.1.0(graphql@16.14.2) + '@graphql-codegen/visitor-plugin-common': 7.2.4(graphql@16.14.2) auto-bind: 5.0.1 graphql: 16.14.2 tslib: 2.8.1 - '@graphql-codegen/visitor-plugin-common@7.1.0(graphql@16.14.2)': + '@graphql-codegen/visitor-plugin-common@7.2.4(graphql@16.14.2)': dependencies: - '@graphql-codegen/plugin-helpers': 7.0.1(graphql@16.14.2) + '@graphql-codegen/plugin-helpers': 7.1.0(graphql@16.14.2) '@graphql-tools/optimize': 2.0.0(graphql@16.14.2) - '@graphql-tools/relay-operation-optimizer': 7.1.4(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/relay-operation-optimizer': 7.1.8(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) auto-bind: 5.0.1 change-case-all: 2.1.0 dependency-graph: 1.0.0 graphql: 16.14.2 - graphql-tag: 2.12.6(graphql@16.14.2) + graphql-tag: 2.12.7(graphql@16.14.2) parse-filepath: 1.0.2 tslib: 2.8.1 '@graphql-hive/signal@2.0.0': {} - '@graphql-tools/apollo-engine-loader@8.0.30(graphql@16.14.2)': + '@graphql-tools/apollo-engine-loader@8.0.34(graphql@16.14.2)': dependencies: - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) '@whatwg-node/fetch': 0.10.13 graphql: 16.14.2 sync-fetch: 0.6.0 tslib: 2.8.1 - '@graphql-tools/batch-execute@10.0.8(graphql@16.14.2)': + '@graphql-tools/batch-execute@10.0.9(graphql@16.14.2)': dependencies: - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) '@whatwg-node/promise-helpers': 1.3.2 dataloader: 2.2.3 graphql: 16.14.2 tslib: 2.8.1 - '@graphql-tools/code-file-loader@8.1.32(graphql@16.14.2)': + '@graphql-tools/code-file-loader@8.1.36(graphql@16.14.2)': dependencies: - '@graphql-tools/graphql-tag-pluck': 8.3.31(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/graphql-tag-pluck': 8.3.35(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) globby: 11.1.0 graphql: 16.14.2 tslib: 2.8.1 @@ -9580,12 +8584,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@graphql-tools/delegate@12.0.17(graphql@16.14.2)': + '@graphql-tools/delegate@12.1.1(graphql@16.14.2)': dependencies: - '@graphql-tools/batch-execute': 10.0.8(graphql@16.14.2) - '@graphql-tools/executor': 1.5.3(graphql@16.14.2) - '@graphql-tools/schema': 10.0.33(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/batch-execute': 10.0.9(graphql@16.14.2) + '@graphql-tools/executor': 1.5.7(graphql@16.14.2) + '@graphql-tools/schema': 10.0.38(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) '@repeaterjs/repeater': 3.1.0 '@whatwg-node/promise-helpers': 1.3.2 dataloader: 2.2.3 @@ -9601,55 +8605,55 @@ snapshots: '@graphql-tools/executor-common@1.0.6(graphql@16.14.2)': dependencies: '@envelop/core': 5.5.1 - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) graphql: 16.14.2 '@graphql-tools/executor-graphql-ws@3.1.5(graphql@16.14.2)': dependencies: '@graphql-tools/executor-common': 1.0.6(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) '@whatwg-node/disposablestack': 0.0.6 graphql: 16.14.2 - graphql-ws: 6.0.8(graphql@16.14.2)(ws@8.21.0) - isows: 1.0.7(ws@8.21.0) + graphql-ws: 6.2.1(graphql@16.14.2)(ws@8.21.3) + isows: 1.0.7(ws@8.21.3) tslib: 2.8.1 - ws: 8.21.0 + ws: 8.21.3 transitivePeerDependencies: - '@fastify/websocket' - bufferutil - crossws - utf-8-validate - '@graphql-tools/executor-http@3.3.0(@types/node@26.1.1)(graphql@16.14.2)': + '@graphql-tools/executor-http@3.3.0(@types/node@26.2.0)(graphql@16.14.2)': dependencies: '@graphql-hive/signal': 2.0.0 '@graphql-tools/executor-common': 1.0.6(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) '@repeaterjs/repeater': 3.1.0 '@whatwg-node/disposablestack': 0.0.6 '@whatwg-node/fetch': 0.10.13 '@whatwg-node/promise-helpers': 1.3.2 graphql: 16.14.2 - meros: 1.3.2(@types/node@26.1.1) + meros: 1.3.2(@types/node@26.2.0) tslib: 2.8.1 transitivePeerDependencies: - '@types/node' - '@graphql-tools/executor-legacy-ws@1.1.28(graphql@16.14.2)': + '@graphql-tools/executor-legacy-ws@1.1.32(graphql@16.14.2)': dependencies: - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) '@types/ws': 8.18.1 graphql: 16.14.2 - isomorphic-ws: 5.0.0(ws@8.21.0) + isomorphic-ws: 5.0.0(ws@8.21.3) tslib: 2.8.1 - ws: 8.21.0 + ws: 8.21.3 transitivePeerDependencies: - bufferutil - utf-8-validate - '@graphql-tools/executor@1.5.3(graphql@16.14.2)': + '@graphql-tools/executor@1.5.7(graphql@16.14.2)': dependencies: - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) '@graphql-typed-document-node/core': 3.2.0(graphql@16.14.2) '@repeaterjs/repeater': 3.1.0 '@whatwg-node/disposablestack': 0.0.6 @@ -9657,10 +8661,10 @@ snapshots: graphql: 16.14.2 tslib: 2.8.1 - '@graphql-tools/git-loader@8.0.36(graphql@16.14.2)': + '@graphql-tools/git-loader@8.0.40(graphql@16.14.2)': dependencies: - '@graphql-tools/graphql-tag-pluck': 8.3.31(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/graphql-tag-pluck': 8.3.35(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) graphql: 16.14.2 is-glob: 4.0.3 micromatch: 4.0.8 @@ -9669,11 +8673,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@graphql-tools/github-loader@9.1.2(@types/node@26.1.1)(graphql@16.14.2)': + '@graphql-tools/github-loader@9.1.6(@types/node@26.2.0)(graphql@16.14.2)': dependencies: - '@graphql-tools/executor-http': 3.3.0(@types/node@26.1.1)(graphql@16.14.2) - '@graphql-tools/graphql-tag-pluck': 8.3.31(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/executor-http': 3.3.0(@types/node@26.2.0)(graphql@16.14.2) + '@graphql-tools/graphql-tag-pluck': 8.3.35(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) '@whatwg-node/fetch': 0.10.13 '@whatwg-node/promise-helpers': 1.3.2 graphql: 16.14.2 @@ -9683,54 +8687,54 @@ snapshots: - '@types/node' - supports-color - '@graphql-tools/graphql-file-loader@8.1.14(graphql@16.14.2)': + '@graphql-tools/graphql-file-loader@8.1.18(graphql@16.14.2)': dependencies: - '@graphql-tools/import': 7.1.14(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/import': 7.1.18(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) globby: 11.1.0 graphql: 16.14.2 tslib: 2.8.1 unixify: 1.0.0 - '@graphql-tools/graphql-tag-pluck@8.3.31(graphql@16.14.2)': + '@graphql-tools/graphql-tag-pluck@8.3.35(graphql@16.14.2)': dependencies: '@babel/core': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 '@babel/plugin-syntax-import-assertions': 7.29.7(@babel/core@7.29.7) - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) graphql: 16.14.2 tslib: 2.8.1 transitivePeerDependencies: - supports-color - '@graphql-tools/import@7.1.14(graphql@16.14.2)': + '@graphql-tools/import@7.1.18(graphql@16.14.2)': dependencies: - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) graphql: 16.14.2 resolve-from: 5.0.0 tslib: 2.8.1 - '@graphql-tools/json-file-loader@8.0.28(graphql@16.14.2)': + '@graphql-tools/json-file-loader@8.0.32(graphql@16.14.2)': dependencies: - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) globby: 11.1.0 graphql: 16.14.2 tslib: 2.8.1 unixify: 1.0.0 - '@graphql-tools/load@8.1.10(graphql@16.14.2)': + '@graphql-tools/load@8.1.15(graphql@16.14.2)': dependencies: - '@graphql-tools/schema': 10.0.33(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/schema': 10.0.38(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) graphql: 16.14.2 p-limit: 3.1.0 tslib: 2.8.1 - '@graphql-tools/merge@9.1.9(graphql@16.14.2)': + '@graphql-tools/merge@9.2.2(graphql@16.14.2)': dependencies: - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) graphql: 16.14.2 tslib: 2.8.1 @@ -9739,35 +8743,35 @@ snapshots: graphql: 16.14.2 tslib: 2.8.1 - '@graphql-tools/relay-operation-optimizer@7.1.4(graphql@16.14.2)': + '@graphql-tools/relay-operation-optimizer@7.1.8(graphql@16.14.2)': dependencies: - '@ardatan/relay-compiler': 13.0.1(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@ardatan/relay-compiler': 13.0.2(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) graphql: 16.14.2 tslib: 2.8.1 - '@graphql-tools/schema@10.0.33(graphql@16.14.2)': + '@graphql-tools/schema@10.0.38(graphql@16.14.2)': dependencies: - '@graphql-tools/merge': 9.1.9(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/merge': 9.2.2(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) graphql: 16.14.2 tslib: 2.8.1 - '@graphql-tools/url-loader@9.1.2(@types/node@26.1.1)(graphql@16.14.2)': + '@graphql-tools/url-loader@9.1.6(@types/node@26.2.0)(graphql@16.14.2)': dependencies: '@graphql-tools/executor-graphql-ws': 3.1.5(graphql@16.14.2) - '@graphql-tools/executor-http': 3.3.0(@types/node@26.1.1)(graphql@16.14.2) - '@graphql-tools/executor-legacy-ws': 1.1.28(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) - '@graphql-tools/wrap': 11.1.16(graphql@16.14.2) + '@graphql-tools/executor-http': 3.3.0(@types/node@26.2.0)(graphql@16.14.2) + '@graphql-tools/executor-legacy-ws': 1.1.32(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) + '@graphql-tools/wrap': 11.1.21(graphql@16.14.2) '@types/ws': 8.18.1 '@whatwg-node/fetch': 0.10.13 '@whatwg-node/promise-helpers': 1.3.2 graphql: 16.14.2 - isomorphic-ws: 5.0.0(ws@8.21.0) + isomorphic-ws: 5.0.0(ws@8.21.3) sync-fetch: 0.6.0 tslib: 2.8.1 - ws: 8.21.0 + ws: 8.21.3 transitivePeerDependencies: - '@fastify/websocket' - '@types/node' @@ -9775,7 +8779,7 @@ snapshots: - crossws - utf-8-validate - '@graphql-tools/utils@11.1.0(graphql@16.14.2)': + '@graphql-tools/utils@11.2.2(graphql@16.14.2)': dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.14.2) '@whatwg-node/promise-helpers': 1.3.2 @@ -9783,11 +8787,11 @@ snapshots: graphql: 16.14.2 tslib: 2.8.1 - '@graphql-tools/wrap@11.1.16(graphql@16.14.2)': + '@graphql-tools/wrap@11.1.21(graphql@16.14.2)': dependencies: - '@graphql-tools/delegate': 12.0.17(graphql@16.14.2) - '@graphql-tools/schema': 10.0.33(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/delegate': 12.1.1(graphql@16.14.2) + '@graphql-tools/schema': 10.0.38(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) '@whatwg-node/promise-helpers': 1.3.2 graphql: 16.14.2 tslib: 2.8.1 @@ -9843,124 +8847,124 @@ snapshots: '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@5.2.1(@types/node@26.1.1)': + '@inquirer/checkbox@5.2.1(@types/node@26.2.0)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/core': 11.2.1(@types/node@26.2.0) '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.2.0) optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 - '@inquirer/confirm@6.1.1(@types/node@26.1.1)': + '@inquirer/confirm@6.1.1(@types/node@26.2.0)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.1.1) - '@inquirer/type': 4.0.7(@types/node@26.1.1) + '@inquirer/core': 11.2.1(@types/node@26.2.0) + '@inquirer/type': 4.0.7(@types/node@26.2.0) optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 - '@inquirer/core@11.2.1(@types/node@26.1.1)': + '@inquirer/core@11.2.1(@types/node@26.2.0)': dependencies: '@inquirer/ansi': 2.0.7 '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.2.0) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 - '@inquirer/editor@5.2.2(@types/node@26.1.1)': + '@inquirer/editor@5.2.2(@types/node@26.2.0)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.1.1) - '@inquirer/external-editor': 3.0.3(@types/node@26.1.1) - '@inquirer/type': 4.0.7(@types/node@26.1.1) + '@inquirer/core': 11.2.1(@types/node@26.2.0) + '@inquirer/external-editor': 3.0.3(@types/node@26.2.0) + '@inquirer/type': 4.0.7(@types/node@26.2.0) optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 - '@inquirer/expand@5.1.1(@types/node@26.1.1)': + '@inquirer/expand@5.1.1(@types/node@26.2.0)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.1.1) - '@inquirer/type': 4.0.7(@types/node@26.1.1) + '@inquirer/core': 11.2.1(@types/node@26.2.0) + '@inquirer/type': 4.0.7(@types/node@26.2.0) optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 - '@inquirer/external-editor@3.0.3(@types/node@26.1.1)': + '@inquirer/external-editor@3.0.3(@types/node@26.2.0)': dependencies: - chardet: 2.1.1 - iconv-lite: 0.7.2 + chardet: 2.2.0 + iconv-lite: 0.7.3 optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 '@inquirer/figures@2.0.7': {} - '@inquirer/input@5.1.2(@types/node@26.1.1)': + '@inquirer/input@5.1.2(@types/node@26.2.0)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.1.1) - '@inquirer/type': 4.0.7(@types/node@26.1.1) + '@inquirer/core': 11.2.1(@types/node@26.2.0) + '@inquirer/type': 4.0.7(@types/node@26.2.0) optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 - '@inquirer/number@4.1.1(@types/node@26.1.1)': + '@inquirer/number@4.1.1(@types/node@26.2.0)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.1.1) - '@inquirer/type': 4.0.7(@types/node@26.1.1) + '@inquirer/core': 11.2.1(@types/node@26.2.0) + '@inquirer/type': 4.0.7(@types/node@26.2.0) optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 - '@inquirer/password@5.1.1(@types/node@26.1.1)': + '@inquirer/password@5.1.1(@types/node@26.2.0)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@26.1.1) - '@inquirer/type': 4.0.7(@types/node@26.1.1) + '@inquirer/core': 11.2.1(@types/node@26.2.0) + '@inquirer/type': 4.0.7(@types/node@26.2.0) optionalDependencies: - '@types/node': 26.1.1 - - '@inquirer/prompts@8.5.2(@types/node@26.1.1)': - dependencies: - '@inquirer/checkbox': 5.2.1(@types/node@26.1.1) - '@inquirer/confirm': 6.1.1(@types/node@26.1.1) - '@inquirer/editor': 5.2.2(@types/node@26.1.1) - '@inquirer/expand': 5.1.1(@types/node@26.1.1) - '@inquirer/input': 5.1.2(@types/node@26.1.1) - '@inquirer/number': 4.1.1(@types/node@26.1.1) - '@inquirer/password': 5.1.1(@types/node@26.1.1) - '@inquirer/rawlist': 5.3.1(@types/node@26.1.1) - '@inquirer/search': 4.2.1(@types/node@26.1.1) - '@inquirer/select': 5.2.1(@types/node@26.1.1) + '@types/node': 26.2.0 + + '@inquirer/prompts@8.5.2(@types/node@26.2.0)': + dependencies: + '@inquirer/checkbox': 5.2.1(@types/node@26.2.0) + '@inquirer/confirm': 6.1.1(@types/node@26.2.0) + '@inquirer/editor': 5.2.2(@types/node@26.2.0) + '@inquirer/expand': 5.1.1(@types/node@26.2.0) + '@inquirer/input': 5.1.2(@types/node@26.2.0) + '@inquirer/number': 4.1.1(@types/node@26.2.0) + '@inquirer/password': 5.1.1(@types/node@26.2.0) + '@inquirer/rawlist': 5.3.1(@types/node@26.2.0) + '@inquirer/search': 4.2.1(@types/node@26.2.0) + '@inquirer/select': 5.2.1(@types/node@26.2.0) optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 - '@inquirer/rawlist@5.3.1(@types/node@26.1.1)': + '@inquirer/rawlist@5.3.1(@types/node@26.2.0)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.1.1) - '@inquirer/type': 4.0.7(@types/node@26.1.1) + '@inquirer/core': 11.2.1(@types/node@26.2.0) + '@inquirer/type': 4.0.7(@types/node@26.2.0) optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 - '@inquirer/search@4.2.1(@types/node@26.1.1)': + '@inquirer/search@4.2.1(@types/node@26.2.0)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/core': 11.2.1(@types/node@26.2.0) '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.2.0) optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 - '@inquirer/select@5.2.1(@types/node@26.1.1)': + '@inquirer/select@5.2.1(@types/node@26.2.0)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/core': 11.2.1(@types/node@26.2.0) '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.2.0) optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 - '@inquirer/type@4.0.7(@types/node@26.1.1)': + '@inquirer/type@4.0.7(@types/node@26.2.0)': optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 - '@internationalized/date@3.12.2': + '@internationalized/date@3.12.3': dependencies: '@swc/helpers': 0.5.23 @@ -9968,26 +8972,18 @@ snapshots: dependencies: '@swc/helpers': 0.5.23 - '@internationalized/string@3.2.9': + '@internationalized/string@3.2.10': dependencies: '@swc/helpers': 0.5.23 - '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0(typescript@5.9.3)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': + '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0(typescript@5.9.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: glob: 13.0.6 react-docgen-typescript: 2.4.0(typescript@5.9.3) - vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + vite: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) optionalDependencies: typescript: 5.9.3 - '@joshwooding/vite-plugin-react-docgen-typescript@0.7.0(typescript@6.0.3)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': - dependencies: - glob: 13.0.6 - react-docgen-typescript: 2.4.0(typescript@6.0.3) - vite: 8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - optionalDependencies: - typescript: 6.0.3 - '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -10007,6 +9003,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@kwsites/file-exists@1.1.1': dependencies: debug: 4.4.3(supports-color@10.2.2) @@ -10017,7 +9018,7 @@ snapshots: '@lezer/common@1.5.2': {} - '@lezer/css@1.3.3': + '@lezer/css@1.3.6': dependencies: '@lezer/common': 1.5.2 '@lezer/highlight': 1.2.3 @@ -10043,51 +9044,44 @@ snapshots: dependencies: '@lezer/common': 1.5.2 - '@lezer/markdown@1.6.3': + '@lezer/markdown@1.7.2': dependencies: '@lezer/common': 1.5.2 '@lezer/highlight': 1.2.3 - '@marijn/find-cluster-break@1.0.2': {} + '@marijn/find-cluster-break@1.0.3': {} - '@mermaid-js/parser@1.1.1': + '@mermaid-js/parser@1.2.0': dependencies: '@chevrotain/types': 11.1.2 '@n1ru4l/push-pull-async-iterable-iterator@3.2.0': {} - '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)': - dependencies: - '@emnapi/core': 1.9.2 - '@emnapi/runtime': 1.9.2 - '@tybys/wasm-util': 0.10.1 - optional: true - - '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0)': + '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0)': dependencies: '@emnapi/core': 1.11.0 '@emnapi/runtime': 1.11.0 - '@tybys/wasm-util': 0.10.2 + '@tybys/wasm-util': 0.10.3 optional: true - '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: '@emnapi/core': 1.11.1 '@emnapi/runtime': 1.11.1 - '@tybys/wasm-util': 0.10.2 + '@tybys/wasm-util': 0.10.3 optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': dependencies: - '@emnapi/core': 1.11.1 - '@emnapi/runtime': 1.11.1 + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 '@tybys/wasm-util': 0.10.3 optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': + '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)': dependencies: - '@emnapi/core': 1.11.2 - '@emnapi/runtime': 1.11.2 + '@emnapi/core': 1.9.2 + '@emnapi/runtime': 1.9.2 '@tybys/wasm-util': 0.10.3 optional: true @@ -10118,7 +9112,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.220.0 - import-in-the-middle: 3.3.2 + import-in-the-middle: 3.3.3 require-in-the-middle: 8.0.1 transitivePeerDependencies: - supports-color @@ -10152,7 +9146,7 @@ snapshots: '@oxc-parser/binding-android-arm-eabi@0.137.0': optional: true - '@oxc-parser/binding-android-arm-eabi@0.141.0': + '@oxc-parser/binding-android-arm-eabi@0.142.0': optional: true '@oxc-parser/binding-android-arm64@0.127.0': @@ -10161,7 +9155,7 @@ snapshots: '@oxc-parser/binding-android-arm64@0.137.0': optional: true - '@oxc-parser/binding-android-arm64@0.141.0': + '@oxc-parser/binding-android-arm64@0.142.0': optional: true '@oxc-parser/binding-darwin-arm64@0.127.0': @@ -10170,7 +9164,7 @@ snapshots: '@oxc-parser/binding-darwin-arm64@0.137.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.141.0': + '@oxc-parser/binding-darwin-arm64@0.142.0': optional: true '@oxc-parser/binding-darwin-x64@0.127.0': @@ -10179,7 +9173,7 @@ snapshots: '@oxc-parser/binding-darwin-x64@0.137.0': optional: true - '@oxc-parser/binding-darwin-x64@0.141.0': + '@oxc-parser/binding-darwin-x64@0.142.0': optional: true '@oxc-parser/binding-freebsd-x64@0.127.0': @@ -10188,7 +9182,7 @@ snapshots: '@oxc-parser/binding-freebsd-x64@0.137.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.141.0': + '@oxc-parser/binding-freebsd-x64@0.142.0': optional: true '@oxc-parser/binding-linux-arm-gnueabihf@0.127.0': @@ -10197,7 +9191,7 @@ snapshots: '@oxc-parser/binding-linux-arm-gnueabihf@0.137.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.141.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.142.0': optional: true '@oxc-parser/binding-linux-arm-musleabihf@0.127.0': @@ -10206,7 +9200,7 @@ snapshots: '@oxc-parser/binding-linux-arm-musleabihf@0.137.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.141.0': + '@oxc-parser/binding-linux-arm-musleabihf@0.142.0': optional: true '@oxc-parser/binding-linux-arm64-gnu@0.127.0': @@ -10215,7 +9209,7 @@ snapshots: '@oxc-parser/binding-linux-arm64-gnu@0.137.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.141.0': + '@oxc-parser/binding-linux-arm64-gnu@0.142.0': optional: true '@oxc-parser/binding-linux-arm64-musl@0.127.0': @@ -10224,7 +9218,7 @@ snapshots: '@oxc-parser/binding-linux-arm64-musl@0.137.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.141.0': + '@oxc-parser/binding-linux-arm64-musl@0.142.0': optional: true '@oxc-parser/binding-linux-ppc64-gnu@0.127.0': @@ -10233,7 +9227,7 @@ snapshots: '@oxc-parser/binding-linux-ppc64-gnu@0.137.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.141.0': + '@oxc-parser/binding-linux-ppc64-gnu@0.142.0': optional: true '@oxc-parser/binding-linux-riscv64-gnu@0.127.0': @@ -10242,7 +9236,7 @@ snapshots: '@oxc-parser/binding-linux-riscv64-gnu@0.137.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.141.0': + '@oxc-parser/binding-linux-riscv64-gnu@0.142.0': optional: true '@oxc-parser/binding-linux-riscv64-musl@0.127.0': @@ -10251,7 +9245,7 @@ snapshots: '@oxc-parser/binding-linux-riscv64-musl@0.137.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.141.0': + '@oxc-parser/binding-linux-riscv64-musl@0.142.0': optional: true '@oxc-parser/binding-linux-s390x-gnu@0.127.0': @@ -10260,7 +9254,7 @@ snapshots: '@oxc-parser/binding-linux-s390x-gnu@0.137.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.141.0': + '@oxc-parser/binding-linux-s390x-gnu@0.142.0': optional: true '@oxc-parser/binding-linux-x64-gnu@0.127.0': @@ -10269,7 +9263,7 @@ snapshots: '@oxc-parser/binding-linux-x64-gnu@0.137.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.141.0': + '@oxc-parser/binding-linux-x64-gnu@0.142.0': optional: true '@oxc-parser/binding-linux-x64-musl@0.127.0': @@ -10278,7 +9272,7 @@ snapshots: '@oxc-parser/binding-linux-x64-musl@0.137.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.141.0': + '@oxc-parser/binding-linux-x64-musl@0.142.0': optional: true '@oxc-parser/binding-openharmony-arm64@0.127.0': @@ -10287,28 +9281,28 @@ snapshots: '@oxc-parser/binding-openharmony-arm64@0.137.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.141.0': + '@oxc-parser/binding-openharmony-arm64@0.142.0': optional: true '@oxc-parser/binding-wasm32-wasi@0.127.0': dependencies: '@emnapi/core': 1.9.2 '@emnapi/runtime': 1.9.2 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2) + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2) optional: true '@oxc-parser/binding-wasm32-wasi@0.137.0': dependencies: '@emnapi/core': 1.11.1 '@emnapi/runtime': 1.11.1 - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true - '@oxc-parser/binding-wasm32-wasi@0.141.0': + '@oxc-parser/binding-wasm32-wasi@0.142.0': dependencies: '@emnapi/core': 1.11.2 '@emnapi/runtime': 1.11.2 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) optional: true '@oxc-parser/binding-win32-arm64-msvc@0.127.0': @@ -10317,7 +9311,7 @@ snapshots: '@oxc-parser/binding-win32-arm64-msvc@0.137.0': optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.141.0': + '@oxc-parser/binding-win32-arm64-msvc@0.142.0': optional: true '@oxc-parser/binding-win32-ia32-msvc@0.127.0': @@ -10326,7 +9320,7 @@ snapshots: '@oxc-parser/binding-win32-ia32-msvc@0.137.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.141.0': + '@oxc-parser/binding-win32-ia32-msvc@0.142.0': optional: true '@oxc-parser/binding-win32-x64-msvc@0.127.0': @@ -10335,16 +9329,16 @@ snapshots: '@oxc-parser/binding-win32-x64-msvc@0.137.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.141.0': + '@oxc-parser/binding-win32-x64-msvc@0.142.0': optional: true '@oxc-project/types@0.127.0': {} '@oxc-project/types@0.137.0': {} - '@oxc-project/types@0.139.0': {} + '@oxc-project/types@0.142.0': {} - '@oxc-project/types@0.141.0': {} + '@oxc-project/types@0.143.0': {} '@oxc-resolver/binding-android-arm-eabi@11.21.3': optional: true @@ -10446,14 +9440,14 @@ snapshots: dependencies: '@emnapi/core': 1.11.0 '@emnapi/runtime': 1.11.0 - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0) + '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.11.0)(@emnapi/runtime@1.11.0) optional: true '@oxc-resolver/binding-wasm32-wasi@11.24.2': dependencies: '@emnapi/core': 1.11.2 '@emnapi/runtime': 1.11.2 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) optional: true '@oxc-resolver/binding-win32-arm64-msvc@11.21.3': @@ -10468,289 +9462,175 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.24.2': optional: true - '@oxfmt/binding-android-arm-eabi@0.55.0': - optional: true - - '@oxfmt/binding-android-arm-eabi@0.60.0': - optional: true - - '@oxfmt/binding-android-arm64@0.55.0': - optional: true - - '@oxfmt/binding-android-arm64@0.60.0': - optional: true - - '@oxfmt/binding-darwin-arm64@0.55.0': - optional: true - - '@oxfmt/binding-darwin-arm64@0.60.0': - optional: true - - '@oxfmt/binding-darwin-x64@0.55.0': - optional: true - - '@oxfmt/binding-darwin-x64@0.60.0': - optional: true - - '@oxfmt/binding-freebsd-x64@0.55.0': - optional: true - - '@oxfmt/binding-freebsd-x64@0.60.0': - optional: true - - '@oxfmt/binding-linux-arm-gnueabihf@0.55.0': - optional: true - - '@oxfmt/binding-linux-arm-gnueabihf@0.60.0': - optional: true - - '@oxfmt/binding-linux-arm-musleabihf@0.55.0': - optional: true - - '@oxfmt/binding-linux-arm-musleabihf@0.60.0': - optional: true - - '@oxfmt/binding-linux-arm64-gnu@0.55.0': - optional: true - - '@oxfmt/binding-linux-arm64-gnu@0.60.0': - optional: true - - '@oxfmt/binding-linux-arm64-musl@0.55.0': - optional: true - - '@oxfmt/binding-linux-arm64-musl@0.60.0': - optional: true - - '@oxfmt/binding-linux-ppc64-gnu@0.55.0': - optional: true - - '@oxfmt/binding-linux-ppc64-gnu@0.60.0': - optional: true - - '@oxfmt/binding-linux-riscv64-gnu@0.55.0': - optional: true - - '@oxfmt/binding-linux-riscv64-gnu@0.60.0': - optional: true - - '@oxfmt/binding-linux-riscv64-musl@0.55.0': - optional: true - - '@oxfmt/binding-linux-riscv64-musl@0.60.0': - optional: true - - '@oxfmt/binding-linux-s390x-gnu@0.55.0': - optional: true - - '@oxfmt/binding-linux-s390x-gnu@0.60.0': - optional: true - - '@oxfmt/binding-linux-x64-gnu@0.55.0': - optional: true - - '@oxfmt/binding-linux-x64-gnu@0.60.0': - optional: true - - '@oxfmt/binding-linux-x64-musl@0.55.0': - optional: true - - '@oxfmt/binding-linux-x64-musl@0.60.0': - optional: true - - '@oxfmt/binding-openharmony-arm64@0.55.0': - optional: true - - '@oxfmt/binding-openharmony-arm64@0.60.0': - optional: true - - '@oxfmt/binding-win32-arm64-msvc@0.55.0': - optional: true - - '@oxfmt/binding-win32-arm64-msvc@0.60.0': - optional: true - - '@oxfmt/binding-win32-ia32-msvc@0.55.0': - optional: true - - '@oxfmt/binding-win32-ia32-msvc@0.60.0': - optional: true - - '@oxfmt/binding-win32-x64-msvc@0.55.0': - optional: true - - '@oxfmt/binding-win32-x64-msvc@0.60.0': - optional: true - - '@oxlint/binding-android-arm-eabi@1.70.0': + '@oxfmt/binding-android-arm-eabi@0.63.0': optional: true - '@oxlint/binding-android-arm-eabi@1.74.0': + '@oxfmt/binding-android-arm64@0.63.0': optional: true - '@oxlint/binding-android-arm-eabi@1.75.0': + '@oxfmt/binding-darwin-arm64@0.63.0': optional: true - '@oxlint/binding-android-arm64@1.70.0': + '@oxfmt/binding-darwin-x64@0.63.0': optional: true - '@oxlint/binding-android-arm64@1.74.0': + '@oxfmt/binding-freebsd-x64@0.63.0': optional: true - '@oxlint/binding-android-arm64@1.75.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.63.0': optional: true - '@oxlint/binding-darwin-arm64@1.70.0': + '@oxfmt/binding-linux-arm-musleabihf@0.63.0': optional: true - '@oxlint/binding-darwin-arm64@1.74.0': + '@oxfmt/binding-linux-arm64-gnu@0.63.0': optional: true - '@oxlint/binding-darwin-arm64@1.75.0': + '@oxfmt/binding-linux-arm64-musl@0.63.0': optional: true - '@oxlint/binding-darwin-x64@1.70.0': + '@oxfmt/binding-linux-ppc64-gnu@0.63.0': optional: true - '@oxlint/binding-darwin-x64@1.74.0': + '@oxfmt/binding-linux-riscv64-gnu@0.63.0': optional: true - '@oxlint/binding-darwin-x64@1.75.0': + '@oxfmt/binding-linux-riscv64-musl@0.63.0': optional: true - '@oxlint/binding-freebsd-x64@1.70.0': + '@oxfmt/binding-linux-s390x-gnu@0.63.0': optional: true - '@oxlint/binding-freebsd-x64@1.74.0': + '@oxfmt/binding-linux-x64-gnu@0.63.0': optional: true - '@oxlint/binding-freebsd-x64@1.75.0': + '@oxfmt/binding-linux-x64-musl@0.63.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.70.0': + '@oxfmt/binding-openharmony-arm64@0.63.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.74.0': + '@oxfmt/binding-win32-arm64-msvc@0.63.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.75.0': + '@oxfmt/binding-win32-ia32-msvc@0.63.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.70.0': + '@oxfmt/binding-win32-x64-msvc@0.63.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.74.0': + '@oxlint/binding-android-arm-eabi@1.76.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.75.0': + '@oxlint/binding-android-arm-eabi@1.78.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.70.0': + '@oxlint/binding-android-arm64@1.76.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.74.0': + '@oxlint/binding-android-arm64@1.78.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.75.0': + '@oxlint/binding-darwin-arm64@1.76.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.70.0': + '@oxlint/binding-darwin-arm64@1.78.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.74.0': + '@oxlint/binding-darwin-x64@1.76.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.75.0': + '@oxlint/binding-darwin-x64@1.78.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.70.0': + '@oxlint/binding-freebsd-x64@1.76.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.74.0': + '@oxlint/binding-freebsd-x64@1.78.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.75.0': + '@oxlint/binding-linux-arm-gnueabihf@1.76.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.70.0': + '@oxlint/binding-linux-arm-gnueabihf@1.78.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.74.0': + '@oxlint/binding-linux-arm-musleabihf@1.76.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.75.0': + '@oxlint/binding-linux-arm-musleabihf@1.78.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.70.0': + '@oxlint/binding-linux-arm64-gnu@1.76.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.74.0': + '@oxlint/binding-linux-arm64-gnu@1.78.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.75.0': + '@oxlint/binding-linux-arm64-musl@1.76.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.70.0': + '@oxlint/binding-linux-arm64-musl@1.78.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.74.0': + '@oxlint/binding-linux-ppc64-gnu@1.76.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.75.0': + '@oxlint/binding-linux-ppc64-gnu@1.78.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.70.0': + '@oxlint/binding-linux-riscv64-gnu@1.76.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.74.0': + '@oxlint/binding-linux-riscv64-gnu@1.78.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.75.0': + '@oxlint/binding-linux-riscv64-musl@1.76.0': optional: true - '@oxlint/binding-linux-x64-musl@1.70.0': + '@oxlint/binding-linux-riscv64-musl@1.78.0': optional: true - '@oxlint/binding-linux-x64-musl@1.74.0': + '@oxlint/binding-linux-s390x-gnu@1.76.0': optional: true - '@oxlint/binding-linux-x64-musl@1.75.0': + '@oxlint/binding-linux-s390x-gnu@1.78.0': optional: true - '@oxlint/binding-openharmony-arm64@1.70.0': + '@oxlint/binding-linux-x64-gnu@1.76.0': optional: true - '@oxlint/binding-openharmony-arm64@1.74.0': + '@oxlint/binding-linux-x64-gnu@1.78.0': optional: true - '@oxlint/binding-openharmony-arm64@1.75.0': + '@oxlint/binding-linux-x64-musl@1.76.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.70.0': + '@oxlint/binding-linux-x64-musl@1.78.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.74.0': + '@oxlint/binding-openharmony-arm64@1.76.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.75.0': + '@oxlint/binding-openharmony-arm64@1.78.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.70.0': + '@oxlint/binding-win32-arm64-msvc@1.76.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.74.0': + '@oxlint/binding-win32-arm64-msvc@1.78.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.75.0': + '@oxlint/binding-win32-ia32-msvc@1.76.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.70.0': + '@oxlint/binding-win32-ia32-msvc@1.78.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.74.0': + '@oxlint/binding-win32-x64-msvc@1.76.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.75.0': + '@oxlint/binding-win32-x64-msvc@1.78.0': optional: true '@phenomnomnominal/tsquery@6.2.0(typescript@5.9.3)': @@ -10761,7 +9641,7 @@ snapshots: '@phenomnomnominal/tstemplate@0.1.0': {} - '@playwright/test@1.61.1': + '@playwright/test@1.62.1': dependencies: playwright: 1.60.0 @@ -10776,620 +9656,569 @@ snapshots: '@radix-ui/number@1.1.3': {} - '@radix-ui/primitive@1.1.3': {} - '@radix-ui/primitive@1.1.4': {} '@radix-ui/primitive@1.1.7': {} - '@radix-ui/react-accordion@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-accordion@1.2.20(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collapsible': 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collapsible': 1.1.20(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-arrow@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-arrow@1.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-collapsible@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-arrow@1.1.15(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-collection@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-collapsible@1.1.20(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.17)(react@19.2.8)': - dependencies: - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.17 - - '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-collection@1.1.15(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-compose-refs@1.1.5(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.18)(react@19.2.8)': dependencies: react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-context@1.1.2(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-compose-refs@1.1.5(@types/react@19.2.18)(react@19.2.8)': dependencies: react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-context@1.1.4(@types/react@19.2.18)(react@19.2.8)': dependencies: react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-context@1.2.2(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-context@1.2.2(@types/react@19.2.18)(react@19.2.8)': dependencies: react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 - - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.17)(react@19.2.8) - aria-hidden: 1.2.6 - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 - '@radix-ui/react-dialog@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-dialog@1.1.17(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.18)(react@19.2.8) aria-hidden: 1.2.6 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - - '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.8)': - dependencies: - react: 19.2.8 + react-remove-scroll: 2.7.2(@types/react@19.2.18)(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-direction@1.1.4(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-direction@1.1.4(@types/react@19.2.18)(react@19.2.8)': dependencies: react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-dismissable-layer@1.1.13(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-dismissable-layer@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-dismissable-layer@1.1.19(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-dropdown-menu@2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-dropdown-menu@2.1.24(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-menu': 2.1.24(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.18)(react@19.2.8)': dependencies: react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-focus-guards@1.1.6(@types/react@19.2.18)(react@19.2.8)': dependencies: react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-focus-scope@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-focus-scope@1.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-focus-scope@1.1.16(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-id@1.1.1(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-id@1.1.2(@types/react@19.2.18)(react@19.2.8)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-id@1.1.4(@types/react@19.2.18)(react@19.2.8)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-label@2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-label@2.1.15(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-menu@2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-menu@2.1.24(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) aria-hidden: 1.2.6 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.8) + react-remove-scroll: 2.7.2(@types/react@19.2.18)(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-popover@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-popover@1.1.23(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-focus-guards': 1.1.6(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-popper': 1.3.7(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-portal': 1.1.17(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) aria-hidden: 1.2.6 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - - '@radix-ui/react-popper@1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-arrow': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.8) + react-remove-scroll: 2.7.2(@types/react@19.2.18)(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) + + '@radix-ui/react-popper@1.3.1(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-arrow': 1.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.18)(react@19.2.8) '@radix-ui/rect': 1.1.2 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - - '@radix-ui/react-portal@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.8) - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-presence@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-popper@1.3.7(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.8) + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-arrow': 1.1.15(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-rect': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-size': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/rect': 1.1.3 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-portal@1.1.12(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-portal@1.1.17(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-primitive@2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-presence@1.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-slot': 1.3.3(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-primitive@2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-slot': 1.3.3(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-primitive@2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-primitive@2.1.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-progress@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-progress@1.1.16(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-roving-focus@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-roving-focus@1.1.19(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-collection': 1.1.15(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-is-hydrated': 0.1.3(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-scroll-area@1.2.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-scroll-area@1.2.18(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: '@radix-ui/number': 1.1.3 '@radix-ui/primitive': 1.1.7 - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-context': 1.2.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-direction': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-callback-ref': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - - '@radix-ui/react-slot@1.2.3(@types/react@19.2.17)(react@19.2.8)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.8) - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.17 - - '@radix-ui/react-slot@1.2.4(@types/react@19.2.17)(react@19.2.8)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.8) - react: 19.2.8 - optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-slot@1.3.0(@types/react@19.2.18)(react@19.2.8)': dependencies: - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-slot@1.3.3(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-slot@1.3.3(@types/react@19.2.18)(react@19.2.8)': dependencies: - '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-tabs@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-tabs@1.1.21(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-context': 1.2.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-direction': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-presence': 1.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-use-controllable-state': 1.2.6(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-tooltip@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-tooltip@1.2.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: '@radix-ui/primitive': 1.1.4 - '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.18)(react@19.2.8)': dependencies: react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-callback-ref@1.1.4(@types/react@19.2.18)(react@19.2.8)': dependencies: react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-use-callback-ref@1.1.4(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.18)(react@19.2.8)': dependencies: + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-controllable-state@1.2.6(@types/react@19.2.18)(react@19.2.8)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/primitive': 1.1.7 + '@radix-ui/react-use-effect-event': 0.0.5(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.18)(react@19.2.8)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-effect-event@0.0.5(@types/react@19.2.18)(react@19.2.8)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-escape-keydown@1.1.2(@types/react@19.2.18)(react@19.2.8)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-is-hydrated@0.1.3(@types/react@19.2.18)(react@19.2.8)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.17)(react@19.2.8) react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-use-escape-keydown@1.1.2(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.18)(react@19.2.8)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.8) react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-layout-effect@1.1.4(@types/react@19.2.18)(react@19.2.8)': dependencies: react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.18)(react@19.2.8)': dependencies: + '@radix-ui/rect': 1.1.2 react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-use-layout-effect@1.1.4(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-rect@1.1.4(@types/react@19.2.18)(react@19.2.8)': dependencies: + '@radix-ui/rect': 1.1.3 react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.18)(react@19.2.8)': dependencies: - '@radix-ui/rect': 1.1.2 + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.8)': + '@radix-ui/react-use-size@1.1.4(@types/react@19.2.18)(react@19.2.8)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.8) + '@radix-ui/react-use-layout-effect': 1.1.4(@types/react@19.2.18)(react@19.2.8) react: 19.2.8 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@radix-ui/react-visually-hidden@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@radix-ui/react-visually-hidden@1.2.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) '@radix-ui/rect@1.1.2': {} + '@radix-ui/rect@1.1.3': {} + '@react-aria/focus@3.22.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: '@swc/helpers': 0.5.23 @@ -11399,7 +10228,7 @@ snapshots: '@react-aria/interactions@3.28.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@react-types/shared': 3.35.0(react@19.2.8) + '@react-types/shared': 3.36.1(react@19.2.8) '@swc/helpers': 0.5.23 react: 19.2.8 react-aria: 3.48.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -11414,13 +10243,9 @@ snapshots: package-manager-detector: 1.8.0 picocolors: 1.1.1 prompts: 2.4.2 - tinyexec: 1.2.4 - - '@react-types/shared@3.35.0(react@19.2.8)': - dependencies: - react: 19.2.8 + tinyexec: 1.3.0 - '@react-types/shared@3.36.0(react@19.2.8)': + '@react-types/shared@3.36.1(react@19.2.8)': dependencies: react: 19.2.8 @@ -11433,100 +10258,84 @@ snapshots: '@redocly/config@0.22.0': {} - '@redocly/openapi-core@1.34.12(supports-color@10.2.2)': + '@redocly/openapi-core@1.34.19(supports-color@10.2.2)': dependencies: '@redocly/ajv': 8.11.2 '@redocly/config': 0.22.0 colorette: 1.4.0 https-proxy-agent: 7.0.6(supports-color@10.2.2) js-levenshtein: 1.1.6 - js-yaml: 4.1.1 + js-yaml: 4.3.1 minimatch: 5.1.9 pluralize: 8.0.0 yaml-ast-parser: 0.0.43 transitivePeerDependencies: - supports-color - '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.17)(react@19.2.8)(redux@5.0.1))(react@19.2.8)': + '@reduxjs/toolkit@2.12.0(react-redux@9.3.0(@types/react@19.2.18)(react@19.2.8)(redux@5.0.1))(react@19.2.8)': dependencies: '@standard-schema/spec': 1.1.0 '@standard-schema/utils': 0.3.0 - immer: 11.1.4 + immer: 11.1.16 redux: 5.0.1 redux-thunk: 3.1.0(redux@5.0.1) reselect: 5.2.0 optionalDependencies: react: 19.2.8 - react-redux: 9.2.0(@types/react@19.2.17)(react@19.2.8)(redux@5.0.1) + react-redux: 9.3.0(@types/react@19.2.18)(react@19.2.8)(redux@5.0.1) '@repeaterjs/repeater@3.1.0': {} - '@rolldown/binding-android-arm64@1.1.5': + '@rolldown/binding-android-arm64@1.2.3': optional: true - '@rolldown/binding-darwin-arm64@1.1.5': + '@rolldown/binding-darwin-arm64@1.2.3': optional: true - '@rolldown/binding-darwin-x64@1.1.5': + '@rolldown/binding-darwin-x64@1.2.3': optional: true - '@rolldown/binding-freebsd-x64@1.1.5': + '@rolldown/binding-freebsd-x64@1.2.3': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.1.5': + '@rolldown/binding-linux-arm-gnueabihf@1.2.3': optional: true - '@rolldown/binding-linux-arm64-gnu@1.1.5': + '@rolldown/binding-linux-arm64-gnu@1.2.3': optional: true - '@rolldown/binding-linux-arm64-musl@1.1.5': + '@rolldown/binding-linux-arm64-musl@1.2.3': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.1.5': + '@rolldown/binding-linux-ppc64-gnu@1.2.3': optional: true - '@rolldown/binding-linux-s390x-gnu@1.1.5': + '@rolldown/binding-linux-s390x-gnu@1.2.3': optional: true - '@rolldown/binding-linux-x64-gnu@1.1.5': + '@rolldown/binding-linux-x64-gnu@1.2.3': optional: true - '@rolldown/binding-linux-x64-musl@1.1.5': + '@rolldown/binding-linux-x64-musl@1.2.3': optional: true - '@rolldown/binding-openharmony-arm64@1.1.5': - optional: true - - '@rolldown/binding-wasm32-wasi@1.1.5': - dependencies: - '@emnapi/core': 1.11.1 - '@emnapi/runtime': 1.11.1 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@rolldown/binding-openharmony-arm64@1.2.3': optional: true - '@rolldown/binding-win32-arm64-msvc@1.1.5': + '@rolldown/binding-win32-arm64-msvc@1.2.3': optional: true - '@rolldown/binding-win32-x64-msvc@1.1.5': + '@rolldown/binding-win32-x64-msvc@1.2.3': optional: true - '@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.1.5)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': - dependencies: - '@babel/core': 7.29.7 - picomatch: 4.0.4 - rolldown: 1.1.5 - optionalDependencies: - '@babel/runtime': 7.29.7 - vite: 8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - - '@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': + '@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@8.0.0)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.7 - picomatch: 4.0.4 - rolldown: 1.1.5 + picomatch: 4.0.5 + rolldown: 1.2.3 optionalDependencies: - '@babel/runtime': 7.29.7 - vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + '@babel/runtime': 8.0.0 + vite: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) '@rolldown/pluginutils@1.0.1': {} @@ -11534,56 +10343,56 @@ snapshots: dependencies: '@types/estree': 1.0.9 estree-walker: 2.0.2 - picomatch: 4.0.4 + picomatch: 4.0.5 '@sentry/conventions@0.16.0': {} - '@sentry/core@10.68.0': + '@sentry/core@10.70.0': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/node-core@10.68.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))': + '@sentry/node-core@10.70.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))': dependencies: '@sentry/conventions': 0.16.0 - '@sentry/core': 10.68.0 - '@sentry/opentelemetry': 10.68.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) - import-in-the-middle: 3.3.2 + '@sentry/core': 10.70.0 + '@sentry/opentelemetry': 10.70.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) + import-in-the-middle: 3.3.3 optionalDependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1) - '@sentry/node@10.68.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))': + '@sentry/node@10.70.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 - '@sentry/core': 10.68.0 - '@sentry/node-core': 10.68.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) - '@sentry/opentelemetry': 10.68.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) - '@sentry/server-utils': 10.68.0 - import-in-the-middle: 3.3.2 + '@sentry/core': 10.70.0 + '@sentry/node-core': 10.70.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) + '@sentry/opentelemetry': 10.70.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1)) + '@sentry/server-utils': 10.70.0 + import-in-the-middle: 3.3.3 transitivePeerDependencies: - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' - supports-color - '@sentry/opentelemetry@10.68.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))': + '@sentry/opentelemetry@10.70.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.10.0(@opentelemetry/api@1.9.1))': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace-base': 2.10.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.16.0 - '@sentry/core': 10.68.0 + '@sentry/core': 10.70.0 - '@sentry/server-utils@10.68.0': + '@sentry/server-utils@10.70.0': dependencies: - '@apm-js-collab/code-transformer-bundler-plugins': 0.7.1 + '@apm-js-collab/code-transformer-bundler-plugins': 0.7.4 '@apm-js-collab/tracing-hooks': 0.13.0 '@sentry/conventions': 0.16.0 - '@sentry/core': 10.68.0 + '@sentry/core': 10.70.0 meriyah: 6.1.4 transitivePeerDependencies: - supports-color @@ -11596,118 +10405,59 @@ snapshots: dependencies: '@simple-git/args-pathspec': 1.0.3 - '@standard-schema/spec@1.0.0': {} - '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} - '@storybook/builder-vite@10.4.6(esbuild@0.28.1)(storybook@10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': - dependencies: - '@storybook/csf-plugin': 10.4.6(esbuild@0.28.1)(storybook@10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - storybook: 10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - ts-dedent: 2.2.0 - vite: 8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - transitivePeerDependencies: - - esbuild - - rollup - - webpack - - '@storybook/builder-vite@10.5.5(esbuild@0.28.1)(storybook@10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8))(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': + '@storybook/builder-vite@10.5.7(esbuild@0.28.1)(storybook@10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8))(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: - '@storybook/csf-plugin': 10.5.5(esbuild@0.28.1)(storybook@10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8))(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - storybook: 10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8) + '@storybook/csf-plugin': 10.5.7(esbuild@0.28.1)(storybook@10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8))(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + storybook: 10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8) ts-dedent: 2.3.0 - vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + vite: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) transitivePeerDependencies: - esbuild - rollup - webpack - '@storybook/csf-plugin@10.4.6(esbuild@0.28.1)(storybook@10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': + '@storybook/csf-plugin@10.5.7(esbuild@0.28.1)(storybook@10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8))(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: - storybook: 10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + storybook: 10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8) unplugin: 2.3.11 optionalDependencies: esbuild: 0.28.1 - vite: 8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - - '@storybook/csf-plugin@10.5.5(esbuild@0.28.1)(storybook@10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8))(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': - dependencies: - storybook: 10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8) - unplugin: 2.3.11 - optionalDependencies: - esbuild: 0.28.1 - vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + vite: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) '@storybook/global@5.0.0': {} - '@storybook/icons@2.0.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - '@storybook/icons@2.1.0(react@19.2.8)': dependencies: react: 19.2.8 - '@storybook/react-dom-shim@10.4.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))': - dependencies: - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - storybook: 10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - - '@storybook/react-dom-shim@10.5.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8))': + '@storybook/react-dom-shim@10.5.7(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8))': dependencies: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - storybook: 10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8) + storybook: 10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - - '@storybook/react-vite@10.4.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(typescript@6.0.3)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': - dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@6.0.3)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - '@rollup/pluginutils': 5.4.0 - '@storybook/builder-vite': 10.4.6(esbuild@0.28.1)(storybook@10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - '@storybook/react': 10.4.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(typescript@6.0.3) - empathic: 2.0.1 - magic-string: 0.30.21 - react: 19.2.8 - react-docgen: 8.0.3 - react-dom: 19.2.8(react@19.2.8) - resolve: 1.22.12 - storybook: 10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - tsconfig-paths: 4.2.0 - vite: 8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - - esbuild - - rollup - - supports-color - - typescript - - webpack + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - '@storybook/react-vite@10.5.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8))(typescript@5.9.3)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': + '@storybook/react-vite@10.5.7(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(esbuild@0.28.1)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8))(typescript@5.9.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: - '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@5.9.3)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + '@joshwooding/vite-plugin-react-docgen-typescript': 0.7.0(typescript@5.9.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) '@rollup/pluginutils': 5.4.0 - '@storybook/builder-vite': 10.5.5(esbuild@0.28.1)(storybook@10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8))(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - '@storybook/react': 10.5.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8))(typescript@5.9.3) + '@storybook/builder-vite': 10.5.7(esbuild@0.28.1)(storybook@10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8))(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + '@storybook/react': 10.5.7(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8))(typescript@5.9.3) empathic: 2.0.1 magic-string: 0.30.21 react: 19.2.8 react-docgen: 8.0.3 react-dom: 19.2.8(react@19.2.8) resolve: 1.22.12 - storybook: 10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8) + storybook: 10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8) tsconfig-paths: 4.2.0 - vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + vite: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -11718,34 +10468,18 @@ snapshots: - supports-color - webpack - '@storybook/react@10.4.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(typescript@6.0.3)': - dependencies: - '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.4.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)) - react: 19.2.8 - react-docgen: 8.0.3 - react-docgen-typescript: 2.4.0(typescript@6.0.3) - react-dom: 19.2.8(react@19.2.8) - storybook: 10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@storybook/react@10.5.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8))(typescript@5.9.3)': + '@storybook/react@10.5.7(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8))(typescript@5.9.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 10.5.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8)) + '@storybook/react-dom-shim': 10.5.7(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(storybook@10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8)) react: 19.2.8 react-docgen: 8.0.3 react-docgen-typescript: 2.4.0(typescript@5.9.3) react-dom: 19.2.8(react@19.2.8) - storybook: 10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8) + storybook: 10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -11807,7 +10541,7 @@ snapshots: '@svgr/hast-util-to-babel-ast@8.0.0': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 entities: 4.5.0 '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.3))': @@ -11885,33 +10619,26 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 - '@tailwindcss/vite@4.3.3(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': - dependencies: - '@tailwindcss/node': 4.3.3 - '@tailwindcss/oxide': 4.3.3 - tailwindcss: 4.3.3 - vite: 8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - - '@tailwindcss/vite@4.3.3(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.3(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: '@tailwindcss/node': 4.3.3 '@tailwindcss/oxide': 4.3.3 tailwindcss: 4.3.3 - vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + vite: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - '@tanstack/query-core@5.101.2': {} + '@tanstack/query-core@5.101.4': {} - '@tanstack/query-devtools@5.101.2': {} + '@tanstack/query-devtools@5.101.4': {} - '@tanstack/react-query-devtools@5.101.2(@tanstack/react-query@5.101.2(react@19.2.8))(react@19.2.8)': + '@tanstack/react-query-devtools@5.101.4(@tanstack/react-query@5.101.4(react@19.2.8))(react@19.2.8)': dependencies: - '@tanstack/query-devtools': 5.101.2 - '@tanstack/react-query': 5.101.2(react@19.2.8) + '@tanstack/query-devtools': 5.101.4 + '@tanstack/react-query': 5.101.4(react@19.2.8) react: 19.2.8 - '@tanstack/react-query@5.101.2(react@19.2.8)': + '@tanstack/react-query@5.101.4(react@19.2.8)': dependencies: - '@tanstack/query-core': 5.101.2 + '@tanstack/query-core': 5.101.4 react: 19.2.8 '@tanstack/react-table@8.21.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': @@ -11954,15 +10681,13 @@ snapshots: dependencies: '@testing-library/dom': 10.4.1 - '@tybys/wasm-util@0.10.1': - dependencies: - tslib: 2.8.1 - optional: true + '@tsconfig/node10@1.0.12': {} - '@tybys/wasm-util@0.10.2': - dependencies: - tslib: 2.8.1 - optional: true + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} '@tybys/wasm-util@0.10.3': dependencies: @@ -11973,24 +10698,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@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.29.7 + '@babel/types': 7.29.8 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@types/chai@5.2.3': dependencies: @@ -12150,25 +10875,21 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@25.9.5': - dependencies: - undici-types: 7.24.6 - - '@types/node@26.1.1': + '@types/node@26.2.0': dependencies: undici-types: 8.3.0 '@types/prismjs@1.26.6': {} - '@types/react-dom@19.2.3(@types/react@19.2.17)': + '@types/react-dom@19.2.4(@types/react@19.2.18)': dependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 '@types/react-syntax-highlighter@15.5.13': dependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - '@types/react@19.2.17': + '@types/react@19.2.18': dependencies: csstype: 3.2.3 @@ -12176,7 +10897,7 @@ snapshots: '@types/sha1@1.1.5': dependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 '@types/trusted-types@2.0.7': optional: true @@ -12189,207 +10910,207 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.9.5 + '@types/node': 26.2.0 - '@typescript-eslint/types@8.65.0': {} + '@typescript-eslint/types@8.67.0': {} - '@uiw/color-convert@2.10.3(@babel/runtime@7.29.7)': + '@uiw/color-convert@2.10.3(@babel/runtime@8.0.0)': dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 8.0.0 - '@uiw/react-color-alpha@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-alpha@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-drag-event-interactive': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-drag-event-interactive': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-block@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-block@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-swatch': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-swatch': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-chrome@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-chrome@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-alpha': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-editable-input-hsla': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-editable-input-rgba': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-github': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-hue': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-saturation': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-alpha': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-editable-input-hsla': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-editable-input-rgba': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-github': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-hue': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-saturation': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-circle@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-circle@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-swatch': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-swatch': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-colorful@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-colorful@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-alpha': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-hue': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-saturation': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-alpha': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-hue': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-saturation': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-compact@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-compact@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-editable-input-rgba': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-swatch': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-editable-input-rgba': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-swatch': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-editable-input-hsla@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-editable-input-hsla@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-editable-input-rgba': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-editable-input-rgba': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-editable-input-rgba@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-editable-input-rgba@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-editable-input@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-editable-input@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 8.0.0 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-github@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-github@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-swatch': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-swatch': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-hue@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-hue@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-alpha': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-alpha': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-material@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-material@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-editable-input-rgba': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-editable-input-rgba': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-name@2.10.3(@babel/runtime@7.29.7)': + '@uiw/react-color-name@2.10.3(@babel/runtime@8.0.0)': dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 8.0.0 colors-named: 1.0.5 colors-named-hex: 1.0.4 - '@uiw/react-color-saturation@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-saturation@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-drag-event-interactive': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-drag-event-interactive': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-shade-slider@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-shade-slider@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-alpha': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-alpha': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-sketch@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-sketch@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-alpha': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-editable-input-rgba': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-hue': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-saturation': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-swatch': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-alpha': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-editable-input-rgba': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-hue': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-saturation': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-swatch': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-slider@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-slider@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-alpha': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-alpha': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-swatch@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-swatch@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color-wheel@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-color-wheel@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-drag-event-interactive': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-drag-event-interactive': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-color@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@babel/runtime': 7.29.7 - '@uiw/color-convert': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-alpha': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-block': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-chrome': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-circle': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-colorful': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-compact': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-editable-input-hsla': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-editable-input-rgba': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-github': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-hue': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-material': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-name': 2.10.3(@babel/runtime@7.29.7) - '@uiw/react-color-saturation': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-shade-slider': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-sketch': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-slider': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-swatch': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@uiw/react-color-wheel': 2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@babel/runtime': 8.0.0 + '@uiw/color-convert': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-alpha': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-block': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-chrome': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-circle': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-colorful': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-compact': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-editable-input': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-editable-input-hsla': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-editable-input-rgba': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-github': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-hue': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-material': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-name': 2.10.3(@babel/runtime@8.0.0) + '@uiw/react-color-saturation': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-shade-slider': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-sketch': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-slider': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-swatch': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@uiw/react-color-wheel': 2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@uiw/react-drag-event-interactive@2.10.3(@babel/runtime@7.29.7)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@uiw/react-drag-event-interactive@2.10.3(@babel/runtime@8.0.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@babel/runtime': 7.29.7 + '@babel/runtime': 8.0.0 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) @@ -12402,7 +11123,7 @@ snapshots: '@urql/core@6.0.3(graphql@16.14.2)': dependencies: - '@0no-co/graphql.web': 1.3.2(graphql@16.14.2) + '@0no-co/graphql.web': 1.3.3(graphql@16.14.2) wonka: 6.3.6 transitivePeerDependencies: - graphql @@ -12412,144 +11133,59 @@ snapshots: '@urql/core': 6.0.3(graphql@16.14.2) wonka: 6.3.6 - '@vitejs/plugin-react@6.0.4(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.1.5)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': - dependencies: - '@rolldown/pluginutils': 1.0.1 - vite: 8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - optionalDependencies: - '@rolldown/plugin-babel': 0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.1.5)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - babel-plugin-react-compiler: 1.0.0 - - '@vitejs/plugin-react@6.0.4(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': + '@vitejs/plugin-react@6.0.5(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@8.0.0)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + vite: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) optionalDependencies: - '@rolldown/plugin-babel': 0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.1.5)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + '@rolldown/plugin-babel': 0.2.3(@babel/core@7.29.7)(@babel/runtime@8.0.0)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) babel-plugin-react-compiler: 1.0.0 - '@vitest/browser-playwright@4.1.8(playwright@1.60.0)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.8)': - dependencies: - '@vitest/browser': 4.1.8(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.8) - '@vitest/mocker': 4.1.8(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - playwright: 1.60.0 - tinyrainbow: 3.1.0 - vitest: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.8)(@vitest/coverage-v8@4.1.8)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - transitivePeerDependencies: - - bufferutil - - msw - - utf-8-validate - - vite - - '@vitest/browser-playwright@4.1.9(playwright@1.60.0)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.9)': - dependencies: - '@vitest/browser': 4.1.9(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.9) - '@vitest/mocker': 4.1.9(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - playwright: 1.60.0 - tinyrainbow: 3.1.0 - vitest: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@25.9.5)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - transitivePeerDependencies: - - bufferutil - - msw - - utf-8-validate - - vite - - '@vitest/browser-playwright@4.1.9(playwright@1.60.0)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.9)': + '@vitest/browser-playwright@4.1.10(playwright@1.60.0)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10)': dependencies: - '@vitest/browser': 4.1.9(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.9) - '@vitest/mocker': 4.1.9(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + '@vitest/browser': 4.1.10(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10) + '@vitest/mocker': 4.1.10(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) playwright: 1.60.0 - tinyrainbow: 3.1.0 - vitest: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - transitivePeerDependencies: - - bufferutil - - msw - - utf-8-validate - - vite - - '@vitest/browser@4.1.8(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.8)': - dependencies: - '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.8(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - '@vitest/utils': 4.1.8 - magic-string: 0.30.21 - pngjs: 7.0.0 - sirv: 3.0.2 - tinyrainbow: 3.1.0 - vitest: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.8)(@vitest/coverage-v8@4.1.8)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - ws: 8.21.0 - transitivePeerDependencies: - - bufferutil - - msw - - utf-8-validate - - vite - - '@vitest/browser@4.1.9(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.9)': - dependencies: - '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.9(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - '@vitest/utils': 4.1.9 - magic-string: 0.30.21 - pngjs: 7.0.0 - sirv: 3.0.2 - tinyrainbow: 3.1.0 - vitest: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@25.9.5)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - ws: 8.21.0 + tinyrainbow: 3.1.1 + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.2.0)(@vitest/browser-playwright@4.1.10)(@vitest/coverage-v8@4.1.10)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.1.9(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.9)': + '@vitest/browser@4.1.10(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.9(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - '@vitest/utils': 4.1.9 + '@vitest/mocker': 4.1.10(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + '@vitest/utils': 4.1.10 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 - tinyrainbow: 3.1.0 - vitest: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - ws: 8.21.0 + tinyrainbow: 3.1.1 + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.2.0)(@vitest/browser-playwright@4.1.10)(@vitest/coverage-v8@4.1.10)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + ws: 8.21.3 transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/coverage-v8@4.1.8(@vitest/browser@4.1.8)(vitest@4.1.8)': + '@vitest/coverage-v8@4.1.10(@vitest/browser@4.1.10)(vitest@4.1.10)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.1.8 + '@vitest/utils': 4.1.10 ast-v8-to-istanbul: 1.0.5 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-reports: 3.2.0 - magicast: 0.5.3 + magicast: 0.5.4 obug: 2.1.4 std-env: 4.2.0 - tinyrainbow: 3.1.0 - vitest: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.8)(@vitest/coverage-v8@4.1.8)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - optionalDependencies: - '@vitest/browser': 4.1.8(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.8) - optional: true - - '@vitest/coverage-v8@4.1.9(@vitest/browser@4.1.9)(vitest@4.1.9)': - dependencies: - '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.1.9 - ast-v8-to-istanbul: 1.0.4 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-reports: 3.2.0 - magicast: 0.5.3 - obug: 2.1.3 - std-env: 4.1.0 - tinyrainbow: 3.1.0 - vitest: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + tinyrainbow: 3.1.1 + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.2.0)(@vitest/browser-playwright@4.1.10)(@vitest/coverage-v8@4.1.10)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) optionalDependencies: - '@vitest/browser': 4.1.9(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.9) + '@vitest/browser': 4.1.10(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10) '@vitest/expect@3.2.4': dependencies: @@ -12559,81 +11195,40 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/expect@4.1.8': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.8 - '@vitest/utils': 4.1.8 - chai: 6.2.2 - tinyrainbow: 3.1.0 - - '@vitest/expect@4.1.9': + '@vitest/expect@4.1.10': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.9 - '@vitest/utils': 4.1.9 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 chai: 6.2.2 - tinyrainbow: 3.1.0 - - '@vitest/mocker@4.1.8(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': - dependencies: - '@vitest/spy': 4.1.8 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - - '@vitest/mocker@4.1.9(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': - dependencies: - '@vitest/spy': 4.1.9 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.9(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': + '@vitest/mocker@4.1.10(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.1.9 + '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + vite: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 - '@vitest/pretty-format@4.1.8': - dependencies: - tinyrainbow: 3.1.0 - - '@vitest/pretty-format@4.1.9': - dependencies: - tinyrainbow: 3.1.0 - - '@vitest/runner@4.1.8': - dependencies: - '@vitest/utils': 4.1.8 - pathe: 2.0.3 - - '@vitest/runner@4.1.9': + '@vitest/pretty-format@4.1.10': dependencies: - '@vitest/utils': 4.1.9 - pathe: 2.0.3 + tinyrainbow: 3.1.1 - '@vitest/snapshot@4.1.8': + '@vitest/runner@4.1.10': dependencies: - '@vitest/pretty-format': 4.1.8 - '@vitest/utils': 4.1.8 - magic-string: 0.30.21 + '@vitest/utils': 4.1.10 pathe: 2.0.3 - '@vitest/snapshot@4.1.9': + '@vitest/snapshot@4.1.10': dependencies: - '@vitest/pretty-format': 4.1.9 - '@vitest/utils': 4.1.9 + '@vitest/pretty-format': 4.1.10 + '@vitest/utils': 4.1.10 magic-string: 0.30.21 pathe: 2.0.3 @@ -12641,9 +11236,7 @@ snapshots: dependencies: tinyspy: 4.0.4 - '@vitest/spy@4.1.8': {} - - '@vitest/spy@4.1.9': {} + '@vitest/spy@4.1.10': {} '@vitest/utils@3.2.4': dependencies: @@ -12651,17 +11244,11 @@ snapshots: loupe: 3.2.1 tinyrainbow: 2.0.0 - '@vitest/utils@4.1.8': - dependencies: - '@vitest/pretty-format': 4.1.8 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 - - '@vitest/utils@4.1.9': + '@vitest/utils@4.1.10': dependencies: - '@vitest/pretty-format': 4.1.9 + '@vitest/pretty-format': 4.1.10 convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 + tinyrainbow: 3.1.1 '@webcontainer/env@1.1.1': {} @@ -12686,68 +11273,20 @@ snapshots: dependencies: tslib: 2.8.1 - '@xyflow/react@12.10.2(@types/react@19.2.17)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@xyflow/system': 0.0.76 - classcat: 5.0.5 - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - zustand: 4.5.7(@types/react@19.2.17)(immer@11.1.4)(react@19.2.8) - transitivePeerDependencies: - - '@types/react' - - immer - - '@xyflow/react@12.11.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': - dependencies: - '@xyflow/system': 0.0.77 - classcat: 5.0.5 - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - zustand: 4.5.7(@types/react@19.2.17)(immer@11.1.4)(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - transitivePeerDependencies: - - immer - - '@xyflow/react@12.11.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@xyflow/react@12.11.2(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - '@xyflow/system': 0.0.78 + '@xyflow/system': 0.0.79 classcat: 5.0.5 react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - zustand: 4.5.7(@types/react@19.2.17)(immer@11.1.4)(react@19.2.8) - optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - transitivePeerDependencies: - - immer - - '@xyflow/system@0.0.76': - dependencies: - '@types/d3-drag': 3.0.7 - '@types/d3-interpolate': 3.0.4 - '@types/d3-selection': 3.0.11 - '@types/d3-transition': 3.0.9 - '@types/d3-zoom': 3.0.8 - d3-drag: 3.0.0 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-zoom: 3.0.0 - - '@xyflow/system@0.0.77': - dependencies: - '@types/d3-drag': 3.0.7 - '@types/d3-interpolate': 3.0.4 - '@types/d3-selection': 3.0.11 - '@types/d3-transition': 3.0.9 - '@types/d3-zoom': 3.0.8 - d3-drag: 3.0.0 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-zoom: 3.0.0 + react-dom: 19.2.8(react@19.2.8) + zustand: 4.5.7(@types/react@19.2.18)(immer@11.1.16)(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) + transitivePeerDependencies: + - immer - '@xyflow/system@0.0.78': + '@xyflow/system@0.0.79': dependencies: '@types/d3-drag': 3.0.7 '@types/d3-interpolate': 3.0.4 @@ -12759,13 +11298,15 @@ snapshots: d3-selection: 3.0.0 d3-zoom: 3.0.0 - acorn-jsx@5.3.2(acorn@8.17.0): + acorn-jsx@5.3.2(acorn@8.18.0): dependencies: - acorn: 8.17.0 + acorn: 8.18.0 - acorn@8.16.0: {} + acorn-walk@8.3.5: + dependencies: + acorn: 8.18.0 - acorn@8.17.0: {} + acorn@8.18.0: {} agent-base@7.1.4: {} @@ -12801,7 +11342,7 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.4 + fast-uri: 3.1.5 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -12828,6 +11369,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.2 + arg@4.1.3: {} + argparse@2.0.1: {} aria-hidden@1.2.6: @@ -12848,18 +11391,11 @@ snapshots: dependencies: tslib: 2.8.1 - ast-v8-to-istanbul@1.0.4: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - estree-walker: 3.0.3 - js-tokens: 10.0.0 - ast-v8-to-istanbul@1.0.5: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 js-tokens: 10.0.0 - optional: true astring@1.9.0: {} @@ -12872,7 +11408,7 @@ snapshots: babel-plugin-react-compiler@1.0.0: dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 bail@2.0.2: {} @@ -12892,11 +11428,11 @@ snapshots: dependencies: react: 19.2.8 - brace-expansion@2.1.0: + brace-expansion@2.1.4: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.5: + brace-expansion@5.0.9: dependencies: balanced-match: 4.0.4 @@ -12960,7 +11496,7 @@ snapshots: character-reference-invalid@2.0.1: {} - chardet@2.1.1: {} + chardet@2.2.0: {} charenc@0.0.2: {} @@ -12978,7 +11514,7 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chromatic@18.1.0: + chromatic@18.2.0: dependencies: semver: 7.8.5 @@ -12994,28 +11530,15 @@ snapshots: classnames@2.5.1: {} - cli-boxes@4.0.1: {} - - cli-cursor@4.0.0: - dependencies: - restore-cursor: 4.0.0 - cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 - cli-spinners@2.9.2: {} - cli-spinners@3.4.0: {} cli-truncate@5.2.0: dependencies: slice-ansi: 8.0.0 - string-width: 8.2.1 - - cli-truncate@6.1.1: - dependencies: - slice-ansi: 9.0.0 string-width: 8.2.2 cli-width@4.1.0: {} @@ -13032,40 +11555,36 @@ snapshots: clsx@2.1.1: {} - cm6-graphql@0.2.1(@codemirror/autocomplete@6.20.1)(@codemirror/language@6.12.4)(@codemirror/lint@6.9.5)(@codemirror/state@6.7.0)(@codemirror/view@6.43.4)(@lezer/highlight@1.2.3)(graphql@16.14.2): + cm6-graphql@0.2.1(@codemirror/autocomplete@6.20.3)(@codemirror/language@6.12.4)(@codemirror/lint@6.9.7)(@codemirror/state@6.7.1)(@codemirror/view@6.43.8)(@lezer/highlight@1.2.3)(graphql@16.14.2): dependencies: - '@codemirror/autocomplete': 6.20.1 + '@codemirror/autocomplete': 6.20.3 '@codemirror/language': 6.12.4 - '@codemirror/lint': 6.9.5 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.4 + '@codemirror/lint': 6.9.7 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.8 '@lezer/highlight': 1.2.3 graphql: 16.14.2 - graphql-language-service: 5.5.1(graphql@16.14.2) + graphql-language-service: 5.5.2(graphql@16.14.2) - cm6-theme-basic-light@0.2.0(@codemirror/language@6.12.4)(@codemirror/state@6.7.0)(@codemirror/view@6.43.4)(@lezer/highlight@1.2.3): + cm6-theme-basic-light@0.2.0(@codemirror/language@6.12.4)(@codemirror/state@6.7.1)(@codemirror/view@6.43.8)(@lezer/highlight@1.2.3): dependencies: '@codemirror/language': 6.12.4 - '@codemirror/state': 6.7.0 - '@codemirror/view': 6.43.4 + '@codemirror/state': 6.7.1 + '@codemirror/view': 6.43.8 '@lezer/highlight': 1.2.3 - cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + cmdk@1.1.1(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.17)(react@19.2.8) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-compose-refs': 1.1.5(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-dialog': 1.1.17(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@radix-ui/react-id': 1.1.4(@types/react@19.2.18)(react@19.2.8) + '@radix-ui/react-primitive': 2.1.10(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react: 19.2.8 react-dom: 19.2.8(react@19.2.8) transitivePeerDependencies: - '@types/react' - '@types/react-dom' - code-excerpt@4.0.0: - dependencies: - convert-to-spaces: 2.0.1 - color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -13108,8 +11627,6 @@ snapshots: convert-source-map@2.0.0: {} - convert-to-spaces@2.0.1: {} - cookie-es@3.1.1: {} core-js@3.49.0: {} @@ -13125,7 +11642,7 @@ snapshots: cosmiconfig@8.3.6(typescript@5.9.3): dependencies: import-fresh: 3.3.1 - js-yaml: 4.2.0 + js-yaml: 4.3.1 parse-json: 5.2.0 path-type: 4.0.0 optionalDependencies: @@ -13135,12 +11652,14 @@ snapshots: dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 - js-yaml: 4.2.0 + js-yaml: 4.3.1 parse-json: 5.2.0 optionalDependencies: typescript: 5.9.3 - crelt@1.0.6: {} + create-require@1.1.1: {} + + crelt@1.0.7: {} cross-inspect@1.0.1: dependencies: @@ -13398,12 +11917,12 @@ snapshots: dequal@2.0.3: {} - deslop-js@0.9.2: + deslop-js@0.9.11: dependencies: - '@oxc-project/types': 0.141.0 + '@oxc-project/types': 0.142.0 fast-glob: 3.3.3 - minimatch: 10.2.5 - oxc-parser: 0.141.0 + minimatch: 10.2.6 + oxc-parser: 0.142.0 oxc-resolver: 11.24.2 typescript: 5.9.3 @@ -13421,6 +11940,8 @@ snapshots: diff-sequences@27.5.1: {} + diff@4.0.4: {} + diff@5.2.2: {} dir-glob@3.0.1: @@ -13437,7 +11958,7 @@ snapshots: dom-accessibility-api@0.6.3: {} - dompurify@3.4.10: + dompurify@3.4.13: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -13477,43 +11998,10 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@2.1.0: {} - es-module-lexer@2.3.1: {} - es-toolkit@1.45.1: {} - es-toolkit@1.50.0: {} - esbuild@0.28.0: - optionalDependencies: - '@esbuild/aix-ppc64': 0.28.0 - '@esbuild/android-arm': 0.28.0 - '@esbuild/android-arm64': 0.28.0 - '@esbuild/android-x64': 0.28.0 - '@esbuild/darwin-arm64': 0.28.0 - '@esbuild/darwin-x64': 0.28.0 - '@esbuild/freebsd-arm64': 0.28.0 - '@esbuild/freebsd-x64': 0.28.0 - '@esbuild/linux-arm': 0.28.0 - '@esbuild/linux-arm64': 0.28.0 - '@esbuild/linux-ia32': 0.28.0 - '@esbuild/linux-loong64': 0.28.0 - '@esbuild/linux-mips64el': 0.28.0 - '@esbuild/linux-ppc64': 0.28.0 - '@esbuild/linux-riscv64': 0.28.0 - '@esbuild/linux-s390x': 0.28.0 - '@esbuild/linux-x64': 0.28.0 - '@esbuild/netbsd-arm64': 0.28.0 - '@esbuild/netbsd-x64': 0.28.0 - '@esbuild/openbsd-arm64': 0.28.0 - '@esbuild/openbsd-x64': 0.28.0 - '@esbuild/openharmony-arm64': 0.28.0 - '@esbuild/sunos-x64': 0.28.0 - '@esbuild/win32-arm64': 0.28.0 - '@esbuild/win32-ia32': 0.28.0 - '@esbuild/win32-x64': 0.28.0 - esbuild@0.28.1: optionalDependencies: '@esbuild/aix-ppc64': 0.28.1 @@ -13545,8 +12033,6 @@ snapshots: escalade@3.2.0: {} - escape-string-regexp@2.0.0: {} - escape-string-regexp@4.0.0: {} escape-string-regexp@5.0.0: {} @@ -13554,7 +12040,7 @@ snapshots: eslint-plugin-react-hooks@7.1.1(eslint@10.4.1(jiti@2.7.0)): dependencies: '@babel/core': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.29.8 eslint: 10.4.1(jiti@2.7.0) hermes-parser: 0.25.1 zod: 4.4.3 @@ -13602,7 +12088,7 @@ snapshots: imurmurhash: 0.1.4 is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 - minimatch: 10.2.5 + minimatch: 10.2.6 natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: @@ -13612,8 +12098,8 @@ snapshots: espree@11.2.0: dependencies: - acorn: 8.17.0 - acorn-jsx: 5.3.2(acorn@8.17.0) + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) eslint-visitor-keys: 5.0.1 esprima@4.0.1: {} @@ -13640,7 +12126,7 @@ snapshots: eventemitter3@5.0.4: {} - expect-type@1.3.0: {} + expect-type@1.4.0: {} extend@3.0.2: {} @@ -13666,7 +12152,7 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-uri@3.1.4: {} + fast-uri@3.1.5: {} fast-wrap-ansi@0.2.2: dependencies: @@ -13684,10 +12170,6 @@ snapshots: dependencies: walk-up-path: 4.0.0 - fdir@6.5.0(picomatch@4.0.4): - optionalDependencies: - picomatch: 4.0.4 - fdir@6.5.0(picomatch@4.0.5): optionalDependencies: picomatch: 4.0.5 @@ -13722,10 +12204,10 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.4.3 + flatted: 3.4.4 keyv: 4.5.4 - flatted@3.4.3: {} + flatted@3.4.4: {} format@0.2.2: {} @@ -13782,13 +12264,11 @@ snapshots: glob@13.0.6: dependencies: - minimatch: 10.2.5 + minimatch: 10.2.6 minipass: 7.1.3 path-scurry: 2.0.2 - globals@17.6.0: {} - - globals@17.8.0: {} + globals@17.10.0: {} globby@11.1.0: dependencies: @@ -13799,12 +12279,12 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 - gql.tada@1.11.2(graphql@16.14.2)(typescript@5.9.3): + gql.tada@1.11.3(graphql@16.14.2)(typescript@5.9.3): dependencies: - '@0no-co/graphql.web': 1.3.2(graphql@16.14.2) + '@0no-co/graphql.web': 1.3.3(graphql@16.14.2) '@0no-co/graphqlsp': 1.17.3(graphql@16.14.2)(typescript@5.9.3) - '@gql.tada/cli-utils': 1.9.2(@0no-co/graphqlsp@1.17.3(graphql@16.14.2)(typescript@5.9.3))(graphql@16.14.2)(typescript@5.9.3) - '@gql.tada/internal': 1.2.1(graphql@16.14.2)(typescript@5.9.3) + '@gql.tada/cli-utils': 1.9.3(@0no-co/graphqlsp@1.17.3(graphql@16.14.2)(typescript@5.9.3))(graphql@16.14.2)(typescript@5.9.3) + '@gql.tada/internal': 1.2.2(graphql@16.14.2)(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - '@gql.tada/svelte-support' @@ -13819,11 +12299,11 @@ snapshots: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - graphiql@5.2.4(@types/node@26.1.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)): + graphiql@5.2.4(@types/node@26.2.0)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)): dependencies: - '@graphiql/plugin-doc-explorer': 0.4.2(@graphiql/react@0.37.7(@types/node@26.1.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))(@types/react@19.2.17)(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) - '@graphiql/plugin-history': 0.4.2(@graphiql/react@0.37.7(@types/node@26.1.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))(@types/node@26.1.1)(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) - '@graphiql/react': 0.37.7(@types/node@26.1.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0))(graphql@16.14.2)(immer@11.1.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) + '@graphiql/plugin-doc-explorer': 0.4.2(@graphiql/react@0.37.7(@types/node@26.2.0)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))(@types/react@19.2.18)(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) + '@graphiql/plugin-history': 0.4.2(@graphiql/react@0.37.7(@types/node@26.2.0)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)))(@types/node@26.2.0)(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) + '@graphiql/react': 0.37.7(@types/node@26.2.0)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3))(graphql@16.14.2)(immer@11.1.16)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)) graphql: 16.14.2 react: 19.2.8 react-compiler-runtime: 19.1.0-rc.1(react@19.2.8) @@ -13841,18 +12321,18 @@ snapshots: dependencies: lodash: 4.18.1 - graphql-config@5.1.6(@types/node@26.1.1)(graphql@16.14.2)(typescript@5.9.3): + graphql-config@5.1.6(@types/node@26.2.0)(graphql@16.14.2)(typescript@5.9.3): dependencies: - '@graphql-tools/graphql-file-loader': 8.1.14(graphql@16.14.2) - '@graphql-tools/json-file-loader': 8.0.28(graphql@16.14.2) - '@graphql-tools/load': 8.1.10(graphql@16.14.2) - '@graphql-tools/merge': 9.1.9(graphql@16.14.2) - '@graphql-tools/url-loader': 9.1.2(@types/node@26.1.1)(graphql@16.14.2) - '@graphql-tools/utils': 11.1.0(graphql@16.14.2) + '@graphql-tools/graphql-file-loader': 8.1.18(graphql@16.14.2) + '@graphql-tools/json-file-loader': 8.0.32(graphql@16.14.2) + '@graphql-tools/load': 8.1.15(graphql@16.14.2) + '@graphql-tools/merge': 9.2.2(graphql@16.14.2) + '@graphql-tools/url-loader': 9.1.6(@types/node@26.2.0)(graphql@16.14.2) + '@graphql-tools/utils': 11.2.2(graphql@16.14.2) cosmiconfig: 8.3.6(typescript@5.9.3) graphql: 16.14.2 jiti: 2.7.0 - minimatch: 10.2.5 + minimatch: 10.2.6 string-env-interpolation: 1.0.1 tslib: 2.8.1 transitivePeerDependencies: @@ -13863,13 +12343,6 @@ snapshots: - typescript - utf-8-validate - graphql-language-service@5.5.1(graphql@16.14.2): - dependencies: - debounce-promise: 3.1.2 - graphql: 16.14.2 - nullthrows: 1.1.1 - vscode-languageserver-types: 3.17.5 - graphql-language-service@5.5.2(graphql@16.14.2): dependencies: debounce-promise: 3.1.2 @@ -13877,16 +12350,16 @@ snapshots: nullthrows: 1.1.1 vscode-languageserver-types: 3.18.0 - graphql-tag@2.12.6(graphql@16.14.2): + graphql-tag@2.12.7(graphql@16.14.2): dependencies: graphql: 16.14.2 tslib: 2.8.1 - graphql-ws@6.0.8(graphql@16.14.2)(ws@8.21.0): + graphql-ws@6.2.1(graphql@16.14.2)(ws@8.21.3): dependencies: graphql: 16.14.2 optionalDependencies: - ws: 8.21.0 + ws: 8.21.3 graphql@16.14.2: {} @@ -14009,7 +12482,7 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.2: + iconv-lite@0.7.3: dependencies: safer-buffer: 2.1.2 @@ -14021,11 +12494,9 @@ snapshots: ignore@7.0.6: {} - immer@10.2.0: {} + immer@11.1.16: {} - immer@11.1.4: {} - - immutable@5.1.5: {} + immutable@5.1.9: {} import-fresh@3.3.1: dependencies: @@ -14034,7 +12505,7 @@ snapshots: import-from@4.0.0: {} - import-in-the-middle@3.3.2: + import-in-the-middle@3.3.3: dependencies: cjs-module-lexer: 2.2.0 es-module-lexer: 2.3.1 @@ -14046,50 +12517,8 @@ snapshots: indent-string@4.0.0: {} - indent-string@5.0.0: {} - index-to-position@1.2.0: {} - ink-spinner@5.0.0(ink@7.1.1(@types/react@19.2.17)(react@19.2.5))(react@19.2.5): - dependencies: - cli-spinners: 2.9.2 - ink: 7.1.1(@types/react@19.2.17)(react@19.2.5) - react: 19.2.5 - - ink@7.1.1(@types/react@19.2.17)(react@19.2.5): - dependencies: - '@alcalzone/ansi-tokenize': 0.3.0 - ansi-escapes: 7.3.0 - ansi-styles: 6.2.3 - auto-bind: 5.0.1 - chalk: 5.6.2 - cli-boxes: 4.0.1 - cli-cursor: 4.0.0 - cli-truncate: 6.1.1 - code-excerpt: 4.0.0 - es-toolkit: 1.50.0 - indent-string: 5.0.0 - is-in-ci: 2.0.0 - patch-console: 2.0.0 - react: 19.2.5 - react-reconciler: 0.33.0(react@19.2.5) - scheduler: 0.27.0 - signal-exit: 3.0.7 - slice-ansi: 9.0.0 - stack-utils: 2.0.6 - string-width: 8.2.2 - terminal-size: 4.0.1 - type-fest: 5.8.0 - widest-line: 6.0.0 - wrap-ansi: 10.0.0 - ws: 8.21.1 - yoga-layout: 3.2.1 - optionalDependencies: - '@types/react': 19.2.17 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - inline-style-parser@0.2.7: {} internmap@1.0.1: {} @@ -14138,8 +12567,6 @@ snapshots: is-hexadecimal@2.0.1: {} - is-in-ci@2.0.0: {} - is-inside-container@1.0.0: dependencies: is-docker: 3.0.0 @@ -14176,13 +12603,13 @@ snapshots: isobject@3.0.1: {} - isomorphic-ws@5.0.0(ws@8.21.0): + isomorphic-ws@5.0.0(ws@8.21.3): dependencies: - ws: 8.21.0 + ws: 8.21.3 - isows@1.0.7(ws@8.21.0): + isows@1.0.7(ws@8.21.3): dependencies: - ws: 8.21.0 + ws: 8.21.3 istanbul-lib-coverage@3.2.2: {} @@ -14208,18 +12635,11 @@ snapshots: jiti@2.7.0: {} - jotai@2.20.0(@babel/core@7.29.7)(@babel/template@7.29.7)(@types/react@19.2.17)(react@19.2.8): - optionalDependencies: - '@babel/core': 7.29.7 - '@babel/template': 7.29.7 - '@types/react': 19.2.17 - react: 19.2.8 - - jotai@2.20.1(@babel/core@7.29.7)(@babel/template@7.29.7)(@types/react@19.2.17)(react@19.2.8): + jotai@2.20.2(@babel/core@7.29.7)(@babel/template@7.29.7)(@types/react@19.2.18)(react@19.2.8): optionalDependencies: '@babel/core': 7.29.7 '@babel/template': 7.29.7 - '@types/react': 19.2.17 + '@types/react': 19.2.18 react: 19.2.8 js-levenshtein@1.1.6: {} @@ -14228,11 +12648,7 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@4.1.1: - dependencies: - argparse: 2.0.1 - - js-yaml@4.2.0: + js-yaml@4.3.1: dependencies: argparse: 2.0.1 @@ -14273,19 +12689,19 @@ snapshots: kleur@3.0.3: {} - knip@6.23.0: + knip@6.27.0: dependencies: - fdir: 6.5.0(picomatch@4.0.4) + fdir: 6.5.0(picomatch@4.0.5) formatly: 0.3.0 get-tsconfig: 4.14.0 jiti: 2.7.0 oxc-parser: 0.137.0 oxc-resolver: 11.21.3 - picomatch: 4.0.4 - smol-toml: 1.6.1 + picomatch: 4.0.5 + smol-toml: 1.7.2 strip-json-comments: 5.0.3 tinyglobby: 0.2.17 - unbash: 4.0.1 + unbash: 4.0.10 yaml: 2.9.0 zod: 4.4.3 @@ -14301,36 +12717,69 @@ snapshots: lightningcss-android-arm64@1.32.0: optional: true + lightningcss-android-arm64@1.33.0: + optional: true + lightningcss-darwin-arm64@1.32.0: optional: true + lightningcss-darwin-arm64@1.33.0: + optional: true + lightningcss-darwin-x64@1.32.0: optional: true + lightningcss-darwin-x64@1.33.0: + optional: true + lightningcss-freebsd-x64@1.32.0: optional: true + lightningcss-freebsd-x64@1.33.0: + optional: true + lightningcss-linux-arm-gnueabihf@1.32.0: optional: true + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + lightningcss-linux-arm64-gnu@1.32.0: optional: true + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + lightningcss-linux-arm64-musl@1.32.0: optional: true + lightningcss-linux-arm64-musl@1.33.0: + optional: true + lightningcss-linux-x64-gnu@1.32.0: optional: true + lightningcss-linux-x64-gnu@1.33.0: + optional: true + lightningcss-linux-x64-musl@1.32.0: optional: true + lightningcss-linux-x64-musl@1.33.0: + optional: true + lightningcss-win32-arm64-msvc@1.32.0: optional: true + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + lightningcss-win32-x64-msvc@1.32.0: optional: true + lightningcss-win32-x64-msvc@1.33.0: + optional: true + lightningcss@1.32.0: dependencies: detect-libc: 2.1.2 @@ -14347,15 +12796,31 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + lines-and-columns@1.2.4: {} lines-and-columns@2.0.4: {} - linkify-it@5.0.1: + linkify-it@5.0.2: dependencies: uc.micro: 2.1.0 - listr2@10.2.1: + listr2@10.2.2: dependencies: cli-truncate: 5.2.0 eventemitter3: 5.0.4 @@ -14380,7 +12845,7 @@ snapshots: log-symbols@7.0.1: dependencies: is-unicode-supported: 2.1.0 - yoctocolors: 2.1.2 + yoctocolors: 2.2.0 log-update@6.1.0: dependencies: @@ -14413,7 +12878,7 @@ snapshots: dependencies: yallist: 3.1.1 - lucide-react@1.27.0(react@19.2.8): + lucide-react@1.31.0(react@19.2.8): dependencies: react: 19.2.8 @@ -14423,23 +12888,25 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - magicast@0.5.3: + magicast@0.5.4: dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 source-map-js: 1.2.1 make-dir@4.0.0: dependencies: semver: 7.8.5 + make-error@1.3.6: {} + map-cache@0.2.2: {} markdown-it@14.2.0: dependencies: argparse: 2.0.1 entities: 4.5.0 - linkify-it: 5.0.1 + linkify-it: 5.0.2 mdurl: 2.0.0 punycode.js: 2.3.1 uc.micro: 2.1.0 @@ -14616,15 +13083,15 @@ snapshots: dependencies: '@fortawesome/fontawesome-free': 6.7.2 katex: 0.16.47 - mermaid: 11.15.0 + mermaid: 11.16.1 optionalDependencies: playwright: 1.60.0 - mermaid@11.15.0: + mermaid@11.16.1: dependencies: '@braintree/sanitize-url': 7.1.2 '@iconify/utils': 3.1.3 - '@mermaid-js/parser': 1.1.1 + '@mermaid-js/parser': 1.2.0 '@types/d3': 7.4.3 '@upsetjs/venn.js': 2.0.0 cytoscape: 3.34.0 @@ -14634,19 +13101,19 @@ snapshots: d3-sankey: 0.12.3 dagre-d3-es: 7.0.14 dayjs: 1.11.21 - dompurify: 3.4.10 - es-toolkit: 1.45.1 + dompurify: 3.4.13 + es-toolkit: 1.50.0 katex: 0.16.47 khroma: 2.1.0 marked: 16.4.2 roughjs: 4.6.6 stylis: 4.4.0 - ts-dedent: 2.2.0 + ts-dedent: 2.3.0 uuid: 14.0.0 - meros@1.3.2(@types/node@26.1.1): + meros@1.3.2(@types/node@26.2.0): optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 micromark-core-commonmark@2.0.3: dependencies: @@ -14844,25 +13311,23 @@ snapshots: braces: 3.0.3 picomatch: 2.3.2 - mimic-fn@2.1.0: {} - mimic-function@5.0.1: {} min-indent@1.0.1: {} mini-svg-data-uri@1.4.4: {} - minimatch@10.2.5: + minimatch@10.2.6: dependencies: - brace-expansion: 5.0.5 + brace-expansion: 5.0.9 minimatch@5.1.9: dependencies: - brace-expansion: 2.1.0 + brace-expansion: 2.1.4 minimatch@9.0.9: dependencies: - brace-expansion: 2.1.0 + brace-expansion: 2.1.4 minimist@1.2.8: {} @@ -14875,7 +13340,7 @@ snapshots: monaco-graphql@1.8.0(graphql@16.14.2)(monaco-editor@0.52.2)(prettier@3.8.4): dependencies: graphql: 16.14.2 - graphql-language-service: 5.5.1(graphql@16.14.2) + graphql-language-service: 5.5.2(graphql@16.14.2) monaco-editor: 0.52.2 picomatch-browser: 2.2.6 prettier: 3.8.4 @@ -14892,7 +13357,7 @@ snapshots: mute-stream@3.0.0: {} - nanoid@3.3.16: {} + nanoid@3.3.18: {} natural-compare@1.4.0: {} @@ -14919,9 +13384,9 @@ snapshots: nullthrows@1.1.1: {} - nuqs@2.8.9(react-router@8.3.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8): + nuqs@2.9.5(react-router@8.3.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8): dependencies: - '@standard-schema/spec': 1.0.0 + '@standard-schema/spec': 1.1.0 react: 19.2.8 optionalDependencies: react-router: 8.3.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -14930,20 +13395,11 @@ snapshots: dependencies: citty: 0.2.2 pathe: 2.0.3 - tinyexec: 1.2.4 + tinyexec: 1.3.0 object-assign@4.1.1: {} - obug@2.1.1: {} - - obug@2.1.3: {} - - obug@2.1.4: - optional: true - - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 + obug@2.1.4: {} onetime@7.0.0: dependencies: @@ -14964,7 +13420,7 @@ snapshots: openapi-typescript@7.13.0(typescript@5.9.3): dependencies: - '@redocly/openapi-core': 1.34.12(supports-color@10.2.2) + '@redocly/openapi-core': 1.34.19(supports-color@10.2.2) ansi-colors: 4.1.3 change-case: 5.4.4 parse-json: 8.3.0 @@ -15042,30 +13498,30 @@ snapshots: '@oxc-parser/binding-win32-ia32-msvc': 0.137.0 '@oxc-parser/binding-win32-x64-msvc': 0.137.0 - oxc-parser@0.141.0: - dependencies: - '@oxc-project/types': 0.141.0 - optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.141.0 - '@oxc-parser/binding-android-arm64': 0.141.0 - '@oxc-parser/binding-darwin-arm64': 0.141.0 - '@oxc-parser/binding-darwin-x64': 0.141.0 - '@oxc-parser/binding-freebsd-x64': 0.141.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.141.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.141.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.141.0 - '@oxc-parser/binding-linux-arm64-musl': 0.141.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.141.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.141.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.141.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.141.0 - '@oxc-parser/binding-linux-x64-gnu': 0.141.0 - '@oxc-parser/binding-linux-x64-musl': 0.141.0 - '@oxc-parser/binding-openharmony-arm64': 0.141.0 - '@oxc-parser/binding-wasm32-wasi': 0.141.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.141.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.141.0 - '@oxc-parser/binding-win32-x64-msvc': 0.141.0 + oxc-parser@0.142.0: + dependencies: + '@oxc-project/types': 0.142.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.142.0 + '@oxc-parser/binding-android-arm64': 0.142.0 + '@oxc-parser/binding-darwin-arm64': 0.142.0 + '@oxc-parser/binding-darwin-x64': 0.142.0 + '@oxc-parser/binding-freebsd-x64': 0.142.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.142.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.142.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.142.0 + '@oxc-parser/binding-linux-arm64-musl': 0.142.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.142.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.142.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.142.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.142.0 + '@oxc-parser/binding-linux-x64-gnu': 0.142.0 + '@oxc-parser/binding-linux-x64-musl': 0.142.0 + '@oxc-parser/binding-openharmony-arm64': 0.142.0 + '@oxc-parser/binding-wasm32-wasi': 0.142.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.142.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.142.0 + '@oxc-parser/binding-win32-x64-msvc': 0.142.0 oxc-resolver@11.21.3: optionalDependencies: @@ -15111,127 +13567,82 @@ snapshots: '@oxc-resolver/binding-win32-arm64-msvc': 11.24.2 '@oxc-resolver/binding-win32-x64-msvc': 11.24.2 - oxfmt@0.55.0: - dependencies: - tinypool: 2.1.0 - optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.55.0 - '@oxfmt/binding-android-arm64': 0.55.0 - '@oxfmt/binding-darwin-arm64': 0.55.0 - '@oxfmt/binding-darwin-x64': 0.55.0 - '@oxfmt/binding-freebsd-x64': 0.55.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.55.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.55.0 - '@oxfmt/binding-linux-arm64-gnu': 0.55.0 - '@oxfmt/binding-linux-arm64-musl': 0.55.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.55.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.55.0 - '@oxfmt/binding-linux-riscv64-musl': 0.55.0 - '@oxfmt/binding-linux-s390x-gnu': 0.55.0 - '@oxfmt/binding-linux-x64-gnu': 0.55.0 - '@oxfmt/binding-linux-x64-musl': 0.55.0 - '@oxfmt/binding-openharmony-arm64': 0.55.0 - '@oxfmt/binding-win32-arm64-msvc': 0.55.0 - '@oxfmt/binding-win32-ia32-msvc': 0.55.0 - '@oxfmt/binding-win32-x64-msvc': 0.55.0 - - oxfmt@0.60.0: + oxfmt@0.63.0: dependencies: tinypool: 2.1.0 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.60.0 - '@oxfmt/binding-android-arm64': 0.60.0 - '@oxfmt/binding-darwin-arm64': 0.60.0 - '@oxfmt/binding-darwin-x64': 0.60.0 - '@oxfmt/binding-freebsd-x64': 0.60.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.60.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.60.0 - '@oxfmt/binding-linux-arm64-gnu': 0.60.0 - '@oxfmt/binding-linux-arm64-musl': 0.60.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.60.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.60.0 - '@oxfmt/binding-linux-riscv64-musl': 0.60.0 - '@oxfmt/binding-linux-s390x-gnu': 0.60.0 - '@oxfmt/binding-linux-x64-gnu': 0.60.0 - '@oxfmt/binding-linux-x64-musl': 0.60.0 - '@oxfmt/binding-openharmony-arm64': 0.60.0 - '@oxfmt/binding-win32-arm64-msvc': 0.60.0 - '@oxfmt/binding-win32-ia32-msvc': 0.60.0 - '@oxfmt/binding-win32-x64-msvc': 0.60.0 - - oxlint-plugin-react-doctor@0.9.2: + '@oxfmt/binding-android-arm-eabi': 0.63.0 + '@oxfmt/binding-android-arm64': 0.63.0 + '@oxfmt/binding-darwin-arm64': 0.63.0 + '@oxfmt/binding-darwin-x64': 0.63.0 + '@oxfmt/binding-freebsd-x64': 0.63.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.63.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.63.0 + '@oxfmt/binding-linux-arm64-gnu': 0.63.0 + '@oxfmt/binding-linux-arm64-musl': 0.63.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.63.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.63.0 + '@oxfmt/binding-linux-riscv64-musl': 0.63.0 + '@oxfmt/binding-linux-s390x-gnu': 0.63.0 + '@oxfmt/binding-linux-x64-gnu': 0.63.0 + '@oxfmt/binding-linux-x64-musl': 0.63.0 + '@oxfmt/binding-openharmony-arm64': 0.63.0 + '@oxfmt/binding-win32-arm64-msvc': 0.63.0 + '@oxfmt/binding-win32-ia32-msvc': 0.63.0 + '@oxfmt/binding-win32-x64-msvc': 0.63.0 + + oxlint-plugin-react-doctor@0.9.11: dependencies: '@shaderfrog/glsl-parser': 7.0.1 - '@typescript-eslint/types': 8.65.0 + '@typescript-eslint/types': 8.67.0 eslint-scope: 9.1.2 eslint-visitor-keys: 5.0.1 - oxc-parser: 0.141.0 - - oxlint@1.70.0: - optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.70.0 - '@oxlint/binding-android-arm64': 1.70.0 - '@oxlint/binding-darwin-arm64': 1.70.0 - '@oxlint/binding-darwin-x64': 1.70.0 - '@oxlint/binding-freebsd-x64': 1.70.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.70.0 - '@oxlint/binding-linux-arm-musleabihf': 1.70.0 - '@oxlint/binding-linux-arm64-gnu': 1.70.0 - '@oxlint/binding-linux-arm64-musl': 1.70.0 - '@oxlint/binding-linux-ppc64-gnu': 1.70.0 - '@oxlint/binding-linux-riscv64-gnu': 1.70.0 - '@oxlint/binding-linux-riscv64-musl': 1.70.0 - '@oxlint/binding-linux-s390x-gnu': 1.70.0 - '@oxlint/binding-linux-x64-gnu': 1.70.0 - '@oxlint/binding-linux-x64-musl': 1.70.0 - '@oxlint/binding-openharmony-arm64': 1.70.0 - '@oxlint/binding-win32-arm64-msvc': 1.70.0 - '@oxlint/binding-win32-ia32-msvc': 1.70.0 - '@oxlint/binding-win32-x64-msvc': 1.70.0 - - oxlint@1.74.0: - optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.74.0 - '@oxlint/binding-android-arm64': 1.74.0 - '@oxlint/binding-darwin-arm64': 1.74.0 - '@oxlint/binding-darwin-x64': 1.74.0 - '@oxlint/binding-freebsd-x64': 1.74.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.74.0 - '@oxlint/binding-linux-arm-musleabihf': 1.74.0 - '@oxlint/binding-linux-arm64-gnu': 1.74.0 - '@oxlint/binding-linux-arm64-musl': 1.74.0 - '@oxlint/binding-linux-ppc64-gnu': 1.74.0 - '@oxlint/binding-linux-riscv64-gnu': 1.74.0 - '@oxlint/binding-linux-riscv64-musl': 1.74.0 - '@oxlint/binding-linux-s390x-gnu': 1.74.0 - '@oxlint/binding-linux-x64-gnu': 1.74.0 - '@oxlint/binding-linux-x64-musl': 1.74.0 - '@oxlint/binding-openharmony-arm64': 1.74.0 - '@oxlint/binding-win32-arm64-msvc': 1.74.0 - '@oxlint/binding-win32-ia32-msvc': 1.74.0 - '@oxlint/binding-win32-x64-msvc': 1.74.0 - - oxlint@1.75.0: - optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.75.0 - '@oxlint/binding-android-arm64': 1.75.0 - '@oxlint/binding-darwin-arm64': 1.75.0 - '@oxlint/binding-darwin-x64': 1.75.0 - '@oxlint/binding-freebsd-x64': 1.75.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.75.0 - '@oxlint/binding-linux-arm-musleabihf': 1.75.0 - '@oxlint/binding-linux-arm64-gnu': 1.75.0 - '@oxlint/binding-linux-arm64-musl': 1.75.0 - '@oxlint/binding-linux-ppc64-gnu': 1.75.0 - '@oxlint/binding-linux-riscv64-gnu': 1.75.0 - '@oxlint/binding-linux-riscv64-musl': 1.75.0 - '@oxlint/binding-linux-s390x-gnu': 1.75.0 - '@oxlint/binding-linux-x64-gnu': 1.75.0 - '@oxlint/binding-linux-x64-musl': 1.75.0 - '@oxlint/binding-openharmony-arm64': 1.75.0 - '@oxlint/binding-win32-arm64-msvc': 1.75.0 - '@oxlint/binding-win32-ia32-msvc': 1.75.0 - '@oxlint/binding-win32-x64-msvc': 1.75.0 + lightningcss: 1.33.0 + oxc-parser: 0.142.0 + + oxlint@1.76.0: + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.76.0 + '@oxlint/binding-android-arm64': 1.76.0 + '@oxlint/binding-darwin-arm64': 1.76.0 + '@oxlint/binding-darwin-x64': 1.76.0 + '@oxlint/binding-freebsd-x64': 1.76.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.76.0 + '@oxlint/binding-linux-arm-musleabihf': 1.76.0 + '@oxlint/binding-linux-arm64-gnu': 1.76.0 + '@oxlint/binding-linux-arm64-musl': 1.76.0 + '@oxlint/binding-linux-ppc64-gnu': 1.76.0 + '@oxlint/binding-linux-riscv64-gnu': 1.76.0 + '@oxlint/binding-linux-riscv64-musl': 1.76.0 + '@oxlint/binding-linux-s390x-gnu': 1.76.0 + '@oxlint/binding-linux-x64-gnu': 1.76.0 + '@oxlint/binding-linux-x64-musl': 1.76.0 + '@oxlint/binding-openharmony-arm64': 1.76.0 + '@oxlint/binding-win32-arm64-msvc': 1.76.0 + '@oxlint/binding-win32-ia32-msvc': 1.76.0 + '@oxlint/binding-win32-x64-msvc': 1.76.0 + + oxlint@1.78.0: + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.78.0 + '@oxlint/binding-android-arm64': 1.78.0 + '@oxlint/binding-darwin-arm64': 1.78.0 + '@oxlint/binding-darwin-x64': 1.78.0 + '@oxlint/binding-freebsd-x64': 1.78.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.78.0 + '@oxlint/binding-linux-arm-musleabihf': 1.78.0 + '@oxlint/binding-linux-arm64-gnu': 1.78.0 + '@oxlint/binding-linux-arm64-musl': 1.78.0 + '@oxlint/binding-linux-ppc64-gnu': 1.78.0 + '@oxlint/binding-linux-riscv64-gnu': 1.78.0 + '@oxlint/binding-linux-riscv64-musl': 1.78.0 + '@oxlint/binding-linux-s390x-gnu': 1.78.0 + '@oxlint/binding-linux-x64-gnu': 1.78.0 + '@oxlint/binding-linux-x64-musl': 1.78.0 + '@oxlint/binding-openharmony-arm64': 1.78.0 + '@oxlint/binding-win32-arm64-msvc': 1.78.0 + '@oxlint/binding-win32-ia32-msvc': 1.78.0 + '@oxlint/binding-win32-x64-msvc': 1.78.0 p-limit@3.1.0: dependencies: @@ -15288,8 +13699,6 @@ snapshots: dependencies: entities: 6.0.1 - patch-console@2.0.0: {} - path-data-parser@0.1.0: {} path-exists@4.0.0: {} @@ -15323,8 +13732,6 @@ snapshots: picomatch@2.3.2: {} - picomatch@4.0.4: {} - picomatch@4.0.5: {} playwright-core@1.60.0: {} @@ -15346,9 +13753,9 @@ snapshots: path-data-parser: 0.1.0 points-on-curve: 0.2.0 - postcss@8.5.23: + postcss@8.5.26: dependencies: - nanoid: 3.3.16 + nanoid: 3.3.18 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -15356,8 +13763,6 @@ snapshots: prelude-ls@1.2.1: {} - prettier@3.8.3: {} - prettier@3.8.4: {} pretty-format@27.5.1: @@ -15387,34 +13792,24 @@ snapshots: queue-microtask@1.2.3: {} - react-aria-components@1.18.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8): - dependencies: - '@internationalized/date': 3.12.2 - '@react-types/shared': 3.35.0(react@19.2.8) - '@swc/helpers': 0.5.23 - client-only: 0.0.1 - react: 19.2.8 - react-aria: 3.49.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - react-dom: 19.2.8(react@19.2.8) - react-stately: 3.47.0(react@19.2.8) - - react-aria-components@1.19.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + react-aria-components@1.20.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: - '@internationalized/date': 3.12.2 - '@react-types/shared': 3.36.0(react@19.2.8) + '@internationalized/date': 3.12.3 + '@internationalized/string': 3.2.10 + '@react-types/shared': 3.36.1(react@19.2.8) '@swc/helpers': 0.5.23 client-only: 0.0.1 react: 19.2.8 - react-aria: 3.50.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + react-aria: 3.51.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) react-dom: 19.2.8(react@19.2.8) - react-stately: 3.48.0(react@19.2.8) + react-stately: 3.49.0(react@19.2.8) react-aria@3.48.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: - '@internationalized/date': 3.12.2 + '@internationalized/date': 3.12.3 '@internationalized/number': 3.6.7 - '@internationalized/string': 3.2.9 - '@react-types/shared': 3.35.0(react@19.2.8) + '@internationalized/string': 3.2.10 + '@react-types/shared': 3.36.1(react@19.2.8) '@swc/helpers': 0.5.23 aria-hidden: 1.2.6 clsx: 2.1.1 @@ -15423,32 +13818,18 @@ snapshots: react-stately: 3.46.0(react@19.2.8) use-sync-external-store: 1.6.0(react@19.2.8) - react-aria@3.49.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8): - dependencies: - '@internationalized/date': 3.12.2 - '@internationalized/number': 3.6.7 - '@internationalized/string': 3.2.9 - '@react-types/shared': 3.35.0(react@19.2.8) - '@swc/helpers': 0.5.23 - aria-hidden: 1.2.6 - clsx: 2.1.1 - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - react-stately: 3.47.0(react@19.2.8) - use-sync-external-store: 1.6.0(react@19.2.8) - - react-aria@3.50.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + react-aria@3.51.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: - '@internationalized/date': 3.12.2 + '@internationalized/date': 3.12.3 '@internationalized/number': 3.6.7 - '@internationalized/string': 3.2.9 - '@react-types/shared': 3.36.0(react@19.2.8) + '@internationalized/string': 3.2.10 + '@react-types/shared': 3.36.1(react@19.2.8) '@swc/helpers': 0.5.23 aria-hidden: 1.2.6 clsx: 2.1.1 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - react-stately: 3.48.0(react@19.2.8) + react-stately: 3.49.0(react@19.2.8) use-sync-external-store: 1.6.0(react@19.2.8) react-compiler-runtime@19.1.0-rc.1(react@19.2.8): @@ -15477,15 +13858,11 @@ snapshots: dependencies: typescript: 5.9.3 - react-docgen-typescript@2.4.0(typescript@6.0.3): - dependencies: - typescript: 6.0.3 - react-docgen@8.0.3: dependencies: '@babel/core': 7.29.7 - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.28.0 '@types/doctrine': 0.0.9 @@ -15496,40 +13873,35 @@ snapshots: transitivePeerDependencies: - supports-color - react-doctor@0.9.2(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@types/react@19.2.17)(eslint@10.4.1(jiti@2.7.0)): + react-doctor@0.9.11(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(eslint@10.4.1(jiti@2.7.0)): dependencies: + '@astrojs/compiler': 4.0.0 '@babel/code-frame': 7.29.7 - '@sentry/node': 10.68.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1)) + '@sentry/node': 10.70.0(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1)) agent-install: 0.0.5 conf: 15.1.0 confbox: 0.2.4 - deslop-js: 0.9.2 + deslop-js: 0.9.11 eslint-plugin-react-hooks: 7.1.1(eslint@10.4.1(jiti@2.7.0)) figures: 6.1.0 - ink: 7.1.1(@types/react@19.2.17)(react@19.2.5) - ink-spinner: 5.0.0(ink@7.1.1(@types/react@19.2.17)(react@19.2.5))(react@19.2.5) jiti: 2.7.0 - magicast: 0.5.3 + magicast: 0.5.4 oxc-resolver: 11.24.2 - oxlint: 1.74.0 - oxlint-plugin-react-doctor: 0.9.2 + oxlint: 1.76.0 + oxlint-plugin-react-doctor: 0.9.11 prompts: 2.4.2 - react: 19.2.5 typescript: 5.9.3 vscode-languageserver: 9.0.1 vscode-languageserver-textdocument: 1.0.12 vscode-uri: 3.1.0 yaml: 2.9.0 + yoga-layout: 3.2.1 transitivePeerDependencies: - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' - - '@types/react' - - bufferutil - eslint - oxlint-tsgolint - - react-devtools-core - supports-color - - utf-8-validate - vite-plus react-dom@19.2.8(react@19.2.8): @@ -15556,11 +13928,11 @@ snapshots: react-is@17.0.2: {} - react-markdown@10.1.0(@types/react@19.2.17)(react@19.2.8): + react-markdown@10.1.0(@types/react@19.2.18)(react@19.2.8): dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@types/react': 19.2.17 + '@types/react': 19.2.18 devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 @@ -15579,38 +13951,33 @@ snapshots: prop-types: 15.8.1 react: 19.2.8 - react-reconciler@0.33.0(react@19.2.5): - dependencies: - react: 19.2.5 - scheduler: 0.27.0 - - react-redux@9.2.0(@types/react@19.2.17)(react@19.2.8)(redux@5.0.1): + react-redux@9.3.0(@types/react@19.2.18)(react@19.2.8)(redux@5.0.1): dependencies: '@types/use-sync-external-store': 0.0.6 react: 19.2.8 use-sync-external-store: 1.6.0(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 redux: 5.0.1 - react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.8): + react-remove-scroll-bar@2.3.8(@types/react@19.2.18)(react@19.2.8): dependencies: react: 19.2.8 - react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.8) + react-style-singleton: 2.2.3(@types/react@19.2.18)(react@19.2.8) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.8): + react-remove-scroll@2.7.2(@types/react@19.2.18)(react@19.2.8): dependencies: react: 19.2.8 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.8) - react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.8) + react-remove-scroll-bar: 2.3.8(@types/react@19.2.18)(react@19.2.8) + react-style-singleton: 2.2.3(@types/react@19.2.18)(react@19.2.8) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.8) - use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.8) + use-callback-ref: 1.3.3(@types/react@19.2.18)(react@19.2.8) + use-sidecar: 1.1.3(@types/react@19.2.18)(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 react-resizable-panels@4.12.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: @@ -15624,10 +13991,10 @@ snapshots: optionalDependencies: react-dom: 19.2.8(react@19.2.8) - react-scan@0.5.7(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@types/react@19.2.17)(esbuild@0.28.1)(eslint@10.4.1(jiti@2.7.0))(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + react-scan@0.5.7(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(esbuild@0.28.1)(eslint@10.4.1(jiti@2.7.0))(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: '@babel/core': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.8 '@preact/signals': 2.9.1(preact@10.29.2) '@rollup/pluginutils': 5.4.0 bippy: 0.5.41(react@19.2.8) @@ -15636,7 +14003,7 @@ snapshots: preact: 10.29.2 prompts: 2.4.2 react: 19.2.8 - react-doctor: 0.9.2(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(@types/react@19.2.17)(eslint@10.4.1(jiti@2.7.0)) + react-doctor: 0.9.11(@opentelemetry/core@2.10.0(@opentelemetry/api@1.9.1))(eslint@10.4.1(jiti@2.7.0)) react-dom: 19.2.8(react@19.2.8) react-grab: 0.1.50(react@19.2.8) optionalDependencies: @@ -15645,14 +14012,10 @@ snapshots: transitivePeerDependencies: - '@opentelemetry/core' - '@opentelemetry/exporter-trace-otlp-http' - - '@types/react' - - bufferutil - eslint - oxlint-tsgolint - - react-devtools-core - rollup - supports-color - - utf-8-validate - vite-plus react-simple-code-editor@0.14.1(react-dom@19.2.8(react@19.2.8))(react@19.2.8): @@ -15662,45 +14025,35 @@ snapshots: react-stately@3.46.0(react@19.2.8): dependencies: - '@internationalized/date': 3.12.2 - '@internationalized/number': 3.6.7 - '@internationalized/string': 3.2.9 - '@react-types/shared': 3.35.0(react@19.2.8) - '@swc/helpers': 0.5.23 - react: 19.2.8 - use-sync-external-store: 1.6.0(react@19.2.8) - - react-stately@3.47.0(react@19.2.8): - dependencies: - '@internationalized/date': 3.12.2 + '@internationalized/date': 3.12.3 '@internationalized/number': 3.6.7 - '@internationalized/string': 3.2.9 - '@react-types/shared': 3.35.0(react@19.2.8) + '@internationalized/string': 3.2.10 + '@react-types/shared': 3.36.1(react@19.2.8) '@swc/helpers': 0.5.23 react: 19.2.8 use-sync-external-store: 1.6.0(react@19.2.8) - react-stately@3.48.0(react@19.2.8): + react-stately@3.49.0(react@19.2.8): dependencies: - '@internationalized/date': 3.12.2 + '@internationalized/date': 3.12.3 '@internationalized/number': 3.6.7 - '@internationalized/string': 3.2.9 - '@react-types/shared': 3.36.0(react@19.2.8) + '@internationalized/string': 3.2.10 + '@react-types/shared': 3.36.1(react@19.2.8) '@swc/helpers': 0.5.23 react: 19.2.8 use-sync-external-store: 1.6.0(react@19.2.8) - react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.8): + react-style-singleton@2.2.3(@types/react@19.2.18)(react@19.2.8): dependencies: get-nonce: 1.0.1 react: 19.2.8 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 react-syntax-highlighter@16.1.1(react@19.2.8): dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 highlight.js: 10.7.3 highlightjs-vue: 1.0.0 lowlight: 1.20.0 @@ -15714,27 +14067,17 @@ snapshots: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - react-zoom-pan-pinch@4.0.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + react-zoom-pan-pinch@4.0.4(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - react@19.2.5: {} - react@19.2.8: {} readdirp@3.6.0: dependencies: picomatch: 2.3.2 - recast@0.23.11: - dependencies: - ast-types: 0.16.1 - esprima: 4.0.1 - source-map: 0.6.1 - tiny-invariant: 1.3.3 - tslib: 2.8.1 - recast@0.23.12: dependencies: ast-types: 0.16.1 @@ -15743,18 +14086,18 @@ snapshots: tiny-invariant: 1.3.3 tslib: 2.8.1 - recharts@3.9.0(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react-is@17.0.2)(react@19.2.8)(redux@5.0.1): + recharts@3.10.1(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react-is@17.0.2)(react@19.2.8)(redux@5.0.1): dependencies: - '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.2.17)(react@19.2.8)(redux@5.0.1))(react@19.2.8) + '@reduxjs/toolkit': 2.12.0(react-redux@9.3.0(@types/react@19.2.18)(react@19.2.8)(redux@5.0.1))(react@19.2.8) clsx: 2.1.1 decimal.js-light: 2.5.1 - es-toolkit: 1.45.1 + es-toolkit: 1.50.0 eventemitter3: 5.0.4 - immer: 10.2.0 + immer: 11.1.16 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) react-is: 17.0.2 - react-redux: 9.2.0(@types/react@19.2.17)(react@19.2.8)(redux@5.0.1) + react-redux: 9.3.0(@types/react@19.2.18)(react@19.2.8)(redux@5.0.1) reselect: 5.2.0 tiny-invariant: 1.3.3 use-sync-external-store: 1.6.0(react@19.2.8) @@ -15867,11 +14210,6 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - restore-cursor@4.0.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - restore-cursor@5.1.0: dependencies: onetime: 7.0.0 @@ -15883,26 +14221,25 @@ snapshots: robust-predicates@3.0.3: {} - rolldown@1.1.5: + rolldown@1.2.3: dependencies: - '@oxc-project/types': 0.139.0 + '@oxc-project/types': 0.143.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.1.5 - '@rolldown/binding-darwin-arm64': 1.1.5 - '@rolldown/binding-darwin-x64': 1.1.5 - '@rolldown/binding-freebsd-x64': 1.1.5 - '@rolldown/binding-linux-arm-gnueabihf': 1.1.5 - '@rolldown/binding-linux-arm64-gnu': 1.1.5 - '@rolldown/binding-linux-arm64-musl': 1.1.5 - '@rolldown/binding-linux-ppc64-gnu': 1.1.5 - '@rolldown/binding-linux-s390x-gnu': 1.1.5 - '@rolldown/binding-linux-x64-gnu': 1.1.5 - '@rolldown/binding-linux-x64-musl': 1.1.5 - '@rolldown/binding-openharmony-arm64': 1.1.5 - '@rolldown/binding-wasm32-wasi': 1.1.5 - '@rolldown/binding-win32-arm64-msvc': 1.1.5 - '@rolldown/binding-win32-x64-msvc': 1.1.5 + '@rolldown/binding-android-arm64': 1.2.3 + '@rolldown/binding-darwin-arm64': 1.2.3 + '@rolldown/binding-darwin-x64': 1.2.3 + '@rolldown/binding-freebsd-x64': 1.2.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.3 + '@rolldown/binding-linux-arm64-gnu': 1.2.3 + '@rolldown/binding-linux-arm64-musl': 1.2.3 + '@rolldown/binding-linux-ppc64-gnu': 1.2.3 + '@rolldown/binding-linux-s390x-gnu': 1.2.3 + '@rolldown/binding-linux-x64-gnu': 1.2.3 + '@rolldown/binding-linux-x64-musl': 1.2.3 + '@rolldown/binding-openharmony-arm64': 1.2.3 + '@rolldown/binding-win32-arm64-msvc': 1.2.3 + '@rolldown/binding-win32-x64-msvc': 1.2.3 roughjs@4.6.6: dependencies: @@ -15927,8 +14264,6 @@ snapshots: semver@6.3.1: {} - semver@7.8.4: {} - semver@7.8.5: {} set-value@4.1.0: @@ -15949,12 +14284,10 @@ snapshots: shebang-regex@3.0.0: {} - shell-quote@1.8.4: {} + shell-quote@1.10.0: {} siginfo@2.0.0: {} - signal-exit@3.0.7: {} - signal-exit@4.1.0: {} simple-git@3.36.0: @@ -15987,12 +14320,7 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 - slice-ansi@9.0.0: - dependencies: - ansi-styles: 6.2.3 - is-fullwidth-code-point: 5.1.0 - - smol-toml@1.6.1: {} + smol-toml@1.7.2: {} snake-case@3.0.4: dependencies: @@ -16007,47 +14335,13 @@ snapshots: sponge-case@2.0.3: {} - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 - stackback@0.0.2: {} - std-env@4.1.0: {} - - std-env@4.2.0: - optional: true + std-env@4.2.0: {} stdin-discarder@0.3.2: {} - storybook@10.4.6(@testing-library/dom@10.4.1)(@types/react@19.2.17)(prettier@3.8.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): - dependencies: - '@storybook/global': 5.0.0 - '@storybook/icons': 2.0.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - '@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/spy': 3.2.4 - '@webcontainer/env': 1.1.1 - esbuild: 0.28.0 - open: 10.2.0 - oxc-parser: 0.127.0 - oxc-resolver: 11.21.3 - recast: 0.23.11 - semver: 7.8.4 - use-sync-external-store: 1.6.0(react@19.2.8) - ws: 8.21.0 - optionalDependencies: - '@types/react': 19.2.17 - prettier: 3.8.4 - transitivePeerDependencies: - - '@testing-library/dom' - - bufferutil - - react - - react-dom - - utf-8-validate - - storybook@10.5.5(@types/react@19.2.17)(prettier@3.8.4)(react@19.2.8): + storybook@10.5.7(@types/react@19.2.18)(prettier@3.8.4)(react@19.2.8): dependencies: '@storybook/global': 5.0.0 '@storybook/icons': 2.1.0(react@19.2.8) @@ -16065,9 +14359,9 @@ snapshots: recast: 0.23.12 semver: 7.8.5 use-sync-external-store: 1.6.0(react@19.2.8) - ws: 8.21.1 + ws: 8.21.3 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 prettier: 3.8.4 transitivePeerDependencies: - bufferutil @@ -16082,11 +14376,6 @@ snapshots: get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 - string-width@8.2.1: - dependencies: - get-east-asian-width: 1.6.0 - strip-ansi: 7.2.0 - string-width@8.2.2: dependencies: get-east-asian-width: 1.6.0 @@ -16153,13 +14442,7 @@ snapshots: tailwind-merge@3.6.0: {} - tailwind-variants@3.2.2(tailwind-merge@3.6.0)(tailwindcss@4.3.3): - dependencies: - tailwindcss: 4.3.3 - optionalDependencies: - tailwind-merge: 3.6.0 - - tailwind-variants@3.3.0(tailwind-merge@3.6.0)(tailwindcss@4.3.3): + tailwind-variants@3.3.1(tailwind-merge@3.6.0)(tailwindcss@4.3.3): optionalDependencies: tailwind-merge: 3.6.0 tailwindcss: 4.3.3 @@ -16172,15 +14455,13 @@ snapshots: tapable@2.3.3: {} - terminal-size@4.0.1: {} - timeout-signal@2.0.0: {} tiny-invariant@1.3.3: {} tinybench@2.9.0: {} - tinyexec@1.2.4: {} + tinyexec@1.3.0: {} tinyglobby@0.2.17: dependencies: @@ -16191,7 +14472,7 @@ snapshots: tinyrainbow@2.0.0: {} - tinyrainbow@3.1.0: {} + tinyrainbow@3.1.1: {} tinyspy@4.0.4: {} @@ -16209,12 +14490,28 @@ snapshots: trough@2.2.0: {} - ts-dedent@2.2.0: {} - ts-dedent@2.3.0: {} ts-log@3.0.2: {} + ts-node@10.9.2(@types/node@26.2.0)(typescript@5.9.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.12 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 26.2.0 + acorn: 8.18.0 + acorn-walk: 8.3.5 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.4 + make-error: 1.3.6 + typescript: 5.9.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + tsconfig-paths@4.2.0: dependencies: json5: 2.2.3 @@ -16250,7 +14547,7 @@ snapshots: uint8array-extras@1.5.0: {} - ultracite@7.8.3(oxfmt@0.60.0)(oxlint@1.75.0): + ultracite@7.8.3(oxfmt@0.63.0)(oxlint@1.78.0): dependencies: '@clack/prompts': 1.5.1 commander: 15.0.0 @@ -16262,15 +14559,13 @@ snapshots: yaml: 2.9.0 zod: 4.4.3 optionalDependencies: - oxfmt: 0.60.0 - oxlint: 1.75.0 + oxfmt: 0.63.0 + oxlint: 1.78.0 - unbash@4.0.1: {} + unbash@4.0.10: {} unc-path-regex@0.1.2: {} - undici-types@7.24.6: {} - undici-types@8.3.0: {} unicorn-magic@0.1.0: {} @@ -16329,14 +14624,14 @@ snapshots: unplugin@2.3.11: dependencies: '@jridgewell/remapping': 2.3.5 - acorn: 8.16.0 + acorn: 8.18.0 picomatch: 4.0.5 webpack-virtual-modules: 0.6.2 unplugin@3.0.0: dependencies: '@jridgewell/remapping': 2.3.5 - picomatch: 4.0.4 + picomatch: 4.0.5 webpack-virtual-modules: 0.6.2 optional: true @@ -16354,20 +14649,20 @@ snapshots: urlpattern-polyfill@10.1.0: {} - use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.8): + use-callback-ref@1.3.3(@types/react@19.2.18)(react@19.2.8): dependencies: react: 19.2.8 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 - use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.8): + use-sidecar@1.1.3(@types/react@19.2.18)(react@19.2.8): dependencies: detect-node-es: 1.1.0 react: 19.2.8 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.17 + '@types/react': 19.2.18 use-sync-external-store@1.6.0(react@19.2.8): dependencies: @@ -16375,6 +14670,8 @@ snapshots: uuid@14.0.0: {} + v8-compile-cache-lib@3.0.1: {} + vfile-location@5.0.3: dependencies: '@types/unist': 3.0.3 @@ -16407,156 +14704,72 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite-plugin-monaco-editor-esm@2.0.2(monaco-editor@0.52.2): + vite-plugin-monaco-editor-esm@2.0.3(monaco-editor@0.52.2): dependencies: monaco-editor: 0.52.2 - vite-plugin-svgr@5.2.0(typescript@5.9.3)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)): + vite-plugin-svgr@5.2.0(typescript@5.9.3)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)): dependencies: '@rollup/pluginutils': 5.4.0 '@svgr/core': 8.1.0(typescript@5.9.3) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3)) - vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + vite: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) transitivePeerDependencies: - rollup - supports-color - typescript - vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0): - dependencies: - lightningcss: 1.32.0 - picomatch: 4.0.5 - postcss: 8.5.23 - rolldown: 1.1.5 - tinyglobby: 0.2.17 - optionalDependencies: - '@types/node': 25.9.5 - esbuild: 0.28.1 - fsevents: 2.3.3 - jiti: 2.7.0 - tsx: 4.22.4 - yaml: 2.9.0 - - vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0): + vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0): dependencies: - lightningcss: 1.32.0 + lightningcss: 1.33.0 picomatch: 4.0.5 - postcss: 8.5.23 - rolldown: 1.1.5 + postcss: 8.5.26 + rolldown: 1.2.3 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 26.1.1 + '@types/node': 26.2.0 esbuild: 0.28.1 fsevents: 2.3.3 jiti: 2.7.0 tsx: 4.22.4 yaml: 2.9.0 - vitest-browser-react@2.2.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.8): + vitest-browser-react@2.2.0(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10): dependencies: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - vitest: 4.1.8(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.8)(@vitest/coverage-v8@4.1.8)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.2.0)(@vitest/browser-playwright@4.1.10)(@vitest/coverage-v8@4.1.10)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - vitest-browser-react@2.2.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.9): + vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@26.2.0)(@vitest/browser-playwright@4.1.10)(@vitest/coverage-v8@4.1.10)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)): dependencies: - react: 19.2.8 - react-dom: 19.2.8(react@19.2.8) - vitest: 4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - optionalDependencies: - '@types/react': 19.2.17 - '@types/react-dom': 19.2.3(@types/react@19.2.17) - - vitest@4.1.8(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.8)(@vitest/coverage-v8@4.1.8)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)): - dependencies: - '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.8 - '@vitest/runner': 4.1.8 - '@vitest/snapshot': 4.1.8 - '@vitest/spy': 4.1.8 - '@vitest/utils': 4.1.8 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.2.4 - tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.1 - '@types/node': 26.1.1 - '@vitest/browser-playwright': 4.1.8(playwright@1.60.0)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.8) - '@vitest/coverage-v8': 4.1.8(@vitest/browser@4.1.8)(vitest@4.1.8) - transitivePeerDependencies: - - msw - - vitest@4.1.9(@opentelemetry/api@1.9.1)(@types/node@25.9.5)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)): - dependencies: - '@vitest/expect': 4.1.9 - '@vitest/mocker': 4.1.9(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.9 - '@vitest/runner': 4.1.9 - '@vitest/snapshot': 4.1.9 - '@vitest/spy': 4.1.9 - '@vitest/utils': 4.1.9 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.3 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.2.4 - tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.1 - '@types/node': 25.9.5 - '@vitest/browser-playwright': 4.1.9(playwright@1.60.0)(vite@8.1.5(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.9) - '@vitest/coverage-v8': 4.1.9(@vitest/browser@4.1.9)(vitest@4.1.9) - transitivePeerDependencies: - - msw - - vitest@4.1.9(@opentelemetry/api@1.9.1)(@types/node@26.1.1)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)): - dependencies: - '@vitest/expect': 4.1.9 - '@vitest/mocker': 4.1.9(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.9 - '@vitest/runner': 4.1.9 - '@vitest/snapshot': 4.1.9 - '@vitest/spy': 4.1.9 - '@vitest/utils': 4.1.9 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 + '@vitest/expect': 4.1.10 + '@vitest/mocker': 4.1.10(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.10 + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + es-module-lexer: 2.3.1 + expect-type: 1.4.0 magic-string: 0.30.21 - obug: 2.1.3 + obug: 2.1.4 pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 + picomatch: 4.0.5 + std-env: 4.2.0 tinybench: 2.9.0 - tinyexec: 1.2.4 + tinyexec: 1.3.0 tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) + tinyrainbow: 3.1.1 + vite: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 26.1.1 - '@vitest/browser-playwright': 4.1.9(playwright@1.60.0)(vite@8.1.5(@types/node@26.1.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.9) - '@vitest/coverage-v8': 4.1.9(@vitest/browser@4.1.9)(vitest@4.1.9) + '@types/node': 26.2.0 + '@vitest/browser-playwright': 4.1.10(playwright@1.60.0)(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10) + '@vitest/coverage-v8': 4.1.10(@vitest/browser@4.1.10)(vitest@4.1.10) transitivePeerDependencies: - msw @@ -16606,10 +14819,6 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 - widest-line@6.0.0: - dependencies: - string-width: 8.2.2 - wonka@6.3.6: {} word-wrap@1.2.5: {} @@ -16617,7 +14826,7 @@ snapshots: wrap-ansi@10.0.0: dependencies: ansi-styles: 6.2.3 - string-width: 8.2.1 + string-width: 8.2.2 strip-ansi: 7.2.0 wrap-ansi@9.0.2: @@ -16626,9 +14835,7 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.2.0 - ws@8.21.0: {} - - ws@8.21.1: {} + ws@8.21.3: {} wsl-utils@0.1.0: dependencies: @@ -16646,20 +14853,22 @@ snapshots: yargs-parser@22.0.0: {} - yargs@18.0.0: + yargs@18.1.0: dependencies: cliui: 9.0.1 escalade: 3.2.0 get-caller-file: 2.0.5 - string-width: 7.2.0 + string-width: 8.2.2 y18n: 5.0.8 yargs-parser: 22.0.0 + yn@3.1.1: {} + yocto-queue@0.1.0: {} yocto-queue@1.2.2: {} - yoctocolors@2.1.2: {} + yoctocolors@2.2.0: {} yoga-layout@3.2.1: {} @@ -16669,18 +14878,18 @@ snapshots: zod@4.4.3: {} - zustand@4.5.7(@types/react@19.2.17)(immer@11.1.4)(react@19.2.8): + zustand@4.5.7(@types/react@19.2.18)(immer@11.1.16)(react@19.2.8): dependencies: use-sync-external-store: 1.6.0(react@19.2.8) optionalDependencies: - '@types/react': 19.2.17 - immer: 11.1.4 + '@types/react': 19.2.18 + immer: 11.1.16 react: 19.2.8 - zustand@5.0.14(@types/react@19.2.17)(immer@11.1.4)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)): + zustand@5.0.14(@types/react@19.2.18)(immer@11.1.16)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)): optionalDependencies: - '@types/react': 19.2.17 - immer: 11.1.4 + '@types/react': 19.2.18 + immer: 11.1.16 react: 19.2.8 use-sync-external-store: 1.6.0(react@19.2.8) diff --git a/frontend/pnpm-workspace.yaml b/frontend/pnpm-workspace.yaml index 716e28651db..500e330b7e4 100644 --- a/frontend/pnpm-workspace.yaml +++ b/frontend/pnpm-workspace.yaml @@ -14,19 +14,19 @@ allowBuilds: # with `"": "catalog:"`. Add a dep here only if it's used by more than one workspace member. catalog: "@tailwindcss/vite": ^4.3.3 - "@types/node": ^26.1.1 - "@types/react": ^19.2.17 - "@types/react-dom": ^19.2.3 - "@vitejs/plugin-react": ^6.0.4 + "@types/node": ^26.2.0 + "@types/react": ^19.2.18 + "@types/react-dom": ^19.2.4 + "@vitejs/plugin-react": ^6.0.5 babel-plugin-react-compiler: ^1.0.0 - lucide-react: ^1.27.0 + lucide-react: ^1.31.0 react: ^19.2.8 - react-aria-components: ^1.19.0 + react-aria-components: ^1.20.0 react-dom: ^19.2.8 tailwind-merge: ^3.6.0 tailwindcss: ^4.3.3 typescript: ^5.9.3 - vite: ^8.1.5 + vite: ^8.2.1 overrides: playwright: 1.60.0 From bc7a578cfa062908c16ef0ab1d79ac0070c048fa Mon Sep 17 00:00:00 2001 From: Yvonne Date: Wed, 12 Aug 2026 12:10:34 -0400 Subject: [PATCH 39/48] docs: link marketplace page to marketplace.infrahub.app (#10222) The Marketplace docs page only linked to the live marketplace once, inside the fetch instructions. Link it from the intro, point the find-schema step at the browse catalog, link the example identifiers to their detail pages, and list the marketplace under related resources. Co-authored-by: Yvonne Jouffrault Co-authored-by: Claude Fable 5 --- docs/docs/schema/marketplace/index.mdx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/docs/schema/marketplace/index.mdx b/docs/docs/schema/marketplace/index.mdx index 28aae432176..fd36fe71be6 100644 --- a/docs/docs/schema/marketplace/index.mdx +++ b/docs/docs/schema/marketplace/index.mdx @@ -4,8 +4,9 @@ title: Infrahub Marketplace # Infrahub Marketplace -The Infrahub Marketplace is a public catalog of pre-built schemas and schema collections that you can fetch and load into any Infrahub instance. +The [Infrahub Marketplace](https://marketplace.infrahub.app) is a public catalog of pre-built schemas and schema collections that you can fetch and load into any Infrahub instance. It provides a curated starting point for common infrastructure domains — from physical device modeling to IP address management — so you can get a working data model without building one from scratch. +Browse the full catalog at [marketplace.infrahub.app/browse](https://marketplace.infrahub.app/browse). ## What the Marketplace contains @@ -18,8 +19,8 @@ The Marketplace hosts two types of content: Every item in the Marketplace is identified by a `namespace/name` reference, similar to Docker Hub image names or npm package scopes. -- `infrahub/dcim` — the `dcim` schema published by the `infrahub` namespace -- `infrahub/base-schemas` — the `base-schemas` collection published by `infrahub` +- [`infrahub/dcim`](https://marketplace.infrahub.app/schemas/infrahub/dcim) — the `dcim` schema published by the `infrahub` namespace +- [`infrahub/base-schemas`](https://marketplace.infrahub.app/collections/infrahub/base-schemas) — the `base-schemas` collection published by `infrahub` Namespaces are tied to the account of the schema's author on the Marketplace. @@ -42,7 +43,7 @@ The Marketplace is an external catalog — it stores schema definitions, not you ### Find the schema -Browse [marketplace.infrahub.app](https://marketplace.infrahub.app) and identify the schema or collection you want. +Browse the [Marketplace catalog](https://marketplace.infrahub.app/browse) and identify the schema or collection you want. Each item has an identifier in `namespace/name` format — for example, `infrahub/dcim`. Note this identifier; you will use it in the next step. @@ -129,6 +130,7 @@ Check the schema's Marketplace page for the minimum supported Infrahub version b ## Related resources +- [Infrahub Marketplace](https://marketplace.infrahub.app) — the catalog of published schemas and collections - [About Schema](../overview.mdx) — core schema concepts and how schemas govern data in Infrahub - [Create and load schema](../create-and-load.mdx) — create a schema from scratch and load it into Infrahub - [Schema extensions](../extensions.mdx) — add attributes and relationships to existing nodes using extension files From 40167728ec4996cbcf3f6615c263c1798bf43683 Mon Sep 17 00:00:00 2001 From: iddocohen Date: Thu, 13 Aug 2026 07:10:15 +0200 Subject: [PATCH 40/48] fix: advance number pool past values already present on the target kind (#10180) * fix: advance number pool past values already present on the target kind A CoreNumberPool derived its next value only from its own reservations, so a value already present on the target kind (created directly or brought in by a brownfield import) was invisible. The pool offered it, the uniqueness constraint rejected the save, and since the failed allocation reserved nothing the pool re-offered the same value on every attempt, parking permanently at that value. Allocation now excludes values already held on the target kind, matching the uniqueness-constraint validator's visibility (is_isolated=False, deletions), and only for globally unique attributes so per-relationship and non-unique attributes stay fully allocatable. closes #10179 * refactor: address review on number pool taken-value query - Drop the toInteger cast on the range predicate so it can use the value index (av.value is a native integer, as NumberPoolGetAllocated relies on). - Add WITH DISTINCT n, attr, av before the CALL so it runs once per (n, attr, av) instead of once per edge pair. - Condense the guard comment. - Remove an em dash from the changelog fragment. * refactor: group number pool taken-value query by attribute and dedupe tests Group the taken-value lookup by (n, attr) and return the latest active value per attribute, dropping the unused ha/hv bindings and re-applying the range filter after the subquery. Extract the shared ticket-schema and pool setup into a fixture. * test: load number pool schema once via class-based allocation tests Group the three shared-setup allocation tests into a class whose class-scoped fixture loads the ticket schema and pool a single time, instead of reloading per test. The methods run in definition order and build on shared data. --- .../core/node/resource_manager/number_pool.py | 16 ++++ .../infrahub/core/query/resource_manager.py | 65 +++++++++++++ .../core/resource_manager/test_number_pool.py | 96 ++++++++++++++++++- changelog/10179.fixed.md | 1 + 4 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 changelog/10179.fixed.md diff --git a/backend/infrahub/core/node/resource_manager/number_pool.py b/backend/infrahub/core/node/resource_manager/number_pool.py index 86869cd0667..cc2fa6f5892 100644 --- a/backend/infrahub/core/node/resource_manager/number_pool.py +++ b/backend/infrahub/core/node/resource_manager/number_pool.py @@ -7,6 +7,7 @@ from infrahub.core.query.resource_manager import ( NumberPoolGetFree, NumberPoolGetReserved, + NumberPoolGetTaken, NumberPoolGetUsed, NumberPoolSetReserved, ) @@ -71,6 +72,15 @@ async def get_free( return query.get_result_value() + async def get_taken( + self, db: InfrahubDatabase, branch: Branch, min_value: int | None = None, max_value: int | None = None + ) -> set[int]: + """Values already present on the target kind for the pool's attribute, within range.""" + query = await NumberPoolGetTaken.init(db=db, branch=branch, pool=self, min_value=min_value, max_value=max_value) + await query.execute(db=db) + + return query.get_taken_values() + async def reserve(self, db: InfrahubDatabase, number: int, identifier: str, at: Timestamp | None = None) -> None: """Reserve a number in the pool for a specific identifier.""" query = await NumberPoolSetReserved.init( @@ -147,6 +157,12 @@ async def get_next(self, db: InfrahubDatabase, branch: Branch, attribute: Attrib if effective_start > effective_end: raise PoolExhaustedError("There are no more values available in this pool.") + # Only a globally unique attribute rejects a duplicate, so skip existing values only then. + if attribute.unique: + excluded_values |= await self.get_taken( + db=db, branch=branch, min_value=effective_start, max_value=effective_end + ) + def skip_excluded(value: int) -> int | None: """Skip past any excluded values/ranges starting from value. diff --git a/backend/infrahub/core/query/resource_manager.py b/backend/infrahub/core/query/resource_manager.py index 272289d8662..60149c247cb 100644 --- a/backend/infrahub/core/query/resource_manager.py +++ b/backend/infrahub/core/query/resource_manager.py @@ -560,6 +560,71 @@ def get_result_value(self) -> int | None: return None +class NumberPoolGetTaken(Query): + """Values held on the target kind for the pool's attribute, whatever set them. + + Sees values created outside the pool, which allocation must skip or it stalls on a value the + uniqueness constraint rejects. + """ + + name = "number_pool_get_taken" + type = QueryType.READ + + def __init__( + self, + pool: CoreNumberPool, + min_value: int | None = None, + max_value: int | None = None, + **kwargs: dict[str, Any], + ) -> None: + self.pool = pool + self.min_value = min_value + self.max_value = max_value + + super().__init__(**kwargs) # type: ignore[arg-type] + + async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: # noqa: ARG002 + self.params["start_range"] = self.min_value if self.min_value is not None else self.pool.start_range.value + self.params["end_range"] = self.max_value if self.max_value is not None else self.pool.end_range.value + + # is_isolated=False mirrors the uniqueness validator: a value added to the origin branch after + # this branch point still collides here. + branch_filter, branch_params = self.branch.get_query_filter_path( + at=self.at.to_string(), branch_agnostic=self.branch_agnostic, is_isolated=False + ) + + self.params.update(branch_params) + self.params["attribute_name"] = self.pool.node_attribute.value + + query = """ + MATCH (n:%(node)s)-[:HAS_ATTRIBUTE]->(attr:Attribute { name: $attribute_name })-[:HAS_VALUE]->(av:AttributeValueIndexed) + WHERE av.value >= $start_range and av.value <= $end_range + WITH DISTINCT n, attr + CALL (n, attr) { + MATCH (n)-[ha:HAS_ATTRIBUTE]->(attr)-[hv:HAS_VALUE]->(av:AttributeValueIndexed) + WHERE all(r in [ha, hv] WHERE (%(branch_filter)s)) + ORDER BY ha.branch_level DESC, hv.branch_level DESC, + ha.from DESC, hv.from DESC, + ha.status ASC, hv.status ASC + RETURN av.value AS value, (ha.status = "active" AND hv.status = "active") AS is_active + LIMIT 1 + } + WITH value, is_active + WHERE is_active = True AND value >= $start_range AND value <= $end_range + WITH DISTINCT value + """ % { + "branch_filter": branch_filter, + "node": self.pool.node.value, + } + + self.add_to_query(query) + self.return_labels = ["value"] + self.order_by = ["value"] + + def get_taken_values(self) -> set[int]: + return {result.get_as_type("value", return_type=int) for result in self.get_results()} + + class NumberPoolSetReserved(Query): name = "numberpool_set_reserved" type = QueryType.WRITE diff --git a/backend/tests/component/core/resource_manager/test_number_pool.py b/backend/tests/component/core/resource_manager/test_number_pool.py index 5459b1c2642..a81969a1b6b 100644 --- a/backend/tests/component/core/resource_manager/test_number_pool.py +++ b/backend/tests/component/core/resource_manager/test_number_pool.py @@ -1,5 +1,9 @@ +from copy import deepcopy + +import pytest + from infrahub.core.branch import Branch -from infrahub.core.initialization import initialize_registry +from infrahub.core.initialization import create_branch, initialize_registry from infrahub.core.node import Node from infrahub.core.node.resource_manager.number_pool import CoreNumberPool from infrahub.core.schema import SchemaRoot @@ -52,6 +56,96 @@ async def test_allocate_from_number_pool( assert await np1.get_free(db=db, branch=default_branch) == 3 +async def test_allocate_reuses_value_when_attribute_not_globally_unique( + db: InfrahubDatabase, default_branch: Branch, register_core_models_schema: SchemaBranch +) -> None: + """Existing target values are skipped only when the attribute is globally unique.""" + schema = deepcopy(TICKET) + next(attr for attr in schema.attributes if attr.name == "ticket_id").unique = False + await load_schema(db=db, schema=SchemaRoot(nodes=[schema])) + await initialize_registry(db=db) + + np1 = await CoreNumberPool.init(db=db, schema="CoreNumberPool") + await np1.new(db=db, name="pool1", node="TestingTicket", node_attribute="ticket_id", start_range=1, end_range=10) + await np1.save(db=db) + + # Created by hand inside the pool range, without going through the pool. + manual_ticket = await Node.init(db=db, schema=TICKET.kind) + await manual_ticket.new(db=db, title="manual", ticket_id=1) + await manual_ticket.save(db=db) + + ticket = await Node.init(db=db, schema=TICKET.kind) + await ticket.new(db=db, title="ticket", ticket_id={"from_pool": {"id": np1.id}}) + await ticket.save(db=db) + + assert ticket.ticket_id.value == 1 + + +class TestNumberPoolAllocation: + """Allocation against one unique ticket schema and a 1-10 pool, loaded once for the class. + + The methods share the loaded schema, pool and accumulated data, and run in definition order: each + builds on the state the previous one leaves behind. + """ + + @pytest.fixture(scope="class") + async def pool(self, db: InfrahubDatabase, register_core_models_schema_scope_class: SchemaBranch) -> CoreNumberPool: + await load_schema(db=db, schema=SchemaRoot(nodes=[TICKET])) + await initialize_registry(db=db) + + pool = await CoreNumberPool.init(db=db, schema="CoreNumberPool") + await pool.new( + db=db, name="pool1", node="TestingTicket", node_attribute="ticket_id", start_range=1, end_range=10 + ) + await pool.save(db=db) + return pool + + @pytest.fixture(scope="class") + async def present_ticket(self, db: InfrahubDatabase, pool: CoreNumberPool) -> Node: + """A ticket created by hand at value 1, inside the pool range but never handed out by the pool.""" + ticket = await Node.init(db=db, schema=TICKET.kind) + await ticket.new(db=db, title="manual", ticket_id=1) + await ticket.save(db=db) + return ticket + + async def test_taken_values_see_origin_branch_after_branch_point( + self, db: InfrahubDatabase, pool: CoreNumberPool + ) -> None: + """A value added to the origin branch after the branch point counts as taken on the branch.""" + branch = await create_branch(db=db, branch_name="feat") + + # Created on the origin branch after the branch point. + origin_ticket = await Node.init(db=db, schema=TICKET.kind) + await origin_ticket.new(db=db, title="origin", ticket_id=5) + await origin_ticket.save(db=db) + + assert await pool.get_taken(db=db, branch=branch, min_value=1, max_value=10) == {5} + + async def test_allocate_skips_value_already_present_on_target( + self, db: InfrahubDatabase, pool: CoreNumberPool, present_ticket: Node + ) -> None: + """A value already present on the target kind but never handed out by the pool is skipped.""" + ticket = await Node.init(db=db, schema=TICKET.kind) + await ticket.new(db=db, title="ticket", ticket_id={"from_pool": {"id": pool.id}}) + await ticket.save(db=db) + + # 1 is held by present_ticket, so the pool skips it and hands out the next free value. + assert ticket.ticket_id.value == 2 + + async def test_allocate_reuses_value_after_conflicting_target_deleted( + self, db: InfrahubDatabase, pool: CoreNumberPool, present_ticket: Node + ) -> None: + """A value freed by deleting the conflicting target object becomes allocatable again.""" + await present_ticket.delete(db=db) + + ticket = await Node.init(db=db, schema=TICKET.kind) + await ticket.new(db=db, title="reuse", ticket_id={"from_pool": {"id": pool.id}}) + await ticket.save(db=db) + + # Deleting present_ticket frees value 1, now the lowest available. + assert ticket.ticket_id.value == 1 + + async def test_resource_utilization( db: InfrahubDatabase, default_branch: Branch, register_core_models_schema: SchemaBranch ) -> None: diff --git a/changelog/10179.fixed.md b/changelog/10179.fixed.md new file mode 100644 index 00000000000..145460c885a --- /dev/null +++ b/changelog/10179.fixed.md @@ -0,0 +1 @@ +Fixed `CoreNumberPool` allocation stalling on a value that already exists on the target kind but was created outside the pool. Previously the pool kept offering such a value, the attribute's uniqueness constraint rejected it, and because the failed allocation reserved nothing the pool re-offered the same value on every subsequent attempt, parking permanently at that value. When the target attribute is unique, the pool now skips values already present on the target and advances to the next free one, so pools can be declared over ranges that already contain data. Attributes that are not unique on their own (including per-relationship uniqueness constraints) are unaffected and remain fully allocatable. From 4273ec70189af01e1ddf955bfa349ee11678fdce Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Thu, 13 Aug 2026 07:20:14 +0200 Subject: [PATCH 41/48] fix: forbid unknown fields in the exported node JSON Schema [INFP-234] (#10234) * fix: forbid unknown fields in the exported node JSON Schema [INFP-234] SchemaLoadAPI now derives from the generated write models, which set extra="ignore" because unknown-field policy is applied imperatively when a schema is loaded. The exporter was never revisited, so the published document lost every additionalProperties: false it used to inherit from the internal models' extra="forbid", and an editor validating against it accepted a typo the load endpoint rejects. Close every object in the exported document, and declare the read-only fields alongside it marked deprecated rather than omitting them: the load endpoint accepts one, drops the value and reports a warning, so a closed document that left them out would turn a schema read back from Infrahub into a file full of errors. The read-only names come from the generated contract table, the same one the load endpoint validates against, so the document cannot drift from the endpoint's verdict. Also drop AttributeSchema.model_json_schema. It set out to bind a kind to its parameters for the language server but never reached the published document: pydantic does not call a nested model's classmethod when building the parent, and its refs were Draft-7 style that would not resolve in a $defs document. The write models' discriminated union does that job now. Regenerating openapi.json after removing it produces no diff. * fix: annotate the validator fixture against the jsonschema protocol * test: assert the invariant hardening relies on, and pin each rejection reason Hardening reaches the root and $defs only, which covers the document while pydantic hoists every nested model. Nothing stated that, so a field typed as a mapping would render an inline object the sweep never sees. Assert it instead: such a field now fails the suite and gets a deliberate decision, rather than either leaving one spot open or having its value schema overwritten by a recursive sweep. Every case now pins the message the document must report, so a case cannot go green by failing for an unrelated reason. Drop the escape clause that excused a required read-only field from carrying the deprecation marker: no field reaches it, and it could only ever weaken the assertion. Count what each structural test asserted so an empty document cannot pass it vacuously. Adopt the project's dataclass test-case shape: field docstrings, a typed module-level constant, and IDs that name the expected outcome. * chore: declare jsonschema for tests and build the document without mutating The schema tests validate against jsonschema, which reached the environment only as a transitive dependency, so a change upstream could have removed it. Declare it in the dev group with a floor and no ceiling: the floor is the version whose validation messages the tests assert verbatim, and leaving the ceiling off keeps this pin from ever blocking an upgrade that wants a newer one. Build the hardened document from a copy rather than editing the one handed in. A function named for building a document should not leave the caller's own changed underneath it, and a test now holds that contract. --- backend/infrahub/cli/dev.py | 3 +- .../infrahub/core/schema/attribute_schema.py | 18 -- .../infrahub/core/schema/write_json_schema.py | 74 +++++ .../core/schema/test_write_json_schema.py | 266 ++++++++++++++++++ pyproject.toml | 3 + uv.lock | 2 + 6 files changed, 347 insertions(+), 19 deletions(-) create mode 100644 backend/infrahub/core/schema/write_json_schema.py create mode 100644 backend/tests/unit/core/schema/test_write_json_schema.py diff --git a/backend/infrahub/cli/dev.py b/backend/infrahub/cli/dev.py index 52464f3bc1e..301eb06f89c 100644 --- a/backend/infrahub/cli/dev.py +++ b/backend/infrahub/cli/dev.py @@ -19,6 +19,7 @@ ) from infrahub.core.schema import SchemaRoot, core_models, internal_schema from infrahub.core.schema.schema_branch import SchemaBranch +from infrahub.core.schema.write_json_schema import build_write_json_schema from infrahub.core.utils import delete_all_nodes from infrahub.graphql.manager import GraphQLSchemaManager from infrahub.graphql.schema_sort import sort_schema_ast @@ -79,7 +80,7 @@ async def export_node_schema( ) -> None: """Export the repository configuration to a file.""" config.load_and_exit(config_file_name=config_file) - schema = SchemaLoadAPI.model_json_schema() + schema = build_write_json_schema(schema=SchemaLoadAPI.model_json_schema()) schema["title"] = "InfrahubSchema" content = json.dumps(schema, indent=4) out.parent.mkdir(parents=True, exist_ok=True) diff --git a/backend/infrahub/core/schema/attribute_schema.py b/backend/infrahub/core/schema/attribute_schema.py index 4ea1c95f67e..ad7c6d27cb3 100644 --- a/backend/infrahub/core/schema/attribute_schema.py +++ b/backend/infrahub/core/schema/attribute_schema.py @@ -50,24 +50,6 @@ def source_attribute_id(self) -> str | None: def source_attribute_id(self, value: str | None) -> None: self._source_attribute_id = value - @classmethod - def model_json_schema(cls, *args: Any, **kwargs: Any) -> dict[str, Any]: - schema = super().model_json_schema(*args, **kwargs) - - # Build conditional schema based on attribute_schema_class_by_kind mapping - # This override allows people using the Yaml language server to get the correct mappings - # for the parameters when selecting the appropriate kind - schema["allOf"] = [] - for kind, schema_class in attribute_schema_class_by_kind.items(): - schema["allOf"].append( - { - "if": {"properties": {"kind": {"const": kind}}}, - "then": {"properties": {"parameters": {"$ref": f"#/definitions/{schema_class.__name__}"}}}, - } - ) - - return schema - @property def is_attribute(self) -> bool: return True diff --git a/backend/infrahub/core/schema/write_json_schema.py b/backend/infrahub/core/schema/write_json_schema.py new file mode 100644 index 00000000000..8e61ea5e7b0 --- /dev/null +++ b/backend/infrahub/core/schema/write_json_schema.py @@ -0,0 +1,74 @@ +"""Shape the JSON Schema document published for a user-facing schema file. + +``model_json_schema()`` describes only what pydantic can express. The write models set +``extra="ignore"`` because unknown-field policy is applied imperatively when a schema is loaded, +so nothing in the emitted document forbids an undeclared key: an editor validating against it +accepts a typo the load endpoint rejects. Closing every object restores that check. + +Read-only fields have to be re-declared alongside it. The load endpoint accepts one, drops the +value and reports a warning, so a closed document that simply omitted them would turn a schema +read back from Infrahub into a file full of errors. JSON Schema has no warning level, and +``deprecated`` is its nearest equivalent: accepted, carrying a message, but not to be written. +""" + +from __future__ import annotations + +from copy import deepcopy +from typing import Any + +from infrahub_sdk.schema.generated.contract import READ_ONLY_FIELDS +from infrahub_sdk.schema.generated.write import InfrahubSchemaWrite + +ROOT_CLASS_NAME = InfrahubSchemaWrite.__name__ +"""Write class the exported root describes. Its own title cannot serve: the export renames it.""" + +READ_ONLY_MESSAGE = "'{name}' is a read-only field, the submitted value is ignored" +"""Worded as the load endpoint words the same finding, so both tell the user one story.""" + +DEPRECATED_MESSAGES: dict[str, str] = {"display_labels": "display_labels are deprecated use display_label instead"} +"""Message for a writable field that is on its way out, keyed by field name wherever it appears.""" + + +def _deprecate(definition: dict[str, Any], message: str) -> None: + definition["deprecated"] = True + # Non-standard, and the only keyword yaml-language-server renders a message from. + definition["deprecationMessage"] = message + + +def _harden_definition(definition: dict[str, Any], class_name: str) -> None: + if definition.get("type") != "object": + return + + properties = definition.setdefault("properties", {}) + + for name in sorted(READ_ONLY_FIELDS.get(class_name, frozenset())): + # A name the table lists that the class declares anyway belongs to a sibling variant of a + # discriminated union, which owns it as a writable field. Leave that definition alone. + if name in properties: + continue + message = READ_ONLY_MESSAGE.format(name=name) + properties[name] = {"description": message} + _deprecate(definition=properties[name], message=message) + + for name, message in DEPRECATED_MESSAGES.items(): + if name in properties: + _deprecate(definition=properties[name], message=message) + + definition["additionalProperties"] = False + + +def build_write_json_schema(schema: dict[str, Any]) -> dict[str, Any]: + """Close every object in a generated write JSON Schema, keeping read-only fields accepted. + + Args: + schema: The document generated for the write root. It is not modified. + + Returns: + A new document with every object closed and each read-only field declared as deprecated. + + """ + hardened = deepcopy(schema) + _harden_definition(definition=hardened, class_name=ROOT_CLASS_NAME) + for class_name, definition in hardened.get("$defs", {}).items(): + _harden_definition(definition=definition, class_name=class_name) + return hardened diff --git a/backend/tests/unit/core/schema/test_write_json_schema.py b/backend/tests/unit/core/schema/test_write_json_schema.py new file mode 100644 index 00000000000..ee18ebd60f1 --- /dev/null +++ b/backend/tests/unit/core/schema/test_write_json_schema.py @@ -0,0 +1,266 @@ +from __future__ import annotations + +import json +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any + +import pytest +from infrahub_sdk.schema.generated.contract import READ_ONLY_FIELDS +from infrahub_sdk.schema.validate import validate_schema +from jsonschema import Draft202012Validator + +from infrahub.api.schema import SchemaLoadAPI +from infrahub.core.schema.write_json_schema import ROOT_CLASS_NAME, build_write_json_schema + +if TYPE_CHECKING: + from collections.abc import Iterator + + from jsonschema.exceptions import ValidationError + from jsonschema.protocols import Validator + + +@dataclass(frozen=True) +class WriteJsonSchemaCase: + name: str + """Descriptive name for the test scenario, used as the pytest ID.""" + + payload: dict[str, Any] + """Schema-root payload to validate against the published document.""" + + expected_error: str | None = None + """Message the document must report, or None when the payload must be accepted. + + An attribute is a discriminated union, and a union reports its own failure as "is not valid + under any of the given schemas". The message naming the cause therefore sits in a nested error + rather than the top-level one, which is why the whole error tree is searched. + """ + + +def _node(**fields: Any) -> dict[str, Any]: + return {"version": "1.0", "nodes": [{"name": "Device", "namespace": "Infra", **fields}]} + + +def _attribute(**fields: Any) -> dict[str, Any]: + return _node(attributes=[{"name": "title", "kind": "Text", **fields}]) + + +WRITE_JSON_SCHEMA_CASES: list[WriteJsonSchemaCase] = [ + WriteJsonSchemaCase(name="minimal-attribute-accepted", payload=_attribute()), + WriteJsonSchemaCase(name="text-parameter-on-text-accepted", payload=_attribute(parameters={"max_length": 10})), + WriteJsonSchemaCase( + name="number-parameter-on-number-accepted", + payload=_node(attributes=[{"name": "speed", "kind": "Number", "parameters": {"min_value": 5}}]), + ), + WriteJsonSchemaCase( + name="pool-parameter-on-pool-accepted", + payload=_node( + attributes=[{"name": "index", "kind": "NumberPool", "parameters": {"start_range": 1, "end_range": 9}}] + ), + ), + WriteJsonSchemaCase( + name="computed-jinja2-accepted", + payload=_attribute(computed_attribute={"kind": "Jinja2", "jinja2_template": "{{ x }}"}), + ), + WriteJsonSchemaCase( + name="computed-transform-accepted", + payload=_attribute(computed_attribute={"kind": "TransformPython", "transform": "device_compliance"}), + ), + WriteJsonSchemaCase( + name="computed-jinja2-carrying-sibling-transform-accepted", + payload=_attribute(computed_attribute={"kind": "Jinja2", "jinja2_template": "{{ x }}", "transform": "t"}), + ), + WriteJsonSchemaCase( + name="payload-read-back-from-infrahub-accepted", + payload=_node( + hash="abc", kind="InfraDevice", attributes=[{"name": "title", "kind": "Text", "inherited": False}] + ), + ), + WriteJsonSchemaCase( + name="unknown-field-on-node-rejected", + payload=_node(labl="Device"), + expected_error="Additional properties are not allowed ('labl' was unexpected)", + ), + WriteJsonSchemaCase( + name="unknown-field-on-attribute-rejected", + payload=_attribute(uniqe=True), + expected_error="Additional properties are not allowed ('uniqe' was unexpected)", + ), + WriteJsonSchemaCase( + name="unknown-field-on-relationship-rejected", + payload=_node(relationships=[{"name": "site", "peer": "InfraSite", "cardinlity": "one"}]), + expected_error="Additional properties are not allowed ('cardinlity' was unexpected)", + ), + WriteJsonSchemaCase( + name="unknown-parameter-rejected", + payload=_attribute(parameters={"regexx": "^a"}), + expected_error="Additional properties are not allowed ('regexx' was unexpected)", + ), + WriteJsonSchemaCase( + name="number-parameter-on-text-rejected", + payload=_attribute(parameters={"min_value": 5}), + expected_error="Additional properties are not allowed ('min_value' was unexpected)", + ), + WriteJsonSchemaCase( + name="pool-parameter-on-text-rejected", + payload=_attribute(parameters={"number_pool_id": "x"}), + expected_error="Additional properties are not allowed ('number_pool_id' was unexpected)", + ), + WriteJsonSchemaCase( + name="text-parameter-on-number-rejected", + payload=_node(attributes=[{"name": "speed", "kind": "Number", "parameters": {"regex": "^a"}}]), + expected_error="Additional properties are not allowed ('regex' was unexpected)", + ), + WriteJsonSchemaCase( + name="parameter-on-kind-taking-none-rejected", + payload=_node(attributes=[{"name": "active", "kind": "Boolean", "parameters": {"regex": "^a"}}]), + expected_error="Additional properties are not allowed ('regex' was unexpected)", + ), + WriteJsonSchemaCase( + name="wrong-parameter-value-type-rejected", + payload=_attribute(parameters={"max_length": "sixty-four"}), + expected_error="'sixty-four' is not of type 'integer'", + ), + WriteJsonSchemaCase( + name="invalid-attribute-kind-rejected", + payload=_node(attributes=[{"name": "title", "kind": "Str"}]), + expected_error="'Str' is not one of ['Text', 'TextArea']", + ), + WriteJsonSchemaCase( + name="computed-jinja2-without-template-rejected", + payload=_attribute(computed_attribute={"kind": "Jinja2"}), + expected_error="'jinja2_template' is a required property", + ), + WriteJsonSchemaCase( + name="computed-transform-without-name-rejected", + payload=_attribute(computed_attribute={"kind": "TransformPython"}), + expected_error="'transform' is a required property", + ), + WriteJsonSchemaCase( + name="invalid-computed-kind-rejected", + payload=_attribute(computed_attribute={"kind": "Handwritten"}), + expected_error="'User' was expected", + ), +] + + +@pytest.fixture(scope="module") +def write_json_schema() -> dict[str, Any]: + return build_write_json_schema(schema=SchemaLoadAPI.model_json_schema()) + + +@pytest.fixture(scope="module") +def validator(write_json_schema: dict[str, Any]) -> Validator: + Draft202012Validator.check_schema(write_json_schema) + return Draft202012Validator(write_json_schema) + + +def _object_schema_paths(node: Any, path: str = "$") -> Iterator[str]: + """Yield the path of every object subschema anywhere in a JSON Schema document.""" + if isinstance(node, dict): + if node.get("type") == "object": + yield path + for key, value in node.items(): + yield from _object_schema_paths(node=value, path=f"{path}.{key}") + elif isinstance(node, list): + for index, value in enumerate(node): + yield from _object_schema_paths(node=value, path=f"{path}[{index}]") + + +def _addressable_object_paths(schema: dict[str, Any]) -> set[str]: + return {"$"} | {f"$.$defs.{name}" for name in schema["$defs"]} + + +def _flatten(error: ValidationError) -> Iterator[ValidationError]: + yield error + for nested in error.context or []: + yield from _flatten(error=nested) + + +def test_source_document_is_left_untouched() -> None: + """Callers keep using the document they passed in, so hardening must not reach back into it.""" + source = SchemaLoadAPI.model_json_schema() + before = json.dumps(source, sort_keys=True) + + hardened = build_write_json_schema(schema=source) + + assert json.dumps(source, sort_keys=True) == before + assert hardened != source + + +def test_every_object_schema_is_addressable(write_json_schema: dict[str, Any]) -> None: + """Hardening reaches the root and $defs only, which covers the document while every model is hoisted. + + Pydantic lifts each nested model into $defs and refers to it, so no object subschema is inline. + A field typed as a mapping would break that: it renders inline, carrying an additionalProperties + that describes its values, and closing it would leave a mapping that accepts no keys at all. + Such a field needs a deliberate decision rather than the surrounding sweep, so it fails here. + """ + addressable = _addressable_object_paths(schema=write_json_schema) + inline = set(_object_schema_paths(node=write_json_schema)) - addressable + + assert inline == set() + assert len(addressable) > 1 + + +def test_every_object_forbids_additional_properties(write_json_schema: dict[str, Any]) -> None: + closed = { + name + for name, definition in write_json_schema["$defs"].items() + if definition.get("type") == "object" and definition.get("additionalProperties") is False + } + objects = {name for name, definition in write_json_schema["$defs"].items() if definition.get("type") == "object"} + + assert objects - closed == set() + assert len(closed) > 0 + assert write_json_schema["additionalProperties"] is False + + +def test_read_only_fields_are_declared_as_deprecated(write_json_schema: dict[str, Any]) -> None: + """A closed document that omitted these would reject a schema read back from Infrahub.""" + missing: set[str] = set() + not_deprecated: set[str] = set() + asserted = 0 + + for class_name, field_names in READ_ONLY_FIELDS.items(): + definition = write_json_schema if class_name == ROOT_CLASS_NAME else write_json_schema["$defs"].get(class_name) + if definition is None: + # A base class pydantic inlines because no field refers to it by name. + continue + for field_name in field_names: + asserted += 1 + declared = definition["properties"].get(field_name) + if declared is None: + missing.add(f"{class_name}.{field_name}") + elif declared.get("deprecated") is not True: + not_deprecated.add(f"{class_name}.{field_name}") + + assert missing == set() + assert not_deprecated == set() + assert asserted > 0 + + +def test_display_labels_is_deprecated(write_json_schema: dict[str, Any]) -> None: + display_labels = write_json_schema["$defs"]["NodeSchemaWrite"]["properties"]["display_labels"] + + assert display_labels["deprecated"] is True + assert display_labels["deprecationMessage"] == "display_labels are deprecated use display_label instead" + + +@pytest.mark.parametrize("case", [pytest.param(tc, id=tc.name) for tc in WRITE_JSON_SCHEMA_CASES]) +def test_published_schema_verdict(validator: Validator, case: WriteJsonSchemaCase) -> None: + messages = {nested.message for error in validator.iter_errors(case.payload) for nested in _flatten(error=error)} + + if case.expected_error is None: + assert messages == set() + else: + assert case.expected_error in messages, sorted(messages) + + +@pytest.mark.parametrize("case", [pytest.param(tc, id=tc.name) for tc in WRITE_JSON_SCHEMA_CASES]) +def test_published_schema_matches_load_contract_verdict(case: WriteJsonSchemaCase) -> None: + # An editor validating against the published document must reach the same accept/reject verdict + # as the load endpoint, or it reports an error on a file the server takes, or stays silent on one + # the server refuses. + result = validate_schema(schema=case.payload) + + assert result.valid is (case.expected_error is None), result.messages diff --git a/pyproject.toml b/pyproject.toml index 9bc3fc9bbc6..8fe977cd87f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -114,6 +114,9 @@ dev = [ "semver>=3.0.2,<4", "ruamel-yaml==0.18.6", "pytest-httpx>=0.33", + # No upper bound, so a prefect upgrade is never blocked by this pin. The floor is the version + # whose validation messages the schema tests assert verbatim. + "jsonschema>=4.26", "docker==7.1.0", "psutil==6.1.0", "jwcrypto==1.5.7", diff --git a/uv.lock b/uv.lock index c6a430bbb15..e723c45c03e 100644 --- a/uv.lock +++ b/uv.lock @@ -1487,6 +1487,7 @@ dev = [ { name = "docker" }, { name = "invoke" }, { name = "ipython" }, + { name = "jsonschema" }, { name = "jwcrypto" }, { name = "matplotlib" }, { name = "mypy" }, @@ -1580,6 +1581,7 @@ dev = [ { name = "docker", specifier = "==7.1.0" }, { name = "invoke", specifier = "==2.2.1" }, { name = "ipython", specifier = ">=8,<9" }, + { name = "jsonschema", specifier = ">=4.26" }, { name = "jwcrypto", specifier = "==1.5.7" }, { name = "matplotlib", specifier = "==3.10.7" }, { name = "mypy", specifier = ">=1.15,<1.16" }, From 624eeb4fc54dcccb2e80fffd0fbe59feea2bd05b Mon Sep 17 00:00:00 2001 From: Baptiste <32564248+BaptisteGi@users.noreply.github.com> Date: Thu, 13 Aug 2026 09:57:55 +0200 Subject: [PATCH 42/48] docs: Rework learn documentation (#10212) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * reorganize learn folder * Move everything to learn * Cleanup - improvements - links * docs: rework Infrahub Labs hub per review — value-first intro, topic grouping, text links - Rewrite the labs overview: lead with what labs are for and what you gain, add when-to-use guidance, group standalone labs by topic (getting started vs advanced) with time estimates and when/why for each lab - Rename page and sidebar label from Labs to Infrahub Labs - Replace StandoutLink buttons with linked text at the end of each paragraph; remove the now-unused StandoutLink component - Say lab instead of tutorial on lab pages, now that Tutorials is a separate section Co-Authored-By: Claude Fable 5 * various fixes * Keep transformation naming consistent --------- Co-authored-by: Yvonne Jouffrault Co-authored-by: Claude Fable 5 --- docs/docs/academy/academy.mdx | 27 -------- .../deploy-first-configuration.mdx | 40 ------------ .../getting-started/infrahub-introduction.mdx | 43 ------------- docs/docs/artifacts/content-composition.mdx | 6 +- docs/docs/artifacts/use.mdx | 4 +- docs/docs/checks/overview.mdx | 2 +- docs/docs/computed-attributes/overview.mdx | 6 +- .../graphql-fragments.mdx | 4 +- .../graphql/single-target-queries.mdx | 4 +- docs/docs/generators/build.mdx | 2 +- .../generators/modular-best-practices.mdx | 6 +- docs/docs/generators/modular.mdx | 6 +- docs/docs/generators/overview.mdx | 2 +- .../graph-traversal/query-with-graphql.mdx | 2 +- docs/docs/groups/overview.mdx | 2 +- .../learn/labs/deploy-first-configuration.mdx | 34 ++++++++++ .../learn/labs/fundamentals-to-expert.mdx | 63 +++++++++++++++++++ .../docs/learn/labs/infrahub-introduction.mdx | 35 +++++++++++ docs/docs/learn/labs/overview.mdx | 38 +++++++++++ docs/docs/learn/labs/schema-deep-dive.mdx | 30 +++++++++ .../tutorials/build-a-check.mdx | 0 .../tutorials/build-your-first-schema.mdx | 0 .../generators/build-chained-generators.mdx | 0 .../generators/build-your-first-generator.mdx | 0 .../{academy => learn}/tutorials/groups.mdx | 0 docs/docs/learn/tutorials/overview.mdx | 37 +++++++++++ .../build-a-jinja2-transformation.mdx | 0 .../build-a-python-transformation.mdx | 0 docs/docs/object-templates/use.mdx | 2 +- docs/docs/overview/concepts.mdx | 2 +- docs/docs/overview/next-steps.mdx | 4 +- docs/docs/schema/create-and-load.mdx | 2 +- docs/docs/transformations/jinja2.mdx | 2 +- docs/docs/transformations/overview.mdx | 8 +-- docs/docs/transformations/python.mdx | 2 +- docs/docs/webhooks/custom-transformation.mdx | 2 +- docs/sidebars.ts | 29 ++++----- 37 files changed, 287 insertions(+), 159 deletions(-) delete mode 100644 docs/docs/academy/academy.mdx delete mode 100644 docs/docs/academy/getting-started/deploy-first-configuration.mdx delete mode 100644 docs/docs/academy/getting-started/infrahub-introduction.mdx create mode 100644 docs/docs/learn/labs/deploy-first-configuration.mdx create mode 100644 docs/docs/learn/labs/fundamentals-to-expert.mdx create mode 100644 docs/docs/learn/labs/infrahub-introduction.mdx create mode 100644 docs/docs/learn/labs/overview.mdx create mode 100644 docs/docs/learn/labs/schema-deep-dive.mdx rename docs/docs/{academy => learn}/tutorials/build-a-check.mdx (100%) rename docs/docs/{academy => learn}/tutorials/build-your-first-schema.mdx (100%) rename docs/docs/{academy => learn}/tutorials/generators/build-chained-generators.mdx (100%) rename docs/docs/{academy => learn}/tutorials/generators/build-your-first-generator.mdx (100%) rename docs/docs/{academy => learn}/tutorials/groups.mdx (100%) create mode 100644 docs/docs/learn/tutorials/overview.mdx rename docs/docs/{academy => learn}/tutorials/transformations/build-a-jinja2-transformation.mdx (100%) rename docs/docs/{academy => learn}/tutorials/transformations/build-a-python-transformation.mdx (100%) diff --git a/docs/docs/academy/academy.mdx b/docs/docs/academy/academy.mdx deleted file mode 100644 index fbe1602f22a..00000000000 --- a/docs/docs/academy/academy.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: Academy ---- - -# Welcome to Infrahub academy - -Infrahub Academy offers a collection of practical, hands-on tutorials designed to help you master Infrahub's powerful capabilities. These step-by-step guides will walk you through key features and workflows, providing real-world experience with the platform. - -## About our tutorials - -Each tutorial in the Academy is: - -- **Hands-on**: Learn by doing with practical exercises -- **Progressive**: Start with basics and build to advanced topics -- **Scenario-based**: Focused on real-world use cases -- **Self-contained**: Complete each tutorial at your own pace - -## Tutorial categories - -Our tutorials are organized to support your learning journey: - -- **Getting started**: Essential tutorials for new users to learn Infrahub basics -- **Advanced tutorials**: Complex scenarios and specialized use cases - -:::tip -Each tutorial includes estimated completion time and prerequisites, so you can choose the right learning path for your current knowledge level and available time. -::: diff --git a/docs/docs/academy/getting-started/deploy-first-configuration.mdx b/docs/docs/academy/getting-started/deploy-first-configuration.mdx deleted file mode 100644 index bc52ab7a302..00000000000 --- a/docs/docs/academy/getting-started/deploy-first-configuration.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: Deploy your first network configuration -description: Learn how to design, build and deploy infrastructure configurations using Infrahub's artifact feature. -hide_table_of_contents: true ---- - -import ReferenceLink from "@site/src/components/Card"; - -# Generate and deploy your first network configuration using Infrahub - -| | | -|------|---------| -| **Prerequisites:** | Basic experience with Infrahub, Ansible, Jinja and GraphQL | -| **Time to Completion:** | Approximately 1 hour | - -:::info Hands-on Learning -This tutorial is supported by an Instruqt track. Instruqt is an online lab platform providing a pre-configured environment so you can focus on learning with hands-on experience. -::: - -## Introduction - -Infrastructure as Code is essential for modern infrastructure management, but traditional tools often lack proper data organization and version control capabilities. Infrahub solves this challenge by providing a centralized hub where infrastructure data, templates, and deployment logic work together seamlessly. - -In this tutorial, you'll learn how to leverage Infrahub's artifact generation system to create and deploy network device configurations based on structured data and templates. - -## What you'll learn - -By the end of this hands-on tutorial, you'll master the following skills: - -- Creating focused GraphQL queries to extract specific infrastructure data -- Building reusable Jinja2 templates for generating device configurations -- Integrating queries and templates using Infrahub's Transformation system -- Testing configuration changes safely using Infrahub's branching capabilities -- Validating generated artifacts before deployment to production - -## Get started - -Ready to begin your journey with network configuration in Infrahub? Click the link below to access the hands-on laboratory environment: - - diff --git a/docs/docs/academy/getting-started/infrahub-introduction.mdx b/docs/docs/academy/getting-started/infrahub-introduction.mdx deleted file mode 100644 index 93bb54e1f1c..00000000000 --- a/docs/docs/academy/getting-started/infrahub-introduction.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Infrahub Introduction -description: A comprehensive introduction to Infrahub, its core concepts, and how it revolutionizes infrastructure management. -hide_table_of_contents: true ---- - -import ReferenceLink from "@site/src/components/Card"; - -# Infrahub introduction - -| | | -|------|---------| -| **Prerequisites:** | No prior experience with Infrahub required | -| **Time to Completion:** | Approximately 30 minutes | - -:::info Hands-on Learning -This tutorial is supported by an Instruqt track. Instruqt is an online lab platform providing a pre-configured environment so you can focus on learning with hands-on experience. -::: - -## Introduction - -Infrahub unifies infrastructure management by combining Git-like version control with a flexible graph database. This solves the common challenges of fragmented tools, inconsistent data, and difficult collaboration. - -This tutorial provides a ready-to-use environment where you can explore Infrahub's core features without setup overhead. You'll engage with practical exercises that demonstrate how Infrahub centralizes and streamlines infrastructure data management. - -Perfect for both curious newcomers and technical evaluators, this introduction builds a foundation for understanding how Infrahub can transform your infrastructure workflows. - -## What you'll learn - -In this introductory track, we've designed a focused learning experience that will help you: - -- Understand how Infrahub's core concepts work in practice -- Determine whether Infrahub aligns with your infrastructure management needs -- Gain the fundamental knowledge needed to explore more advanced topics -- Experience the platform's key features through hands-on exercises - -Whether you're a network engineer, a DevOps professional, or an infrastructure architect, this introduction will provide valuable insights into how Infrahub can transform your approach to infrastructure management. - -## Get started - -Ready to begin your Infrahub journey? Click the link below to access the hands-on laboratory environment: - - diff --git a/docs/docs/artifacts/content-composition.mdx b/docs/docs/artifacts/content-composition.mdx index 279c8cc98aa..7e3301ebc6f 100644 --- a/docs/docs/artifacts/content-composition.mdx +++ b/docs/docs/artifacts/content-composition.mdx @@ -15,7 +15,7 @@ Before starting this guide, ensure you have: - A running Infrahub instance with task workers - At least one source artifact already generating content - An [external repository](../git-integration/connect-repository) connected to Infrahub -- Familiarity with [creating a Jinja2 Transformation](../academy/tutorials/transformations/build-a-jinja2-transformation) or [creating a Python Transformation](../academy/tutorials/transformations/build-a-python-transformation) +- Familiarity with [creating a Jinja2 Transformation](../learn/tutorials/transformations/build-a-jinja2-transformation) or [creating a Python Transformation](../learn/tutorials/transformations/build-a-python-transformation) - Familiarity with [generating artifacts](./use) ## 1. Understand the composition model @@ -265,8 +265,8 @@ The composition filters raise errors in the following situations: ## Next steps -- [Write a Jinja2 Transformation](../academy/tutorials/transformations/build-a-jinja2-transformation) -- [Write a Python Transformation](../academy/tutorials/transformations/build-a-python-transformation) +- [Write a Jinja2 Transformation](../learn/tutorials/transformations/build-a-jinja2-transformation) +- [Write a Python Transformation](../learn/tutorials/transformations/build-a-python-transformation) - [Use artifacts](./use) - [Object storage](../artifact-file-storage/overview.mdx) - [File objects](../schema/file-object) diff --git a/docs/docs/artifacts/use.mdx b/docs/docs/artifacts/use.mdx index c2198aeaac8..0e756e63881 100644 --- a/docs/docs/artifacts/use.mdx +++ b/docs/docs/artifacts/use.mdx @@ -10,7 +10,7 @@ Generate configuration files and other artifacts by combining Infrahub data with For conceptual information about artifacts and their architecture, see [Artifacts](./overview). -> Assumes a working Infrahub instance, an existing Transformation ([Jinja2](../academy/tutorials/transformations/build-a-jinja2-transformation) or [Python](../academy/tutorials/transformations/build-a-python-transformation)), permission to create and modify schemas, and a Git repository connected to Infrahub. +> Assumes a working Infrahub instance, an existing Transformation ([Jinja2](../learn/tutorials/transformations/build-a-jinja2-transformation) or [Python](../learn/tutorials/transformations/build-a-python-transformation)), permission to create and modify schemas, and a Git repository connected to Infrahub. ## Enable artifact generation on your schema @@ -165,6 +165,6 @@ If artifacts aren't generating: - [Artifacts](./overview) — concept and architecture - [Composing artifact content](./content-composition) — assemble a composite artifact from other artifacts or file objects -- [Write a Python Transformation](../academy/tutorials/transformations/build-a-python-transformation) — for non-template logic +- [Write a Python Transformation](../learn/tutorials/transformations/build-a-python-transformation) — for non-template logic - [Transformations](../transformations/overview) — reshape graph data into another format - [Connect a repository](../git-integration/connect-repository) — automate artifact deployment diff --git a/docs/docs/checks/overview.mdx b/docs/docs/checks/overview.mdx index e8cea5318cb..a5f83d50381 100644 --- a/docs/docs/checks/overview.mdx +++ b/docs/docs/checks/overview.mdx @@ -31,7 +31,7 @@ See [Groups](../groups/overview.mdx) for details on creating target groups for y ## Learn by doing -Walk through [Build a check](../academy/tutorials/build-a-check.mdx) in Academy to set up the GraphQL query, implement the check logic, configure `.infrahub.yml`, and validate the check against a proposed change end-to-end. +Walk through [Build a check](../learn/tutorials/build-a-check.mdx) in Academy to set up the GraphQL query, implement the check logic, configure `.infrahub.yml`, and validate the check against a proposed change end-to-end. ## Related diff --git a/docs/docs/computed-attributes/overview.mdx b/docs/docs/computed-attributes/overview.mdx index 007b9ec45f1..7357d8b4bbb 100644 --- a/docs/docs/computed-attributes/overview.mdx +++ b/docs/docs/computed-attributes/overview.mdx @@ -42,7 +42,7 @@ Use Jinja2 when the calculation logic is straightforward and concise. ### How to add a Jinja2 computed attribute -To create a Jinja2 computed attribute everything happens directly in the schema definition. Refer to this guide to find further information about [Schema Creation](../academy/tutorials/build-your-first-schema). +To create a Jinja2 computed attribute everything happens directly in the schema definition. Refer to this guide to find further information about [Schema Creation](../learn/tutorials/build-your-first-schema). In the following example we will create a computed attribute `description` for our `NetworkDevice` node. The value will be dynamically generated by combining the device's role with the name of its associated site. @@ -98,7 +98,7 @@ For more information, please consult the [SDK Templating Reference]($(base_url)p ::: -You can now load this schema into your Infrahub instance. For more details, refer to the [Schema Creation Guide](../academy/tutorials/build-your-first-schema). +You can now load this schema into your Infrahub instance. For more details, refer to the [Schema Creation Guide](../learn/tutorials/build-your-first-schema). ### How to test Jinja2 computed attributes locally @@ -225,7 +225,7 @@ You can specify a Transformation that doesn't yet exist in Infrahub, and the att #### Create the Python Transformation -Please refer to the [Python Transformation guide](../academy/tutorials/transformations/build-a-python-transformation) for further details. +Please refer to the [Python Transformation guide](../learn/tutorials/transformations/build-a-python-transformation) for further details. 1. First we prepare a GraphQL query that returns the data we need diff --git a/docs/docs/development-resources/graphql-fragments.mdx b/docs/docs/development-resources/graphql-fragments.mdx index 77dba28f2a4..39a237de7e3 100644 --- a/docs/docs/development-resources/graphql-fragments.mdx +++ b/docs/docs/development-resources/graphql-fragments.mdx @@ -229,5 +229,5 @@ Infrahub validates fragments during repository sync and raises specific errors: - [GraphQL in Infrahub](./graphql/overview) — query format, stored queries, and endpoint reference - [`.infrahub.yml` configuration](../git-integration/infrahub-yml) — repository manifest structure and resource types -- [Creating a Jinja Transformation](../academy/tutorials/transformations/build-a-jinja2-transformation) — using queries with Jinja2 templates -- [Creating a Python Transformation](../academy/tutorials/transformations/build-a-python-transformation) — using queries with Python code +- [Creating a Jinja Transformation](../learn/tutorials/transformations/build-a-jinja2-transformation) — using queries with Jinja2 templates +- [Creating a Python Transformation](../learn/tutorials/transformations/build-a-python-transformation) — using queries with Python code diff --git a/docs/docs/development-resources/graphql/single-target-queries.mdx b/docs/docs/development-resources/graphql/single-target-queries.mdx index f22abbfa9c7..36432a24f4d 100644 --- a/docs/docs/development-resources/graphql/single-target-queries.mdx +++ b/docs/docs/development-resources/graphql/single-target-queries.mdx @@ -168,8 +168,8 @@ It's planned to add more integrated checks in the future to streamline the devel Single-target queries are **required** for: -- **Python transformations** - See [Creating a Python transformation](../../academy/tutorials/transformations/build-a-python-transformation) -- **Jinja2 transformations** - See [Creating a Jinja transformation](../../academy/tutorials/transformations/build-a-jinja2-transformation) +- **Python transformations** - See [Creating a Python transformation](../../learn/tutorials/transformations/build-a-python-transformation) +- **Jinja2 transformations** - See [Creating a Jinja transformation](../../learn/tutorials/transformations/build-a-jinja2-transformation) - **Generators** - See [Generators](../../generators/overview) - **Artifact definitions** - See [Artifacts](../../artifacts/overview) - **Computed attributes** - See [Computed attributes](../../computed-attributes/overview.mdx) diff --git a/docs/docs/generators/build.mdx b/docs/docs/generators/build.mdx index bd9cf04d642..4216bde0862 100644 --- a/docs/docs/generators/build.mdx +++ b/docs/docs/generators/build.mdx @@ -7,7 +7,7 @@ title: Build a generator A Generator queries data and creates new nodes and relationships from the result. The steps below cover how to create one. For conceptual background, see [About Generators](./overview). -For a step-by-step walkthrough with a running example, see [Build your first generator](../academy/tutorials/generators/build-your-first-generator) in the Academy tutorials. +For a step-by-step walkthrough with a running example, see [Build your first generator](../learn/tutorials/generators/build-your-first-generator) in the Academy tutorials. > Assumes a working Infrahub instance, a connected Git repository, and `infrahubctl` configured locally. See [Installation](../deploy-manage/install-configure/install/overview) and [Connect a repository](../git-integration/connect-repository) if you're starting fresh. diff --git a/docs/docs/generators/modular-best-practices.mdx b/docs/docs/generators/modular-best-practices.mdx index 777eb9ac2d4..9062d8463d1 100644 --- a/docs/docs/generators/modular-best-practices.mdx +++ b/docs/docs/generators/modular-best-practices.mdx @@ -4,7 +4,7 @@ title: Best practices for modular Generators The patterns below come from real-world experience building and operating modular Generator cascades in Infrahub. They address problems that are not obvious until you have built a multi-layer cascade and run it in production. -For foundational concepts, see [modular Generators](./modular). For the chaining mechanism, see [Build chained generators](../academy/tutorials/generators/build-chained-generators). +For foundational concepts, see [modular Generators](./modular). For the chaining mechanism, see [Build chained generators](../learn/tutorials/generators/build-chained-generators). ## 1. One Generator, one layer @@ -192,7 +192,7 @@ Generators work best when the schema supports the generation pattern. A few sche ### Add `GeneratorTarget` to all downstream target nodes -Any node kind that participates in a cascade as a downstream target should inherit from the `GeneratorTarget` generic (see [Build chained generators](../academy/tutorials/generators/build-chained-generators)). This gives it the `checksum` attribute needed for trigger-based chaining. +Any node kind that participates in a cascade as a downstream target should inherit from the `GeneratorTarget` generic (see [Build chained generators](../learn/tutorials/generators/build-chained-generators)). This gives it the `checksum` attribute needed for trigger-based chaining. ```yaml nodes: @@ -255,7 +255,7 @@ Always test cascades in a branch, not on the default branch. This lets you: - Delete the branch and start over if something goes wrong - Review the cascade output in a proposed change before merging -The `branch_scope: "other_branches"` trigger configuration (from [Build chained generators](../academy/tutorials/generators/build-chained-generators)) ensures triggers only fire in branches, giving you a controlled testing environment. +The `branch_scope: "other_branches"` trigger configuration (from [Build chained generators](../learn/tutorials/generators/build-chained-generators)) ensures triggers only fire in branches, giving you a controlled testing environment. ### Monitor Generator instances after changes diff --git a/docs/docs/generators/modular.mdx b/docs/docs/generators/modular.mdx index c112d8855c3..63c8c8f384c 100644 --- a/docs/docs/generators/modular.mdx +++ b/docs/docs/generators/modular.mdx @@ -9,7 +9,7 @@ Real-world automation is rarely that contained. A data center fabric has layers: **Modular Generators** solve this by splitting generation across multiple focused Generators, each responsible for one layer or domain. Later Generators depend on objects created by earlier ones, connected through an event-driven signaling mechanism. :::info -For single-Generator fundamentals, see [Generators](./overview). For a step-by-step walkthrough, see [Build your first Generator](../academy/tutorials/generators/build-your-first-generator) in the Academy tutorials. +For single-Generator fundamentals, see [Generators](./overview). For a step-by-step walkthrough, see [Build your first Generator](../learn/tutorials/generators/build-your-first-generator) in the Academy tutorials. ::: ## Why split into multiple Generators @@ -119,8 +119,8 @@ A fabric with 4 pods and 32 racks per pod runs 4 pod Generators concurrently, th - [Generators](./overview): single-Generator fundamentals, high-level design, and execution methods - [Build a generator](./build): recipe-form how-to for creating a Generator -- [Build your first generator](../academy/tutorials/generators/build-your-first-generator): step-by-step Academy tutorial -- [Build chained generators](../academy/tutorials/generators/build-chained-generators): checksum-based trigger pattern walkthrough +- [Build your first generator](../learn/tutorials/generators/build-your-first-generator): step-by-step Academy tutorial +- [Build chained generators](../learn/tutorials/generators/build-chained-generators): checksum-based trigger pattern walkthrough - [Modular Generator best practices](./modular-best-practices): idempotency, pool scoping, debugging, and operational guidance - [Groups](../groups/overview): Generators use groups to define their targets and track generated objects - [GraphQL queries](../development-resources/graphql/overview): each Generator definition includes a query that collects input data diff --git a/docs/docs/generators/overview.mdx b/docs/docs/generators/overview.mdx index aee4af8a0c6..0e76ff2b253 100644 --- a/docs/docs/generators/overview.mdx +++ b/docs/docs/generators/overview.mdx @@ -308,4 +308,4 @@ In the third video we will look at how a Generator can be created and run in Inf ## Learn by doing -For a step-by-step walkthrough that builds a Generator from scratch, see [Build your first Generator](../academy/tutorials/generators/build-your-first-generator) in the Academy tutorials. +For a step-by-step walkthrough that builds a Generator from scratch, see [Build your first Generator](../learn/tutorials/generators/build-your-first-generator) in the Academy tutorials. diff --git a/docs/docs/graph-traversal/query-with-graphql.mdx b/docs/docs/graph-traversal/query-with-graphql.mdx index 961b06e58a3..277760381cf 100644 --- a/docs/docs/graph-traversal/query-with-graphql.mdx +++ b/docs/docs/graph-traversal/query-with-graphql.mdx @@ -146,7 +146,7 @@ structural requirements that span relationships and fail a Traversal is branch- and time-aware, so a check evaluates connectivity on the proposed change's branch and catches violations before the change merges. See -[Build a check](../academy/tutorials/build-a-check.mdx) to set one up. +[Build a check](../learn/tutorials/build-a-check.mdx) to set one up. ## Full argument reference diff --git a/docs/docs/groups/overview.mdx b/docs/docs/groups/overview.mdx index 13a3cd69e2f..cb7d3e10128 100644 --- a/docs/docs/groups/overview.mdx +++ b/docs/docs/groups/overview.mdx @@ -65,7 +65,7 @@ For concrete patterns, see [Use groups in automation](./use-in-automation.mdx). ## Learn by doing -New to groups? Walk through [Organize objects with groups](../academy/tutorials/groups.mdx) in Academy. It creates a group end-to-end using a running example. +New to groups? Walk through [Organize objects with groups](../learn/tutorials/groups.mdx) in Academy. It creates a group end-to-end using a running example. ## Reference diff --git a/docs/docs/learn/labs/deploy-first-configuration.mdx b/docs/docs/learn/labs/deploy-first-configuration.mdx new file mode 100644 index 00000000000..77519e87cad --- /dev/null +++ b/docs/docs/learn/labs/deploy-first-configuration.mdx @@ -0,0 +1,34 @@ +--- +title: Deploy your first network configuration +description: Learn how to design, build and deploy infrastructure configurations using Infrahub's artifact feature. +hide_table_of_contents: true +--- + +# Generate and deploy your first network configuration using Infrahub + +| | | +|------|---------| +| **Prerequisites** | Basic experience with Infrahub, Ansible, Jinja and GraphQL | +| **Time to Completion** | Approximately 1 hour | + +:::info Hands-on Learning +This lab is supported by an Instruqt track. Instruqt is an online lab platform providing a pre-configured environment so you can focus on learning with hands-on experience. +::: + +## Overview + +Infrahub's artifact generation system combines structured data, GraphQL queries, and Jinja2 templates to produce deployable configuration for your network devices — no infrastructure data disconnected from the templates that consume it. + +In this lab, you'll build that pipeline yourself: define the query, write the template, and generate a real device configuration from Infrahub's source of truth. + +## What you'll learn + +By the end of this hands-on lab, you'll develop the following skills: + +- Creating focused GraphQL queries to extract specific infrastructure data +- Building reusable Jinja2 templates for generating device configurations +- Integrating queries and templates using Infrahub's Transformation system +- Testing configuration changes safely using Infrahub's branching capabilities +- Validating generated artifacts before deployment to production + +[Start the lab: Deploy Your First Configuration →](https://play.instruqt.com/opsmill/invite/hqia0uwvqe94) diff --git a/docs/docs/learn/labs/fundamentals-to-expert.mdx b/docs/docs/learn/labs/fundamentals-to-expert.mdx new file mode 100644 index 00000000000..fe8a8203ba0 --- /dev/null +++ b/docs/docs/learn/labs/fundamentals-to-expert.mdx @@ -0,0 +1,63 @@ +--- +title: 'Infrahub: Fundamentals to Expert' +description: A 5-track sequence taking you from a fresh Infrahub instance to a fully automated site deployment. +hide_table_of_contents: true +--- + +# Infrahub: fundamentals to expert + +| | | +|------|---------| +| **Prerequisites** | No prior experience with Infrahub required | +| **Time to Completion** | Approximately 5 hours across all 5 labs | + +:::info Hands-on Learning +This sequence is supported by 5 chained Instruqt labs. Instruqt is an online lab platform providing a pre-configured environment so you can focus on learning with hands-on experience. + +Each track builds on the concepts and capabilities introduced by the one before it, so we recommend doing them in order — but you can also complete one track and come back for the rest later. +::: + +## Overview + +In this sequence of labs, you'll join the network automation team at a network operator called OtterNet and learn Infrahub by tackling a real-world use case. Together, the 5 labs give you a 360° tour of Infrahub's features, from schema design to automated configuration deployment. + +## What you'll learn + +Across the 5 tracks, you'll build a working mental model of Infrahub's core concepts: + +- Modeling infrastructure data with custom node types, generic hierarchies, and inheritance +- Enforcing data quality through schema constraints, branch isolation, and Python checks +- Keeping shared values consistent using resource pools and Profiles +- Automating provisioning with design-driven Generators +- Rendering deployable configurations by turning source-of-truth data into artifacts with Jinja2 Transformations +- Accessing Infrahub programmatically via `infrahubctl` and the Python SDK + +## 1. Orientation + +Load a base schema, seed a two-site OtterNet topology (London and Amsterdam), and explore the data using the built-in GraphQL explorer in the Infrahub UI. You'll also access Infrahub programmatically via `infrahubctl` and the `InfrahubClientSync` Python SDK. By the end, you'll have a fully populated Infrahub instance ready for the tracks that follow. + +[Start Lab 1: Orientation →](https://play.instruqt.com/opsmill/invite/ndncxvvluews) + +## 2. Schema modeling + +OtterNet standardizes its sites around reusable blueprints called Site Designs. Define an `OtnSiteDesign` generic hierarchy, extend the existing `LocationSite` node with new fields (ASN, design, management subnet), and build the resource pools and device templates a Generator will later draw from. You'll finish by bringing a new site — Munich — online and assigning it a design. + +[Start Lab 2: Schema Modeling →](https://play.instruqt.com/opsmill/invite/2w9mrgb2hiiy) + +## 3. Enforcement & Validation + +Learn how Infrahub enforces data quality at every layer: schema constraints that fire instantly, branch isolation that keeps every mutation off `main` until it's reviewed, Python checks that run on proposed changes, and Profiles that keep shared values consistent by construction. + +[Start Lab 3: Enforcement & Validation →](https://play.instruqt.com/opsmill/invite/qeaxcdqe0sz8) + +## 4. Design-driven Generator + +Write and run a Generator that reads OtterNet's campus site design and provisions every device at the new Munich site automatically — right device names, right resources, nothing typed by hand. + +[Start Lab 4: Design-Driven Generator →](https://play.instruqt.com/opsmill/invite/ul1nwuglxhru) + +## 5. Transformations & configuration rendering + +Close the loop: explore a Jinja2 Transformation and render a complete, deployable router configuration for a Munich device, pulling every value — hostname, FQDN, management IP, BGP ASN — straight from the source of truth. + +[Start Lab 5: Transformations & Configuration Rendering →](https://play.instruqt.com/opsmill/invite/eha2fxwprzok) diff --git a/docs/docs/learn/labs/infrahub-introduction.mdx b/docs/docs/learn/labs/infrahub-introduction.mdx new file mode 100644 index 00000000000..eaadbc5e013 --- /dev/null +++ b/docs/docs/learn/labs/infrahub-introduction.mdx @@ -0,0 +1,35 @@ +--- +title: First Tour of Infrahub +description: A comprehensive introduction to Infrahub, its core concepts, and how it revolutionizes infrastructure management. +hide_table_of_contents: true +--- + +# First tour of Infrahub + +| | | +|------|---------| +| **Prerequisites** | No prior experience with Infrahub required | +| **Time to Completion** | Approximately 30 minutes | + +:::info Hands-on Learning +This lab is supported by an Instruqt track. Instruqt is an online lab platform providing a pre-configured environment so you can focus on learning with hands-on experience. +::: + +## Overview + +Infrahub unifies infrastructure management by combining Git-like version control with a flexible graph database, covering three core pillars: branching and version control, a flexible schema, and unified storage. + +This lab provides a ready-to-use environment where you can explore these core features without setup overhead, through practical exercises against a running Infrahub instance. + +## What you'll learn + +In this introductory track, we've designed a focused learning experience that will help you: + +- Understand how Infrahub's core concepts work in practice +- Determine whether Infrahub aligns with your infrastructure management needs +- Gain the fundamental knowledge needed to explore more advanced topics +- Experience the platform's key features through hands-on exercises + +Whether you're a network engineer, a DevOps professional, or an infrastructure architect, this introduction will provide valuable insights into how Infrahub can transform your approach to infrastructure management. + +[Start the lab: First Tour of Infrahub →](https://play.instruqt.com/opsmill/invite/7jdp4cuqbcvb) diff --git a/docs/docs/learn/labs/overview.mdx b/docs/docs/learn/labs/overview.mdx new file mode 100644 index 00000000000..ae6a72c5040 --- /dev/null +++ b/docs/docs/learn/labs/overview.mdx @@ -0,0 +1,38 @@ +--- +title: Infrahub Labs +--- + +# Infrahub Labs + +The quickest way to learn Infrahub is to run a lab. Each one gives you a live Infrahub environment and a guided scenario to work through in your browser — you learn what Infrahub does, and how to use it, by doing real work rather than reading about it. The investment is small: the shortest lab takes 30 minutes, and there is nothing to set up — OpsMill provisions a sandbox with Infrahub already running for every session, on Instruqt, an online lab platform. + +What you take away is working knowledge, not a product tour: each lab covers one scoped topic, and the concepts you practice in the sandbox — modeling data, enforcing quality, generating configuration — are the same ones you'll apply in your own environment. + +Use a lab when: + +- You're evaluating Infrahub and want to see what the product does before installing it +- You're getting started and want guided practice with the fundamentals +- You run a basic setup already and want to learn a specific topic in more depth + +## Infrahub: fundamentals to expert + +**[Infrahub: Fundamentals to Expert](./fundamentals-to-expert)** takes about 5 hours across five labs: + +1. Orientation +2. Schema modeling +3. Enforcement and validation +4. Design-driven Generators +5. Transformations and configuration rendering + +You join the network automation team of a fictional operator, OtterNet, and take a site from an empty Infrahub instance to a fully automated deployment, all on one shared demo dataset. Start with this sequence for a complete tour of Infrahub's features. + +## Getting started + +Standalone labs for your first sessions with Infrahub: + +- **[First Tour of Infrahub](./infrahub-introduction)** (30 minutes) — a guided overview of Infrahub's core concepts. Take this lab if you're new to Infrahub and want to see what the product does and whether it fits your needs. +- **[Deploy Your First Configuration](./deploy-first-configuration)** (1 hour) — generate and deploy a device configuration using Infrahub's artifact feature. Take this lab once you know the basics and want to see how source-of-truth data becomes deployable configuration. + +## Advanced topics + +- **[Schema Deep Dive](./schema-deep-dive)** (2 hours) — consume the schema library, create your own schema, and extend one that's already in use. Take this lab when you understand the schema fundamentals and are ready to design schemas for your own use case. diff --git a/docs/docs/learn/labs/schema-deep-dive.mdx b/docs/docs/learn/labs/schema-deep-dive.mdx new file mode 100644 index 00000000000..4b8ad185c16 --- /dev/null +++ b/docs/docs/learn/labs/schema-deep-dive.mdx @@ -0,0 +1,30 @@ +--- +title: Schema Deep Dive +description: Getting started with Infrahub's flexible schema — consume the schema library, create a schema, and enhance it. +hide_table_of_contents: true +--- + +# Schema deep dive + +| | | +|------|---------| +| **Prerequisites** | Basic experience with Infrahub (complete "[First Tour of Infrahub](./infrahub-introduction)" first) | +| **Time to Completion** | Approximately 2 hours | + +:::info Hands-on Learning +This lab is supported by an Instruqt track. Instruqt is an online lab platform providing a pre-configured environment so you can focus on learning with hands-on experience. +::: + +## Overview + +Infrahub's schema is flexible by design, but that flexibility raises questions the first time you touch it: where do schemas come from, how do you write your own, and how do you safely extend one that's already in use. This lab walks through all three. + +## What you'll learn + +By the end of this hands-on lab, you'll be able to: + +- Consume the Infrahub schema library to load ready-made node types +- Create a schema from scratch for your own use case +- Enhance an existing schema with new attributes and relationships + +[Start the lab: Schema Deep Dive →](https://play.instruqt.com/opsmill/invite/tvg3faduoyuj) diff --git a/docs/docs/academy/tutorials/build-a-check.mdx b/docs/docs/learn/tutorials/build-a-check.mdx similarity index 100% rename from docs/docs/academy/tutorials/build-a-check.mdx rename to docs/docs/learn/tutorials/build-a-check.mdx diff --git a/docs/docs/academy/tutorials/build-your-first-schema.mdx b/docs/docs/learn/tutorials/build-your-first-schema.mdx similarity index 100% rename from docs/docs/academy/tutorials/build-your-first-schema.mdx rename to docs/docs/learn/tutorials/build-your-first-schema.mdx diff --git a/docs/docs/academy/tutorials/generators/build-chained-generators.mdx b/docs/docs/learn/tutorials/generators/build-chained-generators.mdx similarity index 100% rename from docs/docs/academy/tutorials/generators/build-chained-generators.mdx rename to docs/docs/learn/tutorials/generators/build-chained-generators.mdx diff --git a/docs/docs/academy/tutorials/generators/build-your-first-generator.mdx b/docs/docs/learn/tutorials/generators/build-your-first-generator.mdx similarity index 100% rename from docs/docs/academy/tutorials/generators/build-your-first-generator.mdx rename to docs/docs/learn/tutorials/generators/build-your-first-generator.mdx diff --git a/docs/docs/academy/tutorials/groups.mdx b/docs/docs/learn/tutorials/groups.mdx similarity index 100% rename from docs/docs/academy/tutorials/groups.mdx rename to docs/docs/learn/tutorials/groups.mdx diff --git a/docs/docs/learn/tutorials/overview.mdx b/docs/docs/learn/tutorials/overview.mdx new file mode 100644 index 00000000000..e912743f2c3 --- /dev/null +++ b/docs/docs/learn/tutorials/overview.mdx @@ -0,0 +1,37 @@ +--- +title: Tutorials +--- + +# Tutorials + +The most direct way to learn a specific Infrahub workflow is to run a tutorial. Each one walks you through a real task end to end — modeling a schema, writing a check, building a Generator — against your own Infrahub instance, using the same GraphQL, Python, and schema snippets you'd write in production. You follow the steps directly in these docs, at your own pace, with no external platform involved. + +What you take away is a working example you built yourself, not a copy-pasted snippet: each tutorial explains why each step matters, so you leave with the concepts as well as the commands. + +Use a tutorial when: + +- You already have Infrahub running and want a guided, hands-on introduction to a specific feature +- You learn best by building something real rather than reading a conceptual overview +- You want a working example you can adapt for your own use case + +## Schema + +- **[Build your first schema](./build-your-first-schema)** — model network devices and interfaces (nodes, attributes, relationships, and generics), then load each version into a branch. Take this tutorial first if you're new to schema design. + +## Creating objects + +- **[Organize objects with groups](./groups)** — create a group, add objects to it, and query the result end to end. Take this tutorial before using groups to target checks, Generators, or other automation. + +## Data validation + +- **[Build a check](./build-a-check)** — build, deploy, and validate a custom check that enforces a naming convention, from GraphQL query to proposed change. Take this tutorial once you have a schema in place and want to enforce data-quality rules on it. + +## Transformations + +- **[Build a Jinja2 Transformation](./transformations/build-a-jinja2-transformation)** — render a device configuration snippet from a GraphQL query, test it locally, and call it through the render API. +- **[Build a Python Transformation](./transformations/build-a-python-transformation)** — implement a Transformation class that returns JSON, test it locally, and call it through the REST API. Take this tutorial to see the Python and Jinja2 approaches side by side and pick the one that fits your use case. + +## Generators + +- **[Build your first generator](./generators/build-your-first-generator)** — model two object kinds, write a GraphQL query, and implement a Generator that creates objects automatically whenever a change is proposed. +- **[Build chained generators](./generators/build-chained-generators)** — wire two layers of modular Generators together with a checksum attribute so a downstream Generator only runs once its upstream dependency has finished. Take this tutorial once you're comfortable with a single Generator and need to model a multi-stage pipeline. diff --git a/docs/docs/academy/tutorials/transformations/build-a-jinja2-transformation.mdx b/docs/docs/learn/tutorials/transformations/build-a-jinja2-transformation.mdx similarity index 100% rename from docs/docs/academy/tutorials/transformations/build-a-jinja2-transformation.mdx rename to docs/docs/learn/tutorials/transformations/build-a-jinja2-transformation.mdx diff --git a/docs/docs/academy/tutorials/transformations/build-a-python-transformation.mdx b/docs/docs/learn/tutorials/transformations/build-a-python-transformation.mdx similarity index 100% rename from docs/docs/academy/tutorials/transformations/build-a-python-transformation.mdx rename to docs/docs/learn/tutorials/transformations/build-a-python-transformation.mdx diff --git a/docs/docs/object-templates/use.mdx b/docs/docs/object-templates/use.mdx index 59bda6854a7..29bc422222d 100644 --- a/docs/docs/object-templates/use.mdx +++ b/docs/docs/object-templates/use.mdx @@ -18,7 +18,7 @@ For more details, refer to the [Object Templates](./overview.mdx) topic. ## Enable template support within the schema -If you are already familiar with [Schema Development](../academy/tutorials/build-your-first-schema) in Infrahub, enabling template generation is straightforward. +If you are already familiar with [Schema Development](../learn/tutorials/build-your-first-schema) in Infrahub, enabling template generation is straightforward. At the node level, the `generate_template` property allows users to enable template generation for a given node and its associated components. diff --git a/docs/docs/overview/concepts.mdx b/docs/docs/overview/concepts.mdx index 919c43b1560..28d1a536b6a 100644 --- a/docs/docs/overview/concepts.mdx +++ b/docs/docs/overview/concepts.mdx @@ -18,7 +18,7 @@ Example schemas can be found in the [Infrahub Marketplace](https://marketplace.i The schema can be loaded using the [`infrahubctl schema load`]($(base_url)infrahubctl/infrahubctl-load) command for development, or via Infrahub Git integration for production deployments. Once loaded, it's stored and version controlled in the graph database. Changes to the schema can be made at any time, and it's best practice to make schema changes in a branch to allow for testing before implementation. - + ## Transformations diff --git a/docs/docs/overview/next-steps.mdx b/docs/docs/overview/next-steps.mdx index eb036183451..18f432c4c71 100644 --- a/docs/docs/overview/next-steps.mdx +++ b/docs/docs/overview/next-steps.mdx @@ -12,7 +12,7 @@ The Quick Start used pre-built schemas from the Schema Library. For a POC, model Start with a focused scope. Pick one domain (for example: data center fabric, WAN sites, or firewall policies) and model it. You can always expand later. The [Infrahub Marketplace](https://marketplace.infrahub.app) is a good starting point — browse it for schemas that match your domain and extend from there. - + ## 2. Load real data @@ -52,7 +52,7 @@ A local Docker setup works for development, but for a POC that involves your tea At this point, you have a running Infrahub instance with your own schema, real data, a connected Git repository, and generated artifacts. From here you can: -- [Build Generators](../academy/tutorials/generators/build-your-first-generator) to automate the creation of infrastructure objects from templates and business logic. +- [Build Generators](../learn/tutorials/generators/build-your-first-generator) to automate the creation of infrastructure objects from templates and business logic. - [Set up events and webhooks](../events/overview) to trigger external systems when data changes. - [Deploy artifacts with Ansible]($(base_url)ansible) or [Nornir]($(base_url)nornir) to push configurations to your infrastructure. diff --git a/docs/docs/schema/create-and-load.mdx b/docs/docs/schema/create-and-load.mdx index bd73372432c..73288e52236 100644 --- a/docs/docs/schema/create-and-load.mdx +++ b/docs/docs/schema/create-and-load.mdx @@ -156,4 +156,4 @@ This issue commonly appears in: - [Schema extensions](./extensions) — Add attributes and relationships to existing nodes - [Schema migration](./migration) — How Infrahub handles schema updates and data migrations - [Schema validation](../reference/schema-validation) — Editor validation for schema YAML files -- [Build your first schema](../academy/tutorials/build-your-first-schema) — Step-by-step tutorial for creating a schema from scratch +- [Build your first schema](../learn/tutorials/build-your-first-schema) — Step-by-step tutorial for creating a schema from scratch diff --git a/docs/docs/transformations/jinja2.mdx b/docs/docs/transformations/jinja2.mdx index 00d5d0247df..c719c9cca53 100644 --- a/docs/docs/transformations/jinja2.mdx +++ b/docs/docs/transformations/jinja2.mdx @@ -7,7 +7,7 @@ title: Write a Jinja2 Transformation A Jinja2 Transformation renders Infrahub data through a Jinja template, producing plain text output (configurations, manifests, payloads). The steps below cover how to write one. For conceptual background, see [Transformations](./overview). -For a step-by-step walkthrough with a running example, see [Build a Jinja2 Transformation](../academy/tutorials/transformations/build-a-jinja2-transformation) in the Academy tutorials. +For a step-by-step walkthrough with a running example, see [Build a Jinja2 Transformation](../learn/tutorials/transformations/build-a-jinja2-transformation) in the Academy tutorials. > Assumes a working Infrahub instance, a connected Git repository, and `infrahubctl` configured locally. See [Installation](../deploy-manage/install-configure/install/overview) and [Connect a repository](../git-integration/connect-repository) if you're starting fresh. diff --git a/docs/docs/transformations/overview.mdx b/docs/docs/transformations/overview.mdx index a5ce3e268f3..cd00cb9b3dc 100644 --- a/docs/docs/transformations/overview.mdx +++ b/docs/docs/transformations/overview.mdx @@ -55,7 +55,7 @@ Infrahub can natively render any Jinja templates dynamically. Internally it's re #### Create a Jinja rendered Transformation -See [Write a Jinja2 Transformation](./jinja2.mdx) for the recipe-form how-to, or [Build a Jinja2 Transformation](../academy/tutorials/transformations/build-a-jinja2-transformation) for the step-by-step walkthrough with a running example. +See [Write a Jinja2 Transformation](./jinja2.mdx) for the recipe-form how-to, or [Build a Jinja2 Transformation](../learn/tutorials/transformations/build-a-jinja2-transformation) for the step-by-step walkthrough with a running example. #### Render a Jinja2 Transformation @@ -73,7 +73,7 @@ A `TransformPython` is a Transformation plugin written in Python. It can generat A TransformPython must be written as a Python class that inherits from `InfrahubTransform` and it must implement one `transform` method. The transform method must accept a dict and return one. -See [Write a Python Transformation](./python.mdx) for the recipe-form how-to, or [Build a Python Transformation](../academy/tutorials/transformations/build-a-python-transformation) for the step-by-step walkthrough with a running example. +See [Write a Python Transformation](./python.mdx) for the recipe-form how-to, or [Build a Python Transformation](../learn/tutorials/transformations/build-a-python-transformation) for the step-by-step walkthrough with a running example. ##### Python Transformation accessing local files @@ -131,5 +131,5 @@ For more information, see the [Resource Testing Framework](../testing-framework/ Two Academy tutorials walk through building a Transformation end-to-end with a working device-configuration example: -- [Build a Jinja2 Transformation](../academy/tutorials/transformations/build-a-jinja2-transformation) -- [Build a Python Transformation](../academy/tutorials/transformations/build-a-python-transformation) +- [Build a Jinja2 Transformation](../learn/tutorials/transformations/build-a-jinja2-transformation) +- [Build a Python Transformation](../learn/tutorials/transformations/build-a-python-transformation) diff --git a/docs/docs/transformations/python.mdx b/docs/docs/transformations/python.mdx index 7dcb6c6cfd3..4611a954a42 100644 --- a/docs/docs/transformations/python.mdx +++ b/docs/docs/transformations/python.mdx @@ -7,7 +7,7 @@ title: Write a Python Transformation A Python Transformation processes Infrahub data through user-written Python code, producing JSON output (or any structured data you serialize). Use this when Jinja templating isn't enough — for example, conditional logic, external API calls, or complex aggregation. The steps below cover how to write one. For conceptual background, see [Transformations](./overview). -For a step-by-step walkthrough with a running example, see [Build a Python Transformation](../academy/tutorials/transformations/build-a-python-transformation) in the Academy tutorials. +For a step-by-step walkthrough with a running example, see [Build a Python Transformation](../learn/tutorials/transformations/build-a-python-transformation) in the Academy tutorials. > Assumes a working Infrahub instance, a connected Git repository, and `infrahubctl` configured locally. See [Installation](../deploy-manage/install-configure/install/overview) and [Connect a repository](../git-integration/connect-repository) if you're starting fresh. diff --git a/docs/docs/webhooks/custom-transformation.mdx b/docs/docs/webhooks/custom-transformation.mdx index e244c452f57..f3a1156f9d1 100644 --- a/docs/docs/webhooks/custom-transformation.mdx +++ b/docs/docs/webhooks/custom-transformation.mdx @@ -8,7 +8,7 @@ import TabItem from '@theme/TabItem'; Custom webhooks allow you to transform event data before sending it to an external endpoint. This is useful when the receiving system expects a specific payload format, such as Slack, Microsoft Teams, GitHub Actions, or other third-party APIs. -For general information about Python Transformations, see the [Python Transformation guide](../academy/tutorials/transformations/build-a-python-transformation). For details about webhook events and payloads, see [Webhooks](./overview). +For general information about Python Transformations, see the [Python Transformation guide](../learn/tutorials/transformations/build-a-python-transformation). For details about webhook events and payloads, see [Webhooks](./overview). :::note diff --git a/docs/sidebars.ts b/docs/sidebars.ts index 76d530387d5..ab7722803f7 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -59,28 +59,29 @@ const sidebars: SidebarsConfig = { collapsible: false, collapsed: false, items: [ - { type: 'doc', id: 'academy/academy', label: 'About Academy' }, { type: 'category', - label: 'Getting Started', - link: { type: 'generated-index' }, + label: 'Infrahub Labs', + link: { type: 'doc', id: 'learn/labs/overview' }, // hub items: [ - 'academy/getting-started/infrahub-introduction', - 'academy/getting-started/deploy-first-configuration', + { type: 'doc', id: 'learn/labs/fundamentals-to-expert', label: 'Infrahub: Fundamentals to Expert' }, + { type: 'doc', id: 'learn/labs/infrahub-introduction', label: 'First Tour of Infrahub' }, + { type: 'doc', id: 'learn/labs/schema-deep-dive', label: 'Schema Deep Dive' }, + 'learn/labs/deploy-first-configuration', ], }, { type: 'category', label: 'Tutorials', - link: { type: 'generated-index' }, - items: [ - 'academy/tutorials/build-your-first-schema', - 'academy/tutorials/groups', - 'academy/tutorials/build-a-check', - 'academy/tutorials/transformations/build-a-jinja2-transformation', - 'academy/tutorials/transformations/build-a-python-transformation', - 'academy/tutorials/generators/build-your-first-generator', - 'academy/tutorials/generators/build-chained-generators', + link: { type: 'doc', id: 'learn/tutorials/overview' }, // hub + items: [ + 'learn/tutorials/build-your-first-schema', + 'learn/tutorials/groups', + 'learn/tutorials/build-a-check', + 'learn/tutorials/transformations/build-a-jinja2-transformation', + 'learn/tutorials/transformations/build-a-python-transformation', + 'learn/tutorials/generators/build-your-first-generator', + 'learn/tutorials/generators/build-chained-generators', ], }, ], From 7b36dc0d4bc42cf6b9b0a0f3c333bca520d14fb8 Mon Sep 17 00:00:00 2001 From: Baptiste <32564248+BaptisteGi@users.noreply.github.com> Date: Thu, 13 Aug 2026 10:28:14 +0200 Subject: [PATCH 43/48] docs(objects): add Create objects overview page (#10233) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs(objects): add "Create objects" overview page Introduces a single entry point listing every way to create objects (web interface, infrahubctl, YAML object files, Python SDK, GraphQL API, Generators, Infrahub Sync) and links it from the related pages. Also drops the redundant "What you can do with objects" list from overview.mdx and shortens a couple of page titles. Co-Authored-By: Claude Sonnet 5 * docs(objects): give Create objects real per-method context The page shipped as a routing table: it named each method and linked out, but gave no basis for choosing and showed no example of any method. It also stated the web UI button as "Create " when the button reads "Add ". Each method now carries a paragraph on when to use it, and one running example (InfraDevice atl1-edge1) is shown four ways through the shared groupId="method" tabs, so a reader's interface choice persists across the docs site. Generators and Infrahub Sync move out of the method table into their own section, since they create objects from a workflow rather than from a command. Adds sections on Object Templates, Profiles, and resource pools, and on what happens after an object is created. Restores outbound navigation on the Objects hub as "## In this section", matching the format used by the IPAM and Schema hubs. The PR had removed the hub's only links to its own child pages. Facts verified against models/base/dcim.yml (required attributes and the site relationship), object-create-form-trigger.tsx (button and panel labels), generators/overview.mdx (execution model), and the Python SDK and Infrahub Sync docs. * docs(objects): restructure Create objects into three categories The page presented seven creation methods in three formats across four locations: five in a table, four of those repeated as tabs, one as its own section, and two more introduced only as intro bullets and then described again below. Nothing shared a shape, so the methods could not be compared. Groups them under what drives the create — directly, from a file, from a workflow — and gives every method the same three beats: when to use it, an example or the mechanism, and where to read more. Drops the table, since the useful content per method is a descriptive paragraph rather than a cell value, and three categories are easier to hold than seven rows. Drops the duplicated intro bullets. Folds Object Templates, Profiles, and resource pools into "After an object is created", framed as where an attribute's value came from, which ties them to the metadata and lineage they are recorded in. Follows the structure Kubernetes uses for the equivalent page (Kubernetes Object Management), where parallel treatment of each technique is what makes them comparable. * docs(objects): fix voice regressions in Create objects Words removed earlier had returned during the restructure. Fixes each against the rules they broke: Figurative usage: "a file that lives in Git" -> "stored in Git"; "what drives it" -> "what starts the work"; "three commands drive it" -> "run it"; "objects follow from data" -> "are determined by data"; "circuits a service definition implies" -> "required by"; "a Group naming the objects" -> "that lists". Vague UI jargon: "appear as selectable controls" -> "pick an Object Template, assign Profiles, and allocate from a resource pool", naming what the reader does. Features as the agent, in the section flagged for exactly this: "Object Templates supply structure" -> "Pick an Object Template to start the object with...". The reader is now the subject of all three. Definition by negation: "without leaving the terminal" and "without changes" removed. Unverifiable claims: "the quickest way", "actually needs", "straightforward scripting". Overclaim: "Infrahub has four interfaces" -> "Four interfaces create objects", since the REST API exists too. Splits the four Generator trigger conditions out of one long sentence into a list. * docs(objects): restore the category summary at the top The restructure compressed the three categories into a single sentence, so a reader had no way to see what each one is for before scrolling into it. Restores a three-item summary, each naming what the category is and when to use it, linking to its section so a reader can go straight to the one they need. Reworks the "from a workflow" opener so it draws the distinction between the two tools rather than repeating the summary: a Generator works from data already in Infrahub, Infrahub Sync from data in another system. * docs(objects): make the category summary tell them apart "Use this when you are deciding what to create as you go" gave a reader nothing to choose on, and the three descriptions were not comparable to each other. Each now states the same three things, so they can be read against one another: how many objects at a time, where the definition comes from, and whether it happens once or keeps happening. The lead-in names those axes instead of "what starts the work". One at a time, manually or in a short script -> directly. Many declared together and reviewed in Git -> from a file. Created and updated as the source changes, without anyone issuing a command -> from a workflow. * docs(objects): make each section opener say what it is for Applies the test used on the category summary to every section and tab opener: say what the thing is for, be concrete, give the reader something to decide on. Object files led with "data that changes rarely", which describes a property rather than a purpose. Now states what the file is and what keeping it in Git buys: rebuild an instance from it, promote the same dataset from development to production, review a data change in a pull request. Names the data it suits — sites, roles, platforms, device types, tags. GraphQL was defined by negation ("when the client is not Python"). Now positive and concrete: any language or tool that can send an HTTP request, with examples. "Create objects directly" said the four interfaces are equivalent but not what separates them; it now names the four contexts. The web interface opener explains why starting there helps — the form shows what a script will need to supply later. The workflow opener states what both tools are for rather than only how they run. Promotion between environments verified against git-integration/multi-environment.mdx. * docs(objects): fix three awkward openers "Four interfaces create objects" made the interface the actor, the same problem flagged earlier with features. Now "There are four ways to create an object one at a time", and the abstract noun is gone. "How you create objects depends on..." opened on a nominalization instead of the fact the reader wants. Now leads with "There are three ways to add objects to Infrahub", then the criteria for choosing. The workflow opener started on "Both", referring to nothing yet introduced in the section, and ended in "That is what suits them to data no one should be maintaining manually — the objects that ought to exist because something else is true", which said very little at length. Replaced with the actual use case: some objects exist because other data does and have to stay correct as it changes, so maintaining them manually means repeating the work every time. Uses "choose" rather than "pick" throughout. * docs(objects): say what applies to which creation methods "All three work whichever method created the object" left both halves unclear: "work" stated nothing, and the clause did not say what the methods were. Names the subjects and points back to the page's own three categories, so the scope is explicit. Also drops the negation in the lead-in — "so that you do not fill it in each time" becomes "so the object receives it on creation". * fix linting --------- Co-authored-by: Claude Sonnet 5 Co-authored-by: Yvonne Jouffrault --- .vale/styles/spelling-exceptions.txt | 2 + docs/docs/objects/create-objects.mdx | 177 ++++++++++++++++++++++++++ docs/docs/objects/load-from-yaml.mdx | 4 +- docs/docs/objects/manage-from-cli.mdx | 4 +- docs/docs/objects/overview.mdx | 11 +- docs/sidebars.ts | 5 +- 6 files changed, 192 insertions(+), 11 deletions(-) create mode 100644 docs/docs/objects/create-objects.mdx diff --git a/.vale/styles/spelling-exceptions.txt b/.vale/styles/spelling-exceptions.txt index 732dab4f691..d30c93258a6 100644 --- a/.vale/styles/spelling-exceptions.txt +++ b/.vale/styles/spelling-exceptions.txt @@ -32,6 +32,7 @@ Ceph changelog conftest Conftest +cron check_color_tags_name check_definitions class_name @@ -173,6 +174,7 @@ Observium OIDC Okta Onboarding +onboarding OpenAPI order_by order_weight diff --git a/docs/docs/objects/create-objects.mdx b/docs/docs/objects/create-objects.mdx new file mode 100644 index 00000000000..736f2394f1f --- /dev/null +++ b/docs/docs/objects/create-objects.mdx @@ -0,0 +1,177 @@ +--- +title: Create objects +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +There are three ways to add objects to Infrahub. Which one you need depends on how many objects you are creating and where their definition comes from: + +- **[Directly](#create-objects-directly)** — create objects one at a time through the web interface, `infrahubctl`, the Python SDK, or GraphQL. Use this for a single device, a few prefixes, or any change small enough to enter manually or in a short script. +- **[From a file](#create-objects-from-a-file)** — declare many objects in a YAML file kept in Git, then load them together. Use this to populate a new instance, or for data that someone should review before it changes. +- **[From a workflow](#create-objects-from-a-workflow)** — configure a Generator or Infrahub Sync once, then objects are created and updated as the underlying data changes. Use this when a rule determines what should exist, or when another system is already the source. + +## Create objects directly + +There are four ways to create an object one at a time, and all four produce the same result: the same object, validated against the same schema constraints, on the branch you are working in. Choose based on where you are already working — a browser, a terminal, a Python program, or a GraphQL client. + +The example below creates a device named `atl1-edge1` at site `atl1`. On `InfraDevice`, `name` and `type` are required and `site` is a relationship. + + + + +Use the web interface for a single object, and for the first object of a kind you have not created before. The form is built from the schema, so it shows every field the kind defines, marks which ones are required, and validates values as you type — which also tells you what a script will need to supply later. + +1. Open the Device list from the left menu. +2. Select **Add Device**. The **Create Device** panel opens. +3. Fill in the name and type. +4. Select a site, then save. + +In the web form you can also choose an Object Template, assign Profiles, and allocate from a resource pool while filling in the object. The other three accept the same values as fields. + + + + + +Use `infrahubctl` for interactive work and for scripting one object at a time — onboarding a device during a maintenance window, or a shell script that creates a few objects as one step in a longer task. + +```bash +infrahubctl object create InfraDevice \ + --set name=atl1-edge1 \ + --set type=7280R3 \ + --set site=atl1 +``` + +Relationship values resolve by name, so `site=atl1` looks up the site and links it. + +[Manage objects with infrahubctl](./manage-from-cli.mdx) covers querying, updating, and deleting. + + + + + +Use the Python SDK for custom scripts and integrations — when a program decides what to create rather than a person. The SDK handles authentication, query construction, and serialization, so your code works with objects rather than HTTP requests. Transformations, Generators, and checks run against this same client, so a script you write standalone runs unchanged inside Infrahub's pipeline. + +```python +from infrahub_sdk import InfrahubClientSync + +client = InfrahubClientSync(address="http://localhost:8000") + +device = client.create( + kind="InfraDevice", + name="atl1-edge1", + type="7280R3", + site="atl1", +) +device.save() +``` + +To create many objects in one run, group the calls into a batch with a concurrency limit. Use the synchronous client for scripting, and the async client for work with concurrent I/O. + +See the [Python SDK]($(base_url)python-sdk/introduction) documentation. + + + + + +Use the GraphQL API to create objects from any language or tool that can send an HTTP request — Go, TypeScript, or a shell script using `curl`. It is also where to write from when you are already reading data over GraphQL and want both in the same place. + +```graphql +mutation { + InfraDeviceCreate( + data: { + name: { value: "atl1-edge1" } + type: { value: "7280R3" } + site: { hfid: ["atl1"] } + } + ) { + ok + object { + id + hfid + } + } +} +``` + +Infrahub generates four mutations for every model in your schema, named from its namespace and name — `InfraDeviceCreate`, `InfraDeviceUpdate`, `InfraDeviceUpsert`, and `InfraDeviceDelete`. Use `Upsert` when the object may already exist and you want one call to cover both cases. To create the object on a branch, post to `/graphql/`. + +See [Queries & mutations](../development-resources/graphql/queries-and-mutations.mdx). + + + + +## Create objects from a file + +An object file declares a set of objects in YAML: which kind they are and what values they carry. Keeping that file in a Git repository makes the data reproducible — you can rebuild an instance from it, promote the same dataset from development to production, and review a change to your data in a pull request before it reaches Infrahub. + +Use object files for the reference data your instance is built on and that rarely changes afterwards — sites, roles, platforms, device types, standard tags — and for the dataset that populates a new instance. + +```yaml +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: InfraDevice + data: + - name: atl1-edge1 + type: 7280R3 + site: atl1 + - name: atl1-edge2 + type: 7280R3 + site: atl1 +``` + +There are two ways to load a file, and they differ in what happens afterwards. `infrahubctl object load` imports the file once, and Infrahub keeps no link to it. Declaring the file under `objects:` in a repository's `.infrahub.yml` means Infrahub tracks it, so removing a record from the file deletes the object on the next import. + +[Load data using YAML file](./load-from-yaml.mdx) covers the file format, nested objects, and load order. + +## Create objects from a workflow + +Some objects exist because other data does, and they have to stay correct as that data changes. Maintaining them manually means repeating the same work every time something upstream changes. + +Configure a Generator or an Infrahub Sync project once, and it creates those objects and updates them when the source data changes. A Generator works from data already in Infrahub. Infrahub Sync works from data in another system. + +### Generators + +Use a Generator when the objects you need are determined by data Infrahub already holds — an IP address for every new interface, or the circuits required by a service definition. You describe the rule once, and it applies to every target, including targets added later. + +A Generator has three parts: a GraphQL query that collects the data, the Python that acts on the results, and a target Group that lists the objects it runs against. Infrahub creates one run per member of that group, so a Generator targeting a group of ten racks produces ten independent runs, each reading only its own rack's data. + +Objects are created when a run executes. A run starts when you: + +- Open a Proposed Change that affects the targets, where the Generator runs as one of the CI checks. +- Run the definition from the UI under **Actions > Generator Definitions**. +- Trigger it with an Event rule you configured. +- Run it locally with `infrahubctl` while developing. + +Each run also deletes objects it created earlier that the current data no longer requires. + +See [Generators](../generators/overview.mdx). + +### Infrahub Sync + +Use Infrahub Sync when another system holds data you need in Infrahub and stays the system of record for it. Sync moves infrastructure data between Infrahub and external systems — NetBox, Nautobot, IP Fabric, Slurp'it, Cisco ACI, Peering Manager, and any system with a REST API. Sync also runs in the other direction, publishing Infrahub data into monitoring, observability, or CMDB systems. + +Define a sync project in YAML that maps the source system's models onto your schema. Three commands run it: `generate` builds the adapter code from that mapping, `diff` shows what would change without applying it, and `sync` applies the changes. Objects are created in Infrahub on the `sync` run, for every record in the source with no match in Infrahub. Each run calculates a fresh diff and applies only the deltas, so a run that fails partway can be repeated safely. + +Sync runs as a CLI, so you schedule it with the tooling you already use — cron, a CI job, or Prefect. + +See [Infrahub Sync]($(base_url)sync) for adapters, mapping rules, and configuration. + +## After an object is created + +A new object exists on the branch it was created on. On a branch other than the default, it remains isolated until that branch merges through a [proposed change](../proposed-changes/overview.mdx). + +Every attribute records how its value was set, which you can read back through [metadata and lineage](./metadata.mdx). You either entered the value while creating the object, or you set it up in advance so the object receives it on creation: + +- Choose an [Object Template](../object-templates/overview.mdx) to start the object with a set of components already in place. +- Assign a [Profile](../profiles/overview.mdx) to give the object attribute values it inherits, and override any of them on the object itself. +- Allocate from a [Resource Manager](../resource-manager/overview.mdx) pool to take the next free IP address or VLAN ID. + +Templates, Profiles, and pools apply whichever of the three ways you used to create the object — directly, from a file, or from a workflow. + +## Related + +- [Objects](./overview.mdx) — what an object is and how it relates to schema nodes +- [Convert object kind](./convert-object-kind.mdx) — change the schema kind of an existing object diff --git a/docs/docs/objects/load-from-yaml.mdx b/docs/docs/objects/load-from-yaml.mdx index 816db28bb93..482eaa0d50b 100644 --- a/docs/docs/objects/load-from-yaml.mdx +++ b/docs/docs/objects/load-from-yaml.mdx @@ -1,5 +1,5 @@ --- -title: Load data in bulk using YAML file +title: Load data using YAML file --- # Object files @@ -93,7 +93,7 @@ infrahubctl schema load /path/to/schema.yml ## Defining a object file -Our goal is to define data that can be loaded into Infrahub based on the schema we just defined. We will create an object file that defines a Country and a Site. +Our goal is to define data that can be loaded into Infrahub based on the schema defined above. We will create an object file that defines a Country and a Site. ```yaml --- diff --git a/docs/docs/objects/manage-from-cli.mdx b/docs/docs/objects/manage-from-cli.mdx index 0877a8f2b9f..9cea3433ca9 100644 --- a/docs/docs/objects/manage-from-cli.mdx +++ b/docs/docs/objects/manage-from-cli.mdx @@ -1,10 +1,10 @@ --- -title: Manage objects from the command line +title: Manage objects with infrahubctl --- Use `infrahubctl` to query, create, update, and delete objects directly from your terminal. The commands accept any schema kind in your instance and can display results as a table, JSON, CSV, or YAML. -These commands operate on individual objects, which suits interactive work and scripting. To load many objects at once from version-controlled files, see [Load data in bulk using YAML file](./load-from-yaml.mdx). +These commands operate on individual objects, which suits interactive work and scripting. To load many objects at once from version-controlled files, see [Load data using YAML file](./load-from-yaml.mdx). For an overview of every way to create an object, see [Create objects](./create-objects.mdx). ## Prerequisites diff --git a/docs/docs/objects/overview.mdx b/docs/docs/objects/overview.mdx index 1db9d1cb209..6126db97a4b 100644 --- a/docs/docs/objects/overview.mdx +++ b/docs/docs/objects/overview.mdx @@ -35,9 +35,10 @@ Objects, their attribute values, and their relationships all carry metadata. At | Object Template | A reusable structural starting point for creating objects of a given kind | | Group | A collection of objects used for querying or automation targeting | -## What you can do with objects +## In this section -- **[Manage objects from the command line](./manage-from-cli.mdx)** — Query, create, update, and delete individual objects with `infrahubctl` -- **[Convert object kind](./convert-object-kind.mdx)** — Change the schema kind of an existing object while preserving its data -- **[Metadata & lineage](./metadata.mdx)** — Inspect where each attribute value came from and track data origin -- **[Load data in bulk using YAML file](./load-from-yaml.mdx)** — Bulk-create or update objects by loading structured YAML files +- [Create objects](./create-objects.mdx) — Compare every way to create an object, and see one created four ways +- [Manage objects with infrahubctl](./manage-from-cli.mdx) — Query, create, update, and delete individual objects from your terminal +- [Load data using YAML file](./load-from-yaml.mdx) — Author object files and load them, once or tracked in a Git repository +- [Convert object kind](./convert-object-kind.mdx) — Change the schema kind of an existing object while preserving its data +- [Metadata & lineage](./metadata.mdx) — Trace where each attribute value came from diff --git a/docs/sidebars.ts b/docs/sidebars.ts index ab7722803f7..c81b46d7c1b 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -149,10 +149,11 @@ const sidebars: SidebarsConfig = { label: 'Objects', link: { type: 'doc', id: 'objects/overview' }, // hub items: [ - { type: 'doc', id: 'objects/manage-from-cli', label: 'Manage objects from the command line' }, + { type: 'doc', id: 'objects/create-objects', label: 'Create objects' }, + { type: 'doc', id: 'objects/manage-from-cli', label: 'Manage objects with infrahubctl' }, + { type: 'doc', id: 'objects/load-from-yaml', label: 'Load data using YAML file' }, { type: 'doc', id: 'objects/convert-object-kind', label: 'Convert object kind' }, { type: 'doc', id: 'objects/metadata', label: 'Metadata & lineage' }, - { type: 'doc', id: 'objects/load-from-yaml', label: 'Load data in bulk using YAML file' }, ], }, { From ce445fd16fb92e77389da306852b2467b90db3b7 Mon Sep 17 00:00:00 2001 From: Guillaume Mazoyer Date: Thu, 13 Aug 2026 11:56:21 +0200 Subject: [PATCH 44/48] fix(recompute): keep the branch and node tags on a task that writes (#10237) The bulk recompute dispatcher tagged its run as a database change once it had values to write. add_tags rebuilds the whole tag list from the runtime snapshot taken when the run started, and a tag update never refreshes that snapshot, so this second call dropped the branch tag and every related-node tag the flow had added at its start. A recompute task then fell out of the task list filtered by branch and out of the task list of a node, which is why the tasks looked like they vanished one by one. The call was redundant anyway. Every flow that reaches the dispatcher already declares the database-change tag on its workflow definition, and Prefect merges the deployment tags into the run when it creates it. Drop the call. --- backend/infrahub/core/recompute/dispatch.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/infrahub/core/recompute/dispatch.py b/backend/infrahub/core/recompute/dispatch.py index 2b014dffc9d..ed82ff51e35 100644 --- a/backend/infrahub/core/recompute/dispatch.py +++ b/backend/infrahub/core/recompute/dispatch.py @@ -14,7 +14,6 @@ from infrahub.events.constants import NodeMutationOrigin from infrahub.exceptions import BranchNotFoundError from infrahub.workers.dependencies import get_database, get_event_service, get_workflow -from infrahub.workflows.utils import add_tags if TYPE_CHECKING: from infrahub.core.recompute.bulk_write import AttributeValueWrite @@ -53,7 +52,6 @@ async def dispatch( if not writes: return - await add_tags(db_change=True) try: branch = await registry.get_branch(db=self._db, branch=branch_name) except BranchNotFoundError: From 2ffcc79172280940aeb18fb11f0a79a8c826aed1 Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Thu, 13 Aug 2026 13:29:06 +0200 Subject: [PATCH 45/48] fix: narrow Python transform closure to its own file (#10240) * fix: narrow Python transform closure to its own file (closes #9644) A Python transform's and a generator's dependency closure was every git-tracked file in the directory holding its file_path. Sharing a directory between definitions - each sitting next to its own query and helper modules - therefore regenerated every artifact rooted there on any single-file edit, including edits to files the definition never used. Auto-detection now claims the entry file alone. Files a source genuinely depends on are declared through watch.files, where naming the containing directory restores the previous closure for that definition. A definition that has not declared watch still folds the commit id into its fingerprint, so no safety net is lost. * docs: fix release-note style and correct closure wording from review Vale enforces the branded plural over changelog fragments, which the release-note style job checks and the fragment failed. The docs style guide also forbids "transform" as a noun, which Vale only catches in the plural, so the singular slips in the fragment and in python.mdx are normalised too. The knowledge note claimed the untrusted-closure fallback was effectively dead for Python. A watch.files pathspec git cannot enumerate raises GitCommandError, which the aggregator isolates to complete=False, so both routes are now named. The component selection tests hand-set their stored closures rather than declaring watch, so their comments no longer credit watch.files for what the fixtures hardcode, and the two renamed tests state what they actually cover: the gate treats every closure member alike. * docs: attribute Python regeneration to the fingerprint, not an incomplete closure An undeclared Python Transformation was described under the incomplete-closure fallback, which is the wrong mechanism: its closure is always complete, and the regeneration comes from the fingerprint being tied to the current commit while no watch declaration exists. Three pages claimed the closure was incomplete or that declaring watch marks it complete; an empty files list never reaches the closure union at all. * docs: keep closure-completeness claims in one place The user-facing pages restated an internal invariant in their own words, which is how three review rounds each caught a different overstatement of it. They now describe only what a reader acts on, and mention dependencies_complete solely for Jinja2, where auto-detection really does report it. The knowledge note is the single place that spells out when a Python closure can be incomplete, and it now scopes that to misconfiguration rather than claiming the fallback is dead. --- .../git/closure_builder/python_closure.py | 57 ++-- backend/infrahub/git/fingerprint/composer.py | 16 +- .../test_artifact_regen_selection.py | 19 +- .../test_generator_regen_selection.py | 22 +- .../initial__main/.infrahub.yml | 3 + .../transforms/foo/unused_sibling.py | 5 + .../git/test_fingerprint_transformation.py | 6 +- .../git/test_generator_import_closure.py | 42 ++- .../test_artifact_regen_e2e.py | 30 +- .../regeneration/test_generator_predicates.py | 44 ++- .../git/closure_builder/test_dispatcher.py | 10 +- .../closure_builder/test_python_closure.py | 262 ++++++++++-------- changelog/9644.fixed.md | 1 + .../backend/selective-merge-regeneration.md | 2 +- docs/docs/artifacts/overview.mdx | 2 +- docs/docs/git-integration/infrahub-yml.mdx | 26 +- docs/docs/proposed-changes/overview.mdx | 17 +- docs/docs/transformations/overview.mdx | 4 +- docs/docs/transformations/python.mdx | 28 +- 19 files changed, 340 insertions(+), 256 deletions(-) create mode 100644 backend/tests/fixtures/repos/artifact-regen-e2e/initial__main/transforms/foo/unused_sibling.py create mode 100644 changelog/9644.fixed.md diff --git a/backend/infrahub/git/closure_builder/python_closure.py b/backend/infrahub/git/closure_builder/python_closure.py index add665afbec..ff93f5059ac 100644 --- a/backend/infrahub/git/closure_builder/python_closure.py +++ b/backend/infrahub/git/closure_builder/python_closure.py @@ -2,12 +2,10 @@ from typing import TYPE_CHECKING -from git import Repo -from git.exc import GitCommandError, InvalidGitRepositoryError from infrahub_sdk.schema.repository import InfrahubGeneratorDefinitionConfig, InfrahubPythonTransformConfig from infrahub.git.closure_builder.canonicalizer import canonicalize_path -from infrahub.git.closure_builder.result import ClosureResult, UnresolvedRef +from infrahub.git.closure_builder.result import ClosureResult if TYPE_CHECKING: from pathlib import Path @@ -16,14 +14,21 @@ class PythonClosure: - """Compute a Python source's dependency closure as the package-directory floor. + """Compute a Python source's dependency closure as the single file it points at. Handles any Python-backed config that exposes a ``file_path`` and a ``name``: - Python transforms and generator definitions alike. The closure is every - git-tracked file under the directory containing the source's ``file_path``, - minus ``.pyc`` files and ``__pycache__/`` entries. A source that sits at the - repository root collapses to its own file instead of pulling in the entire - repository. + Python transforms and generator definitions alike. Only the file named by + ``file_path`` is auto-detected. Sitting next to that file is not evidence of being + an input to it - a directory commonly holds several unrelated sources, each with + its own queries and helpers - so siblings stay out of the closure and editing one + of them does not regenerate this source's output. + + Anything the source depends on beyond its own file, such as a helper module it + imports, is declared by the author through ``watch.files``; naming the containing + directory there brings every tracked file beneath it back into the closure. + + ``worktree_root`` is part of the shared builder contract and unused here: naming + the single dependency needs no filesystem access. """ def supports(self, transform_config: TransformConfig) -> bool: @@ -32,37 +37,7 @@ def supports(self, transform_config: TransformConfig) -> bool: def build( self, transform_config: InfrahubPythonTransformConfig | InfrahubGeneratorDefinitionConfig, - worktree_root: Path, + worktree_root: Path, # noqa: ARG002 ) -> ClosureResult: entry_path = canonicalize_path(str(transform_config.file_path)) - - if "/" not in entry_path: - return ClosureResult(dependencies=(entry_path,), complete=True, unresolved=()) - - package_dir = entry_path.rsplit("/", 1)[0] - - try: - repo = Repo(worktree_root) - output = repo.git.ls_files(package_dir) - except (InvalidGitRepositoryError, GitCommandError): - return ClosureResult( - dependencies=(entry_path,), - complete=False, - unresolved=(UnresolvedRef(file=entry_path, location="git enumeration failed"),), - ) - - dependencies: list[str] = [] - for line in output.splitlines(): - if not line: - continue - canonical = canonicalize_path(line) - if canonical.endswith(".pyc") or "__pycache__" in canonical.split("/"): - continue - dependencies.append(canonical) - - sorted_unique = tuple(sorted(set(dependencies))) - return ClosureResult( - dependencies=sorted_unique, - complete=True, - unresolved=(), - ) + return ClosureResult(dependencies=(entry_path,), complete=True, unresolved=()) diff --git a/backend/infrahub/git/fingerprint/composer.py b/backend/infrahub/git/fingerprint/composer.py index c99f4b8ffc5..9d7e0e68ce3 100644 --- a/backend/infrahub/git/fingerprint/composer.py +++ b/backend/infrahub/git/fingerprint/composer.py @@ -36,11 +36,11 @@ def fold_commit_id( When the list is complete, how far that can be trusted depends on how it was built, which the caller states through `watch_required`: - - `watch_required=True` - the list is a directory listing rather than a real dependency - scan: every tracked file that happens to sit next to the entry point. "Complete" only - means the listing succeeded, so a helper imported from another directory is missing from - the list without anything noticing. Only a `watch` declaration, where the author names - the extra files by hand, is trusted to close the list; without one the commit id goes in. + - `watch_required=True` - the list names the entry point only, with no dependency scan + behind it. "Complete" merely means the entry point was resolved, so a helper the source + imports is missing from the list without anything noticing. Only a `watch` declaration, + where the author names the extra files by hand, is trusted to close the list; without + one the commit id goes in. - `watch_required=False` - the list was built by parsing the source and following every reference it declares, and any reference that could not be followed already set `closure_complete=False`. A complete list is therefore trustworthy by itself, and no @@ -163,9 +163,9 @@ def compose_transformation( case PythonTransformationFingerprintInput(): terms.append(f"class_name={inputs.class_name}") terms.append(f"convert_query_response={inputs.convert_query_response}") - # A Python transform's dependencies are the files sitting next to its source - # file, so an import from anywhere else is absent from the list: the author has - # to name those files in `watch` before the fingerprint can drop the commit id. + # A Python transform's dependencies are auto-detected as its source file alone, + # so anything it imports is absent from the list: the author has to name those + # files in `watch` before the fingerprint can drop the commit id. watch_required = True case Jinja2TransformationFingerprintInput(): terms.append(f"template_path={inputs.template_path}") diff --git a/backend/tests/component/proposed_change/test_artifact_regen_selection.py b/backend/tests/component/proposed_change/test_artifact_regen_selection.py index 1d6c1327949..69eaf71fe41 100644 --- a/backend/tests/component/proposed_change/test_artifact_regen_selection.py +++ b/backend/tests/component/proposed_change/test_artifact_regen_selection.py @@ -39,10 +39,12 @@ } """ -# The closure the integrator would store at import time for each transform. The Jinja2 -# closure carries a transitively-included partial; the Python closure carries a sibling -# helper picked up by the package-directory floor. The repository manifest is part of -# every closure. +# The stored closure for each transform, set by hand rather than built by an import: these +# scenarios drive the selection gate, and the closure builder has its own tests. The Jinja2 +# closure carries a transitively-included partial; the Python one carries a sibling helper, +# which a real import would only put there because the transform declared its directory in +# `watch.files` - auto-detection stops at the source file. The repository manifest is part +# of every closure. JINJA_DEPENDENCIES = [".infrahub.yml", "partials/header.j2", "templates/device.j2"] PYTHON_DEPENDENCIES = [ ".infrahub.yml", @@ -242,7 +244,7 @@ async def test_transform_source_edit_selects_only_owning_definition( ) assert selected == ["artifact-python"] - async def test_sibling_helper_edit_selects_via_package_floor( + async def test_helper_in_stored_closure_selects_definition( self, dataset: dict[str, Any], default_branch: Branch, @@ -250,10 +252,11 @@ async def test_sibling_helper_edit_selects_via_package_floor( memory_cache: MemoryCache, workflow_recorder: WorkflowRecorder, ) -> None: - """A sibling file in the transform's package directory selects the owning definition. + """A closure member that is not the transform's own source file selects the owning definition. - The Python closure includes every sibling under the transform's directory, so a - helper edit that the source file never imports still drives regeneration. + The gate treats every path in the stored closure alike, so a helper beside the source + drives regeneration once it is in there. Whether it gets in there is the closure + builder's decision, covered by its own tests and by the end-to-end import scenarios. """ selected = await self._selected_definitions( dataset=dataset, diff --git a/backend/tests/component/proposed_change/test_generator_regen_selection.py b/backend/tests/component/proposed_change/test_generator_regen_selection.py index bb3257e32b9..0db47ae7597 100644 --- a/backend/tests/component/proposed_change/test_generator_regen_selection.py +++ b/backend/tests/component/proposed_change/test_generator_regen_selection.py @@ -66,10 +66,11 @@ } """ -# Each closure is the set of repo-relative paths the integrator would persist at import time: -# the package-directory floor (every sibling under the generator's directory) plus the -# repository manifest, which is part of every closure. The floors are disjoint so a file edit -# selects exactly one generator. +# Each closure is set by hand rather than built by an import: these scenarios drive the +# selection gate, and the closure builder has its own tests. The shape matches what an import +# persists for a generator that declared its containing directory in `watch.files` - its own +# source file plus that directory's contents - together with the repository manifest, which is +# part of every closure. The closures are disjoint so a file edit selects exactly one generator. DEPENDENCIES_A = [".infrahub.yml", "generators/a/__init__.py", "generators/a/a.py", "generators/a/helpers.py"] DEPENDENCIES_A2 = [".infrahub.yml", "generators/a2/__init__.py", "generators/a2/a2.py"] DEPENDENCIES_B = [".infrahub.yml", "generators/b/__init__.py", "generators/b/b.py"] @@ -239,8 +240,8 @@ async def _selected_definitions( class TestGeneratorRegenSelection(GeneratorRegenTestBase): """The selection gate submits a per-definition check only for the generators a change affects. - Drives the ``run_generators`` flow against four generator definitions backed by distinct - package-directory closures over a single repository, two of which share a query. Each scenario + Drives the ``run_generators`` flow against four generator definitions backed by disjoint + closures over a single repository, two of which share a query. Each scenario asserts the exact set of definitions dispatched, proving unrelated edits and sibling generators are left untouched while data changes, query edits and definition additions still select. """ @@ -463,7 +464,7 @@ async def test_source_edit_selects_only_owning_generator( ) assert selected == ["device-gen-a"] - async def test_sibling_helper_edit_selects_via_package_floor( + async def test_helper_in_stored_closure_selects_generator( self, dataset: dict[str, Any], default_branch: Branch, @@ -471,10 +472,11 @@ async def test_sibling_helper_edit_selects_via_package_floor( memory_cache: MemoryCache, workflow_recorder: WorkflowRecorder, ) -> None: - """A sibling file in the generator's package directory selects the owning generator. + """A closure member that is not the generator's own source file selects the owning generator. - The package-directory floor includes every sibling under the generator's directory, so a - helper edit the source file never imports still drives a re-run. + The gate treats every path in the stored closure alike, so a helper beside the source + drives a re-run once it is in there. Whether it gets in there is the closure builder's + decision, covered by its own tests. """ selected = await self._selected_definitions( dataset=dataset, diff --git a/backend/tests/fixtures/repos/artifact-regen-e2e/initial__main/.infrahub.yml b/backend/tests/fixtures/repos/artifact-regen-e2e/initial__main/.infrahub.yml index 759cc55871e..f63d7bda6d1 100644 --- a/backend/tests/fixtures/repos/artifact-regen-e2e/initial__main/.infrahub.yml +++ b/backend/tests/fixtures/repos/artifact-regen-e2e/initial__main/.infrahub.yml @@ -12,3 +12,6 @@ python_transforms: - name: render-python class_name: Foo file_path: "transforms/foo/foo.py" + watch: + files: + - "transforms/foo/helpers.py" diff --git a/backend/tests/fixtures/repos/artifact-regen-e2e/initial__main/transforms/foo/unused_sibling.py b/backend/tests/fixtures/repos/artifact-regen-e2e/initial__main/transforms/foo/unused_sibling.py new file mode 100644 index 00000000000..b89c83600a3 --- /dev/null +++ b/backend/tests/fixtures/repos/artifact-regen-e2e/initial__main/transforms/foo/unused_sibling.py @@ -0,0 +1,5 @@ +"""A module co-located with the transform that the transform never uses.""" + + +def unused() -> str: + return "unused" diff --git a/backend/tests/integration/git/test_fingerprint_transformation.py b/backend/tests/integration/git/test_fingerprint_transformation.py index f5841a0a03b..9321bd186b1 100644 --- a/backend/tests/integration/git/test_fingerprint_transformation.py +++ b/backend/tests/integration/git/test_fingerprint_transformation.py @@ -60,9 +60,9 @@ async def test_unrelated_commit_keeps_complete_jinja2_stable_but_folds_python( # Neither transform declares a watch, and the commit below touches neither of them. # person_with_cars is a Jinja2 transform whose template includes nothing, so parsing it # found every file that affects the output and the fingerprint can ignore the commit id. - # CarSpecMarkdown is a Python transform: its dependencies are just the files next to it - # on disk, which could always be missing an import from another directory, so its - # fingerprint keeps following the commit id. + # CarSpecMarkdown is a Python transform: its dependencies are just its own source file, + # which could always be missing something it imports, so its fingerprint keeps following + # the commit id. jinja2_before = (await client.get(kind=CoreTransformJinja2, name__value="person_with_cars")).fingerprint.value python_before = (await client.get(kind=CoreTransformPython, name__value="CarSpecMarkdown")).fingerprint.value assert jinja2_before diff --git a/backend/tests/integration/git/test_generator_import_closure.py b/backend/tests/integration/git/test_generator_import_closure.py index 0f2769968d9..6abd0183dda 100644 --- a/backend/tests/integration/git/test_generator_import_closure.py +++ b/backend/tests/integration/git/test_generator_import_closure.py @@ -21,30 +21,26 @@ from infrahub.database import InfrahubDatabase -# Every generator in the fixture lives in the `generators/` package, so each one's -# closure is the package-directory floor (every tracked file under that directory) -# plus the repository config file, which the aggregator includes for every definition. -GENERATOR_PACKAGE_CLOSURE = { - ".infrahub.yml", - "generators/__init__.py", - "generators/cartags.gql", - "generators/cartags.py", - "generators/cartags_convert_response.py", - "generators/cartags_title.py", - "generators/cartags_upper.py", +# No generator in the fixture declares `watch.files`, so each one's closure is its own +# source file plus the repository config file, which the aggregator includes for every +# definition. The four generators share the `generators/` directory and its query, and +# none of that reaches any of their closures. +CLOSURE_BY_GENERATOR = { + "cartags": {".infrahub.yml", "generators/cartags.py"}, + "cartags_convert_response": {".infrahub.yml", "generators/cartags_convert_response.py"}, + "cartags_title": {".infrahub.yml", "generators/cartags_title.py"}, + "cartags_upper": {".infrahub.yml", "generators/cartags_upper.py"}, } -GENERATOR_NAMES = {"cartags", "cartags_convert_response", "cartags_title", "cartags_upper"} - class TestGeneratorImportClosure(TestInfrahubApp): """Importing a repository builds and persists a dependency closure on every generator definition. - Each generator's closure is the package-directory floor of its source file. A - re-import after the stored closure has drifted from the worktree must rewrite it: - the closure comparison is the one behavior this carries that the legacy import - gate did not, so a content change altering only the closure still triggers an - update. + Each generator's closure is its own source file, so four generators sharing one directory + end up with four disjoint closures rather than one shared listing. A re-import after the + stored closure has drifted from the worktree must rewrite it: the closure comparison is the + one behavior this carries that the legacy import gate did not, so a content change altering + only the closure still triggers an update. """ @pytest.fixture(scope="class") @@ -85,9 +81,9 @@ async def test_import_persists_closure_on_each_generator( ) -> None: generators = {gen.name.value: gen for gen in await client.all(kind=CoreGeneratorDefinition)} - assert set(generators) == GENERATOR_NAMES - for generator in generators.values(): - assert set(generator.dependencies.value) == GENERATOR_PACKAGE_CLOSURE + assert set(generators) == set(CLOSURE_BY_GENERATOR) + for name, generator in generators.items(): + assert set(generator.dependencies.value) == CLOSURE_BY_GENERATOR[name] assert generator.dependencies_complete.value is True async def test_reimport_rewrites_drifted_closure( @@ -110,12 +106,12 @@ async def test_reimport_rewrites_drifted_closure( # Drift the stored closure away from the worktree while leaving every other # compared field intact, so only the closure comparison can trigger the update. stale = (await client.filters(kind=CoreGeneratorDefinition, name__value="cartags"))[0] - stale.dependencies.value = ["generators/cartags.py"] + stale.dependencies.value = ["generators/stale_path.py"] stale.dependencies_complete.value = False await stale.save() await repo.import_generator_definitions(branch_name="main", commit=commit, config_file=config_file) # type: ignore[call-overload] refreshed = (await client.filters(kind=CoreGeneratorDefinition, name__value="cartags"))[0] - assert set(refreshed.dependencies.value) == GENERATOR_PACKAGE_CLOSURE + assert set(refreshed.dependencies.value) == CLOSURE_BY_GENERATOR["cartags"] assert refreshed.dependencies_complete.value is True diff --git a/backend/tests/integration/proposed_change/test_artifact_regen_e2e.py b/backend/tests/integration/proposed_change/test_artifact_regen_e2e.py index 91d8f8a9543..0a8a1375bf2 100644 --- a/backend/tests/integration/proposed_change/test_artifact_regen_e2e.py +++ b/backend/tests/integration/proposed_change/test_artifact_regen_e2e.py @@ -159,12 +159,13 @@ async def test_integrator_builds_real_closures( "partials/header.j2", } + # The Python transform declares one of its siblings through `watch.files`; the others + # sit in the same directory and stay out of the closure. assert transform_python.dependencies_complete.value is True assert set(transform_python.dependencies.value) == { ".infrahub.yml", "transforms/foo/foo.py", "transforms/foo/helpers.py", - "transforms/foo/__init__.py", } async def test_readme_edit_regenerates_nothing( @@ -205,7 +206,7 @@ async def test_transform_source_edit_selects_only_owning_definition( ) assert selected == ["artifact-python"] - async def test_sibling_helper_edit_selects_via_package_floor( + async def test_watched_sibling_edit_selects_owning_definition( self, dataset: dict[str, Any], default_branch: Branch, @@ -213,7 +214,7 @@ async def test_sibling_helper_edit_selects_via_package_floor( memory_cache: MemoryCache, workflow_recorder: WorkflowRecorder, ) -> None: - """A sibling file in the transform's package directory selects the owning definition.""" + """A sibling the transform declared through `watch.files` selects the definition using it.""" selected = await self._selected_definitions( dataset=dataset, default_branch=default_branch, @@ -224,6 +225,29 @@ async def test_sibling_helper_edit_selects_via_package_floor( ) assert selected == ["artifact-python"] + async def test_undeclared_sibling_edit_regenerates_nothing( + self, + dataset: dict[str, Any], + default_branch: Branch, + admin_account: CoreAccount, + memory_cache: MemoryCache, + workflow_recorder: WorkflowRecorder, + ) -> None: + """A module sitting beside the transform that it never declared dispatches no regeneration. + + This is the shape that made a shared transform directory regenerate everything rooted + in it: co-location alone must not put a file in the closure. + """ + selected = await self._selected_definitions( + dataset=dataset, + default_branch=default_branch, + admin_account=admin_account, + memory_cache=memory_cache, + workflow_recorder=workflow_recorder, + files_changed=["transforms/foo/unused_sibling.py"], + ) + assert selected == [] + async def test_jinja_partial_edit_selects_via_transitive_include( self, dataset: dict[str, Any], diff --git a/backend/tests/unit/core/regeneration/test_generator_predicates.py b/backend/tests/unit/core/regeneration/test_generator_predicates.py index a8e32f8afa2..45e02e5a2be 100644 --- a/backend/tests/unit/core/regeneration/test_generator_predicates.py +++ b/backend/tests/unit/core/regeneration/test_generator_predicates.py @@ -20,9 +20,15 @@ OTHER_ID = "33333333-3333-3333-3333-333333333333" REPOSITORY_ID = "44444444-4444-4444-4444-444444444444" -# The package-directory floor a generator's closure carries: every sibling under its directory plus -# the repository manifest. A file edit anywhere in the floor must select the generator. -PACKAGE_FLOOR = [ +# What a generator's closure holds when nothing is declared: its own source file plus the +# repository manifest. Files sitting beside the source are not in it. +AUTO_DETECTED_CLOSURE = [ + ".infrahub.yml", + "generators/a/a.py", +] + +# What the closure grows to once the author declares the containing directory in `watch.files`. +WATCHED_DIRECTORY_CLOSURE = [ ".infrahub.yml", "generators/a/__init__.py", "generators/a/a.py", @@ -143,21 +149,28 @@ def test_definition_changed_generator_variant(case: DiffCase) -> None: TRANSFORM_CHANGED_CASES: list[TransformChangedCase] = [ TransformChangedCase( name="complete_closure_unrelated_file_is_false", - dependencies=PACKAGE_FLOOR, + dependencies=WATCHED_DIRECTORY_CLOSURE, dependencies_complete=True, files_changed=["generators/b/b.py"], expected=False, ), TransformChangedCase( - name="source_file_inside_floor_is_true", - dependencies=PACKAGE_FLOOR, + name="source_file_is_true", + dependencies=AUTO_DETECTED_CLOSURE, dependencies_complete=True, files_changed=["generators/a/a.py"], expected=True, ), TransformChangedCase( - name="sibling_module_in_same_package_is_true", - dependencies=PACKAGE_FLOOR, + name="undeclared_sibling_module_is_false", + dependencies=AUTO_DETECTED_CLOSURE, + dependencies_complete=True, + files_changed=["generators/a/helpers.py"], + expected=False, + ), + TransformChangedCase( + name="sibling_module_in_watched_directory_is_true", + dependencies=WATCHED_DIRECTORY_CLOSURE, dependencies_complete=True, files_changed=["generators/a/helpers.py"], expected=True, @@ -171,7 +184,7 @@ def test_definition_changed_generator_variant(case: DiffCase) -> None: ), TransformChangedCase( name="incomplete_closure_falls_back_to_any_file_change", - dependencies=PACKAGE_FLOOR, + dependencies=WATCHED_DIRECTORY_CLOSURE, dependencies_complete=False, files_changed=["unrelated/file.md"], expected=True, @@ -184,7 +197,7 @@ def test_definition_changed_generator_variant(case: DiffCase) -> None: ), TransformChangedCase( name="incomplete_closure_with_no_modifications_is_false", - dependencies=PACKAGE_FLOOR, + dependencies=WATCHED_DIRECTORY_CLOSURE, dependencies_complete=False, expected=False, ), @@ -193,12 +206,13 @@ def test_definition_changed_generator_variant(case: DiffCase) -> None: @pytest.mark.parametrize("case", [pytest.param(c, id=c.name) for c in TRANSFORM_CHANGED_CASES]) def test_transform_changed_generator_variant(case: TransformChangedCase) -> None: - """The closure predicate intersects a generator's package floor with the repo diff. + """The closure predicate intersects a generator's stored closure with the repo diff. - A file edit anywhere in the package-directory floor - the source module or a sibling it never - imports - selects the generator, while an unrelated file does not. The legacy (``dependencies=null``) - and incomplete (``dependencies_complete=False``) states fall back to regenerate-on-any-file-change so - a generator is never under-run. + An edit to the generator's own source file selects it; an edit to a sibling module selects it + only once that sibling is in the closure, which is what declaring the directory in + ``watch.files`` achieves. The legacy (``dependencies=null``) and incomplete + (``dependencies_complete=False``) states fall back to regenerate-on-any-file-change so a + generator is never under-run. """ definition = _build_definition( dependencies=case.dependencies, diff --git a/backend/tests/unit/git/closure_builder/test_dispatcher.py b/backend/tests/unit/git/closure_builder/test_dispatcher.py index b24ccb6ce0e..26822a18ee5 100644 --- a/backend/tests/unit/git/closure_builder/test_dispatcher.py +++ b/backend/tests/unit/git/closure_builder/test_dispatcher.py @@ -47,7 +47,11 @@ def test_jinja2_config_dispatches_to_jinja2_closure(tmp_path: Path) -> None: def test_python_config_dispatches_to_python_closure(tmp_path: Path) -> None: - """A Python transform config is dispatched to the Python builder and the manifest path is appended.""" + """A Python transform config is dispatched to the Python builder and the manifest path is appended. + + The undeclared sibling stays out: neither builder treats co-location as a dependency, + so the Python closure is the entry file alone until `watch.files` says otherwise. + """ repo = Repo.init(tmp_path) _write(tmp_path, "transforms/network/main.py", "") _write(tmp_path, "transforms/network/helpers.py", "") @@ -61,9 +65,7 @@ def test_python_config_dispatches_to_python_closure(tmp_path: Path) -> None: result = build_default_closure_builder(logger=LOGGER).build(transform_config=config, worktree_root=tmp_path) - assert "transforms/network/main.py" in result.dependencies - assert "transforms/network/helpers.py" in result.dependencies - assert ".infrahub.yml" in result.dependencies + assert result.dependencies == (".infrahub.yml", "transforms/network/main.py") assert result.complete is True diff --git a/backend/tests/unit/git/closure_builder/test_python_closure.py b/backend/tests/unit/git/closure_builder/test_python_closure.py index 04dc64bd0af..eba7dad6b5e 100644 --- a/backend/tests/unit/git/closure_builder/test_python_closure.py +++ b/backend/tests/unit/git/closure_builder/test_python_closure.py @@ -1,26 +1,36 @@ from __future__ import annotations +import logging from pathlib import Path from git import Repo -from infrahub_sdk.schema.repository import InfrahubGeneratorDefinitionConfig, InfrahubPythonTransformConfig +from infrahub_sdk.schema.repository import ( + InfrahubGeneratorDefinitionConfig, + InfrahubPythonTransformConfig, + InfrahubWatchConfig, +) from infrahub.git.closure_builder.python_closure import PythonClosure +from infrahub.git.closure_builder.watch import union_watch_files -def _config(*, name: str, file_path: str) -> InfrahubPythonTransformConfig: +def _config(*, name: str, file_path: str, watch: InfrahubWatchConfig | None = None) -> InfrahubPythonTransformConfig: return InfrahubPythonTransformConfig( name=name, file_path=Path(file_path), + watch=watch, ) -def _generator_config(*, name: str, file_path: str) -> InfrahubGeneratorDefinitionConfig: +def _generator_config( + *, name: str, file_path: str, watch: InfrahubWatchConfig | None = None +) -> InfrahubGeneratorDefinitionConfig: return InfrahubGeneratorDefinitionConfig( name=name, file_path=Path(file_path), query="some_query", targets="some_group", + watch=watch, ) @@ -45,25 +55,24 @@ def _track(repo: Repo, *rels: str) -> None: repo.index.commit("seed") -def test_package_directory_floor_includes_all_python_siblings(tmp_path: Path) -> None: - """All `.py` files under the transform's package directory are included in the closure. +def test_closure_is_the_entry_file_only(tmp_path: Path) -> None: + """Files sitting next to the transform's source file stay out of its closure. - The package-directory floor catches the common transform-plus-sibling-helpers - pattern at zero user cost. AST-precise import analysis was rejected because - runtime imports (`importlib`, `__import__`, in-function imports) are invisible - to it and missing one silently violates the correctness invariant. + A transform directory routinely holds several unrelated transforms with their own + queries and helpers, so co-location is no evidence of a dependency. Auto-detection + claims only the file the config points at; anything else is the author's to declare. """ repo = _init_repo(tmp_path) _write(tmp_path, "transforms/network/main.py", "# entry\n") _write(tmp_path, "transforms/network/helpers.py", "# helper\n") + _write(tmp_path, "transforms/network/other.gql", "query {}\n") _write(tmp_path, "transforms/network/sub/inner.py", "# inner\n") - _write(tmp_path, "transforms/other/unrelated.py", "# unrelated\n") _track( repo, "transforms/network/main.py", "transforms/network/helpers.py", + "transforms/network/other.gql", "transforms/network/sub/inner.py", - "transforms/other/unrelated.py", ) result = PythonClosure().build( @@ -71,184 +80,195 @@ def test_package_directory_floor_includes_all_python_siblings(tmp_path: Path) -> worktree_root=tmp_path, ) - assert "transforms/network/main.py" in result.dependencies - assert "transforms/network/helpers.py" in result.dependencies - assert "transforms/network/sub/inner.py" in result.dependencies - assert "transforms/other/unrelated.py" not in result.dependencies + assert result.dependencies == ("transforms/network/main.py",) assert result.complete is True assert result.unresolved == () -def test_pyc_files_are_excluded(tmp_path: Path) -> None: - """Bytecode artifacts must not appear in the stored closure. - - `.pyc` files are not source inputs to the rendered output; including them - would create false positives in the regeneration gate when Python touches - its cache. - """ +def test_repo_root_transform_closure_is_the_entry_file(tmp_path: Path) -> None: + """A transform at the repository root behaves like any other: only its own file.""" repo = _init_repo(tmp_path) - _write(tmp_path, "transforms/network/main.py", "") - _write(tmp_path, "transforms/network/cached.pyc", "") - _track(repo, "transforms/network/main.py", "transforms/network/cached.pyc") + _write(tmp_path, "root_transform.py", "") + _write(tmp_path, "unrelated/sibling.py", "") + _write(tmp_path, "README.md", "") + _track(repo, "root_transform.py", "unrelated/sibling.py", "README.md") result = PythonClosure().build( - transform_config=_config(name="net", file_path="transforms/network/main.py"), + transform_config=_config(name="root", file_path="root_transform.py"), worktree_root=tmp_path, ) - assert "transforms/network/cached.pyc" not in result.dependencies + assert result.dependencies == ("root_transform.py",) + assert result.complete is True -def test_pycache_directory_is_excluded(tmp_path: Path) -> None: - """The `__pycache__/` directory is excluded from the closure regardless of git tracking. +def test_closure_is_computed_without_a_git_repository(tmp_path: Path) -> None: + """Naming the entry file needs no git enumeration, so a non-git worktree is not a failure. - `__pycache__/` is a runtime artifact directory and should never feed the - regeneration decision. + The closure no longer depends on `git ls-files`, so there is nothing left that can + fail here and drop the result to `complete=False`. """ - repo = _init_repo(tmp_path) _write(tmp_path, "transforms/network/main.py", "") - _write(tmp_path, "transforms/network/__pycache__/main.cpython-313.pyc", "") - _track( - repo, - "transforms/network/main.py", - "transforms/network/__pycache__/main.cpython-313.pyc", - ) result = PythonClosure().build( transform_config=_config(name="net", file_path="transforms/network/main.py"), worktree_root=tmp_path, ) - assert not any("__pycache__" in entry for entry in result.dependencies) + assert result.dependencies == ("transforms/network/main.py",) + assert result.complete is True + assert result.unresolved == () -def test_gitignored_files_are_excluded(tmp_path: Path) -> None: - """Files matched by `.gitignore` do not enter the closure. +def test_entry_path_is_canonicalized(tmp_path: Path) -> None: + """A config path written with a leading `./` is stored in canonical repo-relative form. - The closure must match what git considers part of the repository so that - the read-side intersection against `repo_diff.files_*` cannot diverge from - the write-side dependency list. + The stored closure is intersected against git's diff output, so both sides have to + agree on the spelling of a path. """ - repo = _init_repo(tmp_path, gitignore="transforms/network/secret.py\n") - _write(tmp_path, "transforms/network/main.py", "") - _write(tmp_path, "transforms/network/secret.py", "") - _track(repo, "transforms/network/main.py") - result = PythonClosure().build( - transform_config=_config(name="net", file_path="transforms/network/main.py"), + transform_config=_config(name="net", file_path="./transforms/network/main.py"), worktree_root=tmp_path, ) - assert "transforms/network/secret.py" not in result.dependencies + assert result.dependencies == ("transforms/network/main.py",) -def test_repo_root_transform_collapses_to_entry_file(tmp_path: Path) -> None: - """A transform at the repository root must not pull every tracked file into its closure. +def test_supports_generator_definition_config() -> None: + """The Python closure builder claims generator definitions, not just transforms. - The package-directory floor has no parent to bound below the entry file when - the transform sits at the root, so the closure collapses to the entry file - only. Including the whole repository would defeat the precise-regeneration - gate entirely for any root-level transform. + Generators are Python sources with the same `file_path` shape, so the same builder + must dispatch for them; otherwise the aggregator would have no builder to compute a + generator's closure and the import would persist none. """ + assert PythonClosure().supports(_generator_config(name="gen", file_path="generators/widget/main.py")) is True + + +def test_generator_closure_is_the_entry_file_only(tmp_path: Path) -> None: + """A generator's closure excludes its siblings exactly as a Python transform's does.""" repo = _init_repo(tmp_path) - _write(tmp_path, "root_transform.py", "") - _write(tmp_path, "unrelated/sibling.py", "") - _write(tmp_path, "README.md", "") - _track(repo, "root_transform.py", "unrelated/sibling.py", "README.md") + _write(tmp_path, "generators/widget/main.py", "# entry\n") + _write(tmp_path, "generators/widget/helpers.py", "# helper\n") + _track(repo, "generators/widget/main.py", "generators/widget/helpers.py") result = PythonClosure().build( - transform_config=_config(name="root", file_path="root_transform.py"), + transform_config=_generator_config(name="widget", file_path="generators/widget/main.py"), worktree_root=tmp_path, ) - assert result.dependencies == ("root_transform.py",) + assert result.dependencies == ("generators/widget/main.py",) assert result.complete is True + assert result.unresolved == () -def test_git_enumeration_failure_flips_complete_false(tmp_path: Path) -> None: - """When git cannot enumerate tracked files, the closure falls back with `complete=False`. +def test_watching_the_containing_directory_readmits_the_siblings(tmp_path: Path) -> None: + """Declaring the transform's own directory in `watch.files` brings every sibling back. - The package-directory floor relies on `git ls-files` to enumerate. If the - worktree is not a git repository (or the command fails), returning a trusted - one-file closure would cause the regeneration gate to silently skip - regenerations for any real sibling change. Flipping the trust bit forces the - pipeline to fall back to the coarser file-change gate. + This is the escape hatch for the transform-plus-helper-modules layout: auto-detection + no longer assumes it, so the author asks for it by naming the directory. """ - _write(tmp_path, "transforms/network/main.py", "") + repo = _init_repo(tmp_path) + _write(tmp_path, "transforms/network/main.py", "# entry\n") + _write(tmp_path, "transforms/network/helpers.py", "# helper\n") + _write(tmp_path, "transforms/network/sub/inner.py", "# inner\n") + _write(tmp_path, "transforms/other/unrelated.py", "# unrelated\n") + _track( + repo, + "transforms/network/main.py", + "transforms/network/helpers.py", + "transforms/network/sub/inner.py", + "transforms/other/unrelated.py", + ) + transform_config = _config( + name="net", + file_path="transforms/network/main.py", + watch=InfrahubWatchConfig(files=["transforms/network/"]), + ) - result = PythonClosure().build( - transform_config=_config(name="net", file_path="transforms/network/main.py"), + result = union_watch_files( + result=PythonClosure().build(transform_config=transform_config, worktree_root=tmp_path), + transform_config=transform_config, worktree_root=tmp_path, + logger=logging.getLogger(__name__), ) - assert result.dependencies == ("transforms/network/main.py",) - assert result.complete is False - assert any( - ref.file == "transforms/network/main.py" and ref.location == "git enumeration failed" - for ref in result.unresolved + assert result.dependencies == ( + "transforms/network/helpers.py", + "transforms/network/main.py", + "transforms/network/sub/inner.py", ) + assert result.complete is True -def test_dependencies_are_sorted(tmp_path: Path) -> None: - """Returned dependencies are lexicographically sorted for byte-stable storage.""" +def test_watching_a_single_sibling_admits_only_that_sibling(tmp_path: Path) -> None: + """A `watch.files` entry naming one file adds that file and nothing else beside it. + + Declaring one helper must not drag in the rest of the directory, otherwise narrowing + the auto-detected closure would buy nothing for anyone who uses `watch` at all. + """ repo = _init_repo(tmp_path) - _write(tmp_path, "transforms/network/main.py", "") - _write(tmp_path, "transforms/network/zeta.py", "") - _write(tmp_path, "transforms/network/alpha.py", "") + _write(tmp_path, "transforms/network/main.py", "# entry\n") + _write(tmp_path, "transforms/network/helpers.py", "# helper\n") + _write(tmp_path, "transforms/network/noise.gql", "query {}\n") _track( repo, "transforms/network/main.py", - "transforms/network/zeta.py", - "transforms/network/alpha.py", + "transforms/network/helpers.py", + "transforms/network/noise.gql", + ) + transform_config = _config( + name="net", + file_path="transforms/network/main.py", + watch=InfrahubWatchConfig(files=["transforms/network/helpers.py"]), ) - result = PythonClosure().build( - transform_config=_config(name="net", file_path="transforms/network/main.py"), + result = union_watch_files( + result=PythonClosure().build(transform_config=transform_config, worktree_root=tmp_path), + transform_config=transform_config, worktree_root=tmp_path, + logger=logging.getLogger(__name__), ) - expected_subset = ["transforms/network/alpha.py", "transforms/network/main.py", "transforms/network/zeta.py"] - deps = list(result.dependencies) - assert deps == sorted(deps) - for entry in expected_subset: - assert entry in deps - - -def test_supports_generator_definition_config() -> None: - """The Python closure builder claims generator definitions, not just transforms. - - Generators share the package-directory floor model with Python transforms, so - the same builder must dispatch for them; otherwise the aggregator would have no - builder to compute a generator's closure and the import would persist none. - """ - assert PythonClosure().supports(_generator_config(name="gen", file_path="generators/widget/main.py")) is True + assert result.dependencies == ( + "transforms/network/helpers.py", + "transforms/network/main.py", + ) + assert result.complete is True -def test_generator_definition_package_directory_floor(tmp_path: Path) -> None: - """A generator's closure is the package-directory floor built from its entry file and name. +def test_watched_directory_excludes_bytecode_and_gitignored_files(tmp_path: Path) -> None: + """Re-admitting a directory through `watch.files` still drops bytecode and ignored files. - The builder reads only `file_path` and `name`, both present on a generator - config, so the package-directory floor that catches sibling helpers applies to - generators identically to Python transforms. + `.pyc`, `__pycache__/` and Git-ignored paths are not source inputs; letting them in + would fire the regeneration gate whenever Python touches its cache. """ - repo = _init_repo(tmp_path) - _write(tmp_path, "generators/widget/main.py", "# entry\n") - _write(tmp_path, "generators/widget/helpers.py", "# helper\n") - _write(tmp_path, "generators/other/unrelated.py", "# unrelated\n") + repo = _init_repo(tmp_path, gitignore="transforms/network/secret.py\n") + _write(tmp_path, "transforms/network/main.py", "") + _write(tmp_path, "transforms/network/helpers.py", "") + _write(tmp_path, "transforms/network/cached.pyc", "") + _write(tmp_path, "transforms/network/__pycache__/main.cpython-313.pyc", "") + _write(tmp_path, "transforms/network/secret.py", "") _track( repo, - "generators/widget/main.py", - "generators/widget/helpers.py", - "generators/other/unrelated.py", + "transforms/network/main.py", + "transforms/network/helpers.py", + "transforms/network/cached.pyc", + "transforms/network/__pycache__/main.cpython-313.pyc", + ) + transform_config = _config( + name="net", + file_path="transforms/network/main.py", + watch=InfrahubWatchConfig(files=["transforms/network/"]), ) - result = PythonClosure().build( - transform_config=_generator_config(name="widget", file_path="generators/widget/main.py"), + result = union_watch_files( + result=PythonClosure().build(transform_config=transform_config, worktree_root=tmp_path), + transform_config=transform_config, worktree_root=tmp_path, + logger=logging.getLogger(__name__), ) - assert "generators/widget/main.py" in result.dependencies - assert "generators/widget/helpers.py" in result.dependencies - assert "generators/other/unrelated.py" not in result.dependencies - assert result.complete is True - assert result.unresolved == () + assert result.dependencies == ( + "transforms/network/helpers.py", + "transforms/network/main.py", + ) diff --git a/changelog/9644.fixed.md b/changelog/9644.fixed.md new file mode 100644 index 00000000000..751e3b8cdf6 --- /dev/null +++ b/changelog/9644.fixed.md @@ -0,0 +1 @@ +A Python Transformation's auto-detected dependency closure is now the file named by its `file_path` alone, rather than every Git-tracked file in the directory containing it. Keeping several Transformations in one directory - a common layout, where each sits next to its own query and helper modules - made every artifact rooted in that directory regenerate on any single-file edit there, including edits to files it never used. Helper modules a Transformation depends on are declared with the `watch.files` key in `.infrahub.yml`, where naming the containing directory covers all of them at once. Declaring `watch` with an empty `files` list on a Transformation that depends on nothing but its own file is what lets Infrahub stop treating every commit to the repository as a possible change to it. diff --git a/dev/knowledge/backend/selective-merge-regeneration.md b/dev/knowledge/backend/selective-merge-regeneration.md index 7097340440f..b11674a97a1 100644 --- a/dev/knowledge/backend/selective-merge-regeneration.md +++ b/dev/knowledge/backend/selective-merge-regeneration.md @@ -45,7 +45,7 @@ Artifact generation also deletes artifacts whose target has left the target grou The two filters are a conjunction and key on different things: `members` on the member node id, so a member with no artifact yet is still selected; `limit` on the existing artifact id, so it can only narrow to members that already have one. No caller sets both, and consolidation clears either filter as soon as one side of a merge left it empty. -The dependency closure and `fingerprint` are computed at repository import. The closure is trusted (`dependencies_complete = true`) or not; see [code-generation.md](code-generation.md) and the [proposed changes overview](../../../docs/docs/proposed-changes/overview.mdx) for how a closure is built (Python: the git-tracked files under the entry's directory; Jinja2: the static include/import/extends graph) and how `watch.files` restores a trusted closure. +The dependency closure and `fingerprint` are computed at repository import. The closure is trusted (`dependencies_complete = true`) or not; see [code-generation.md](code-generation.md) and the [proposed changes overview](../../../docs/docs/proposed-changes/overview.mdx) for how a closure is built (Python: the entry file alone, since imports are not analyzed; Jinja2: the static include/import/extends graph) and how `watch.files` restores a trusted closure. Note that `PythonClosure` reads nothing off disk and never reports `complete = false` itself, so a correctly configured Python definition lands on `complete = true` and the commit-id fold in the fingerprint is its only safety net. `complete = false` is reachable for Python only through the aggregator's failure isolation, from an unusable `file_path` or a `watch.files` entry git cannot enumerate (a pathspec escaping the repository raises `GitCommandError`). Both are misconfiguration, so the untrusted-closure fallback does not fire for a Python definition in normal operation. This asymmetry is the reason the user-facing docs describe the undeclared-`watch` case as a fallback without attributing it to `dependencies_complete`. ## The generator-to-artifact cascade diff --git a/docs/docs/artifacts/overview.mdx b/docs/docs/artifacts/overview.mdx index 656064d722e..a59d0fe5d4a 100644 --- a/docs/docs/artifacts/overview.mdx +++ b/docs/docs/artifacts/overview.mdx @@ -89,7 +89,7 @@ An artifact is the cached output of a Transformation, so Infrahub regenerates it - **The target's data changes** — a node read by the artifact's GraphQL query is modified, so that target's artifact is regenerated. - **A new target joins the group** — an artifact is generated for the new member; existing artifacts are left untouched. - **A target leaves the group** — the former member's artifact is deleted on the next generation pass over the definition (for example after the change merges), so no stale artifact is left behind. -- **The definition's code or configuration changes** — when a proposed change commits to a linked repository, Infrahub regenerates an artifact only if the change touches that definition's GraphQL query, its Transformation's [dependency closure](../transformations/overview#dependency-tracking-and-regeneration), or the artifact definition itself. An unrelated commit — a README edit, or a helper no Transformation uses — regenerates nothing. +- **The definition's code or configuration changes** — when a proposed change commits to a linked repository, Infrahub regenerates an artifact only if the change touches that definition's GraphQL query, its Transformation's [dependency closure](../transformations/overview#dependency-tracking-and-regeneration), or the artifact definition itself. An unrelated commit — a README edit, or a helper no Transformation uses — regenerates nothing, once the Transformation is precise enough to say so: a Python Transformation that has not declared [`watch`](../git-integration/infrahub-yml#declaring-extra-dependencies-with-watch) regenerates on every commit, because Infrahub cannot rule out a dependency it has no way to see. Every regeneration decision during a proposed change is recorded in the pipeline's task log, naming the file, query, or field that triggered it. See [Understanding artifact regeneration](../proposed-changes/overview#understanding-artifact-regeneration). diff --git a/docs/docs/git-integration/infrahub-yml.mdx b/docs/docs/git-integration/infrahub-yml.mdx index 23127db005f..bad083b3e07 100644 --- a/docs/docs/git-integration/infrahub-yml.mdx +++ b/docs/docs/git-integration/infrahub-yml.mdx @@ -86,9 +86,9 @@ This dependency management ensures that all resources have what they need when t ### Declaring extra dependencies with `watch` -Infrahub automatically detects the files a Transformation or Generator reads: a Python Transformation's or a Generator's package directory, and the templates a Jinja2 Transformation statically includes, imports, or extends. When a file in that detected set changes in a proposed change, only the artifacts of the affected Transformation regenerate, or the instances of the affected Generator re-run. +Infrahub automatically detects the files a Transformation or Generator reads: a Python Transformation's or a Generator's own source file, and the templates a Jinja2 Transformation statically includes, imports, or extends. When a file in that detected set changes in a proposed change, only the artifacts of the affected Transformation regenerate, or the instances of the affected Generator re-run. A file that merely sits in the same directory as a Python source is not a detected dependency of it. -Some dependencies cannot be detected automatically, such as a template pulled in through a dynamic `{% include some_variable %}` or a helper module imported at runtime from another package. Declare these with the optional `watch` key on a `jinja2_transforms`, `python_transforms`, or `generator_definitions` entry. Watched files are added to the definition's auto-detected dependencies; they never replace them. +Some dependencies cannot be detected automatically, such as a template pulled in through a dynamic `{% include some_variable %}`, or any module a Python Transformation or Generator imports - Infrahub does not analyze imports. Declare these with the optional `watch` key on a `jinja2_transforms`, `python_transforms`, or `generator_definitions` entry. Watched files are added to the definition's auto-detected dependencies; they never replace them. `watch` is a strict object. Today it accepts a single key, `files`: @@ -113,7 +113,7 @@ jinja2_transforms: - templates/partials/ ``` -Python example, where a Transformation imports helpers from a sibling top-level package: +Python example, where a Transformation imports helpers from a sibling top-level package and from a module next to it: ```yaml python_transforms: @@ -124,6 +124,7 @@ python_transforms: files: - utils/ - shared/helpers.py + - transforms/device_name_helpers.py ``` Generator example, where a Generator imports helpers from a sibling top-level package. The `watch` key behaves identically on a `generator_definitions` entry: @@ -141,7 +142,24 @@ generator_definitions: - common/constants.py ``` -Declaring any `watch.files` entry also marks the definition's dependency closure as complete, which suppresses the conservative regenerate-on-any-change fallback that an incomplete auto-detection would otherwise trigger. See [the proposed changes overview](../proposed-changes/overview#understanding-artifact-regeneration) for what an incomplete closure means and how to resolve it. +Declaring any `watch.files` entry also marks the definition's dependency closure as complete, which suppresses the conservative regenerate-on-any-change fallback that an incomplete Jinja2 auto-detection would otherwise trigger. On a Python Transformation or Generator, where auto-detection has no references to fail on, the declaration earns its precision the way described in the next section instead. See [the proposed changes overview](../proposed-changes/overview#understanding-artifact-regeneration) for what an incomplete closure means and how to resolve it. + +#### Declaring `watch` with no extra files + +On a Python Transformation or a Generator, declare `watch` with an empty list when the source depends on nothing but its own file: + +```yaml +python_transforms: + - name: DeviceNameAttribute + class_name: DeviceNameAttribute + file_path: transforms/device_name_attribute.py + watch: + files: [] +``` + +Because imports are never analyzed, Infrahub cannot distinguish a Python source with no further dependencies from one whose dependencies it has no way to see. Until `watch` is present it assumes the latter, and ties the definition's fingerprint to the repository's current commit, so every commit is treated as a possible change to it. The empty list is your statement that the auto-detected closure already names everything, which is what unties the fingerprint from the commit and makes regeneration precise. The key has to carry the `files` list: a bare `watch:` with nothing under it reads as no declaration at all. + +A Jinja2 Transformation needs no such declaration. Its detection parses the template and follows every reference it declares, and any reference it cannot follow already marks the closure incomplete, so a complete Jinja2 closure is trusted on its own. ## Group targeting diff --git a/docs/docs/proposed-changes/overview.mdx b/docs/docs/proposed-changes/overview.mdx index 5e7f7142c05..e7213bc3885 100644 --- a/docs/docs/proposed-changes/overview.mdx +++ b/docs/docs/proposed-changes/overview.mdx @@ -72,7 +72,7 @@ Learn more about [Transformations](../transformations/overview) and [Artifacts]( ### Understanding artifact regeneration -When a proposed change includes commits to a linked Git repository, Infrahub regenerates only the artifacts whose Transformation actually depends on a changed file. Each Transformation's dependency closure - the templates it includes for a Jinja2 Transformation, or the files in its package directory for a Python Transformation - is computed when the repository is imported and stored on the Transformation. A file change regenerates a definition's artifacts only when the changed file is in that definition's closure. A change to a GraphQL query the definition uses, or to the artifact definition itself, also triggers regeneration. +When a proposed change includes commits to a linked Git repository, Infrahub regenerates only the artifacts whose Transformation actually depends on a changed file. Each Transformation's dependency closure - the templates it includes for a Jinja2 Transformation, or its own source file for a Python Transformation - is computed when the repository is imported and stored on the Transformation. A file change regenerates a definition's artifacts only when the changed file is in that definition's closure. A change to a GraphQL query the definition uses, or to the artifact definition itself, also triggers regeneration. Two repository-level rules also apply: @@ -89,20 +89,21 @@ Definition device-config (17e9c3b2-...): file templates/partials/header.j2 chang When nothing regenerated for a definition the log says so as well, so you can confirm a quiet pipeline was intentional rather than a missed dependency. -#### When a Transformation's dependencies are incomplete +#### When a Transformation regenerates on every change -Infrahub cannot always detect every file a Transformation depends on. A Jinja2 template that pulls in a partial through a runtime variable (`{% include some_variable %}`), or a Python Transformation that imports a helper at runtime, leaves the auto-detected closure **incomplete**. Infrahub records this as `dependencies_complete = false` on the Transformation. +Two situations leave Infrahub unable to attribute a change, so it falls back to regenerating a Transformation's artifacts on any commit to the repository: -While a Transformation's closure is incomplete, Infrahub cannot tell which file changes are relevant, so it conservatively regenerates that Transformation's artifacts on **any** file change in the repository: safe, but the noisy behavior the precise gate is meant to avoid. The task log explains each such fallback and names the Transformation, and the repository import log records every reference auto-detection could not resolve, naming the template and the location of the unresolved include. +- **A Jinja2 template reference it cannot follow**, such as a partial pulled in through a runtime variable (`{% include some_variable %}`). Auto-detection records the closure as incomplete (`dependencies_complete = false` on the Transformation), and the repository import log names the template and the location of the unresolved reference. +- **A Python Transformation with no `watch` declaration.** Its imports are never analyzed, so with nothing declared there is no basis for ruling any file out. -To restore precise regeneration, do one of the following, then commit so the closure recomputes on the next import: +The task log explains each fallback and names the Transformation, so a broad regeneration can be told apart from a precise one. To get precise regeneration back, do one of the following, then commit so the closure recomputes on the next import: - **Rewrite the dynamic reference with a literal name** so auto-detection can follow it, for example replacing `{% include partial_name %}` with `{% include "partials/header.j2" %}`. -- **Declare the missing files with `watch.files`** on the Transformation entry in `.infrahub.yml`. Listing the files, or the directory that contains them, both adds them to the closure and marks the closure complete. See [declaring extra dependencies](../git-integration/infrahub-yml#declaring-extra-dependencies-with-watch). +- **Declare the dependencies with `watch.files`** on the Transformation entry in `.infrahub.yml`, listing the files or the directory that contains them. On a Python Transformation, declare the key even with an empty `files` list when there is nothing to add. See [declaring extra dependencies](../git-integration/infrahub-yml#declaring-extra-dependencies-with-watch). ### Generator integration -Infrahub automatically runs Generators as part of the proposed change workflow, and applies the same precise regeneration that artifacts use. Each Generator's dependency closure - the files in its package directory, plus any extra paths you declare with `watch.files` - is computed when the repository is imported and stored on the Generator definition. A file change re-runs a Generator's instances only when the changed file is in that Generator's closure. A change to the GraphQL query the Generator uses, or to the Generator definition itself, also triggers a re-run, and a data change on a node the query reads continues to run the Generator exactly as before. +Infrahub automatically runs Generators as part of the proposed change workflow, and applies the same precise regeneration that artifacts use. Each Generator's dependency closure - its own source file, plus any extra paths you declare with `watch.files` - is computed when the repository is imported and stored on the Generator definition. A file change re-runs a Generator's instances only when the changed file is in that Generator's closure. A change to the GraphQL query the Generator uses, or to the Generator definition itself, also triggers a re-run, and a data change on a node the query reads continues to run the Generator exactly as before. The two repository-level rules above apply to Generators as well: editing a repository's `.infrahub.yml` re-runs every Generator in that repository, and read-only repositories participate on the same terms - bumping a `CoreReadOnlyRepository` to a new pinned commit re-runs the Generators affected by that commit, even on branches where `sync_with_git` is disabled. @@ -112,7 +113,7 @@ The why trail covers Generators too. Each run or skip decision is recorded in th Definition device-tags (17e9c3b2-...): file generators/tags/tags.py changed and is in this generator source's dependency closure - all instances will run. ``` -Generators are Python-only, so auto-detection cannot follow a helper imported from a sibling top-level package. When a Generator depends on files outside its own package directory, declare them with `watch.files` on the Generator entry in `.infrahub.yml`, exactly as you would for a Python Transformation. See [declaring extra dependencies](../git-integration/infrahub-yml#declaring-extra-dependencies-with-watch). A Generator imported before precise triggering shipped has no stored closure and falls back to re-running on any file change with no error; it self-heals to precise triggering the next time the repository is imported. +Generators are Python-only, and Infrahub does not analyze imports, so auto-detection never follows a helper module - whether it sits beside the Generator or in another package. Declare every such file with `watch.files` on the Generator entry in `.infrahub.yml`, and declare the key with an empty `files` list when there is nothing to add, exactly as you would for a Python Transformation. See [declaring extra dependencies](../git-integration/infrahub-yml#declaring-extra-dependencies-with-watch). A Generator imported before precise triggering shipped has no stored closure and falls back to re-running on any file change with no error; it self-heals to precise triggering the next time the repository is imported. Learn more about [Generators](../generators/overview). diff --git a/docs/docs/transformations/overview.mdx b/docs/docs/transformations/overview.mdx index a5ce3e268f3..f5c95b46048 100644 --- a/docs/docs/transformations/overview.mdx +++ b/docs/docs/transformations/overview.mdx @@ -113,9 +113,9 @@ When a Transformation feeds an [artifact](../artifacts/overview), Infrahub regen Infrahub builds the closure differently per language: - **Jinja2**: the template itself, plus every template it reaches through static `{% include %}`, `{% import %}`, or `{% extends %}`, resolved transitively. See [Write a Jinja2 Transformation](./jinja2#dependency-tracking). -- **Python**: every Git-tracked file in the directory that contains the transform's `file_path`. See [Write a Python Transformation](./python#dependency-tracking). +- **Python**: the file named by the transform's `file_path`. Imports are not analyzed, so helper modules are declared rather than detected. See [Write a Python Transformation](./python#dependency-tracking). -A change to a file outside a Transformation's closure no longer regenerates its artifacts. When automatic detection can't follow a dependency — a template name resolved at runtime, or a module imported from another package — declare the extra files with the [`watch` key](../git-integration/infrahub-yml#declaring-extra-dependencies-with-watch) in `.infrahub.yml`. +A change to a file outside a Transformation's closure no longer regenerates its artifacts. When automatic detection can't follow a dependency - a template name resolved at runtime, or any module a Python Transformation imports - declare the extra files with the [`watch` key](../git-integration/infrahub-yml#declaring-extra-dependencies-with-watch) in `.infrahub.yml`. A Python Transformation or Generator should declare `watch` even when it needs no extra files, using an empty `files` list, so that Infrahub can stop treating every commit as a possible change to it. For how these decisions surface during a proposed change, see [Understanding artifact regeneration](../proposed-changes/overview#understanding-artifact-regeneration). diff --git a/docs/docs/transformations/python.mdx b/docs/docs/transformations/python.mdx index 7dcb6c6cfd3..6c98fce96f4 100644 --- a/docs/docs/transformations/python.mdx +++ b/docs/docs/transformations/python.mdx @@ -113,12 +113,32 @@ To cache transform output and tie it to a specific target object, define an arti ## Dependency tracking -When a Python Transformation feeds an artifact, Infrahub regenerates that artifact only when a file the transform depends on changes. On import, Infrahub records the transform's dependency closure as every Git-tracked file in the directory that contains its `file_path`, including subdirectories — so a transform in `transforms/` picks up its sibling modules and any package nested under that directory. `.pyc` files, `__pycache__/`, and Git-ignored files are excluded. A change to any file in that directory regenerates the artifacts of definitions that use this Transformation. +When a Python Transformation feeds an artifact, Infrahub regenerates that artifact only when a file it depends on changes. On import, Infrahub records the Transformation's dependency closure as the file named by its `file_path`, and nothing else. Files that merely sit in the same directory are not part of the closure, so several unrelated Transformations can share a directory without each one regenerating whenever any of them is edited. -This is a directory-level heuristic, not import analysis, so two cases need attention: +Infrahub does not analyze your imports, so anything a Transformation depends on beyond its own file has to be declared with the [`watch` key](../git-integration/infrahub-yml#declaring-extra-dependencies-with-watch) in `.infrahub.yml` - a helper module sitting beside it just as much as one in another package such as `shared/`. A `watch` entry naming a directory watches every Git-tracked file beneath it, so a Transformation with several helper modules alongside it can declare its own directory in one line: -- **A transform at the repository root** has no containing directory to scope to, so its closure collapses to the single file. Keep the transform in a subdirectory to get directory-level tracking. -- **Imports from another top-level package** — a helper in `shared/` that a transform under `transforms/` imports — fall outside the directory and aren't detected. A change to such a helper won't regenerate the artifacts unless you declare it with the [`watch` key](../git-integration/infrahub-yml#declaring-extra-dependencies-with-watch) in `.infrahub.yml`. +```yaml +python_transforms: + - name: OCInterfaces + class_name: OCInterfaces + file_path: transforms/openconfig.py + watch: + files: + - transforms/ +``` + +**Declare `watch` even when the Transformation depends on nothing but its own file**, using an empty list: + +```yaml +python_transforms: + - name: OCInterfaces + class_name: OCInterfaces + file_path: transforms/openconfig.py + watch: + files: [] +``` + +Because imports are never analyzed, Infrahub cannot tell a Transformation with no further dependencies from one whose dependencies it simply has no way to see. Until the `watch` key is present, it assumes the latter and ties the Transformation's fingerprint to the repository's current commit, so every commit regenerates its artifacts. Declaring `watch` - whether it lists files or not - is your statement that the closure already names everything, which is what unties the fingerprint from the commit and makes regeneration precise. Note that the key has to carry the `files` list: a bare `watch:` with nothing under it reads as no declaration at all. See [Understanding artifact regeneration](../proposed-changes/overview#understanding-artifact-regeneration) for how to read which change triggered a regeneration. From 353a3867b6eff89523408d7244b36b59f040bcdc Mon Sep 17 00:00:00 2001 From: Pol Michel <40861490+polmichel@users.noreply.github.com> Date: Thu, 13 Aug 2026 15:40:25 +0200 Subject: [PATCH 46/48] fix: validate preference timezone at write and correct the UI source hint (closes #10174) (#10198) * test: add failing test for 10174-timezone-preference-validation Co-Authored-By: Claude Opus 4.8 (1M context) * fix: validate preference timezone at write and correct the UI source hint Reject a non-IANA timezone when setting a user or global preference, so an unusable zone can no longer be persisted through the API/SDK. Validation runs at the write path only (resolving the value against the runtime zone database); the model stays lenient so a bad value stored earlier still reads back, and an empty value normalizes to unset so the write reply agrees with later reads. Correct the preferences screen so it no longer claims a stored zone is in effect when the viewer's browser cannot render it; the hint now reports the browser fallback instead. Co-Authored-By: Claude Opus 4.8 (1M context) * fix: reject implementation-defined timezone keys at write A full system zone tree resolves entries like localtime, posix/*, and right/* that browsers reject and that no client should store as a preference. Reject them explicitly before construction so the guarantee holds regardless of which zone database the runtime ships (verified: the slim runtime resolves localtime). Co-Authored-By: Claude Opus 4.8 (1M context) * test: lock in construction-based acceptance of a canonical offset zone Guards against a future regression to an enumerated allowlist, which would reject a resolvable zone the runtime can apply. Co-Authored-By: Claude Opus 4.8 (1M context) * test: cover the GLOBAL-source unrenderable timezone hint The corrected hint fires for an org-default zone the browser cannot render, not only a user override. Co-Authored-By: Claude Opus 4.8 (1M context) * fix: reject the posixrules pseudo-zone at write posixrules resolves in the runtime zone tree but is not a browser-renderable IANA zone; add it to the rejected keys alongside localtime and posix/right. Co-Authored-By: Claude Opus 4.8 (1M context) * test: assert the exact error message when rejecting a non-IANA timezone Co-Authored-By: Claude Opus 4.8 (1M context) * refactor: rename the timezone preference validator to validate_timezone The function validates by construction and returns the value unchanged, so the name now reflects that it validates rather than normalizes. Trims the docstring and notes the accepted set is runtime-dependent, best confirmed live. Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: Claude Opus 4.8 (1M context) --- .../infrahub/core/preferences/validation.py | 36 ++++++++++++ .../infrahub/graphql/mutations/preferences.py | 4 ++ .../graphql/mutations/test_preferences.py | 24 ++++++++ .../unit/core/test_preference_validation.py | 56 ++++++++++++++++++ dev/knowledge/backend/preferences.md | 10 +++- .../preferences/ui/preference-fields.tsx | 19 ++++-- .../ui/user-preferences-card.test.tsx | 58 +++++++++++++++++++ frontend/app/src/shared/utils/date.ts | 2 +- 8 files changed, 203 insertions(+), 6 deletions(-) create mode 100644 backend/infrahub/core/preferences/validation.py create mode 100644 backend/tests/unit/core/test_preference_validation.py diff --git a/backend/infrahub/core/preferences/validation.py b/backend/infrahub/core/preferences/validation.py new file mode 100644 index 00000000000..d0e711b579a --- /dev/null +++ b/backend/infrahub/core/preferences/validation.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +from zoneinfo import ZoneInfo, ZoneInfoNotFoundError + +from infrahub.exceptions import ValidationError + +# Implementation-defined entries a full zone tree resolves but browsers reject; listed explicitly +# so they are refused regardless of which zone tree the runtime ships. +_REJECTED_KEYS = frozenset({"localtime", "posixrules"}) +_REJECTED_PREFIXES = ("posix/", "right/") + + +def validate_timezone(value: str | None) -> str | None: + """Validate a timezone for storage; empty becomes None, a valid value is returned unchanged. + + A non-empty value must resolve against the runtime's zone database and not be an + implementation-defined key. It is checked by construction rather than an allowlist, so + backward-compatibility aliases are accepted and stored as given. The accepted set follows the + interpreter's own tzdata, so confirm new accept/reject behavior against a running instance, + not by unit test alone. + + Raises: + ValidationError: the value is non-empty but is not a storable IANA timezone. + + """ + if not value: + return None + if value in _REJECTED_KEYS or value.startswith(_REJECTED_PREFIXES): + raise ValidationError(input_value=f"'{value}' is not a valid IANA timezone") + try: + # OSError covers keys the resolver rejects at the filesystem layer (e.g. an over-long name); + # ValueError covers malformed keys; ZoneInfoNotFoundError covers well-formed-but-absent ones. + ZoneInfo(value) + except (ZoneInfoNotFoundError, ValueError, OSError) as exc: + raise ValidationError(input_value=f"'{value}' is not a valid IANA timezone") from exc + return value diff --git a/backend/infrahub/graphql/mutations/preferences.py b/backend/infrahub/graphql/mutations/preferences.py index 703e80c931c..dbe33f8849c 100644 --- a/backend/infrahub/graphql/mutations/preferences.py +++ b/backend/infrahub/graphql/mutations/preferences.py @@ -12,6 +12,7 @@ from infrahub.core.preferences.models import Preference from infrahub.core.preferences.permissions import MANAGE_GLOBAL_PREFERENCES_PERMISSION from infrahub.core.preferences.repository import PreferenceRepository +from infrahub.core.preferences.validation import validate_timezone from infrahub.database import retry_db_transaction from infrahub.graphql.types.preferences import DateFormat, PreferenceWriteScope, PreferenceWriteScopeType @@ -77,6 +78,9 @@ async def mutate( else: owner_id = account_id + if timezone is not _UNSET: + timezone = validate_timezone(timezone) + return await cls._set( graphql_context, owner_id=owner_id, actor_id=account_id, date_format=date_format, timezone=timezone ) diff --git a/backend/tests/component/graphql/mutations/test_preferences.py b/backend/tests/component/graphql/mutations/test_preferences.py index 386b14c1c3e..3110f65fe77 100644 --- a/backend/tests/component/graphql/mutations/test_preferences.py +++ b/backend/tests/component/graphql/mutations/test_preferences.py @@ -189,6 +189,30 @@ async def test_user_rejects_unknown_date_format( assert await PreferenceRepository(db=db).get_for_owner(owner_id=first_account.id) is None +async def test_user_rejects_non_iana_timezone( + db: InfrahubDatabase, + default_branch: Branch, + register_core_models_schema: None, + first_account: Node, + session_first_account: AccountSession, +) -> None: + """Reject a timezone that is not a valid IANA name, and persist nothing. + + A timezone must be an IANA zone the runtime can resolve; a string that names no zone is + rejected at the write path and no Preference row is created. + """ + result = await run_mutation( + db=db, + branch=default_branch, + account_session=session_first_account, + variables={"scope": "USER", "timezone": "Not/AZone"}, + ) + assert result.errors is not None + assert len(result.errors) == 1 + assert result.errors[0].message == "'Not/AZone' is not a valid IANA timezone" + assert await PreferenceRepository(db=db).get_for_owner(owner_id=first_account.id) is None + + # -------------------------------------------------------------------------------------------- # scope=GLOBAL — gated on manage_global_preferences; nothing written when denied. # -------------------------------------------------------------------------------------------- diff --git a/backend/tests/unit/core/test_preference_validation.py b/backend/tests/unit/core/test_preference_validation.py new file mode 100644 index 00000000000..ad49e908aa0 --- /dev/null +++ b/backend/tests/unit/core/test_preference_validation.py @@ -0,0 +1,56 @@ +from __future__ import annotations + +import re +from dataclasses import dataclass + +import pytest + +from infrahub.core.preferences.validation import validate_timezone +from infrahub.exceptions import ValidationError + + +@dataclass +class NormalizeCase: + name: str + value: str | None + expected: str | None + + +NORMALIZE_CASES = [ + NormalizeCase(name="iana_region_city", value="Asia/Tokyo", expected="Asia/Tokyo"), + NormalizeCase(name="iana_europe", value="Europe/Paris", expected="Europe/Paris"), + NormalizeCase(name="utc", value="UTC", expected="UTC"), + NormalizeCase(name="etc_offset_zone", value="Etc/GMT+5", expected="Etc/GMT+5"), + NormalizeCase(name="empty_string_is_unset", value="", expected=None), + NormalizeCase(name="none_is_unset", value=None, expected=None), +] + + +@pytest.mark.parametrize("case", NORMALIZE_CASES, ids=lambda case: case.name) +def test_validate_timezone_accepts_and_normalizes(case: NormalizeCase) -> None: + assert validate_timezone(case.value) == case.expected + + +@dataclass +class RejectCase: + name: str + value: str + + +REJECT_CASES = [ + RejectCase(name="unknown_zone", value="Not/AZone"), + RejectCase(name="offset_is_not_a_zone", value="UTC+25"), + RejectCase(name="injection_string", value="'; DROP TABLE--"), + RejectCase(name="overlong_string", value="A" * 300), + RejectCase(name="localtime_pseudo_zone", value="localtime"), + RejectCase(name="posixrules_pseudo_zone", value="posixrules"), + RejectCase(name="posix_implementation_key", value="posix/UTC"), + RejectCase(name="right_implementation_key", value="right/UTC"), +] + + +@pytest.mark.parametrize("case", REJECT_CASES, ids=lambda case: case.name) +def test_validate_timezone_rejects_non_iana(case: RejectCase) -> None: + expected = f"'{case.value}' is not a valid IANA timezone" + with pytest.raises(ValidationError, match=rf"^{re.escape(expected)}$"): + validate_timezone(case.value) diff --git a/dev/knowledge/backend/preferences.md b/dev/knowledge/backend/preferences.md index 3e9324d239e..409aca8ae5b 100644 --- a/dev/knowledge/backend/preferences.md +++ b/dev/knowledge/backend/preferences.md @@ -27,11 +27,19 @@ Fields: |-------|------|-------| | `owner_id` | `str` | plain string, not a graph relationship | | `date_format` | `DateFormat` enum, nullable | a semantic key (e.g. `ISO_DATETIME`), not a render pattern; each client maps the key to its own formatter | -| `timezone` | `str` (IANA name), nullable | currently accepts any string; no backend validation | +| `timezone` | `str` (IANA name), nullable | validated at write, not on the model (see below) | `date_format` is enum-typed, so an unknown key is rejected at construction, including when a row is loaded from the database. It round-trips as a plain string because the enum subclasses `str`. +`timezone` is validated on the **write path only**, never on the model. A model-level validator +would run on every read (rows are reconstructed from the database), and since user and global rows +are fetched together, one bad stored value would break effective-preference reads for every user. +The set mutation validates a provided timezone by resolving it against the runtime's zone database +(construction, not an enumerated allowlist — so backward-compatibility aliases a client offers are +accepted) and normalizes an empty value to unset so the write reply agrees with later reads. Reads +stay lenient, so any value stored before this validation existed is still returned as-is. + ## Reads never create a row Every read path treats a missing row as "nothing set". A read never writes. The write path (the set diff --git a/frontend/app/src/entities/preferences/ui/preference-fields.tsx b/frontend/app/src/entities/preferences/ui/preference-fields.tsx index 30b3ba16821..3f69b7f6ae2 100644 --- a/frontend/app/src/entities/preferences/ui/preference-fields.tsx +++ b/frontend/app/src/entities/preferences/ui/preference-fields.tsx @@ -10,6 +10,7 @@ import type { FormAttributeValue } from "@/shared/components/form/type"; import { Combobox, type ComboboxItem } from "@/shared/components/inputs/combobox"; import { FormField } from "@/shared/components/ui/form"; import { formatWithPreferences } from "@/shared/context/date-preferences-context"; +import { supportedTimezone } from "@/shared/utils/date"; import type { Preference } from "@/entities/preferences/domain/model/preference"; import { @@ -138,15 +139,25 @@ export function DateFormatField({ ); } +/** Resolves the (i) hint for a timezone, correcting the source claim when this browser can't apply it. + * A resolved zone this runtime cannot render is silently displayed in the browser's own zone, so the + * hint must report that fallback rather than claim the stored zone is in effect. */ +function timezoneSourceMessage(preference: Preference, browserZone: string): string { + if (preference.value && !supportedTimezone(preference.value)) { + return `This browser can't display ${preference.value}; times are shown in ${browserZone}.`; + } + return sourceMessage(preference, { + formatGlobalValue: (value) => value, + browserValue: browserZone, + }); +} + export function TimezoneField({ preference, emptyValueLabel = EMPTY_VALUE_LABEL, }: PreferenceFieldProps) { const message = preference - ? sourceMessage(preference, { - formatGlobalValue: (value) => value, - browserValue: Intl.DateTimeFormat().resolvedOptions().timeZone, - }) + ? timezoneSourceMessage(preference, Intl.DateTimeFormat().resolvedOptions().timeZone) : null; return ( diff --git a/frontend/app/src/entities/preferences/ui/user-preferences-card.test.tsx b/frontend/app/src/entities/preferences/ui/user-preferences-card.test.tsx index 2d8c13ab830..6b4ca6496f2 100644 --- a/frontend/app/src/entities/preferences/ui/user-preferences-card.test.tsx +++ b/frontend/app/src/entities/preferences/ui/user-preferences-card.test.tsx @@ -271,6 +271,64 @@ describe("UserPreferencesCard", () => { await initPointerTracking(component.locator); }); + test("the (i) tooltip reports the browser fallback when the stored zone can't be rendered here", async () => { + vi.mocked(getEffectivePreferences).mockResolvedValue({ + ...baseEffective, + timezone: { value: "Not/AZone", source: "USER" }, + }); + + const component = await render(); + + await expect.element(component.getByRole("button", { name: /timezone/i })).toBeVisible(); + + const browserZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + + // The timezone field is the second info trigger. + const triggers = component.getByRole("button", { name: "Where this value comes from" }); + await initPointerTracking(component.locator); + await triggers.nth(1).hover(); + + await expect + .element( + component.getByRole("tooltip", { + name: `This browser can't display Not/AZone; times are shown in ${browserZone}.`, + }) + ) + .toBeVisible(); + + // Park the pointer away from the trigger so the tooltip closes before the next test renders. + await initPointerTracking(component.locator); + }); + + test("the (i) tooltip reports the browser fallback for an unrenderable organisation-default zone", async () => { + vi.mocked(getEffectivePreferences).mockResolvedValue({ + ...baseEffective, + timezone: { value: "Not/AZone", source: "GLOBAL" }, + }); + + const component = await render(); + + await expect.element(component.getByRole("button", { name: /timezone/i })).toBeVisible(); + + const browserZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + + // The timezone field is the second info trigger. + const triggers = component.getByRole("button", { name: "Where this value comes from" }); + await initPointerTracking(component.locator); + await triggers.nth(1).hover(); + + await expect + .element( + component.getByRole("tooltip", { + name: `This browser can't display Not/AZone; times are shown in ${browserZone}.`, + }) + ) + .toBeVisible(); + + // Park the pointer away from the trigger so the tooltip closes before the next test renders. + await initPointerTracking(component.locator); + }); + test("the (i) tooltip falls back to the browser source when neither user nor global is set", async () => { vi.mocked(getEffectivePreferences).mockResolvedValue({ ...baseEffective, diff --git a/frontend/app/src/shared/utils/date.ts b/frontend/app/src/shared/utils/date.ts index 915c244a99b..744fecfcc04 100644 --- a/frontend/app/src/shared/utils/date.ts +++ b/frontend/app/src/shared/utils/date.ts @@ -15,7 +15,7 @@ export interface FormatDateOptions { // A zone valid on the preference setter's machine may be absent on the viewer's browser; // `@date-fns/tz` builds a `TZDate` for an unknown zone without complaint and only throws lazily // on use, so we validate up front with `Intl`, which rejects an unknown zone synchronously. -function supportedTimezone(timezone?: string | null): string | undefined { +export function supportedTimezone(timezone?: string | null): string | undefined { if (!timezone) { return; } From a95933f115521e205b33a19316aac6d4400cce9d Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Thu, 13 Aug 2026 17:53:26 +0200 Subject: [PATCH 47/48] fix(core): reserve headroom below the Prefect related-resources maximum [IFC-3008] (#10242) * fix(core): reserve headroom below the Prefect related-resources maximum [IFC-3008] Node and group mutation events truncated their related resources to exactly the configured Prefect maximum. Prefect's events worker then extends that list in place with run-context resources - flow run, task run, flow, deployment, work queue, work pool and one per flow-run tag - which skips the client-side validation, so the enlarged event arrives above the maximum. The Prefect API answers by closing the /events/in websocket rather than by dropping the single event. Both call sites now truncate to get_related_resource_budget(), which reserves a tenth of the maximum with a floor of 20. get_prefect_max_related_resources() keeps returning the raw maximum and stays the base for the submission chunk size, which is a different constraint. * fix(core): tie the run-context headroom to a single declared worst case [IFC-3008] The headroom floor and the worst-case append size the tests exercised were two independent numbers in two files, so raising one would leave the other behind and silently reinstate the overflow. MAX_RUN_CONTEXT_RESOURCES now declares the append size in one place, the reservation floor derives from it and the tests import it. The survival check also only ran at a maximum where the proportional reservation binds, leaving the floor untested. It now spans both sides of the reservation. Also name the task run in the changelog, matching the docstring. --- backend/infrahub/events/group_action.py | 18 ++--- backend/infrahub/events/limits.py | 21 +++++ backend/infrahub/events/node_action.py | 18 ++--- backend/tests/unit/event/test_group_action.py | 14 ++-- backend/tests/unit/event/test_limits.py | 76 ++++++++++++++++++- backend/tests/unit/event/test_node_action.py | 6 +- changelog/10241.fixed.md | 1 + dev/knowledge/backend/events.md | 13 +++- 8 files changed, 136 insertions(+), 31 deletions(-) create mode 100644 changelog/10241.fixed.md diff --git a/backend/infrahub/events/group_action.py b/backend/infrahub/events/group_action.py index 32aedd6d76e..8c997e7e78e 100644 --- a/backend/infrahub/events/group_action.py +++ b/backend/infrahub/events/group_action.py @@ -8,7 +8,7 @@ from infrahub.log import get_logger from .constants import EVENT_NAMESPACE -from .limits import get_prefect_max_related_resources +from .limits import get_related_resource_budget from .models import EventNode, InfrahubEvent log = get_logger() @@ -41,9 +41,9 @@ def get_related(self) -> list[dict[str, str]]: ) # Members and ancestors grow with the size of the mutation, so they come - # last and the list is capped: the Prefect API rejects any event whose - # related resources exceed the configured maximum, and an oversized event - # would never be recorded at all. Each member and ancestor is a single + # last and the list is capped: an event carrying more related resources than + # the Prefect API accepts is rejected outright and never recorded. + # Each member and ancestor is a single # entry (also matched as a related node through its own role), so a plain # ordered truncation keeps the fixed and group-scoped entries intact. for member in self.members: @@ -64,17 +64,17 @@ def get_related(self) -> list[dict[str, str]]: } ) - max_related = get_prefect_max_related_resources() - if len(related) > max_related: + budget = get_related_resource_budget() + if len(related) > budget: log.warning( - "Truncating the related resources of a group mutation event to the Prefect maximum", + "Truncating the related resources of a group mutation event to the Prefect budget", event_name=self.event_name, kind=self.kind, node_id=self.node_id, related_resources=len(related), - maximum=max_related, + budget=budget, ) - related = related[:max_related] + related = related[:budget] return related diff --git a/backend/infrahub/events/limits.py b/backend/infrahub/events/limits.py index 1035bb4122e..15090ea757b 100644 --- a/backend/infrahub/events/limits.py +++ b/backend/infrahub/events/limits.py @@ -2,6 +2,12 @@ _DEFAULT_MAX_RELATED_RESOURCES = 500 +# Six fixed entries - flow run, task run, flow, deployment, work queue, work pool - plus one per +# flow-run tag. Only tags present when the run was created reach an event: a run refreshes its tags +# once before its context is entered, so anything a flow tags itself with later stays out. Infrahub +# renders four tag kinds today; the rest of the allowance absorbs tags added later. +MAX_RUN_CONTEXT_RESOURCES = 6 + 14 + def get_prefect_max_related_resources() -> int: """Return the maximum number of related resources the Prefect API accepts per event. @@ -19,6 +25,21 @@ def get_prefect_max_related_resources() -> int: return max_related_resources +def get_related_resource_budget() -> int: + """Return the number of related resources an event may still carry when it leaves Infrahub. + + Prefect's events worker appends run-context resources to an event after it has been handed + over, by extending the list in place, which does not re-run the client-side validation. An + event that leaves on the maximum therefore arrives above it, and the Prefect API answers by + closing the event stream rather than by dropping the single event. The budget stays below the + maximum so the enlarged event is still accepted. + + The reservation is a tenth of the maximum, never less than what the append can add. + """ + maximum = get_prefect_max_related_resources() + return max(1, maximum - max(MAX_RUN_CONTEXT_RESOURCES, maximum // 10)) + + def get_submission_chunk_size() -> int: """Return the maximum number of node ids to carry in one recompute submission. diff --git a/backend/infrahub/events/node_action.py b/backend/infrahub/events/node_action.py index fce9708a017..92936337941 100644 --- a/backend/infrahub/events/node_action.py +++ b/backend/infrahub/events/node_action.py @@ -11,7 +11,7 @@ from infrahub.log import get_logger from .constants import EVENT_NAMESPACE, NODE_ORIGIN_LABEL -from .limits import get_prefect_max_related_resources +from .limits import get_related_resource_budget from .models import InfrahubEvent log = get_logger() @@ -68,9 +68,9 @@ def get_related(self) -> list[dict[str, str]]: ) # The remaining entries grow with the number of relationship peers, so they - # come last and the list is capped: the Prefect API rejects any event whose - # related resources exceed the configured maximum, and an oversized event - # would never be recorded at all. Relationship updates are appended before + # come last and the list is capped: an event carrying more related resources + # than the Prefect API accepts is rejected outright and never recorded. + # Relationship updates are appended before # the per-peer related-node entries because automation triggers match on # them, while related-node entries only feed event queries. for relationship in self.changelog.relationships.values(): @@ -116,17 +116,17 @@ def get_related(self) -> list[dict[str, str]]: } ) - max_related = get_prefect_max_related_resources() - if len(related) > max_related: + budget = get_related_resource_budget() + if len(related) > budget: log.warning( - "Truncating the related resources of a node mutation event to the Prefect maximum", + "Truncating the related resources of a node mutation event to the Prefect budget", event_name=self.event_name, kind=self.kind, node_id=self.node_id, related_resources=len(related), - maximum=max_related, + budget=budget, ) - related = related[:max_related] + related = related[:budget] return related diff --git a/backend/tests/unit/event/test_group_action.py b/backend/tests/unit/event/test_group_action.py index 63953afc795..9e8b2b11175 100644 --- a/backend/tests/unit/event/test_group_action.py +++ b/backend/tests/unit/event/test_group_action.py @@ -17,7 +17,7 @@ GroupAutoCreateRejectedEvent, GroupMemberAddedEvent, ) -from infrahub.events.limits import get_prefect_max_related_resources +from infrahub.events.limits import get_related_resource_budget from infrahub.events.models import EventMeta, EventNode from infrahub.external_protocols import ExternalAuthProtocol from infrahub.task_manager.event.models import InfrahubEventFilter @@ -295,20 +295,20 @@ def test_group_member_added_get_related_consolidates_member_and_ancestor_entries def test_group_member_added_related_resources_stay_within_prefect_maximum() -> None: """A member add of any size keeps its event: the related list is capped.""" - max_related = get_prefect_max_related_resources() - members = [EventNode(id=str(uuid4()), kind="TestPerson") for _ in range(max_related + 50)] + budget = get_related_resource_budget() + members = [EventNode(id=str(uuid4()), kind="TestPerson") for _ in range(budget + 50)] event = _make_member_added_event(node_id=str(uuid4()), members=members) related = event.get_related() - assert len(related) == max_related + assert len(related) == budget def test_group_member_added_cap_keeps_fixed_and_group_scoped_entries() -> None: """Truncation drops overflow members, never the fixed or group-scoped entries.""" group_id = str(uuid4()) - max_related = get_prefect_max_related_resources() - members = [EventNode(id=str(uuid4()), kind="TestPerson") for _ in range(max_related + 50)] + budget = get_related_resource_budget() + members = [EventNode(id=str(uuid4()), kind="TestPerson") for _ in range(budget + 50)] event = _make_member_added_event(node_id=group_id, members=members) related = event.get_related() @@ -317,7 +317,7 @@ def test_group_member_added_cap_keeps_fixed_and_group_scoped_entries() -> None: item["prefect.resource.id"] for item in related if item["prefect.resource.role"] == "infrahub.related.node" ] assert related_node_ids == [group_id] - assert len(related) == max_related + assert len(related) == budget def test_related_node_filter_matches_old_and_new_group_event_formats() -> None: diff --git a/backend/tests/unit/event/test_limits.py b/backend/tests/unit/event/test_limits.py index 416d074354b..fff29b9f5cd 100644 --- a/backend/tests/unit/event/test_limits.py +++ b/backend/tests/unit/event/test_limits.py @@ -1,8 +1,15 @@ from dataclasses import dataclass import pytest +from prefect.events.schemas.events import Event, RelatedResource, Resource +from prefect.settings import PREFECT_SERVER_EVENTS_MAXIMUM_RELATED_RESOURCES, temporary_settings -from infrahub.events.limits import get_submission_chunk_size +from infrahub.events.limits import ( + MAX_RUN_CONTEXT_RESOURCES, + get_prefect_max_related_resources, + get_related_resource_budget, + get_submission_chunk_size, +) ENV_VAR = "PREFECT_SERVER_EVENTS_MAXIMUM_RELATED_RESOURCES" @@ -26,3 +33,70 @@ class ChunkSizeCase: def test_submission_chunk_size_is_floored_at_one(case: ChunkSizeCase, monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv(ENV_VAR, case.configured_max) assert get_submission_chunk_size() == case.expected + + +@dataclass +class BudgetCase: + name: str + configured_max: str + expected: int + + +BUDGET_CASES = [ + BudgetCase(name="one_floored_to_one", configured_max="1", expected=1), # headroom exceeds the maximum + BudgetCase(name="twenty_floored_to_one", configured_max="20", expected=1), + BudgetCase(name="hundred_reserves_the_minimum", configured_max="100", expected=80), # 100 // 10 < 20 + BudgetCase(name="default_reserves_a_tenth", configured_max="500", expected=450), + BudgetCase(name="large_reserves_a_tenth", configured_max="5000", expected=4500), +] + + +@pytest.mark.parametrize("case", [pytest.param(case, id=case.name) for case in BUDGET_CASES]) +def test_related_resource_budget_reserves_headroom(case: BudgetCase, monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv(ENV_VAR, case.configured_max) + assert get_related_resource_budget() == case.expected + + +@dataclass +class SurvivalCase: + name: str + configured_max: int + + +SURVIVAL_CASES = [ + SurvivalCase(name="tenth_reservation", configured_max=500), # a tenth exceeds the append + SurvivalCase(name="reservations_meet", configured_max=200), # a tenth equals the append + SurvivalCase(name="floor_reservation", configured_max=100), # the append exceeds a tenth +] + + +@pytest.mark.parametrize("case", [pytest.param(case, id=case.name) for case in SURVIVAL_CASES]) +def test_event_on_the_budget_survives_the_prefect_run_context_append( + case: SurvivalCase, monkeypatch: pytest.MonkeyPatch +) -> None: + """An event emitted on the budget must still be accepted once Prefect has enlarged it. + + Prefect's events worker extends the related list in place, which skips the client-side + validation, so the enlarged event is only ever checked by the Prefect API. Emitting on the + maximum rather than under it therefore produces an event the API refuses. The cases span both + sides of the reservation, so the floor is covered as well as the proportional part. + """ + monkeypatch.setenv(ENV_VAR, str(case.configured_max)) + with temporary_settings({PREFECT_SERVER_EVENTS_MAXIMUM_RELATED_RESOURCES: case.configured_max}): + event = Event( + event="infrahub.node.updated", + resource=Resource({"prefect.resource.id": "infrahub.node.abc"}), + related=[ + RelatedResource( + {"prefect.resource.id": f"infrahub.node.{index}", "prefect.resource.role": "infrahub.related.node"} + ) + for index in range(get_related_resource_budget()) + ], + ) + event.related += [ + RelatedResource({"prefect.resource.id": f"prefect.tag.{index}", "prefect.resource.role": "tag"}) + for index in range(MAX_RUN_CONTEXT_RESOURCES) + ] + + assert len(event.related) <= get_prefect_max_related_resources() + Event.model_validate(event.model_dump()) diff --git a/backend/tests/unit/event/test_node_action.py b/backend/tests/unit/event/test_node_action.py index 1c953da1569..9917148b46a 100644 --- a/backend/tests/unit/event/test_node_action.py +++ b/backend/tests/unit/event/test_node_action.py @@ -11,7 +11,7 @@ RelationshipPeerChangelog, ) from infrahub.core.constants import DiffAction -from infrahub.events.limits import get_prefect_max_related_resources +from infrahub.events.limits import get_prefect_max_related_resources, get_related_resource_budget from infrahub.events.node_action import NodeCreatedEvent from tests.helpers.events import dummy_event_meta @@ -84,7 +84,7 @@ def test_truncation_drops_peer_entries_not_node_scoped_entries() -> None: # Far more peers than remaining slots: the per-peer related-node entries are # all dropped (only the node's own remains) and every remaining slot goes to - # a relationship update, up to the maximum. + # a relationship update, up to the budget. related_node_ids = [ item["prefect.resource.id"] for item in related if item["prefect.resource.role"] == "infrahub.related.node" ] @@ -94,7 +94,7 @@ def test_truncation_drops_peer_entries_not_node_scoped_entries() -> None: item for item in related if item["prefect.resource.role"] == "infrahub.node.relationship_update" ] assert relationship_entries - assert len(related) == get_prefect_max_related_resources() + assert len(related) == get_related_resource_budget() def test_small_changelog_keeps_every_peer_entry() -> None: diff --git a/changelog/10241.fixed.md b/changelog/10241.fixed.md new file mode 100644 index 00000000000..c4143947fe1 --- /dev/null +++ b/changelog/10241.fixed.md @@ -0,0 +1 @@ +Node and group mutation events no longer overflow the Prefect related-resources maximum after Prefect enlarges them. Events were truncated to exactly the configured maximum, and Prefect's events worker then appended its own run-context resources (flow run, task run, flow, deployment, work queue, work pool and one per flow-run tag) to the list in place, which skips the client-side validation. The enlarged event was refused by the Prefect API, which closes the event stream rather than dropping the single event. Events are now truncated to a budget that leaves room for that append. diff --git a/dev/knowledge/backend/events.md b/dev/knowledge/backend/events.md index 85848df7725..de9360b2fce 100644 --- a/dev/knowledge/backend/events.md +++ b/dev/knowledge/backend/events.md @@ -33,17 +33,26 @@ an oversized event is dropped entirely, never recorded. Node mutation events build their related resources in priority order — node-scoped entries first (attribute updates, parent, the node's own related-node entry), then relationship updates (which automation triggers match on), then per-peer -related-node entries — and truncate at that maximum with a warning log. A node +related-node entries — and truncate with a warning log. A node with a very large cardinality-many relationship therefore keeps its event, but not every peer is represented in `related`; the full peer list remains available in the event payload's changelog. +Events truncate to `get_related_resource_budget()`, which sits below that +maximum rather than on it. Prefect's events worker appends run-context +resources — flow run, task run, flow, deployment, work queue, work pool, and +one per flow-run tag — after the event has been handed over, extending the list +in place in a way that skips the client-side validation. An event that leaves +Infrahub on the maximum therefore arrives above it, and the Prefect API answers +by closing the `/events/in` websocket rather than by dropping the single event. +The reserved headroom keeps the enlarged event acceptable. + Group mutation events (`member_added` / `member_removed`) follow the same rule. Each member and each ancestor is a single related resource carrying its own role (`infrahub.group.member` / `infrahub.group.ancestor`) rather than a role-plus-duplicate pair, so the list grows by one per member instead of two. The fixed group-scoped entries come first and members/ancestors come last, so -the same ordered truncation keeps the event within the maximum. Group +the same ordered truncation keeps the event within the budget. Group automations match the primary group resource and read the changed members from the payload, so truncating overflow members only trims the event-query display; the event is always recorded and automations always fire. The event query From e330c247663ee4969d8cd92da94c99210d03212f Mon Sep 17 00:00:00 2001 From: Patrick Ogenstad Date: Thu, 13 Aug 2026 18:01:25 +0200 Subject: [PATCH 48/48] test(events): prove an event on the maximum is rejected after the run-context append [IFC-3008] The related-resource budget was covered only from the positive side: an event built on the budget still validates once Prefect has enlarged it. That passes just as well against a mechanism that never rejects anything, so it does not show the reservation earns its place. Add the matching control. An event built on the maximum instead has no room for the in-place append, lands above the limit and is refused, which is the failure the budget exists to prevent. Both run over the same maximums, so the pair holds on either side of the reservation. --- backend/tests/unit/event/test_limits.py | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/backend/tests/unit/event/test_limits.py b/backend/tests/unit/event/test_limits.py index fff29b9f5cd..7fbddc55308 100644 --- a/backend/tests/unit/event/test_limits.py +++ b/backend/tests/unit/event/test_limits.py @@ -3,6 +3,7 @@ import pytest from prefect.events.schemas.events import Event, RelatedResource, Resource from prefect.settings import PREFECT_SERVER_EVENTS_MAXIMUM_RELATED_RESOURCES, temporary_settings +from pydantic import ValidationError from infrahub.events.limits import ( MAX_RUN_CONTEXT_RESOURCES, @@ -100,3 +101,36 @@ def test_event_on_the_budget_survives_the_prefect_run_context_append( assert len(event.related) <= get_prefect_max_related_resources() Event.model_validate(event.model_dump()) + + +@pytest.mark.parametrize("case", [pytest.param(case, id=case.name) for case in SURVIVAL_CASES]) +def test_event_on_the_maximum_is_rejected_after_the_prefect_run_context_append( + case: SurvivalCase, monkeypatch: pytest.MonkeyPatch +) -> None: + """An event emitted on the maximum is refused once Prefect has enlarged it. + + This is the failure the budget exists to prevent, and the control that proves the reservation + does real work: an event that leaves on the maximum has no room for the in-place run-context + append, so the enlarged event lands above the limit and no longer validates. Building the same + event on the budget instead is what the companion test shows still validates. + """ + monkeypatch.setenv(ENV_VAR, str(case.configured_max)) + with temporary_settings({PREFECT_SERVER_EVENTS_MAXIMUM_RELATED_RESOURCES: case.configured_max}): + event = Event( + event="infrahub.node.updated", + resource=Resource({"prefect.resource.id": "infrahub.node.abc"}), + related=[ + RelatedResource( + {"prefect.resource.id": f"infrahub.node.{index}", "prefect.resource.role": "infrahub.related.node"} + ) + for index in range(get_prefect_max_related_resources()) + ], + ) + event.related += [ + RelatedResource({"prefect.resource.id": f"prefect.tag.{index}", "prefect.resource.role": "tag"}) + for index in range(MAX_RUN_CONTEXT_RESOURCES) + ] + + assert len(event.related) > get_prefect_max_related_resources() + with pytest.raises(ValidationError, match=rf"The maximum number of related resources is {case.configured_max}"): + Event.model_validate(event.model_dump())